Skip to content

Encryption init #126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ CONVERSATION_MAX_PARTICIPANTS=50
CONVERSATION_PRELOAD_COUNT=1000
JWT_ACCESS_SECRET=token
JWT_ACCESS_TOKEN_EXPIRES_IN=10800
JWT_REFRESH_SECRET=refresh
JWT_REFRESH_TOKEN_EXPIRES_IN=1209600
ENCRYPTION_MESSAGE_EXPIRED_IN=2592000 # 30 days
ENCRYPTION_DEVICE_TOKEN_EXPIRES_IN=2592000 # 30 days
OPERATIONS_LOG_EXPIRES_IN=1209600
NODE_CLUSTER_DATA_EXPIRES_IN=30000
REDIS_SUB_DATA_EXPIRES_IN=30000
Expand Down
45 changes: 45 additions & 0 deletions APIs/JSON/controllers/encryption.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import BaseJSONController from "./base.js"

import ServiceLocatorContainer from "@sama/common/ServiceLocatorContainer.js"

import Response from "@sama/networking/models/Response.js"

class EncryptionController extends BaseJSONController {
async register(ws, data) {
const { id: requestId } = data

const encryptionRegisterOperation = ServiceLocatorContainer.use("EncryptionRegisterOperation")
await encryptionRegisterOperation.perform(ws, data.device_register)

return new Response().addBackMessage({ response: { id: requestId, success: true } })
}

async list(ws, data) {
const { id: requestId } = data

const encryptionListOperation = ServiceLocatorContainer.use("EncryptionListOperation")
const deviceList = await encryptionListOperation.perform(ws)

return new Response().addBackMessage({ response: { id: requestId, devices: deviceList } })
}

async request_keys(ws, data) {
const { id: requestId } = data

const encryptionRequestKeysOperation = ServiceLocatorContainer.use("EncryptionRequestKeysOperation")
const deviceList = await encryptionRequestKeysOperation.perform(ws, data.request_keys)

return new Response().addBackMessage({ response: { id: requestId, devices: deviceList } })
}

async delete(ws, data) {
const { id: requestId } = data

const encryptionDeleteOperation = ServiceLocatorContainer.use("EncryptionDeleteOperation")
await encryptionDeleteOperation.perform(ws, data.device_delete)

return new Response().addBackMessage({ response: { id: requestId, success: true } })
}
}

export default new EncryptionController()
22 changes: 22 additions & 0 deletions APIs/JSON/controllers/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import MessageResponse from "@sama/DTO/Response/message/create/response.js"
import SystemMessageResponse from "@sama/DTO/Response/message/system/response.js"
import EditMessageResponse from "@sama/DTO/Response/message/edit/response.js"
import ReadMessagesResponse from "@sama/DTO/Response/message/read/response.js"
import DecryptionFailedMessagesResponse from "@sama/DTO/Response/message/decryption_failed/response.js"
import DeleteMessagesResponse from "@sama/DTO/Response/message/delete/response.js"

import DeliverMessage from "@sama/networking/models/DeliverMessage.js"
Expand Down Expand Up @@ -93,6 +94,27 @@ class MessagesController extends BaseJSONController {
})
}

async decryption_failed(ws, data) {
const { id: requestId, message_decryption_failed: messagesDecryptionFailedOptions } = data

const messageDecryptionFailedOperation = ServiceLocatorContainer.use("MessageDecryptionFailedOperation")
const { receiverUserId, statuses } = await messageDecryptionFailedOperation.perform(
ws,
messagesDecryptionFailedOptions
)

const response = new Response()

response.addDeliverMessage(new DeliverMessage([receiverUserId], new DecryptionFailedMessagesResponse(statuses)))

return response.addBackMessage({
response: {
id: requestId,
success: true,
},
})
}

async delete(ws, data) {
const { id: requestId, message_delete: messageDeleteParams } = data

Expand Down
2 changes: 0 additions & 2 deletions APIs/JSON/controllers/operations_log.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import BaseJSONController from "./base.js"

import OpLog from "@sama/models/operations_log.js"

import ServiceLocatorContainer from "@sama/common/ServiceLocatorContainer.js"

import MappableMessage from "@sama/networking/models/MappableMessage.js"
Expand Down
22 changes: 22 additions & 0 deletions APIs/JSON/routes/routes.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { default as ContactsController } from "../controllers/contacts.js"
import { default as ConversationsController } from "../controllers/conversations.js"
import { default as EncryptionController } from "../controllers/encryption.js"
import { default as FilesController } from "../controllers/files.js"
import { default as LastActivityiesController } from "../controllers/activities.js"
import { default as MessagesController } from "../controllers/messages.js"
Expand All @@ -14,6 +15,7 @@ import authGuardMiddleware from "../middleware/auth_guard.js"
import { activitiesSchemaValidation } from "../validations/activities_schema_validation.js"
import { contactsSchemaValidation } from "../validations/contacts_schema_validation.js"
import { conversationsSchemaValidation } from "../validations/conversations_schema_validation.js"
import { encryptionSchemaValidation } from "../validations/encryption_schema_validation.js"
import { filesSchemaValidation } from "../validations/files_schema_validation.js"
import { messagesSchemaValidation } from "../validations/messages_schema_validation.js"
import { operationsLogSchemaValidation } from "../validations/operations_log_schema_validation.js"
Expand Down Expand Up @@ -44,6 +46,10 @@ export const routes = {
MessagesController.middleware(authGuardMiddleware, ws, json)
.validate(json.message_read, messagesSchemaValidation.read)
.read(ws, json),
message_decryption_failed: (ws, json) =>
MessagesController.middleware(authGuardMiddleware, ws, json)
.validate(json.message_decryption_failed, messagesSchemaValidation.decryption_failed)
.decryption_failed(ws, json),
message_delete: (ws, json) =>
MessagesController.middleware(authGuardMiddleware, ws, json)
.validate(json.message_delete, messagesSchemaValidation.delete)
Expand Down Expand Up @@ -193,4 +199,20 @@ export const routes = {
PushNotificationsController.middleware(authGuardMiddleware, ws, json)
.validate(json.push_event_create, pushNotificationsSchemaValidation.push_event_create)
.push_event_create(ws, json),
device_register: (ws, json) =>
EncryptionController.middleware(authGuardMiddleware, ws, json)
.validate(json.device_register, encryptionSchemaValidation.device_register)
.register(ws, json),
device_list: (ws, json) =>
EncryptionController.middleware(authGuardMiddleware, ws, json)
.validate(json.device_list, encryptionSchemaValidation.device_list)
.list(ws, json),
request_keys: (ws, json) =>
EncryptionController.middleware(authGuardMiddleware, ws, json)
.validate(json.request_keys, encryptionSchemaValidation.request_keys)
.request_keys(ws, json),
device_delete: (ws, json) =>
EncryptionController.middleware(authGuardMiddleware, ws, json)
.validate(json.device_delete, encryptionSchemaValidation.device_delete)
.delete(ws, json),
}
1 change: 1 addition & 0 deletions APIs/JSON/validations/conversations_schema_validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export const conversationsSchemaValidation = {
file_name: Joi.string().max(255),
file_blur_hash: Joi.string().max(255),
}),
is_encrypted: Joi.boolean().allow(true),
}).required(),
update: Joi.object({
id: Joi.string().required(),
Expand Down
51 changes: 51 additions & 0 deletions APIs/JSON/validations/encryption_schema_validation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import Joi from "joi"
import { ERROR_STATUES } from "@sama/constants/errors.js"

export const encryptionSchemaValidation = {
device_register: Joi.object({
identity_key: Joi.string()
.max(255)
.required()
.error(
new Error(ERROR_STATUES.INCORRECT_IDENTITY_KEY.message, {
cause: ERROR_STATUES.INCORRECT_IDENTITY_KEY,
})
),
signed_key: Joi.string()
.max(255)
.required()
.error(
new Error(ERROR_STATUES.INCORRECT_SIGNED_KEY.message, {
cause: ERROR_STATUES.INCORRECT_SIGNED_KEY,
})
),
one_time_pre_keys: Joi.alternatives(Joi.object().max(100), Joi.array().items(Joi.string().max(255)).max(100))
.required()
.error(
new Error(ERROR_STATUES.INCORRECT_ONE_TIME_PRE_KEYS.message, {
cause: ERROR_STATUES.INCORRECT_ONE_TIME_PRE_KEYS,
})
),
}),
device_list: Joi.object({}),
request_keys: Joi.object({
user_ids: Joi.array()
.items(
Joi.string().error(
new Error(ERROR_STATUES.INCORRECT_USER_ID.message, {
cause: ERROR_STATUES.INCORRECT_USER_ID,
})
)
)
.max(50)
.required()
.error(
new Error(ERROR_STATUES.INCORRECT_USERS_ARRAY.message, {
cause: ERROR_STATUES.INCORRECT_USERS_ARRAY,
})
),
}),
device_delete: Joi.object({
device_id: Joi.alternatives().try(Joi.number().max(255).required(), Joi.string().max(255).required()).required(),
}),
}
11 changes: 11 additions & 0 deletions APIs/JSON/validations/messages_schema_validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const messagesSchemaValidation = {
})
),
deleted_for: Joi.array().items(Joi.alternatives().try(Joi.object(), Joi.string(), Joi.number()).required()),
encrypted_message_type: Joi.number().allow(1, 0),
})
.or("body", "attachments"),
edit: Joi.object({
Expand Down Expand Up @@ -82,6 +83,16 @@ export const messagesSchemaValidation = {
),
ids: Joi.array().items(Joi.alternatives().try(Joi.object(), Joi.string())),
}).required(),
decryption_failed: Joi.object({
cid: Joi.string()
.required()
.error(
new Error(ERROR_STATUES.CID_REQUIRED.message, {
cause: ERROR_STATUES.CID_REQUIRED,
})
),
ids: Joi.array().items(Joi.alternatives().try(Joi.object(), Joi.string())).min(1).required(),
}).required(),
delete: Joi.object({
cid: Joi.string()
.required()
Expand Down
2 changes: 1 addition & 1 deletion APIs/XMPP
Submodule XMPP updated from aa7ac7 to e2b44c
1 change: 1 addition & 0 deletions app/DTO/Response/message/create/public_fields.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class MessagePublicFields {
this.body = messageModel.body
this.attachments = messageModel.attachments
this.x = messageModel.x
this.encrypted_message_type = messageModel.encrypted_message_type

this.created_at = messageModel.created_at

Expand Down
9 changes: 9 additions & 0 deletions app/DTO/Response/message/decryption_failed/public_fields.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class DecryptionFailedMessagesPublicFields {
constructor(params) {
this.ids = params.messageIds
this.cid = params.cid
this.from = params.from
}
}

export default DecryptionFailedMessagesPublicFields
7 changes: 7 additions & 0 deletions app/DTO/Response/message/decryption_failed/response.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class DecryptionFailedMessagesResponse {
constructor(decryptionFailedMessagesPublicFields) {
this.message_decryption_failed = decryptionFailedMessagesPublicFields
}
}

export default DecryptionFailedMessagesResponse
6 changes: 6 additions & 0 deletions app/constants/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ export const ERROR_STATUES = {
message: "Push notification record not found.",
},
INCORRECT_KEYS: { status: 422, message: "Incorrect keys." },
//Encryption -->
INCORRECT_IDENTITY_KEY: { status: 422, message: "Incorrect identity device key." },
INCORRECT_SIGNED_KEY: { status: 422, message: "Incorrect signed key." },
INCORRECT_ONE_TIME_PRE_KEYS: { status: 422, message: "Incorrect one time pre keys." },
INCORRECT_USER_ID: { status: 422, message: "Incorrect user id." },
INCORRECT_USERS_ARRAY: { status: 422, message: "Incorrect users array." },
// Other -->
LOG_TIMETAMP_MISSED: { status: 422, message: "Gt or lt query missed." },
CID_REQUIRED: {
Expand Down
1 change: 1 addition & 0 deletions app/models/conversation.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default class Conversation extends BaseModel {
"description",
"image_object",
"image_url",
"is_encrypted",

"owner_id",
"opponent_id",
Expand Down
13 changes: 13 additions & 0 deletions app/models/encrypted_device.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import BaseModel from "./base.js"

class EncryptedDevice extends BaseModel {
static get collection() {
return "encrypted_devices"
}

static get visibleFields() {
return ["_id", "identity_key", "signed_key", "one_time_pre_keys"]
}
}

export default EncryptedDevice
15 changes: 15 additions & 0 deletions app/models/encrypted_message_status.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import BaseModel from "./base.js"

export default class EncryptedMessageStatus extends BaseModel {
static get collection() {
return "encrypted_message_statuses"
}

static get visibleFields() {
return ["_id", "mid", "cid"]
}

static get hiddenFields() {
return ["updated_at", "created_at"]
}
}
1 change: 1 addition & 0 deletions app/models/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default class Message extends BaseModel {
"body",
"x",
"attachments",
"encrypted_message_type",

"created_at",

Expand Down
Loading