![ظاهر برنامه](https://quera.org/qbox/view/hgRMniVxzs/F2.png)
پروژهی اولیه را از [این لینک](/contest/assignments/71326/download_problem_initial_project/243949/) دانلود کنید.
<details class="green">
<summary>
ساختار فایلهای پروژه
</summary>
شما تنها مجاز به انجام تغییرات در فایلهای علامت زده میباشید
```
Zero-Trust-Rooms
├── index.html
├── package.json
├── postcss.config.js
├── server
│ ├── index.js
│ └── package.json
├── src
│ ├── encryption.ts
│ ├── main.ts
│ ├── memory.ts
│ ├── room.ts
│ ├── socket.ts
│ ├── style.css
│ └── vite-env.d.ts
├── tailwind.config.js
└── tsconfig.json
3 directories, 14 files
```
</details>
<details class="blue">
<summary>
راه اندازی پروژه و سرور
</summary>
- فایلهای پروژه اولیه را دانلود و از حالت فشرده خارج نمایید
- سپس دستور زیر را برای نصب پکیجها و وابستگیها در پوشه پروژه درون ترمینال خود وارد نمایید:
```bash
cd initial && npm i
```
- در نهایت برای راه اندازی سرور سوال دستور زیر را وارد نمایید:
```bash
cd server && npm i
node index.js
```
</details>
<details class="red">
<summary>
نکات
</summary>
هر قسمت سوال شامل دو بخش میباشد
### منطق و پیاده سازی الگوریتمها
+ پیاده سازی مربوطه توابع و کلاسها که امضای آنها درون هر فایل به صورت مجزا قرار داده شده است.
+ شما موظف هستید که تابع را بر حسب خواسته آن بخش تکمیل نمایید.
+ رعایت کردن تایپها **الزامی** است!
+ تمامی توابع، متغیرها و کلاسهایی که export شدهاند و یا دارای متدهای عمومی (`public`) میباشند، توسط سیستم داوری بررسی و نمره دهی میشوند.
### پیاده سازی رابط کاربری
+ ساختاری کلی رابط کاربری از قبل با استفاده از کتابخانه tailwind پیاده شده است.
+ برای بعضی از بخشها (مانند نمایش پیام) یک ساختار کلی برای شما تدارک دیده شده است و میتوانید از آن استفاده نمایید.
+ در بخشهایی که شروط خطا ذکر شده است، باید پیغام خطا درون `div#logs` نمایش داده شوند.
+ پیاده سازی این بخش اجباری بوده و توسط سیستم داوری خودکار نمره دهی میشود.
### کتابخانههای مورد استفاده و مجاز در این تمرین
در این سوال شما تنها مجاز به استفاده از کتابخانههای زیر میباشید:
+ پکیج [axios](https://www.npmjs.com/package/axios)
+ پکیج [socket.io-client](https://www.npmjs.com/package/socket.io-client)
+ پکیج [crypto-js](https://www.npmjs.com/package/crypto-js)
+ پکیج [node-rsa](https://www.npmjs.com/package/node-rsa)
</details>
# مقدمه
ماجرا از این قرار است که مهیار نه تنها به هیچکس، حتی خودش اعتماد ندارد بلکه به نرمافزارها نیز اعتماد ندارد. به همین منظور، قصد دارد یک پیامرسان بسیار امن با رمزگذاری سرتاسری (***End To End Encryption*** یه به اختصار ***E2EE***) راهاندازی کند. در این پیامرسان پیامها به صورت کاملا رمزگذاری شده به ازای هر فرد بین کاربران ارسال میشود.
از دیگر قابلیتهای این پیامرسان امکان ایجاد گروه است! حتی چتهای شخصی در قالب یک گروه صورت میگیرند، در این پیامرسان از گروهها به عنوان اتاق (Room) یاد میشود.
همچنین این پیامرسان هیچ اطلاعاتی را نه در سمت سرور و نه در سمت کاربر نگه داری نمیکند فلذا بدین معنا است که پس از بسته شدن مرورگر توسط کاربر تمامی اطلاعات فراموششده و پس از بازگشت به برنامه باید اطلاعات مورد نیاز مجددا توسط کاربر وارد شوند.
![معماری برنامه](https://quera.org/qbox/view/QTxtqTW6a6/F1.png)
# حافظه موقت
به منظور مدیریت متغیرها به شیوه صحیح و با رعایت قید ذکر شده نیاز به پیاده سازی یک حافظه متغیر بسیار ساده ***“Key-Value Pair”*** وجود دارد که باید آن را درون فایل `memory.ts` تکمیل نمایید، این حافظه چیزی جز یک` object `ساده نیست که چهار تابع برای مدیریت حافظه مذکور را دار میباشد:
+ تابع `getMemory` که از حافظه موقت یک رونوشت (Copy) را برمیگرداند.
+ تابع `memoryForget` که با دریافت نام کلید آن را از حافظه موقت حذف میکند.
+ تابع` memoryRecall که` با دریافت نام کلید مقدار تعیین شده برای آن کلید را بر میگرداند، در صورتی که کلید مذکور وجود نداشت باید مقدار`undefined` برگردانده شود.
+ تابع `memoryRemember` که با دریافت یک کلید و مقدار که میتواند یک متغیر باشد یا یک تابع که به عنوان ورودی مقدار متغیر قبلی را دریافت کرده و مقدار جدیدی را برمیگرداند، درون حافظه مقدار کلید را تنظیم و یا باز تنظیم کند.
# اطلاعات کاربر
+ برای این که کاربر بتواند از این برنامه طراحی شده استفاده کند باید اطلاعات زیر را درون مرورگر در قالب یک فایل `.`json با کلیدهای زیر بارگذاری نماید:
1. نام کاربری که درون فیلد` name `وارد میشود و از جنس رشته میباشد و باید حداقل ۳ کارکتر باشد در صورتی که طول رشته کمتر از ۳ بود باید ارور با پیغام زیر برگردانده شود:
```
"username" must be at least 3 characters
```
2. کلید عمومی (***Public Key***) که درون فیلد `public` در قالب یک رشته میباشد.
3. کلید شخصی (***Private Key***) که درون فیلد` private` در قالب یک رشته میباشد.
+ در صورتی که کاربر هر یک از فیلدهای زیر را وارد نکرده بود باید اروری با پیغام زیر برگردانده شود:
```
You did not provide "<FIELD_NAME>"
```
+ همچنین در مورد کلیدهای شخصی و عمومی این رشته کدگذاری شده باید موارد زیر بررسی و صحت سنجی شوند:
+ آیا کلیدهای ارسالی از نوع `RSA256` میباشد؟ در غیر این صورت اروری با پیغام زیر برگردانده شود
```
Keys are not RSA256
```
+ آیا کلیدها با یکدیگر همخوانی دارند؟ در صورت کلیدها با یکدیگر همخوانی نداشتند ارور با پیغام زیر برگردانده شود:
```
Invalid Key Pairs
```
## مثال ورودی
-برای مثال فایلی با محتوای زیر تمامی شرایط فوق را دارا میباشد:
```json
{
"name": "Mahyar",
"public": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A0UaGNOz5e6QmGl63GgP\nWHw8xFE+r2MYVeGVGP2OD9juhIXg3ePOvSw8sakGprESFz7shkNCeAQ8q+affSeA\n8+u83Gn+twjnxij99nQ+fgdpHZN4WYzruJ2N76kxUYm5oMfYjU+Ao\nfxxl4E+q3UXZAQ/hnq/nNLAylnsxs175eaH0wFf9q80smAcCUaE/0CzRTDaw4bHi\nkFiUcpnVWFM7TiRHFWmNXIF2srAqA+jEPRQ+yjtCuDA9NdmRnAdFLItQI9UdnnV7\nj96hqWXcAgQDlr4NTyX1aNjaKFzuRz/w6F0P03UOVHP6okUGp6vjmsv/3sY6AydE\nTwIDAQAB",
"private": "MIIEowIBAAKCAQEA70UaGNOz5e6QmGl63GgPWHw8xFE+r2MYVeGVGP2OD9juhIXg\n3ePOvSw8sakGprESFz7shkNCeAQ8q+affSeA8+u83Gn+twjnxij99nQ+fgdpHZN4\nWYzruJ2NpWw0Zcm4wdZ76kxUYm5oMfYjU+Aofxxl4E+q3UXZAQ/hnq/nNLAylnsx\ns175eaH0wFf9q80smAcCUaE/0CzRTDaw4bHikFiUcpnVWFM7TiRHFWmNXIF2srAq\nA+jEPRQ+yjtCuDA9NdmRnAdFLItQI9UdnnV7j96hqWXcAgQDlr4NTyX1aNjaKFzu\nRz/w6F0P03UOVHP6okUGp6vjmsv/3sY6AydETwIDAQABAoIBAE0H1zYE+hbdbinT\nU52f1sjq/nlJmr/GTHBhtqmb/990PJ3nqBRnoghfNI8R1rVVrnhTZrx27PBMlOfW\n0KPvUuG0lFH1we5VbKDsU47Dr9bNNfcyof1alA0UlCScqGXYwqWNEsnElyo1V1A2\naPs44HDDyLyliWHBefKQlVeMVgLs4f2QkidpUD5djPx38ObKknmP\nnSm2uVedP+KxC/uD7AwwWIKAw/XrIppWHZE1a+//NwsJOnZ2UwWp4QM41K/RZzgA\nwyMQ3Tl9SDCfNmm4Gs0OmF73V959B7yC4k3iTUJpRnru6wJVrOEIj2LcSUwPbI0c\nxSK7NZECgYEA+S/D4bQ9Afu8PGCide1F5wvtJy852pTsCnUnZLyA59KY9U\nbTvY468OH7le9zdDS93KC0QOEMESw75KJ+Ntd9BH5eW4PP8/7fvGIjAWPest0SFq\nzuHznchRYhs09/0BFGbGg2/FxeRUYazQ9sPy94+E9VRGlA14cph5DAkCgYEA9c/s\nTG7mJnuFP+KPJ76llpA68HhtBSmCYJLyr/jhqTOPIMQ+qQlENm2tc/zkMF43ciE6\n2wbXLteq/FKcGIbK/vXiCka1RCm9bbZ/be4IV9prdvm3MuTKUKjJQOdg2adwlM0Q\nRm+jvyr65GZ6U/IEgN519+cNfEE/ICwX3ikYk5cCgYEAw3j2vg03dmDVLr/Xihhl\n8MxtlUZoRyirR/O7pq12FDf8BnlnMbzfy7iQ0Tq3Z6dwmZleTxsyrVe7eOUcJTUW\n0ueTLdPTMsLbne3VZoiEyrjSGcxs6oq+UH/rLTDAtqcFDCwKNsjQnLaHfE866kw4\nt/rKk+yfrhtoKifO/oYhHpECgYB4YUYovFrL18wO2EYhQbVYQfQLVf6V+5kU8IIX\nebwRn+VlN5Y0NAyK1zWNyvh5U3q10t4+61+wWgArp804GbMKH6J+Q9h7tDFHrItF\na7/8PkRItMxqCS+nRrGgrm94yA5TYQWqaPv5H/RUrHcV6z0XW1rMeAPbqRxgBhf+\nPYQCqwKBgAfn3oxLjx0mSjtr2iDIaMHoZQq5Dl97ac3+nMpqEUJVS/F6bHRE4eTz\ni/NrMSJzwuMOrMFVBKpnDc9STsRuv+oGvoxeXA4taGlJVj5lAa83b0PEKCQV9Uzn\nk4K/8DCEKqLQPtXQI0Xdszx344RLINELoVm3p3jSFEyaOTK40uvp"
}
```
## پیاده سازی
برای تکمیل این قسمت شما باید موارد زیر را تکمیل نمایید
### فایل `encrypt.ts`
در این فایل شما باید `isValidData`از کلاس `EncryptionService` را تکمیل نمایید بدین نحو که با دریافت کردن اطلاعات کاربر در قالب یک `Object` ، خطاها را در صورت وجود در قالب یک آرایه شامل رشتهها برگرداند و درصورتی که هیچ مشکلی در داده ورودی نبود آرایه خالی برگرداند.
### فایل `main.ts`
در این فایل شما باید قابلیتی را پیاده سازی کنید در هنگامی که کاربر فایل جدید را در `#file-upload` بارگذاری کرد آن را صحت سنجی کرده و آن را درون حافظه با کلید `credentials` ذخیره نماید. و در صورت بروز خطا، خطاها را به کاربر نمایش دهد. همچنین در صورتی که فایل دارای مشکل بود باید پیغام زیر به کاربر نمایش داده شود:
```
File is Not Valid
```
# اتاقهایی برای گفتوگو
همانطور که قبلا ذکر شد در این پیامرسان، مفهومی تحت عنوان اتاق وجود دارد که میتواند حداقل یک عضو داشته باشد. تمامی افراد میتوانند به شرط داشتن گذرواژه وارد این اتاقها شوند، البته فردی که اتاق را در ابتدا درست کرده است، میتواند به نحو خاصی که در ادامه به آن اشاره خواهد شد، فقط به افراد به خصوصی امکان اجازه دسترسی بدهد حتی اگر رمز و نام اتاق را به نحوی به دست آورده باشند این مکانیزم منجر به این قضیه خواهد شد تا افراد فاقد صلاحیت نتوانند وارد اتاق شوند.
## ایجاد و عضویت
همانطور که ذکر شد، کاربر برای ایجاد و یا عضویت در اتاق باید نام اتاق (`roomName`) به همراه گذرواژه (`password`) و کلید عمومی خود را در قالب یک شی به سمت سرور ارسال کند:
```ts
interface Request{
roomName: string;
password: string;
publicKey: string;
}
```
### مسیر `/api/join/`
در گام اول، کاربر اطلاعات مذکور را به آدرس `/api/join/` ارسال میکند و با یکی از سه سناریوی زیر مواجه میشود:
1. در صورتی که کاربر اطلاعات را به درستی وارد کرده باشد ولی با خطای ***404*** مواجه شود، باید همان اطلاعات را به مسیر `/api/create/` ارسال کند و پس از ایجاد، مجدداً درخواست اول تکرار و ارسال شود.
2. در صورتی که گذرواژه به اشتباه وارد شده باشد، باید پیغامی با محتوای زیر در `div#request-status` نمایش داده شود:
```
Bad Credentials!
```
3. در صورتی که کاربر امکان دسترسی به گروه درخواست شده را نداشته باشد، در همان `div#request-status` پیغام زیر نمایش داده شود:
```
Unauthorized Access!
```
### مسیر `/api/create/`
کاربر اطلاعات زیر را به آدرس `/api/create/` ارسال میکند تا یک اتاق جدید ایجاد کند:
```ts
interface CreateRequest {
groupName: string;
password: string;
publicKey: string;
}
```
اگر ایجاد اتاق موفقیتآمیز باشد، سرور یک پیام موفقیت با محتوای زیر به کاربر بازمیگرداند:
```
Room created successfully!
```
در غیر این صورت، باید خطای دریافتی از سمت سرور درو `div#request-status` نمایش داده شود.
## مدیریت اتاق
الزاما داشتن نام اتاق و گذرواژه آن راه مطلوبی برای تضمین امنیت گروهها نیست زیرا در صورتی که اطلاعات حساس اتاق وصل شود هر فردی میتواند به این اتاق دسترسی پیدا کند به همین منظور مهیار تصمیم گرفت که برای مدیریت این قضیه قابلیتی اضافه کند تا افرادی که دارای صلاحیت هستند را با استفاده از کلید عمومی آنها به گروه اضافه کند برای این منظور باید به درخواست زیر ارسال شود:
```
// PUT /api/room/
interface AddUserRequest {
Request;
user: string; // new user public key
}
```
+ در صورتی که اتاق وجود نداشت باید خطایی با پیغام زیر برگردانده شود:
```
Room Not Found!
```
+ از آنجایی که فقط فردی که گروه را ایجاد کرده میتواند افراد جدید را اضافه کند، در صورتی که کاربر، ایجاد کننده اتاق نباشد باید خطایی با پیغام زیر برگردانده شود:
```
Forbidden: Not the owner of the room
```
## پیاده سازی
- موارد خواسته شده در خصوص ***API*** ها و ارتباط با سرور را در فایل `room.ts` تکمیل نمایید
- همچنین برای رابط کاربری فایل `main.ts` مطابق فیلدهایی که در فایل `index.html` عملکرد مطلوب را پیاده سازی کنید
# اتصال به سوکت برای ارسال و دریافت پیام
برای ارتباط بلادرنگ، کاربران باید به یک ***WebSocket*** متصل شوند که پیاده سازی آن در فایل `socket.ts` باید صورت بگیرد. برای این کار، باید از آدرس زیر استفاده شود و باید `token` حاصل شده از بخش قبلی را در بخش احراز هویت Header ارسال نماید:
```
/ws/room/<ROOM_NAME>/
```
پس از اتصال موفق، کاربر کلید عمومی همراه با نام خود و شناسه اتصال سوکت را برای تمامی کاربران حاضر در اتاق در قالب یک رخداد تحت عوان ` publicKey ` ارسال مینماید. همچنین باقی کاربران پس از دریافت باید اطلاعات خود را نیز در قالب همان رخداد ارسال نمایند.
## شیوه ارسال پیام
+ در گام صفرم کاربر باید محتوای پیام خود را درون `input` مربوطه بنویسید و سپس دکمه `Send` را فشرده تا روند اصلی ارسال آغاز شود.
+ در گام اول باید به ازای تمامی کاربران موجود در اتاق (به غیر از خودتان) پیام ارسالی مورد نظر آنها را با توجه به کلید عمومی هر کاربر رمزگزاری کنید.
+ در گام بعد جهت اینکه مشخص شود این پیام توسط شما ارسال شده است باید آن را با استفاده از کلید خود امضا کنید.
+ در گام بعدی با توجه به به پیام رمزنگاری شده برای دیگر کاربران باید آن را در قالب یک رخداد message پیام رمزگذاری شده را به همراه امضا در قالب یک شی به همراه شناسه کاربر مورد نظر ارسال نمایید.
## شیوه دریافت پیام
+ پس از دریافت یک پیام در گام اول باید این امر صحت سنجی شود که با توجه به اطلاعات دریافتی (پیام رمزگذاری شده و امضای مذکور) آیا این پیام واقعا از آن کاربر آمده است یا خیر؟ در غیر این صورت اروری تحت عنوان زیر نمایش داده شود
```
Message verification failed for sender <SENDER_USERNAME>
```
+ در گام بعد باید پیام دریافت شده باید با توجه به کلید شخصی کاربری که پیام را دریافت کرده است رمزگشایی شده و بازگردانده شود.