internal: incomplete vault systems behind feature flag (#2340)
This commit is contained in:
BIN
.yarn/cache/@standardnotes-common-npm-1.48.3-83aea658e0-191d97879d.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-common-npm-1.48.3-83aea658e0-191d97879d.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/@typescript-eslint-eslint-plugin-npm-5.59.9-3d09cd2e8f-bd2428e307.zip
vendored
Normal file
BIN
.yarn/cache/@typescript-eslint-eslint-plugin-npm-5.59.9-3d09cd2e8f-bd2428e307.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/@typescript-eslint-parser-npm-5.59.9-3841845448-69b07d0a5b.zip
vendored
Normal file
BIN
.yarn/cache/@typescript-eslint-parser-npm-5.59.9-3841845448-69b07d0a5b.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/@typescript-eslint-scope-manager-npm-5.59.9-c9c714cb56-362c22662d.zip
vendored
Normal file
BIN
.yarn/cache/@typescript-eslint-scope-manager-npm-5.59.9-c9c714cb56-362c22662d.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/@typescript-eslint-type-utils-npm-5.59.9-fc3a85cbad-6bc2619c50.zip
vendored
Normal file
BIN
.yarn/cache/@typescript-eslint-type-utils-npm-5.59.9-fc3a85cbad-6bc2619c50.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/@typescript-eslint-types-npm-5.59.9-9719f93248-283f8fee1e.zip
vendored
Normal file
BIN
.yarn/cache/@typescript-eslint-types-npm-5.59.9-9719f93248-283f8fee1e.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/@typescript-eslint-typescript-estree-npm-5.59.9-ec2ce6608c-c0c9b81f20.zip
vendored
Normal file
BIN
.yarn/cache/@typescript-eslint-typescript-estree-npm-5.59.9-ec2ce6608c-c0c9b81f20.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/@typescript-eslint-utils-npm-5.59.9-d1ab6a9f9a-22ec596288.zip
vendored
Normal file
BIN
.yarn/cache/@typescript-eslint-utils-npm-5.59.9-d1ab6a9f9a-22ec596288.zip
vendored
Normal file
Binary file not shown.
BIN
.yarn/cache/@typescript-eslint-visitor-keys-npm-5.59.9-3e52021052-2909ce761f.zip
vendored
Normal file
BIN
.yarn/cache/@typescript-eslint-visitor-keys-npm-5.59.9-3e52021052-2909ce761f.zip
vendored
Normal file
Binary file not shown.
@@ -26,7 +26,6 @@
|
||||
"build:snjs": "yarn workspaces foreach -p --topological-dev --verbose -R --from @standardnotes/snjs run build",
|
||||
"build:services": "yarn workspaces foreach -pt --topological-dev --verbose -R --from @standardnotes/services run build",
|
||||
"build:api": "yarn workspaces foreach -pt --topological-dev --verbose -R --from @standardnotes/api run build",
|
||||
"start:server:web": "lerna run start --scope=@standardnotes/web",
|
||||
"e2e": "lerna run start:test-server --scope=@standardnotes/snjs",
|
||||
"reset": "find . -type dir -name node_modules | xargs rm -rf && rm -rf yarn.lock && yarn install",
|
||||
"release:prod": "lerna version --conventional-commits --yes -m \"chore(release): publish\"",
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"typescript": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.46.6",
|
||||
"@standardnotes/common": "^1.48.3",
|
||||
"@standardnotes/domain-core": "^1.12.0",
|
||||
"@standardnotes/encryption": "workspace:*",
|
||||
"@standardnotes/models": "workspace:*",
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { ErrorMessage } from '../../Error/ErrorMessage'
|
||||
import { ApiCallError } from '../../Error/ApiCallError'
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
import { ApiEndpointParam } from '../../Request/ApiEndpointParam'
|
||||
|
||||
import { SubscriptionServerInterface } from '../../Server/Subscription/SubscriptionServerInterface'
|
||||
import { AppleIAPConfirmResponseBody } from './../../Response/Subscription/AppleIAPConfirmResponseBody'
|
||||
import { SubscriptionInviteAcceptResponseBody } from '../../Response/Subscription/SubscriptionInviteAcceptResponseBody'
|
||||
import { SubscriptionInviteCancelResponseBody } from '../../Response/Subscription/SubscriptionInviteCancelResponseBody'
|
||||
import { SubscriptionInviteListResponseBody } from '../../Response/Subscription/SubscriptionInviteListResponseBody'
|
||||
import { SubscriptionInviteResponseBody } from '../../Response/Subscription/SubscriptionInviteResponseBody'
|
||||
import { HttpResponse } from '@standardnotes/responses'
|
||||
import { HttpResponse, ApiEndpointParam } from '@standardnotes/responses'
|
||||
|
||||
import { SubscriptionApiServiceInterface } from './SubscriptionApiServiceInterface'
|
||||
import { SubscriptionApiOperations } from './SubscriptionApiOperations'
|
||||
|
||||
@@ -2,4 +2,5 @@ export enum UserApiOperations {
|
||||
Registering,
|
||||
SubmittingRequest,
|
||||
DeletingAccount,
|
||||
UpdatingUser,
|
||||
}
|
||||
|
||||
@@ -5,8 +5,7 @@ import { ErrorMessage } from '../../Error/ErrorMessage'
|
||||
import { ApiCallError } from '../../Error/ApiCallError'
|
||||
import { UserServerInterface } from '../../Server/User/UserServerInterface'
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
import { ApiEndpointParam } from '../../Request/ApiEndpointParam'
|
||||
import { HttpResponse } from '@standardnotes/responses'
|
||||
import { HttpResponse, ApiEndpointParam } from '@standardnotes/responses'
|
||||
|
||||
import { UserDeletionResponseBody } from '../../Response/User/UserDeletionResponseBody'
|
||||
import { UserRegistrationResponseBody } from '../../Response/User/UserRegistrationResponseBody'
|
||||
@@ -15,6 +14,7 @@ import { UserRequestServerInterface } from '../../Server/UserRequest/UserRequest
|
||||
|
||||
import { UserApiOperations } from './UserApiOperations'
|
||||
import { UserApiServiceInterface } from './UserApiServiceInterface'
|
||||
import { UserUpdateResponse } from '../../Response/User/UserUpdateResponse'
|
||||
|
||||
export class UserApiService implements UserApiServiceInterface {
|
||||
private operationsInProgress: Map<UserApiOperations, boolean>
|
||||
@@ -35,7 +35,7 @@ export class UserApiService implements UserApiServiceInterface {
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
throw new ApiCallError(ErrorMessage.GenericRegistrationFail)
|
||||
throw new ApiCallError(ErrorMessage.GenericFail)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,23 @@ export class UserApiService implements UserApiServiceInterface {
|
||||
}
|
||||
}
|
||||
|
||||
async updateUser(updateDTO: { userUuid: string }): Promise<HttpResponse<UserUpdateResponse>> {
|
||||
this.lockOperation(UserApiOperations.UpdatingUser)
|
||||
|
||||
try {
|
||||
const response = await this.userServer.update({
|
||||
[ApiEndpointParam.ApiVersion]: ApiVersion.v0,
|
||||
user_uuid: updateDTO.userUuid,
|
||||
})
|
||||
|
||||
this.unlockOperation(UserApiOperations.UpdatingUser)
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
throw new ApiCallError(ErrorMessage.GenericFail)
|
||||
}
|
||||
}
|
||||
|
||||
private lockOperation(operation: UserApiOperations): void {
|
||||
if (this.operationsInProgress.get(operation)) {
|
||||
throw new ApiCallError(ErrorMessage.GenericInProgress)
|
||||
|
||||
@@ -5,6 +5,7 @@ import { HttpResponse } from '@standardnotes/responses'
|
||||
import { UserDeletionResponseBody } from '../../Response/User/UserDeletionResponseBody'
|
||||
import { UserRegistrationResponseBody } from '../../Response/User/UserRegistrationResponseBody'
|
||||
import { UserRequestResponseBody } from '../../Response/UserRequest/UserRequestResponseBody'
|
||||
import { UserUpdateResponse } from '../../Response/User/UserUpdateResponse'
|
||||
|
||||
export interface UserApiServiceInterface {
|
||||
register(registerDTO: {
|
||||
@@ -13,9 +14,12 @@ export interface UserApiServiceInterface {
|
||||
keyParams: RootKeyParamsInterface
|
||||
ephemeral: boolean
|
||||
}): Promise<HttpResponse<UserRegistrationResponseBody>>
|
||||
updateUser(updateDTO: { userUuid: string }): Promise<HttpResponse<UserUpdateResponse>>
|
||||
|
||||
submitUserRequest(dto: {
|
||||
userUuid: string
|
||||
requestType: UserRequestType
|
||||
}): Promise<HttpResponse<UserRequestResponseBody>>
|
||||
|
||||
deleteAccount(userUuid: string): Promise<HttpResponse<UserDeletionResponseBody>>
|
||||
}
|
||||
|
||||
@@ -53,7 +53,15 @@ export class HttpService implements HttpServiceInterface {
|
||||
this.host = host
|
||||
}
|
||||
|
||||
getHost(): string {
|
||||
return this.host
|
||||
}
|
||||
|
||||
async get<T>(path: string, params?: HttpRequestParams, authentication?: string): Promise<HttpResponse<T>> {
|
||||
if (!this.host) {
|
||||
throw new Error('Attempting to make network request before host is set')
|
||||
}
|
||||
|
||||
return this.runHttp({
|
||||
url: joinPaths(this.host, path),
|
||||
params,
|
||||
@@ -62,7 +70,20 @@ export class HttpService implements HttpServiceInterface {
|
||||
})
|
||||
}
|
||||
|
||||
async getExternal<T>(url: string, params?: HttpRequestParams): Promise<HttpResponse<T>> {
|
||||
return this.runHttp({
|
||||
url,
|
||||
params,
|
||||
verb: HttpVerb.Get,
|
||||
external: true,
|
||||
})
|
||||
}
|
||||
|
||||
async post<T>(path: string, params?: HttpRequestParams, authentication?: string): Promise<HttpResponse<T>> {
|
||||
if (!this.host) {
|
||||
throw new Error('Attempting to make network request before host is set')
|
||||
}
|
||||
|
||||
return this.runHttp({
|
||||
url: joinPaths(this.host, path),
|
||||
params,
|
||||
|
||||
@@ -3,8 +3,10 @@ import { HttpRequest, HttpRequestParams, HttpResponse, HttpResponseMeta } from '
|
||||
|
||||
export interface HttpServiceInterface {
|
||||
setHost(host: string): void
|
||||
getHost(): string
|
||||
setSession(session: Session): void
|
||||
get<T>(path: string, params?: HttpRequestParams, authentication?: string): Promise<HttpResponse<T>>
|
||||
getExternal<T>(url: string, params?: HttpRequestParams): Promise<HttpResponse<T>>
|
||||
post<T>(path: string, params?: HttpRequestParams, authentication?: string): Promise<HttpResponse<T>>
|
||||
put<T>(path: string, params?: HttpRequestParams, authentication?: string): Promise<HttpResponse<T>>
|
||||
patch<T>(path: string, params: HttpRequestParams, authentication?: string): Promise<HttpResponse<T>>
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export enum ApiEndpointParam {
|
||||
LastSyncToken = 'sync_token',
|
||||
PaginationToken = 'cursor_token',
|
||||
SyncDlLimit = 'limit',
|
||||
SyncPayloads = 'items',
|
||||
ApiVersion = 'api',
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export type CreateAsymmetricMessageParams = {
|
||||
recipientUuid: string
|
||||
encryptedMessage: string
|
||||
replaceabilityIdentifier?: string
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type DeleteAsymmetricMessageRequestParams = {
|
||||
messageUuid: string
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export type GetOutboundAsymmetricMessagesRequestParams = {}
|
||||
@@ -0,0 +1,2 @@
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export type GetUserAsymmetricMessagesRequestParams = {}
|
||||
@@ -0,0 +1,4 @@
|
||||
export type UpdateAsymmetricMessageParams = {
|
||||
messageUuid: string
|
||||
encryptedMessage: string
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { ValetTokenOperation } from '@standardnotes/responses'
|
||||
import { SharedVaultMoveType } from './SharedVaultMoveType'
|
||||
|
||||
export type CreateSharedVaultValetTokenParams = {
|
||||
sharedVaultUuid: string
|
||||
fileUuid?: string
|
||||
remoteIdentifier: string
|
||||
operation: ValetTokenOperation
|
||||
unencryptedFileSize?: number
|
||||
moveOperationType?: SharedVaultMoveType
|
||||
sharedVaultToSharedVaultMoveTargetUuid?: string
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export type SharedVaultMoveType = 'shared-vault-to-user' | 'user-to-shared-vault' | 'shared-vault-to-shared-vault'
|
||||
@@ -0,0 +1,4 @@
|
||||
export type AcceptInviteRequestParams = {
|
||||
sharedVaultUuid: string
|
||||
inviteUuid: string
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { SharedVaultPermission } from '@standardnotes/responses'
|
||||
|
||||
export type CreateSharedVaultInviteParams = {
|
||||
sharedVaultUuid: string
|
||||
recipientUuid: string
|
||||
encryptedMessage: string
|
||||
permissions: SharedVaultPermission
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export type DeclineInviteRequestParams = {
|
||||
sharedVaultUuid: string
|
||||
inviteUuid: string
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type DeleteAllSharedVaultInvitesRequestParams = {
|
||||
sharedVaultUuid: string
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export type DeleteInviteRequestParams = {
|
||||
sharedVaultUuid: string
|
||||
inviteUuid: string
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export type GetOutboundUserInvitesRequestParams = {}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type GetSharedVaultInvitesRequestParams = {
|
||||
sharedVaultUuid: string
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export type GetUserInvitesRequestParams = {}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { SharedVaultPermission } from '@standardnotes/responses'
|
||||
|
||||
export type UpdateSharedVaultInviteParams = {
|
||||
sharedVaultUuid: string
|
||||
inviteUuid: string
|
||||
encryptedMessage: string
|
||||
permissions?: SharedVaultPermission
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export type DeleteSharedVaultUserRequestParams = {
|
||||
sharedVaultUuid: string
|
||||
userUuid: string
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type GetSharedVaultUsersRequestParams = {
|
||||
sharedVaultUuid: string
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApiEndpointParam } from '../ApiEndpointParam'
|
||||
import { ApiEndpointParam } from '@standardnotes/responses'
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
|
||||
export type SubscriptionInviteCancelRequestParams = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApiEndpointParam } from '../ApiEndpointParam'
|
||||
import { ApiEndpointParam } from '@standardnotes/responses'
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
|
||||
export type SubscriptionInviteDeclineRequestParams = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApiEndpointParam } from '../ApiEndpointParam'
|
||||
import { ApiEndpointParam } from '@standardnotes/responses'
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
|
||||
export type SubscriptionInviteListRequestParams = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApiEndpointParam } from '../ApiEndpointParam'
|
||||
import { ApiEndpointParam } from '@standardnotes/responses'
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
|
||||
export type SubscriptionInviteRequestParams = {
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { AnyKeyParamsContent } from '@standardnotes/common'
|
||||
import { ApiEndpointParam } from '../ApiEndpointParam'
|
||||
import { ApiEndpointParam } from '@standardnotes/responses'
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
|
||||
export type UserRegistrationRequestParams = AnyKeyParamsContent & {
|
||||
[ApiEndpointParam.ApiVersion]: ApiVersion.v0
|
||||
[additionalParam: string]: unknown
|
||||
password: string
|
||||
email: string
|
||||
ephemeral: boolean
|
||||
[additionalParam: string]: unknown
|
||||
pkcPublicKey?: string
|
||||
pkcEncryptedPrivateKey?: string
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { ApiEndpointParam } from '@standardnotes/responses'
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
|
||||
export type UserUpdateRequestParams = {
|
||||
[ApiEndpointParam.ApiVersion]: ApiVersion.v0
|
||||
user_uuid: string
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from './ApiEndpointParam'
|
||||
export * from './Authenticator/DeleteAuthenticatorRequestParams'
|
||||
export * from './Authenticator/GenerateAuthenticatorAuthenticationOptionsRequestParams'
|
||||
export * from './Authenticator/ListAuthenticatorsRequestParams'
|
||||
@@ -17,3 +16,4 @@ export * from './Subscription/SubscriptionInviteRequestParams'
|
||||
export * from './User/UserRegistrationRequestParams'
|
||||
export * from './UserRequest/UserRequestRequestParams'
|
||||
export * from './WebSocket/WebSocketConnectionTokenRequestParams'
|
||||
export * from './SharedVault/SharedVaultMoveType'
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
|
||||
export type CreateAsymmetricMessageResponse = {
|
||||
message: AsymmetricMessageServerHash
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type DeleteAsymmetricMessageResponse = {
|
||||
success: boolean
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
|
||||
export type GetOutboundAsymmetricMessagesResponse = {
|
||||
messages: AsymmetricMessageServerHash[]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
|
||||
export type GetUserAsymmetricMessagesResponse = {
|
||||
messages: AsymmetricMessageServerHash[]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
|
||||
export type UpdateAsymmetricMessageResponse = {
|
||||
message: AsymmetricMessageServerHash
|
||||
}
|
||||
@@ -7,6 +7,9 @@ export interface GetRevisionResponseBody {
|
||||
items_key_id: string | null
|
||||
enc_item_key: string | null
|
||||
auth_hash: string | null
|
||||
user_uuid: string
|
||||
key_system_identifier: string | null
|
||||
shared_vault_uuid: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { SharedVaultUserServerHash, SharedVaultServerHash } from '@standardnotes/responses'
|
||||
|
||||
export type CreateSharedVaultResponse = {
|
||||
sharedVault: SharedVaultServerHash
|
||||
sharedVaultUser: SharedVaultUserServerHash
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type CreateSharedVaultValetTokenResponse = {
|
||||
valetToken: string
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { SharedVaultServerHash } from '@standardnotes/responses'
|
||||
|
||||
export type GetSharedVaultsResponse = {
|
||||
sharedVaults: SharedVaultServerHash[]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type AcceptInviteResponse = {
|
||||
success: boolean
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { SharedVaultInviteServerHash } from '@standardnotes/responses'
|
||||
|
||||
export type CreateSharedVaultInviteResponse = {
|
||||
invite: SharedVaultInviteServerHash
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type DeclineInviteResponse = {
|
||||
success: boolean
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type DeleteAllSharedVaultInvitesResponse = {
|
||||
success: boolean
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type DeleteInviteResponse = {
|
||||
success: boolean
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { SharedVaultInviteServerHash } from '@standardnotes/responses'
|
||||
|
||||
export type GetOutboundUserInvitesResponse = {
|
||||
invites: SharedVaultInviteServerHash[]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { SharedVaultInviteServerHash } from '@standardnotes/responses'
|
||||
|
||||
export type GetSharedVaultInvitesResponse = {
|
||||
invites: SharedVaultInviteServerHash[]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { SharedVaultInviteServerHash } from '@standardnotes/responses'
|
||||
|
||||
export type GetUserInvitesResponse = {
|
||||
invites: SharedVaultInviteServerHash[]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { SharedVaultInviteServerHash } from '@standardnotes/responses'
|
||||
|
||||
export type UpdateSharedVaultInviteResponse = {
|
||||
invite: SharedVaultInviteServerHash
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type DeleteSharedVaultUserResponse = {
|
||||
success: boolean
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { SharedVaultUserServerHash } from '@standardnotes/responses'
|
||||
|
||||
export type GetSharedVaultUsersResponse = {
|
||||
users: SharedVaultUserServerHash[]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export type UserUpdateResponse = {
|
||||
user: {
|
||||
uuid: string
|
||||
email: string
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,9 @@ export * from './Subscription/SubscriptionInviteCancelResponseBody'
|
||||
export * from './Subscription/SubscriptionInviteDeclineResponseBody'
|
||||
export * from './Subscription/SubscriptionInviteListResponseBody'
|
||||
export * from './Subscription/SubscriptionInviteResponseBody'
|
||||
|
||||
export * from './User/UserDeletionResponseBody'
|
||||
export * from './User/UserRegistrationResponseBody'
|
||||
|
||||
export * from './UserRequest/UserRequestResponseBody'
|
||||
export * from './WebSocket/WebSocketConnectionTokenResponseBody'
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { HttpResponse } from '@standardnotes/responses'
|
||||
import { HttpServiceInterface } from '../../Http'
|
||||
import { CreateAsymmetricMessageParams } from '../../Request/AsymmetricMessage/CreateAsymmetricMessageParams'
|
||||
import { CreateAsymmetricMessageResponse } from '../../Response/AsymmetricMessage/CreateAsymmetricMessageResponse'
|
||||
import { AsymmetricMessagesPaths } from './Paths'
|
||||
import { GetUserAsymmetricMessagesResponse } from '../../Response/AsymmetricMessage/GetUserAsymmetricMessagesResponse'
|
||||
import { AsymmetricMessageServerInterface } from './AsymmetricMessageServerInterface'
|
||||
import { DeleteAsymmetricMessageRequestParams } from '../../Request/AsymmetricMessage/DeleteAsymmetricMessageRequestParams'
|
||||
import { DeleteAsymmetricMessageResponse } from '../../Response/AsymmetricMessage/DeleteAsymmetricMessageResponse'
|
||||
|
||||
export class AsymmetricMessageServer implements AsymmetricMessageServerInterface {
|
||||
constructor(private httpService: HttpServiceInterface) {}
|
||||
|
||||
createMessage(params: CreateAsymmetricMessageParams): Promise<HttpResponse<CreateAsymmetricMessageResponse>> {
|
||||
return this.httpService.post(AsymmetricMessagesPaths.createMessage, {
|
||||
recipient_uuid: params.recipientUuid,
|
||||
encrypted_message: params.encryptedMessage,
|
||||
replaceability_identifier: params.replaceabilityIdentifier,
|
||||
})
|
||||
}
|
||||
|
||||
getInboundUserMessages(): Promise<HttpResponse<GetUserAsymmetricMessagesResponse>> {
|
||||
return this.httpService.get(AsymmetricMessagesPaths.getInboundUserMessages())
|
||||
}
|
||||
|
||||
getOutboundUserMessages(): Promise<HttpResponse<GetUserAsymmetricMessagesResponse>> {
|
||||
return this.httpService.get(AsymmetricMessagesPaths.getOutboundUserMessages())
|
||||
}
|
||||
|
||||
getMessages(): Promise<HttpResponse<GetUserAsymmetricMessagesResponse>> {
|
||||
return this.httpService.get(AsymmetricMessagesPaths.getMessages)
|
||||
}
|
||||
|
||||
deleteMessage(params: DeleteAsymmetricMessageRequestParams): Promise<HttpResponse<DeleteAsymmetricMessageResponse>> {
|
||||
return this.httpService.delete(AsymmetricMessagesPaths.deleteMessage(params.messageUuid))
|
||||
}
|
||||
|
||||
deleteAllInboundMessages(): Promise<HttpResponse<{ success: boolean }>> {
|
||||
return this.httpService.delete(AsymmetricMessagesPaths.deleteAllInboundMessages)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { HttpResponse } from '@standardnotes/responses'
|
||||
import { CreateAsymmetricMessageParams } from '../../Request/AsymmetricMessage/CreateAsymmetricMessageParams'
|
||||
import { CreateAsymmetricMessageResponse } from '../../Response/AsymmetricMessage/CreateAsymmetricMessageResponse'
|
||||
import { GetUserAsymmetricMessagesResponse } from '../../Response/AsymmetricMessage/GetUserAsymmetricMessagesResponse'
|
||||
import { DeleteAsymmetricMessageRequestParams } from '../../Request/AsymmetricMessage/DeleteAsymmetricMessageRequestParams'
|
||||
import { DeleteAsymmetricMessageResponse } from '../../Response/AsymmetricMessage/DeleteAsymmetricMessageResponse'
|
||||
|
||||
export interface AsymmetricMessageServerInterface {
|
||||
createMessage(params: CreateAsymmetricMessageParams): Promise<HttpResponse<CreateAsymmetricMessageResponse>>
|
||||
|
||||
getInboundUserMessages(): Promise<HttpResponse<GetUserAsymmetricMessagesResponse>>
|
||||
getOutboundUserMessages(): Promise<HttpResponse<GetUserAsymmetricMessagesResponse>>
|
||||
getMessages(): Promise<HttpResponse<GetUserAsymmetricMessagesResponse>>
|
||||
|
||||
deleteMessage(params: DeleteAsymmetricMessageRequestParams): Promise<HttpResponse<DeleteAsymmetricMessageResponse>>
|
||||
deleteAllInboundMessages(): Promise<HttpResponse<{ success: boolean }>>
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export const AsymmetricMessagesPaths = {
|
||||
createMessage: '/v1/asymmetric-messages',
|
||||
getMessages: '/v1/asymmetric-messages',
|
||||
updateMessage: (messageUuid: string) => `/v1/asymmetric-messages/${messageUuid}`,
|
||||
getInboundUserMessages: () => '/v1/asymmetric-messages',
|
||||
getOutboundUserMessages: () => '/v1/asymmetric-messages/outbound',
|
||||
deleteMessage: (messageUuid: string) => `/v1/asymmetric-messages/${messageUuid}`,
|
||||
deleteAllInboundMessages: '/v1/asymmetric-messages/inbound',
|
||||
}
|
||||
7
packages/api/src/Domain/Server/SharedVault/Paths.ts
Normal file
7
packages/api/src/Domain/Server/SharedVault/Paths.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const SharedVaultsPaths = {
|
||||
getSharedVaults: '/v1/shared-vaults',
|
||||
createSharedVault: '/v1/shared-vaults',
|
||||
deleteSharedVault: (sharedVaultUuid: string) => `/v1/shared-vaults/${sharedVaultUuid}`,
|
||||
updateSharedVault: (sharedVaultUuid: string) => `/v1/shared-vaults/${sharedVaultUuid}`,
|
||||
createSharedVaultFileValetToken: (sharedVaultUuid: string) => `/v1/shared-vaults/${sharedVaultUuid}/valet-tokens`,
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { HttpResponse } from '@standardnotes/responses'
|
||||
import { HttpServiceInterface } from '../../Http'
|
||||
import { SharedVaultServerInterface } from './SharedVaultServerInterface'
|
||||
import { SharedVaultsPaths } from './Paths'
|
||||
import { CreateSharedVaultResponse } from '../../Response/SharedVault/CreateSharedVaultResponse'
|
||||
import { GetSharedVaultsResponse } from '../../Response/SharedVault/GetSharedVaultsResponse'
|
||||
import { CreateSharedVaultValetTokenResponse } from '../../Response/SharedVault/CreateSharedVaultValetTokenResponse'
|
||||
import { CreateSharedVaultValetTokenParams } from '../../Request/SharedVault/CreateSharedVaultValetTokenParams'
|
||||
|
||||
export class SharedVaultServer implements SharedVaultServerInterface {
|
||||
constructor(private httpService: HttpServiceInterface) {}
|
||||
|
||||
getSharedVaults(): Promise<HttpResponse<GetSharedVaultsResponse>> {
|
||||
return this.httpService.get(SharedVaultsPaths.getSharedVaults)
|
||||
}
|
||||
|
||||
createSharedVault(): Promise<HttpResponse<CreateSharedVaultResponse>> {
|
||||
return this.httpService.post(SharedVaultsPaths.createSharedVault)
|
||||
}
|
||||
|
||||
deleteSharedVault(params: { sharedVaultUuid: string }): Promise<HttpResponse<boolean>> {
|
||||
return this.httpService.delete(SharedVaultsPaths.deleteSharedVault(params.sharedVaultUuid))
|
||||
}
|
||||
|
||||
createSharedVaultFileValetToken(
|
||||
params: CreateSharedVaultValetTokenParams,
|
||||
): Promise<HttpResponse<CreateSharedVaultValetTokenResponse>> {
|
||||
return this.httpService.post(SharedVaultsPaths.createSharedVaultFileValetToken(params.sharedVaultUuid), {
|
||||
file_uuid: params.fileUuid,
|
||||
remote_identifier: params.remoteIdentifier,
|
||||
operation: params.operation,
|
||||
unencrypted_file_size: params.unencryptedFileSize,
|
||||
move_operation_type: params.moveOperationType,
|
||||
shared_vault_to_shared_vault_move_target_uuid: params.sharedVaultToSharedVaultMoveTargetUuid,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { HttpResponse } from '@standardnotes/responses'
|
||||
import { CreateSharedVaultResponse } from '../../Response/SharedVault/CreateSharedVaultResponse'
|
||||
|
||||
import { GetSharedVaultsResponse } from '../../Response/SharedVault/GetSharedVaultsResponse'
|
||||
import { CreateSharedVaultValetTokenResponse } from '../../Response/SharedVault/CreateSharedVaultValetTokenResponse'
|
||||
import { CreateSharedVaultValetTokenParams } from '../../Request/SharedVault/CreateSharedVaultValetTokenParams'
|
||||
|
||||
export interface SharedVaultServerInterface {
|
||||
getSharedVaults(): Promise<HttpResponse<GetSharedVaultsResponse>>
|
||||
|
||||
createSharedVault(): Promise<HttpResponse<CreateSharedVaultResponse>>
|
||||
deleteSharedVault(params: { sharedVaultUuid: string }): Promise<HttpResponse<boolean>>
|
||||
|
||||
createSharedVaultFileValetToken(
|
||||
params: CreateSharedVaultValetTokenParams,
|
||||
): Promise<HttpResponse<CreateSharedVaultValetTokenResponse>>
|
||||
}
|
||||
16
packages/api/src/Domain/Server/SharedVaultInvites/Paths.ts
Normal file
16
packages/api/src/Domain/Server/SharedVaultInvites/Paths.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
export const SharedVaultInvitesPaths = {
|
||||
createInvite: (sharedVaultUuid: string) => `/v1/shared-vaults/${sharedVaultUuid}/invites`,
|
||||
updateInvite: (sharedVaultUuid: string, inviteUuid: string) =>
|
||||
`/v1/shared-vaults/${sharedVaultUuid}/invites/${inviteUuid}`,
|
||||
acceptInvite: (sharedVaultUuid: string, inviteUuid: string) =>
|
||||
`/v1/shared-vaults/${sharedVaultUuid}/invites/${inviteUuid}/accept`,
|
||||
declineInvite: (sharedVaultUuid: string, inviteUuid: string) =>
|
||||
`/v1/shared-vaults/${sharedVaultUuid}/invites/${inviteUuid}/decline`,
|
||||
getInboundUserInvites: () => '/v1/shared-vaults/invites',
|
||||
getOutboundUserInvites: () => '/v1/shared-vaults/invites/outbound',
|
||||
getSharedVaultInvites: (sharedVaultUuid: string) => `/v1/shared-vaults/${sharedVaultUuid}/invites`,
|
||||
deleteInvite: (sharedVaultUuid: string, inviteUuid: string) =>
|
||||
`/v1/shared-vaults/${sharedVaultUuid}/invites/${inviteUuid}`,
|
||||
deleteAllSharedVaultInvites: (sharedVaultUuid: string) => `/v1/shared-vaults/${sharedVaultUuid}/invites`,
|
||||
deleteAllInboundInvites: '/v1/shared-vaults/invites/inbound',
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { HttpResponse } from '@standardnotes/responses'
|
||||
import { HttpServiceInterface } from '../../Http'
|
||||
|
||||
import { AcceptInviteRequestParams } from '../../Request/SharedVaultInvites/AcceptInviteRequestParams'
|
||||
import { AcceptInviteResponse } from '../../Response/SharedVaultInvites/AcceptInviteResponse'
|
||||
import { CreateSharedVaultInviteParams } from '../../Request/SharedVaultInvites/CreateSharedVaultInviteParams'
|
||||
import { CreateSharedVaultInviteResponse } from '../../Response/SharedVaultInvites/CreateSharedVaultInviteResponse'
|
||||
import { DeclineInviteRequestParams } from '../../Request/SharedVaultInvites/DeclineInviteRequestParams'
|
||||
import { DeclineInviteResponse } from '../../Response/SharedVaultInvites/DeclineInviteResponse'
|
||||
import { DeleteInviteRequestParams } from '../../Request/SharedVaultInvites/DeleteInviteRequestParams'
|
||||
import { DeleteInviteResponse } from '../../Response/SharedVaultInvites/DeleteInviteResponse'
|
||||
import { GetSharedVaultInvitesRequestParams } from '../../Request/SharedVaultInvites/GetSharedVaultInvitesRequestParams'
|
||||
import { GetSharedVaultInvitesResponse } from '../../Response/SharedVaultInvites/GetSharedVaultInvitesResponse'
|
||||
import { GetUserInvitesResponse } from '../../Response/SharedVaultInvites/GetUserInvitesResponse'
|
||||
import { SharedVaultInvitesPaths } from './Paths'
|
||||
import { SharedVaultInvitesServerInterface } from './SharedVaultInvitesServerInterface'
|
||||
import { UpdateSharedVaultInviteParams } from '../../Request/SharedVaultInvites/UpdateSharedVaultInviteParams'
|
||||
import { UpdateSharedVaultInviteResponse } from '../../Response/SharedVaultInvites/UpdateSharedVaultInviteResponse'
|
||||
import { DeleteAllSharedVaultInvitesRequestParams } from '../../Request/SharedVaultInvites/DeleteAllSharedVaultInvitesRequestParams'
|
||||
import { DeleteAllSharedVaultInvitesResponse } from '../../Response/SharedVaultInvites/DeleteAllSharedVaultInvitesResponse'
|
||||
|
||||
export class SharedVaultInvitesServer implements SharedVaultInvitesServerInterface {
|
||||
constructor(private httpService: HttpServiceInterface) {}
|
||||
|
||||
createInvite(params: CreateSharedVaultInviteParams): Promise<HttpResponse<CreateSharedVaultInviteResponse>> {
|
||||
return this.httpService.post(SharedVaultInvitesPaths.createInvite(params.sharedVaultUuid), {
|
||||
recipient_uuid: params.recipientUuid,
|
||||
encrypted_message: params.encryptedMessage,
|
||||
permissions: params.permissions,
|
||||
})
|
||||
}
|
||||
|
||||
updateInvite(params: UpdateSharedVaultInviteParams): Promise<HttpResponse<UpdateSharedVaultInviteResponse>> {
|
||||
return this.httpService.patch(SharedVaultInvitesPaths.updateInvite(params.sharedVaultUuid, params.inviteUuid), {
|
||||
encrypted_message: params.encryptedMessage,
|
||||
permissions: params.permissions,
|
||||
})
|
||||
}
|
||||
|
||||
acceptInvite(params: AcceptInviteRequestParams): Promise<HttpResponse<AcceptInviteResponse>> {
|
||||
return this.httpService.post(SharedVaultInvitesPaths.acceptInvite(params.sharedVaultUuid, params.inviteUuid))
|
||||
}
|
||||
|
||||
declineInvite(params: DeclineInviteRequestParams): Promise<HttpResponse<DeclineInviteResponse>> {
|
||||
return this.httpService.post(SharedVaultInvitesPaths.declineInvite(params.sharedVaultUuid, params.inviteUuid))
|
||||
}
|
||||
|
||||
getInboundUserInvites(): Promise<HttpResponse<GetUserInvitesResponse>> {
|
||||
return this.httpService.get(SharedVaultInvitesPaths.getInboundUserInvites())
|
||||
}
|
||||
|
||||
getOutboundUserInvites(): Promise<HttpResponse<GetUserInvitesResponse>> {
|
||||
return this.httpService.get(SharedVaultInvitesPaths.getOutboundUserInvites())
|
||||
}
|
||||
|
||||
getSharedVaultInvites(
|
||||
params: GetSharedVaultInvitesRequestParams,
|
||||
): Promise<HttpResponse<GetSharedVaultInvitesResponse>> {
|
||||
return this.httpService.get(SharedVaultInvitesPaths.getSharedVaultInvites(params.sharedVaultUuid))
|
||||
}
|
||||
|
||||
deleteInvite(params: DeleteInviteRequestParams): Promise<HttpResponse<DeleteInviteResponse>> {
|
||||
return this.httpService.delete(SharedVaultInvitesPaths.deleteInvite(params.sharedVaultUuid, params.inviteUuid))
|
||||
}
|
||||
|
||||
deleteAllSharedVaultInvites(
|
||||
params: DeleteAllSharedVaultInvitesRequestParams,
|
||||
): Promise<HttpResponse<DeleteAllSharedVaultInvitesResponse>> {
|
||||
return this.httpService.delete(SharedVaultInvitesPaths.deleteAllSharedVaultInvites(params.sharedVaultUuid))
|
||||
}
|
||||
|
||||
deleteAllInboundInvites(): Promise<HttpResponse<{ success: boolean }>> {
|
||||
return this.httpService.delete(SharedVaultInvitesPaths.deleteAllInboundInvites)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { HttpResponse } from '@standardnotes/responses'
|
||||
import { AcceptInviteRequestParams } from '../../Request/SharedVaultInvites/AcceptInviteRequestParams'
|
||||
import { AcceptInviteResponse } from '../../Response/SharedVaultInvites/AcceptInviteResponse'
|
||||
import { CreateSharedVaultInviteParams } from '../../Request/SharedVaultInvites/CreateSharedVaultInviteParams'
|
||||
import { CreateSharedVaultInviteResponse } from '../../Response/SharedVaultInvites/CreateSharedVaultInviteResponse'
|
||||
import { DeclineInviteRequestParams } from '../../Request/SharedVaultInvites/DeclineInviteRequestParams'
|
||||
import { DeclineInviteResponse } from '../../Response/SharedVaultInvites/DeclineInviteResponse'
|
||||
import { DeleteInviteRequestParams } from '../../Request/SharedVaultInvites/DeleteInviteRequestParams'
|
||||
import { DeleteInviteResponse } from '../../Response/SharedVaultInvites/DeleteInviteResponse'
|
||||
import { GetSharedVaultInvitesRequestParams } from '../../Request/SharedVaultInvites/GetSharedVaultInvitesRequestParams'
|
||||
import { GetSharedVaultInvitesResponse } from '../../Response/SharedVaultInvites/GetSharedVaultInvitesResponse'
|
||||
import { GetUserInvitesResponse } from '../../Response/SharedVaultInvites/GetUserInvitesResponse'
|
||||
import { UpdateSharedVaultInviteParams } from '../../Request/SharedVaultInvites/UpdateSharedVaultInviteParams'
|
||||
import { UpdateSharedVaultInviteResponse } from '../../Response/SharedVaultInvites/UpdateSharedVaultInviteResponse'
|
||||
import { DeleteAllSharedVaultInvitesRequestParams } from '../../Request/SharedVaultInvites/DeleteAllSharedVaultInvitesRequestParams'
|
||||
import { DeleteAllSharedVaultInvitesResponse } from '../../Response/SharedVaultInvites/DeleteAllSharedVaultInvitesResponse'
|
||||
|
||||
export interface SharedVaultInvitesServerInterface {
|
||||
createInvite(params: CreateSharedVaultInviteParams): Promise<HttpResponse<CreateSharedVaultInviteResponse>>
|
||||
updateInvite(params: UpdateSharedVaultInviteParams): Promise<HttpResponse<UpdateSharedVaultInviteResponse>>
|
||||
acceptInvite(params: AcceptInviteRequestParams): Promise<HttpResponse<AcceptInviteResponse>>
|
||||
declineInvite(params: DeclineInviteRequestParams): Promise<HttpResponse<DeclineInviteResponse>>
|
||||
|
||||
getInboundUserInvites(): Promise<HttpResponse<GetUserInvitesResponse>>
|
||||
getOutboundUserInvites(): Promise<HttpResponse<GetUserInvitesResponse>>
|
||||
getSharedVaultInvites(
|
||||
params: GetSharedVaultInvitesRequestParams,
|
||||
): Promise<HttpResponse<GetSharedVaultInvitesResponse>>
|
||||
|
||||
deleteAllSharedVaultInvites(
|
||||
params: DeleteAllSharedVaultInvitesRequestParams,
|
||||
): Promise<HttpResponse<DeleteAllSharedVaultInvitesResponse>>
|
||||
deleteInvite(params: DeleteInviteRequestParams): Promise<HttpResponse<DeleteInviteResponse>>
|
||||
deleteAllInboundInvites(): Promise<HttpResponse<{ success: boolean }>>
|
||||
}
|
||||
5
packages/api/src/Domain/Server/SharedVaultUsers/Paths.ts
Normal file
5
packages/api/src/Domain/Server/SharedVaultUsers/Paths.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const SharedVaultUsersPaths = {
|
||||
getSharedVaultUsers: (sharedVaultUuid: string) => `/v1/shared-vaults/${sharedVaultUuid}/users`,
|
||||
deleteSharedVaultUser: (sharedVaultUuid: string, userUuid: string) =>
|
||||
`/v1/shared-vaults/${sharedVaultUuid}/users/${userUuid}`,
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { HttpResponse } from '@standardnotes/responses'
|
||||
import { HttpServiceInterface } from '../../Http'
|
||||
import { GetSharedVaultUsersRequestParams } from '../../Request/SharedVaultUser/GetSharedVaultUsersRequestParams'
|
||||
import { DeleteSharedVaultUserRequestParams } from '../../Request/SharedVaultUser/DeleteSharedVaultUserRequestParams'
|
||||
import { DeleteSharedVaultUserResponse } from '../../Response/SharedVaultUsers/DeleteSharedVaultUserResponse'
|
||||
import { SharedVaultUsersServerInterface } from './SharedVaultUsersServerInterface'
|
||||
import { SharedVaultUsersPaths } from './Paths'
|
||||
import { GetSharedVaultUsersResponse } from '../../Response/SharedVaultUsers/GetSharedVaultUsersResponse'
|
||||
|
||||
export class SharedVaultUsersServer implements SharedVaultUsersServerInterface {
|
||||
constructor(private httpService: HttpServiceInterface) {}
|
||||
|
||||
getSharedVaultUsers(params: GetSharedVaultUsersRequestParams): Promise<HttpResponse<GetSharedVaultUsersResponse>> {
|
||||
return this.httpService.get(SharedVaultUsersPaths.getSharedVaultUsers(params.sharedVaultUuid))
|
||||
}
|
||||
|
||||
deleteSharedVaultUser(
|
||||
params: DeleteSharedVaultUserRequestParams,
|
||||
): Promise<HttpResponse<DeleteSharedVaultUserResponse>> {
|
||||
return this.httpService.delete(SharedVaultUsersPaths.deleteSharedVaultUser(params.sharedVaultUuid, params.userUuid))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { HttpResponse } from '@standardnotes/responses'
|
||||
import { GetSharedVaultUsersRequestParams } from '../../Request/SharedVaultUser/GetSharedVaultUsersRequestParams'
|
||||
import { DeleteSharedVaultUserRequestParams } from '../../Request/SharedVaultUser/DeleteSharedVaultUserRequestParams'
|
||||
import { DeleteSharedVaultUserResponse } from '../../Response/SharedVaultUsers/DeleteSharedVaultUserResponse'
|
||||
import { GetSharedVaultUsersResponse } from '../../Response/SharedVaultUsers/GetSharedVaultUsersResponse'
|
||||
|
||||
export interface SharedVaultUsersServerInterface {
|
||||
getSharedVaultUsers(params: GetSharedVaultUsersRequestParams): Promise<HttpResponse<GetSharedVaultUsersResponse>>
|
||||
|
||||
deleteSharedVaultUser(
|
||||
params: DeleteSharedVaultUserRequestParams,
|
||||
): Promise<HttpResponse<DeleteSharedVaultUserResponse>>
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
const UserPaths = {
|
||||
register: '/v1/users',
|
||||
updateAccount: (userUuid: string) => `/v1/users/${userUuid}`,
|
||||
deleteAccount: (userUuid: string) => `/v1/users/${userUuid}`,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UserUpdateResponse } from './../../Response/User/UserUpdateResponse'
|
||||
import { HttpServiceInterface } from '../../Http/HttpServiceInterface'
|
||||
import { UserDeletionRequestParams } from '../../Request/User/UserDeletionRequestParams'
|
||||
import { UserRegistrationRequestParams } from '../../Request/User/UserRegistrationRequestParams'
|
||||
@@ -6,6 +7,7 @@ import { UserDeletionResponseBody } from '../../Response/User/UserDeletionRespon
|
||||
import { UserRegistrationResponseBody } from '../../Response/User/UserRegistrationResponseBody'
|
||||
import { Paths } from './Paths'
|
||||
import { UserServerInterface } from './UserServerInterface'
|
||||
import { UserUpdateRequestParams } from '../../Request/User/UserUpdateRequestParams'
|
||||
|
||||
export class UserServer implements UserServerInterface {
|
||||
constructor(private httpService: HttpServiceInterface) {}
|
||||
@@ -17,4 +19,8 @@ export class UserServer implements UserServerInterface {
|
||||
async register(params: UserRegistrationRequestParams): Promise<HttpResponse<UserRegistrationResponseBody>> {
|
||||
return this.httpService.post(Paths.v1.register, params)
|
||||
}
|
||||
|
||||
async update(params: UserUpdateRequestParams): Promise<HttpResponse<UserUpdateResponse>> {
|
||||
return this.httpService.patch(Paths.v1.updateAccount(params.user_uuid), params)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,11 @@ import { UserDeletionRequestParams } from '../../Request/User/UserDeletionReques
|
||||
import { UserRegistrationRequestParams } from '../../Request/User/UserRegistrationRequestParams'
|
||||
import { UserDeletionResponseBody } from '../../Response/User/UserDeletionResponseBody'
|
||||
import { UserRegistrationResponseBody } from '../../Response/User/UserRegistrationResponseBody'
|
||||
import { UserUpdateResponse } from '../../Response/User/UserUpdateResponse'
|
||||
import { UserUpdateRequestParams } from '../../Request/User/UserUpdateRequestParams'
|
||||
|
||||
export interface UserServerInterface {
|
||||
register(params: UserRegistrationRequestParams): Promise<HttpResponse<UserRegistrationResponseBody>>
|
||||
deleteAccount(params: UserDeletionRequestParams): Promise<HttpResponse<UserDeletionResponseBody>>
|
||||
update(params: UserUpdateRequestParams): Promise<HttpResponse<UserUpdateResponse>>
|
||||
}
|
||||
|
||||
@@ -1,14 +1,32 @@
|
||||
export * from './Auth/AuthServer'
|
||||
export * from './Auth/AuthServerInterface'
|
||||
|
||||
export * from './Authenticator/AuthenticatorServer'
|
||||
export * from './Authenticator/AuthenticatorServerInterface'
|
||||
|
||||
export * from './Revision/RevisionServer'
|
||||
export * from './Revision/RevisionServerInterface'
|
||||
|
||||
export * from './AsymmetricMessage/AsymmetricMessageServer'
|
||||
export * from './AsymmetricMessage/AsymmetricMessageServerInterface'
|
||||
|
||||
export * from './SharedVault/SharedVaultServer'
|
||||
export * from './SharedVault/SharedVaultServerInterface'
|
||||
|
||||
export * from './SharedVaultUsers/SharedVaultUsersServer'
|
||||
export * from './SharedVaultUsers/SharedVaultUsersServerInterface'
|
||||
|
||||
export * from './Subscription/SubscriptionServer'
|
||||
export * from './Subscription/SubscriptionServerInterface'
|
||||
|
||||
export * from './SharedVaultInvites/SharedVaultInvitesServer'
|
||||
export * from './SharedVaultInvites/SharedVaultInvitesServerInterface'
|
||||
|
||||
export * from './User/UserServer'
|
||||
export * from './User/UserServerInterface'
|
||||
|
||||
export * from './UserRequest/UserRequestServer'
|
||||
export * from './UserRequest/UserRequestServerInterface'
|
||||
|
||||
export * from './WebSocket/WebSocketServer'
|
||||
export * from './WebSocket/WebSocketServerInterface'
|
||||
|
||||
@@ -5,22 +5,15 @@
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
"description": "Payload encryption used in SNJS library",
|
||||
"main": "dist/index.js",
|
||||
"main": "./src/index.ts",
|
||||
"private": true,
|
||||
"author": "Standard Notes",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"clean": "rm -fr dist",
|
||||
"prestart": "yarn clean",
|
||||
"start": "tsc -p tsconfig.json --watch",
|
||||
"prebuild": "yarn clean",
|
||||
"build": "tsc -p tsconfig.json",
|
||||
"lint": "eslint src --ext .ts",
|
||||
"format": "prettier --write src",
|
||||
"test": "jest"
|
||||
"test": "jest",
|
||||
"build": "echo 'Empty build script required for yarn topological install'"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@standardnotes/config": "2.4.3",
|
||||
@@ -35,7 +28,7 @@
|
||||
"typescript": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.46.6",
|
||||
"@standardnotes/common": "^1.48.3",
|
||||
"@standardnotes/models": "workspace:*",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
"@standardnotes/sncrypto-common": "workspace:*",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { SodiumConstant } from '@standardnotes/sncrypto-common'
|
||||
|
||||
export const V001Algorithm = Object.freeze({
|
||||
SaltSeedLength: 128,
|
||||
/**
|
||||
@@ -41,11 +43,21 @@ export enum V004Algorithm {
|
||||
ArgonIterations = 5,
|
||||
ArgonMemLimit = 67108864,
|
||||
ArgonOutputKeyBytes = 64,
|
||||
|
||||
EncryptionKeyLength = 256,
|
||||
EncryptionNonceLength = 192,
|
||||
}
|
||||
|
||||
export enum V005Algorithm {
|
||||
AsymmetricEncryptionNonceLength = 192,
|
||||
SymmetricEncryptionNonceLength = 192,
|
||||
|
||||
MasterKeyEncryptionKeyPairSubKeyNumber = 1,
|
||||
MasterKeyEncryptionKeyPairSubKeyContext = 'sn-pkc-e',
|
||||
MasterKeyEncryptionKeyPairSubKeyBytes = SodiumConstant.crypto_box_SEEDBYTES,
|
||||
|
||||
MasterKeySigningKeyPairSubKeyNumber = 2,
|
||||
MasterKeySigningKeyPairSubKeyContext = 'sn-pkc-s',
|
||||
MasterKeySigningKeyPairSubKeyBytes = SodiumConstant.crypto_sign_SEEDBYTES,
|
||||
|
||||
PayloadKeyHashingKeySubKeyNumber = 1,
|
||||
PayloadKeyHashingKeySubKeyContext = 'sn-sym-h',
|
||||
PayloadKeyHashingKeySubKeyBytes = SodiumConstant.crypto_generichash_KEYBYTES,
|
||||
}
|
||||
|
||||
@@ -7,11 +7,10 @@ import {
|
||||
HistoryEntryInterface,
|
||||
ItemsKeyContent,
|
||||
ItemsKeyInterface,
|
||||
RootKeyInterface,
|
||||
} from '@standardnotes/models'
|
||||
|
||||
export function isItemsKey(x: ItemsKeyInterface | RootKeyInterface): x is ItemsKeyInterface {
|
||||
return x.content_type === ContentType.ItemsKey
|
||||
export function isItemsKey(x: unknown): x is ItemsKeyInterface {
|
||||
return (x as ItemsKeyInterface).content_type === ContentType.ItemsKey
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { ContentType, ProtocolVersion } from '@standardnotes/common'
|
||||
import {
|
||||
ConflictStrategy,
|
||||
DecryptedItem,
|
||||
DecryptedItemInterface,
|
||||
DecryptedPayloadInterface,
|
||||
HistoryEntryInterface,
|
||||
KeySystemItemsKeyContent,
|
||||
KeySystemItemsKeyInterface,
|
||||
} from '@standardnotes/models'
|
||||
|
||||
export function isKeySystemItemsKey(x: unknown): x is KeySystemItemsKeyInterface {
|
||||
return (x as KeySystemItemsKeyInterface).content_type === ContentType.KeySystemItemsKey
|
||||
}
|
||||
|
||||
/**
|
||||
* A key used to encrypt other items. Items keys are synced and persisted.
|
||||
*/
|
||||
export class KeySystemItemsKey extends DecryptedItem<KeySystemItemsKeyContent> implements KeySystemItemsKeyInterface {
|
||||
creationTimestamp: number
|
||||
keyVersion: ProtocolVersion
|
||||
itemsKey: string
|
||||
rootKeyToken: string
|
||||
|
||||
constructor(payload: DecryptedPayloadInterface<KeySystemItemsKeyContent>) {
|
||||
super(payload)
|
||||
|
||||
this.creationTimestamp = payload.content.creationTimestamp
|
||||
this.keyVersion = payload.content.version
|
||||
this.itemsKey = this.payload.content.itemsKey
|
||||
this.rootKeyToken = this.payload.content.rootKeyToken
|
||||
}
|
||||
|
||||
/** Do not duplicate vault items keys. Always keep original */
|
||||
override strategyWhenConflictingWithItem(
|
||||
_item: DecryptedItemInterface,
|
||||
_previousRevision?: HistoryEntryInterface,
|
||||
): ConflictStrategy {
|
||||
return ConflictStrategy.KeepBase
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { DecryptedItemMutator, KeySystemItemsKeyContent } from '@standardnotes/models'
|
||||
|
||||
export class KeySystemItemsKeyMutator extends DecryptedItemMutator<KeySystemItemsKeyContent> {}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { DecryptedItemMutator, KeySystemItemsKeyContent, RegisterItemClass } from '@standardnotes/models'
|
||||
import { KeySystemItemsKey } from './KeySystemItemsKey'
|
||||
import { KeySystemItemsKeyMutator } from './KeySystemItemsKeyMutator'
|
||||
|
||||
RegisterItemClass(
|
||||
ContentType.KeySystemItemsKey,
|
||||
KeySystemItemsKey,
|
||||
KeySystemItemsKeyMutator as unknown as DecryptedItemMutator<KeySystemItemsKeyContent>,
|
||||
)
|
||||
@@ -5,11 +5,12 @@ import {
|
||||
PayloadTimestampDefaults,
|
||||
RootKeyContent,
|
||||
RootKeyContentSpecialized,
|
||||
RootKeyInterface,
|
||||
} from '@standardnotes/models'
|
||||
import { UuidGenerator } from '@standardnotes/utils'
|
||||
import { SNRootKey } from './RootKey'
|
||||
|
||||
export function CreateNewRootKey(content: RootKeyContentSpecialized): SNRootKey {
|
||||
export function CreateNewRootKey<K extends RootKeyInterface>(content: RootKeyContentSpecialized): K {
|
||||
const uuid = UuidGenerator.GenerateUuid()
|
||||
|
||||
const payload = new DecryptedPayload<RootKeyContent>({
|
||||
@@ -19,7 +20,7 @@ export function CreateNewRootKey(content: RootKeyContentSpecialized): SNRootKey
|
||||
...PayloadTimestampDefaults(),
|
||||
})
|
||||
|
||||
return new SNRootKey(payload)
|
||||
return new SNRootKey(payload) as K
|
||||
}
|
||||
|
||||
export function FillRootKeyContent(content: Partial<RootKeyContentSpecialized>): RootKeyContent {
|
||||
@@ -37,15 +38,3 @@ export function FillRootKeyContent(content: Partial<RootKeyContentSpecialized>):
|
||||
|
||||
return FillItemContentSpecialized(content)
|
||||
}
|
||||
|
||||
export function ContentTypeUsesRootKeyEncryption(contentType: ContentType): boolean {
|
||||
return (
|
||||
contentType === ContentType.RootKey ||
|
||||
contentType === ContentType.ItemsKey ||
|
||||
contentType === ContentType.EncryptedStorage
|
||||
)
|
||||
}
|
||||
|
||||
export function ItemContentTypeUsesRootKeyEncryption(contentType: ContentType): boolean {
|
||||
return contentType === ContentType.ItemsKey
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
RootKeyContentInStorage,
|
||||
RootKeyInterface,
|
||||
} from '@standardnotes/models'
|
||||
import { timingSafeEqual } from '@standardnotes/sncrypto-common'
|
||||
import { PkcKeyPair, timingSafeEqual } from '@standardnotes/sncrypto-common'
|
||||
import { SNRootKeyParams } from './RootKeyParams'
|
||||
|
||||
/**
|
||||
@@ -47,6 +47,14 @@ export class SNRootKey extends DecryptedItem<RootKeyContent> implements RootKeyI
|
||||
return this.content.serverPassword
|
||||
}
|
||||
|
||||
get encryptionKeyPair(): PkcKeyPair | undefined {
|
||||
return this.content.encryptionKeyPair
|
||||
}
|
||||
|
||||
get signingKeyPair(): PkcKeyPair | undefined {
|
||||
return this.content.signingKeyPair
|
||||
}
|
||||
|
||||
/** 003 and below only. */
|
||||
public get dataAuthenticationKey(): string | undefined {
|
||||
return this.content.dataAuthenticationKey
|
||||
@@ -84,6 +92,8 @@ export class SNRootKey extends DecryptedItem<RootKeyContent> implements RootKeyI
|
||||
const values: NamespacedRootKeyInKeychain = {
|
||||
version: this.keyVersion,
|
||||
masterKey: this.masterKey,
|
||||
encryptionKeyPair: this.encryptionKeyPair,
|
||||
signingKeyPair: this.signingKeyPair,
|
||||
}
|
||||
|
||||
if (this.dataAuthenticationKey) {
|
||||
|
||||
@@ -8,8 +8,13 @@ import {
|
||||
ItemsKeyContent,
|
||||
ItemsKeyInterface,
|
||||
PayloadTimestampDefaults,
|
||||
KeySystemItemsKeyInterface,
|
||||
KeySystemIdentifier,
|
||||
KeySystemRootKeyInterface,
|
||||
RootKeyInterface,
|
||||
KeySystemRootKeyParamsInterface,
|
||||
} from '@standardnotes/models'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { PkcKeyPair, PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { firstHalfOfString, secondHalfOfString, splitString, UuidGenerator } from '@standardnotes/utils'
|
||||
import { V001Algorithm } from '../../Algorithm'
|
||||
import { isItemsKey } from '../../Keys/ItemsKey/ItemsKey'
|
||||
@@ -17,11 +22,16 @@ import { CreateNewRootKey } from '../../Keys/RootKey/Functions'
|
||||
import { Create001KeyParams } from '../../Keys/RootKey/KeyParamsFunctions'
|
||||
import { SNRootKey } from '../../Keys/RootKey/RootKey'
|
||||
import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
|
||||
import { DecryptedParameters, EncryptedParameters, ErrorDecryptingParameters } from '../../Types/EncryptedParameters'
|
||||
import { EncryptedOutputParameters, ErrorDecryptingParameters } from '../../Types/EncryptedParameters'
|
||||
import { DecryptedParameters } from '../../Types/DecryptedParameters'
|
||||
import { ItemAuthenticatedData } from '../../Types/ItemAuthenticatedData'
|
||||
import { LegacyAttachedData } from '../../Types/LegacyAttachedData'
|
||||
import { RootKeyEncryptedAuthenticatedData } from '../../Types/RootKeyEncryptedAuthenticatedData'
|
||||
import { AsynchronousOperator } from '../Operator'
|
||||
import { OperatorInterface } from '../OperatorInterface/OperatorInterface'
|
||||
import { PublicKeySet } from '../Types/PublicKeySet'
|
||||
import { AsymmetricDecryptResult } from '../Types/AsymmetricDecryptResult'
|
||||
import { AsymmetricSignatureVerificationDetachedResult } from '../Types/AsymmetricSignatureVerificationDetachedResult'
|
||||
import { AsyncOperatorInterface } from '../OperatorInterface/AsyncOperatorInterface'
|
||||
|
||||
const NO_IV = '00000000000000000000000000000000'
|
||||
|
||||
@@ -29,7 +39,7 @@ const NO_IV = '00000000000000000000000000000000'
|
||||
* @deprecated
|
||||
* A legacy operator no longer used to generate new accounts
|
||||
*/
|
||||
export class SNProtocolOperator001 implements AsynchronousOperator {
|
||||
export class SNProtocolOperator001 implements OperatorInterface, AsyncOperatorInterface {
|
||||
protected readonly crypto: PureCryptoInterface
|
||||
|
||||
constructor(crypto: PureCryptoInterface) {
|
||||
@@ -68,11 +78,11 @@ export class SNProtocolOperator001 implements AsynchronousOperator {
|
||||
return CreateDecryptedItemFromPayload(payload)
|
||||
}
|
||||
|
||||
public async createRootKey(
|
||||
public async createRootKey<K extends RootKeyInterface>(
|
||||
identifier: string,
|
||||
password: string,
|
||||
origination: KeyParamsOrigination,
|
||||
): Promise<SNRootKey> {
|
||||
): Promise<K> {
|
||||
const pwCost = V001Algorithm.PbkdfMinCost as number
|
||||
const pwNonce = this.crypto.generateRandomKey(V001Algorithm.SaltSeedLength)
|
||||
const pwSalt = await this.crypto.unsafeSha1(identifier + 'SN' + pwNonce)
|
||||
@@ -90,13 +100,13 @@ export class SNProtocolOperator001 implements AsynchronousOperator {
|
||||
return this.deriveKey(password, keyParams)
|
||||
}
|
||||
|
||||
public getPayloadAuthenticatedData(
|
||||
_encrypted: EncryptedParameters,
|
||||
public getPayloadAuthenticatedDataForExternalUse(
|
||||
_encrypted: EncryptedOutputParameters,
|
||||
): RootKeyEncryptedAuthenticatedData | ItemAuthenticatedData | LegacyAttachedData | undefined {
|
||||
return undefined
|
||||
}
|
||||
|
||||
public async computeRootKey(password: string, keyParams: SNRootKeyParams): Promise<SNRootKey> {
|
||||
public async computeRootKey<K extends RootKeyInterface>(password: string, keyParams: SNRootKeyParams): Promise<K> {
|
||||
return this.deriveKey(password, keyParams)
|
||||
}
|
||||
|
||||
@@ -111,7 +121,7 @@ export class SNProtocolOperator001 implements AsynchronousOperator {
|
||||
public async generateEncryptedParametersAsync(
|
||||
payload: DecryptedPayloadInterface,
|
||||
key: ItemsKeyInterface | SNRootKey,
|
||||
): Promise<EncryptedParameters> {
|
||||
): Promise<EncryptedOutputParameters> {
|
||||
/**
|
||||
* Generate new item key that is double the key size.
|
||||
* Will be split to create encryption key and authentication key.
|
||||
@@ -132,16 +142,19 @@ export class SNProtocolOperator001 implements AsynchronousOperator {
|
||||
|
||||
return {
|
||||
uuid: payload.uuid,
|
||||
content_type: payload.content_type,
|
||||
items_key_id: isItemsKey(key) ? key.uuid : undefined,
|
||||
content: ciphertext,
|
||||
enc_item_key: encItemKey,
|
||||
auth_hash: authHash,
|
||||
version: this.version,
|
||||
key_system_identifier: payload.key_system_identifier,
|
||||
shared_vault_uuid: payload.shared_vault_uuid,
|
||||
}
|
||||
}
|
||||
|
||||
public async generateDecryptedParametersAsync<C extends ItemContent = ItemContent>(
|
||||
encrypted: EncryptedParameters,
|
||||
encrypted: EncryptedOutputParameters,
|
||||
key: ItemsKeyInterface | SNRootKey,
|
||||
): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> {
|
||||
if (!encrypted.enc_item_key) {
|
||||
@@ -178,6 +191,7 @@ export class SNProtocolOperator001 implements AsynchronousOperator {
|
||||
return {
|
||||
uuid: encrypted.uuid,
|
||||
content: JSON.parse(content),
|
||||
signatureData: { required: false, contentHash: '' },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,7 +205,7 @@ export class SNProtocolOperator001 implements AsynchronousOperator {
|
||||
}
|
||||
}
|
||||
|
||||
protected async deriveKey(password: string, keyParams: SNRootKeyParams): Promise<SNRootKey> {
|
||||
protected async deriveKey<K extends RootKeyInterface>(password: string, keyParams: SNRootKeyParams): Promise<K> {
|
||||
const derivedKey = await this.crypto.pbkdf2(
|
||||
password,
|
||||
keyParams.content001.pw_salt,
|
||||
@@ -205,11 +219,63 @@ export class SNProtocolOperator001 implements AsynchronousOperator {
|
||||
|
||||
const partitions = splitString(derivedKey, 2)
|
||||
|
||||
return CreateNewRootKey({
|
||||
return CreateNewRootKey<K>({
|
||||
serverPassword: partitions[0],
|
||||
masterKey: partitions[1],
|
||||
version: ProtocolVersion.V001,
|
||||
keyParams: keyParams.getPortableValue(),
|
||||
})
|
||||
}
|
||||
|
||||
createRandomizedKeySystemRootKey(_dto: { systemIdentifier: string }): KeySystemRootKeyInterface {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
createUserInputtedKeySystemRootKey(_dto: {
|
||||
systemIdentifier: string
|
||||
systemName: string
|
||||
userInputtedPassword: string
|
||||
}): KeySystemRootKeyInterface {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
deriveUserInputtedKeySystemRootKey(_dto: {
|
||||
keyParams: KeySystemRootKeyParamsInterface
|
||||
userInputtedPassword: string
|
||||
}): KeySystemRootKeyInterface {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
createKeySystemItemsKey(
|
||||
_uuid: string,
|
||||
_keySystemIdentifier: KeySystemIdentifier,
|
||||
_sharedVaultUuid: string | undefined,
|
||||
): KeySystemItemsKeyInterface {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
versionForAsymmetricallyEncryptedString(_encryptedString: string): ProtocolVersion {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
asymmetricEncrypt(_dto: {
|
||||
stringToEncrypt: string
|
||||
senderKeyPair: PkcKeyPair
|
||||
senderSigningKeyPair: PkcKeyPair
|
||||
recipientPublicKey: string
|
||||
}): string {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
asymmetricDecrypt(_dto: { stringToDecrypt: string; recipientSecretKey: string }): AsymmetricDecryptResult | null {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
asymmetricSignatureVerifyDetached(_encryptedString: string): AsymmetricSignatureVerificationDetachedResult {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
getSenderPublicKeySetFromAsymmetricallyEncryptedString(_string: string): PublicKeySet {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ import { CreateNewRootKey } from '../../Keys/RootKey/Functions'
|
||||
import { Create002KeyParams } from '../../Keys/RootKey/KeyParamsFunctions'
|
||||
import { SNRootKey } from '../../Keys/RootKey/RootKey'
|
||||
import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
|
||||
import { DecryptedParameters, EncryptedParameters, ErrorDecryptingParameters } from '../../Types/EncryptedParameters'
|
||||
import { EncryptedOutputParameters, ErrorDecryptingParameters } from '../../Types/EncryptedParameters'
|
||||
import { DecryptedParameters } from '../../Types/DecryptedParameters'
|
||||
import { ItemAuthenticatedData } from '../../Types/ItemAuthenticatedData'
|
||||
import { LegacyAttachedData } from '../../Types/LegacyAttachedData'
|
||||
import { RootKeyEncryptedAuthenticatedData } from '../../Types/RootKeyEncryptedAuthenticatedData'
|
||||
@@ -50,11 +51,11 @@ export class SNProtocolOperator002 extends SNProtocolOperator001 {
|
||||
return Models.CreateDecryptedItemFromPayload(payload)
|
||||
}
|
||||
|
||||
public override async createRootKey(
|
||||
public override async createRootKey<K extends Models.RootKeyInterface>(
|
||||
identifier: string,
|
||||
password: string,
|
||||
origination: Common.KeyParamsOrigination,
|
||||
): Promise<SNRootKey> {
|
||||
): Promise<K> {
|
||||
const pwCost = Utils.lastElement(V002Algorithm.PbkdfCostsUsed) as number
|
||||
const pwNonce = this.crypto.generateRandomKey(V002Algorithm.SaltSeedLength)
|
||||
const pwSalt = await this.crypto.unsafeSha1(identifier + ':' + pwNonce)
|
||||
@@ -77,7 +78,10 @@ export class SNProtocolOperator002 extends SNProtocolOperator001 {
|
||||
* may have had costs of 5000, and others of 101000. Therefore, when computing
|
||||
* the root key, we must use the value returned by the server.
|
||||
*/
|
||||
public override async computeRootKey(password: string, keyParams: SNRootKeyParams): Promise<SNRootKey> {
|
||||
public override async computeRootKey<K extends Models.RootKeyInterface>(
|
||||
password: string,
|
||||
keyParams: SNRootKeyParams,
|
||||
): Promise<K> {
|
||||
return this.deriveKey(password, keyParams)
|
||||
}
|
||||
|
||||
@@ -141,8 +145,8 @@ export class SNProtocolOperator002 extends SNProtocolOperator001 {
|
||||
return this.decryptString002(contentCiphertext, encryptionKey, iv)
|
||||
}
|
||||
|
||||
public override getPayloadAuthenticatedData(
|
||||
encrypted: EncryptedParameters,
|
||||
public override getPayloadAuthenticatedDataForExternalUse(
|
||||
encrypted: EncryptedOutputParameters,
|
||||
): RootKeyEncryptedAuthenticatedData | ItemAuthenticatedData | LegacyAttachedData | undefined {
|
||||
const itemKeyComponents = this.encryptionComponentsFromString002(encrypted.enc_item_key)
|
||||
const authenticatedData = itemKeyComponents.keyParams
|
||||
@@ -161,7 +165,7 @@ export class SNProtocolOperator002 extends SNProtocolOperator001 {
|
||||
public override async generateEncryptedParametersAsync(
|
||||
payload: Models.DecryptedPayloadInterface,
|
||||
key: Models.ItemsKeyInterface | SNRootKey,
|
||||
): Promise<EncryptedParameters> {
|
||||
): Promise<EncryptedOutputParameters> {
|
||||
/**
|
||||
* Generate new item key that is double the key size.
|
||||
* Will be split to create encryption key and authentication key.
|
||||
@@ -189,15 +193,18 @@ export class SNProtocolOperator002 extends SNProtocolOperator001 {
|
||||
|
||||
return {
|
||||
uuid: payload.uuid,
|
||||
content_type: payload.content_type,
|
||||
items_key_id: isItemsKey(key) ? key.uuid : undefined,
|
||||
content: ciphertext,
|
||||
enc_item_key: encItemKey,
|
||||
version: this.version,
|
||||
key_system_identifier: payload.key_system_identifier,
|
||||
shared_vault_uuid: payload.shared_vault_uuid,
|
||||
}
|
||||
}
|
||||
|
||||
public override async generateDecryptedParametersAsync<C extends ItemContent = ItemContent>(
|
||||
encrypted: EncryptedParameters,
|
||||
encrypted: EncryptedOutputParameters,
|
||||
key: Models.ItemsKeyInterface | SNRootKey,
|
||||
): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> {
|
||||
if (!encrypted.enc_item_key) {
|
||||
@@ -252,11 +259,15 @@ export class SNProtocolOperator002 extends SNProtocolOperator001 {
|
||||
return {
|
||||
uuid: encrypted.uuid,
|
||||
content: JSON.parse(content),
|
||||
signatureData: { required: false, contentHash: '' },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async deriveKey(password: string, keyParams: SNRootKeyParams): Promise<SNRootKey> {
|
||||
protected override async deriveKey<K extends Models.RootKeyInterface>(
|
||||
password: string,
|
||||
keyParams: SNRootKeyParams,
|
||||
): Promise<K> {
|
||||
const derivedKey = await this.crypto.pbkdf2(
|
||||
password,
|
||||
keyParams.content002.pw_salt,
|
||||
@@ -270,7 +281,7 @@ export class SNProtocolOperator002 extends SNProtocolOperator001 {
|
||||
|
||||
const partitions = Utils.splitString(derivedKey, 3)
|
||||
|
||||
return CreateNewRootKey({
|
||||
return CreateNewRootKey<K>({
|
||||
serverPassword: partitions[0],
|
||||
masterKey: partitions[1],
|
||||
dataAuthenticationKey: partitions[2],
|
||||
|
||||
@@ -6,12 +6,12 @@ import {
|
||||
ItemsKeyContent,
|
||||
ItemsKeyInterface,
|
||||
PayloadTimestampDefaults,
|
||||
RootKeyInterface,
|
||||
} from '@standardnotes/models'
|
||||
import { splitString, UuidGenerator } from '@standardnotes/utils'
|
||||
import { V003Algorithm } from '../../Algorithm'
|
||||
import { CreateNewRootKey } from '../../Keys/RootKey/Functions'
|
||||
import { Create003KeyParams } from '../../Keys/RootKey/KeyParamsFunctions'
|
||||
import { SNRootKey } from '../../Keys/RootKey/RootKey'
|
||||
import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
|
||||
import { SNProtocolOperator002 } from '../002/Operator002'
|
||||
|
||||
@@ -53,11 +53,17 @@ export class SNProtocolOperator003 extends SNProtocolOperator002 {
|
||||
return CreateDecryptedItemFromPayload(payload)
|
||||
}
|
||||
|
||||
public override async computeRootKey(password: string, keyParams: SNRootKeyParams): Promise<SNRootKey> {
|
||||
public override async computeRootKey<K extends RootKeyInterface>(
|
||||
password: string,
|
||||
keyParams: SNRootKeyParams,
|
||||
): Promise<K> {
|
||||
return this.deriveKey(password, keyParams)
|
||||
}
|
||||
|
||||
protected override async deriveKey(password: string, keyParams: SNRootKeyParams): Promise<SNRootKey> {
|
||||
protected override async deriveKey<K extends RootKeyInterface>(
|
||||
password: string,
|
||||
keyParams: SNRootKeyParams,
|
||||
): Promise<K> {
|
||||
const salt = await this.generateSalt(
|
||||
keyParams.content003.identifier,
|
||||
ProtocolVersion.V003,
|
||||
@@ -78,7 +84,7 @@ export class SNProtocolOperator003 extends SNProtocolOperator002 {
|
||||
|
||||
const partitions = splitString(derivedKey, 3)
|
||||
|
||||
return CreateNewRootKey({
|
||||
return CreateNewRootKey<K>({
|
||||
serverPassword: partitions[0],
|
||||
masterKey: partitions[1],
|
||||
dataAuthenticationKey: partitions[2],
|
||||
@@ -87,11 +93,11 @@ export class SNProtocolOperator003 extends SNProtocolOperator002 {
|
||||
})
|
||||
}
|
||||
|
||||
public override async createRootKey(
|
||||
public override async createRootKey<K extends RootKeyInterface>(
|
||||
identifier: string,
|
||||
password: string,
|
||||
origination: KeyParamsOrigination,
|
||||
): Promise<SNRootKey> {
|
||||
): Promise<K> {
|
||||
const version = ProtocolVersion.V003
|
||||
const pwNonce = this.crypto.generateRandomKey(V003Algorithm.SaltSeedLength)
|
||||
const keyParams = Create003KeyParams({
|
||||
|
||||
87
packages/encryption/src/Domain/Operator/004/MockedCrypto.ts
Normal file
87
packages/encryption/src/Domain/Operator/004/MockedCrypto.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
|
||||
export function getMockedCrypto(): PureCryptoInterface {
|
||||
const crypto = {} as jest.Mocked<PureCryptoInterface>
|
||||
|
||||
const mockGenerateKeyPair = (seed: string) => {
|
||||
const publicKey = `public-key-${seed}`
|
||||
const privateKey = `private-key-${seed}`
|
||||
|
||||
return {
|
||||
publicKey: `${publicKey}:${privateKey}`,
|
||||
privateKey: `${privateKey}:${publicKey}`,
|
||||
}
|
||||
}
|
||||
|
||||
const replaceColonsToAvoidJSONConflicts = (text: string) => {
|
||||
return text.replace(/:/g, '|')
|
||||
}
|
||||
|
||||
const undoReplaceColonsToAvoidJSONConflicts = (text: string) => {
|
||||
return text.replace(/\|/g, ':')
|
||||
}
|
||||
|
||||
crypto.base64Encode = jest.fn().mockImplementation((text: string) => {
|
||||
return `base64-${replaceColonsToAvoidJSONConflicts(text)}`
|
||||
})
|
||||
|
||||
crypto.base64Decode = jest.fn().mockImplementation((text: string) => {
|
||||
const decodedText = text.split('base64-')[1]
|
||||
return undoReplaceColonsToAvoidJSONConflicts(decodedText)
|
||||
})
|
||||
|
||||
crypto.xchacha20Encrypt = jest.fn().mockImplementation((text: string) => {
|
||||
return `<e>${replaceColonsToAvoidJSONConflicts(text)}<e>`
|
||||
})
|
||||
|
||||
crypto.xchacha20Decrypt = jest.fn().mockImplementation((text: string) => {
|
||||
return undoReplaceColonsToAvoidJSONConflicts(text.split('<e>')[1])
|
||||
})
|
||||
|
||||
crypto.generateRandomKey = jest.fn().mockImplementation(() => {
|
||||
return 'random-string'
|
||||
})
|
||||
|
||||
crypto.sodiumCryptoBoxEasyEncrypt = jest.fn().mockImplementation((text: string) => {
|
||||
return `<e>${replaceColonsToAvoidJSONConflicts(text)}<e>`
|
||||
})
|
||||
|
||||
crypto.sodiumCryptoBoxEasyDecrypt = jest.fn().mockImplementation((text: string) => {
|
||||
return undoReplaceColonsToAvoidJSONConflicts(text.split('<e>')[1])
|
||||
})
|
||||
|
||||
crypto.sodiumCryptoBoxSeedKeypair = jest.fn().mockImplementation((seed: string) => {
|
||||
return mockGenerateKeyPair(seed)
|
||||
})
|
||||
|
||||
crypto.sodiumCryptoKdfDeriveFromKey = jest
|
||||
.fn()
|
||||
.mockImplementation((key: string, subkeyNumber: number, subkeyLength: number, context: string) => {
|
||||
return `subkey-${key}-${subkeyNumber}-${subkeyLength}-${context}`
|
||||
})
|
||||
|
||||
crypto.sodiumCryptoSign = jest.fn().mockImplementation((message: string, privateKey: string) => {
|
||||
const signature = `signature:m=${message}:pk=${privateKey}`
|
||||
return signature
|
||||
})
|
||||
|
||||
crypto.sodiumCryptoSignSeedKeypair = jest.fn().mockImplementation((seed: string) => {
|
||||
return mockGenerateKeyPair(seed)
|
||||
})
|
||||
|
||||
crypto.sodiumCryptoSignVerify = jest
|
||||
.fn()
|
||||
.mockImplementation((message: string, signature: string, publicKey: string) => {
|
||||
const keyComponents = publicKey.split(':')
|
||||
const privateKeyComponent = keyComponents[1]
|
||||
const privateKey = `${privateKeyComponent}:${keyComponents[0]}`
|
||||
const computedSignature = crypto.sodiumCryptoSign(message, privateKey)
|
||||
return computedSignature === signature
|
||||
})
|
||||
|
||||
crypto.sodiumCryptoGenericHash = jest.fn().mockImplementation((message: string, key: string) => {
|
||||
return `hash-${message}-${key}`
|
||||
})
|
||||
|
||||
return crypto
|
||||
}
|
||||
@@ -1,69 +1,34 @@
|
||||
import { ContentType, ProtocolVersion } from '@standardnotes/common'
|
||||
import { DecryptedPayload, ItemContent, ItemsKeyContent, PayloadTimestampDefaults } from '@standardnotes/models'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { SNItemsKey } from '../../Keys/ItemsKey/ItemsKey'
|
||||
import { ItemAuthenticatedData } from '../../Types/ItemAuthenticatedData'
|
||||
import { SNProtocolOperator004 } from './Operator004'
|
||||
|
||||
const b64 = (text: string): string => {
|
||||
return Buffer.from(text).toString('base64')
|
||||
}
|
||||
import { getMockedCrypto } from './MockedCrypto'
|
||||
import { deconstructEncryptedPayloadString } from './V004AlgorithmHelpers'
|
||||
|
||||
describe('operator 004', () => {
|
||||
let crypto: PureCryptoInterface
|
||||
const crypto = getMockedCrypto()
|
||||
|
||||
let operator: SNProtocolOperator004
|
||||
|
||||
beforeEach(() => {
|
||||
crypto = {} as jest.Mocked<PureCryptoInterface>
|
||||
crypto.base64Encode = jest.fn().mockImplementation((text: string) => {
|
||||
return b64(text)
|
||||
})
|
||||
crypto.base64Decode = jest.fn().mockImplementation((text: string) => {
|
||||
return Buffer.from(text, 'base64').toString('ascii')
|
||||
})
|
||||
crypto.xchacha20Encrypt = jest.fn().mockImplementation((text: string) => {
|
||||
return `<e>${text}<e>`
|
||||
})
|
||||
crypto.xchacha20Decrypt = jest.fn().mockImplementation((text: string) => {
|
||||
return text.split('<e>')[1]
|
||||
})
|
||||
crypto.generateRandomKey = jest.fn().mockImplementation(() => {
|
||||
return 'random-string'
|
||||
})
|
||||
|
||||
operator = new SNProtocolOperator004(crypto)
|
||||
})
|
||||
|
||||
it('should generateEncryptedProtocolString', () => {
|
||||
const aad: ItemAuthenticatedData = {
|
||||
u: '123',
|
||||
v: ProtocolVersion.V004,
|
||||
}
|
||||
|
||||
const nonce = 'noncy'
|
||||
const plaintext = 'foo'
|
||||
|
||||
operator.generateEncryptionNonce = jest.fn().mockReturnValue(nonce)
|
||||
|
||||
const result = operator.generateEncryptedProtocolString(plaintext, 'secret', aad)
|
||||
|
||||
expect(result).toEqual(`004:${nonce}:<e>${plaintext}<e>:${b64(JSON.stringify(aad))}`)
|
||||
})
|
||||
|
||||
it('should deconstructEncryptedPayloadString', () => {
|
||||
const string = '004:noncy:<e>foo<e>:eyJ1IjoiMTIzIiwidiI6IjAwNCJ9'
|
||||
|
||||
const result = operator.deconstructEncryptedPayloadString(string)
|
||||
const result = deconstructEncryptedPayloadString(string)
|
||||
|
||||
expect(result).toEqual({
|
||||
version: '004',
|
||||
nonce: 'noncy',
|
||||
ciphertext: '<e>foo<e>',
|
||||
authenticatedData: 'eyJ1IjoiMTIzIiwidiI6IjAwNCJ9',
|
||||
additionalData: 'e30=',
|
||||
})
|
||||
})
|
||||
|
||||
it('should generateEncryptedParametersSync', () => {
|
||||
it('should generateEncryptedParameters', () => {
|
||||
const payload = {
|
||||
uuid: '123',
|
||||
content_type: ContentType.Note,
|
||||
@@ -83,13 +48,16 @@ describe('operator 004', () => {
|
||||
}),
|
||||
)
|
||||
|
||||
const result = operator.generateEncryptedParametersSync(payload, key)
|
||||
const result = operator.generateEncryptedParameters(payload, key)
|
||||
|
||||
expect(result).toEqual({
|
||||
uuid: '123',
|
||||
items_key_id: 'key-456',
|
||||
content: '004:random-string:<e>{"foo":"bar"}<e>:eyJ1IjoiMTIzIiwidiI6IjAwNCJ9',
|
||||
enc_item_key: '004:random-string:<e>random-string<e>:eyJ1IjoiMTIzIiwidiI6IjAwNCJ9',
|
||||
key_system_identifier: undefined,
|
||||
shared_vault_uuid: undefined,
|
||||
content: '004:random-string:<e>{"foo"|"bar"}<e>:base64-{"u"|"123","v"|"004"}:base64-{}',
|
||||
content_type: ContentType.Note,
|
||||
enc_item_key: '004:random-string:<e>random-string<e>:base64-{"u"|"123","v"|"004"}:base64-{}',
|
||||
version: '004',
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,44 +1,56 @@
|
||||
import { ContentType, KeyParamsOrigination, ProtocolVersion } from '@standardnotes/common'
|
||||
import * as Models from '@standardnotes/models'
|
||||
import {
|
||||
CreateDecryptedItemFromPayload,
|
||||
FillItemContent,
|
||||
ItemContent,
|
||||
ItemsKeyContent,
|
||||
ItemsKeyInterface,
|
||||
PayloadTimestampDefaults,
|
||||
DecryptedPayload,
|
||||
DecryptedPayloadInterface,
|
||||
KeySystemItemsKeyInterface,
|
||||
KeySystemRootKeyInterface,
|
||||
FillItemContentSpecialized,
|
||||
ItemsKeyContentSpecialized,
|
||||
KeySystemIdentifier,
|
||||
RootKeyInterface,
|
||||
KeySystemRootKeyParamsInterface,
|
||||
} from '@standardnotes/models'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import * as Utils from '@standardnotes/utils'
|
||||
import { ContentType, KeyParamsOrigination, ProtocolVersion } from '@standardnotes/common'
|
||||
import { HexString, PkcKeyPair, PureCryptoInterface, Utf8String } from '@standardnotes/sncrypto-common'
|
||||
import { V004Algorithm } from '../../Algorithm'
|
||||
import { isItemsKey } from '../../Keys/ItemsKey/ItemsKey'
|
||||
import { ContentTypeUsesRootKeyEncryption, CreateNewRootKey } from '../../Keys/RootKey/Functions'
|
||||
import { Create004KeyParams } from '../../Keys/RootKey/KeyParamsFunctions'
|
||||
import { SNRootKey } from '../../Keys/RootKey/RootKey'
|
||||
import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
|
||||
import { DecryptedParameters, EncryptedParameters, ErrorDecryptingParameters } from '../../Types/EncryptedParameters'
|
||||
import {
|
||||
EncryptedInputParameters,
|
||||
EncryptedOutputParameters,
|
||||
ErrorDecryptingParameters,
|
||||
} from '../../Types/EncryptedParameters'
|
||||
import { DecryptedParameters } from '../../Types/DecryptedParameters'
|
||||
import { ItemAuthenticatedData } from '../../Types/ItemAuthenticatedData'
|
||||
import { LegacyAttachedData } from '../../Types/LegacyAttachedData'
|
||||
import { RootKeyEncryptedAuthenticatedData } from '../../Types/RootKeyEncryptedAuthenticatedData'
|
||||
import { SynchronousOperator } from '../Operator'
|
||||
import { OperatorInterface } from '../OperatorInterface/OperatorInterface'
|
||||
import { AsymmetricallyEncryptedString } from '../Types/Types'
|
||||
import { AsymmetricItemAdditionalData } from '../../Types/EncryptionAdditionalData'
|
||||
import { V004AsymmetricStringComponents } from './V004AlgorithmTypes'
|
||||
import { AsymmetricEncryptUseCase } from './UseCase/Asymmetric/AsymmetricEncrypt'
|
||||
import { ParseConsistentBase64JsonPayloadUseCase } from './UseCase/Utils/ParseConsistentBase64JsonPayload'
|
||||
import { AsymmetricDecryptUseCase } from './UseCase/Asymmetric/AsymmetricDecrypt'
|
||||
import { GenerateDecryptedParametersUseCase } from './UseCase/Symmetric/GenerateDecryptedParameters'
|
||||
import { GenerateEncryptedParametersUseCase } from './UseCase/Symmetric/GenerateEncryptedParameters'
|
||||
import { DeriveRootKeyUseCase } from './UseCase/RootKey/DeriveRootKey'
|
||||
import { GetPayloadAuthenticatedDataDetachedUseCase } from './UseCase/Symmetric/GetPayloadAuthenticatedDataDetached'
|
||||
import { CreateRootKeyUseCase } from './UseCase/RootKey/CreateRootKey'
|
||||
import { UuidGenerator } from '@standardnotes/utils'
|
||||
import { CreateKeySystemItemsKeyUseCase } from './UseCase/KeySystem/CreateKeySystemItemsKey'
|
||||
import { AsymmetricDecryptResult } from '../Types/AsymmetricDecryptResult'
|
||||
import { PublicKeySet } from '../Types/PublicKeySet'
|
||||
import { CreateRandomKeySystemRootKey } from './UseCase/KeySystem/CreateRandomKeySystemRootKey'
|
||||
import { CreateUserInputKeySystemRootKey } from './UseCase/KeySystem/CreateUserInputKeySystemRootKey'
|
||||
import { AsymmetricSignatureVerificationDetachedResult } from '../Types/AsymmetricSignatureVerificationDetachedResult'
|
||||
import { AsymmetricSignatureVerificationDetachedUseCase } from './UseCase/Asymmetric/AsymmetricSignatureVerificationDetached'
|
||||
import { DeriveKeySystemRootKeyUseCase } from './UseCase/KeySystem/DeriveKeySystemRootKey'
|
||||
import { SyncOperatorInterface } from '../OperatorInterface/SyncOperatorInterface'
|
||||
|
||||
type V004StringComponents = [version: string, nonce: string, ciphertext: string, authenticatedData: string]
|
||||
|
||||
type V004Components = {
|
||||
version: V004StringComponents[0]
|
||||
nonce: V004StringComponents[1]
|
||||
ciphertext: V004StringComponents[2]
|
||||
authenticatedData: V004StringComponents[3]
|
||||
}
|
||||
|
||||
const PARTITION_CHARACTER = ':'
|
||||
|
||||
export class SNProtocolOperator004 implements SynchronousOperator {
|
||||
protected readonly crypto: PureCryptoInterface
|
||||
|
||||
constructor(crypto: PureCryptoInterface) {
|
||||
this.crypto = crypto
|
||||
}
|
||||
export class SNProtocolOperator004 implements OperatorInterface, SyncOperatorInterface {
|
||||
constructor(protected readonly crypto: PureCryptoInterface) {}
|
||||
|
||||
public getEncryptionDisplayName(): string {
|
||||
return 'XChaCha20-Poly1305'
|
||||
@@ -50,7 +62,7 @@ export class SNProtocolOperator004 implements SynchronousOperator {
|
||||
|
||||
private generateNewItemsKeyContent() {
|
||||
const itemsKey = this.crypto.generateRandomKey(V004Algorithm.EncryptionKeyLength)
|
||||
const response = FillItemContent<ItemsKeyContent>({
|
||||
const response = FillItemContentSpecialized<ItemsKeyContentSpecialized>({
|
||||
itemsKey: itemsKey,
|
||||
version: ProtocolVersion.V004,
|
||||
})
|
||||
@@ -62,260 +74,130 @@ export class SNProtocolOperator004 implements SynchronousOperator {
|
||||
* The consumer must save/sync this item.
|
||||
*/
|
||||
public createItemsKey(): ItemsKeyInterface {
|
||||
const payload = new Models.DecryptedPayload({
|
||||
uuid: Utils.UuidGenerator.GenerateUuid(),
|
||||
const payload = new DecryptedPayload({
|
||||
uuid: UuidGenerator.GenerateUuid(),
|
||||
content_type: ContentType.ItemsKey,
|
||||
content: this.generateNewItemsKeyContent(),
|
||||
key_system_identifier: undefined,
|
||||
shared_vault_uuid: undefined,
|
||||
...PayloadTimestampDefaults(),
|
||||
})
|
||||
return CreateDecryptedItemFromPayload(payload)
|
||||
}
|
||||
|
||||
/**
|
||||
* We require both a client-side component and a server-side component in generating a
|
||||
* salt. This way, a comprimised server cannot benefit from sending the same seed value
|
||||
* for every user. We mix a client-controlled value that is globally unique
|
||||
* (their identifier), with a server controlled value to produce a salt for our KDF.
|
||||
* @param identifier
|
||||
* @param seed
|
||||
*/
|
||||
private async generateSalt004(identifier: string, seed: string) {
|
||||
const hash = await this.crypto.sha256([identifier, seed].join(PARTITION_CHARACTER))
|
||||
return Utils.truncateHexString(hash, V004Algorithm.ArgonSaltLength)
|
||||
createRandomizedKeySystemRootKey(dto: { systemIdentifier: KeySystemIdentifier }): KeySystemRootKeyInterface {
|
||||
const usecase = new CreateRandomKeySystemRootKey(this.crypto)
|
||||
return usecase.execute(dto)
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a root key given a passworf
|
||||
* qwd and previous keyParams
|
||||
* @param password - Plain string representing raw user password
|
||||
* @param keyParams - KeyParams object
|
||||
*/
|
||||
public async computeRootKey(password: string, keyParams: SNRootKeyParams): Promise<SNRootKey> {
|
||||
return this.deriveKey(password, keyParams)
|
||||
createUserInputtedKeySystemRootKey(dto: {
|
||||
systemIdentifier: KeySystemIdentifier
|
||||
userInputtedPassword: string
|
||||
}): KeySystemRootKeyInterface {
|
||||
const usecase = new CreateUserInputKeySystemRootKey(this.crypto)
|
||||
return usecase.execute(dto)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new root key given an identifier and a user password
|
||||
* @param identifier - Plain string representing a unique identifier
|
||||
* @param password - Plain string representing raw user password
|
||||
*/
|
||||
public async createRootKey(
|
||||
deriveUserInputtedKeySystemRootKey(dto: {
|
||||
keyParams: KeySystemRootKeyParamsInterface
|
||||
userInputtedPassword: string
|
||||
}): KeySystemRootKeyInterface {
|
||||
const usecase = new DeriveKeySystemRootKeyUseCase(this.crypto)
|
||||
return usecase.execute({
|
||||
keyParams: dto.keyParams,
|
||||
password: dto.userInputtedPassword,
|
||||
})
|
||||
}
|
||||
|
||||
public createKeySystemItemsKey(
|
||||
uuid: string,
|
||||
keySystemIdentifier: KeySystemIdentifier,
|
||||
sharedVaultUuid: string | undefined,
|
||||
rootKeyToken: string,
|
||||
): KeySystemItemsKeyInterface {
|
||||
const usecase = new CreateKeySystemItemsKeyUseCase(this.crypto)
|
||||
return usecase.execute({ uuid, keySystemIdentifier, sharedVaultUuid, rootKeyToken })
|
||||
}
|
||||
|
||||
public async computeRootKey<K extends RootKeyInterface>(
|
||||
password: Utf8String,
|
||||
keyParams: SNRootKeyParams,
|
||||
): Promise<K> {
|
||||
const usecase = new DeriveRootKeyUseCase(this.crypto)
|
||||
return usecase.execute(password, keyParams)
|
||||
}
|
||||
|
||||
public async createRootKey<K extends RootKeyInterface>(
|
||||
identifier: string,
|
||||
password: string,
|
||||
password: Utf8String,
|
||||
origination: KeyParamsOrigination,
|
||||
): Promise<SNRootKey> {
|
||||
const version = ProtocolVersion.V004
|
||||
const seed = this.crypto.generateRandomKey(V004Algorithm.ArgonSaltSeedLength)
|
||||
const keyParams = Create004KeyParams({
|
||||
identifier: identifier,
|
||||
pw_nonce: seed,
|
||||
version: version,
|
||||
origination: origination,
|
||||
created: `${Date.now()}`,
|
||||
})
|
||||
return this.deriveKey(password, keyParams)
|
||||
): Promise<K> {
|
||||
const usecase = new CreateRootKeyUseCase(this.crypto)
|
||||
return usecase.execute(identifier, password, origination)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param plaintext - The plaintext to encrypt.
|
||||
* @param rawKey - The key to use to encrypt the plaintext.
|
||||
* @param nonce - The nonce for encryption.
|
||||
* @param authenticatedData - JavaScript object (will be stringified) representing
|
||||
'Additional authenticated data': data you want to be included in authentication.
|
||||
*/
|
||||
encryptString004(plaintext: string, rawKey: string, nonce: string, authenticatedData: ItemAuthenticatedData) {
|
||||
if (!nonce) {
|
||||
throw 'encryptString null nonce'
|
||||
}
|
||||
if (!rawKey) {
|
||||
throw 'encryptString null rawKey'
|
||||
}
|
||||
return this.crypto.xchacha20Encrypt(plaintext, nonce, rawKey, this.authenticatedDataToString(authenticatedData))
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ciphertext The encrypted text to decrypt.
|
||||
* @param rawKey The key to use to decrypt the ciphertext.
|
||||
* @param nonce The nonce for decryption.
|
||||
* @param rawAuthenticatedData String representing
|
||||
'Additional authenticated data' - data you want to be included in authentication.
|
||||
*/
|
||||
private decryptString004(ciphertext: string, rawKey: string, nonce: string, rawAuthenticatedData: string) {
|
||||
return this.crypto.xchacha20Decrypt(ciphertext, nonce, rawKey, rawAuthenticatedData)
|
||||
}
|
||||
|
||||
generateEncryptionNonce(): string {
|
||||
return this.crypto.generateRandomKey(V004Algorithm.EncryptionNonceLength)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param plaintext The plaintext text to decrypt.
|
||||
* @param rawKey The key to use to encrypt the plaintext.
|
||||
*/
|
||||
generateEncryptedProtocolString(plaintext: string, rawKey: string, authenticatedData: ItemAuthenticatedData) {
|
||||
const nonce = this.generateEncryptionNonce()
|
||||
|
||||
const ciphertext = this.encryptString004(plaintext, rawKey, nonce, authenticatedData)
|
||||
|
||||
const components: V004StringComponents = [
|
||||
ProtocolVersion.V004 as string,
|
||||
nonce,
|
||||
ciphertext,
|
||||
this.authenticatedDataToString(authenticatedData),
|
||||
]
|
||||
|
||||
return components.join(PARTITION_CHARACTER)
|
||||
}
|
||||
|
||||
deconstructEncryptedPayloadString(payloadString: string): V004Components {
|
||||
const components = payloadString.split(PARTITION_CHARACTER) as V004StringComponents
|
||||
|
||||
return {
|
||||
version: components[0],
|
||||
nonce: components[1],
|
||||
ciphertext: components[2],
|
||||
authenticatedData: components[3],
|
||||
}
|
||||
}
|
||||
|
||||
public getPayloadAuthenticatedData(
|
||||
encrypted: EncryptedParameters,
|
||||
public getPayloadAuthenticatedDataForExternalUse(
|
||||
encrypted: EncryptedOutputParameters,
|
||||
): RootKeyEncryptedAuthenticatedData | ItemAuthenticatedData | LegacyAttachedData | undefined {
|
||||
const itemKeyComponents = this.deconstructEncryptedPayloadString(encrypted.enc_item_key)
|
||||
const authenticatedDataString = itemKeyComponents.authenticatedData
|
||||
const result = this.stringToAuthenticatedData(authenticatedDataString)
|
||||
|
||||
return result
|
||||
const usecase = new GetPayloadAuthenticatedDataDetachedUseCase(this.crypto)
|
||||
return usecase.execute(encrypted)
|
||||
}
|
||||
|
||||
/**
|
||||
* For items that are encrypted with a root key, we append the root key's key params, so
|
||||
* that in the event the client/user loses a reference to their root key, they may still
|
||||
* decrypt data by regenerating the key based on the attached key params.
|
||||
*/
|
||||
private generateAuthenticatedDataForPayload(
|
||||
payload: Models.DecryptedPayloadInterface,
|
||||
key: ItemsKeyInterface | SNRootKey,
|
||||
): ItemAuthenticatedData | RootKeyEncryptedAuthenticatedData {
|
||||
const baseData: ItemAuthenticatedData = {
|
||||
u: payload.uuid,
|
||||
v: ProtocolVersion.V004,
|
||||
}
|
||||
if (ContentTypeUsesRootKeyEncryption(payload.content_type)) {
|
||||
return {
|
||||
...baseData,
|
||||
kp: (key as SNRootKey).keyParams.content,
|
||||
}
|
||||
} else {
|
||||
if (!isItemsKey(key)) {
|
||||
throw Error('Attempting to use non-items key for regular item.')
|
||||
}
|
||||
return baseData
|
||||
}
|
||||
public generateEncryptedParameters(
|
||||
payload: DecryptedPayloadInterface,
|
||||
key: ItemsKeyInterface | KeySystemItemsKeyInterface | KeySystemRootKeyInterface | RootKeyInterface,
|
||||
signingKeyPair?: PkcKeyPair,
|
||||
): EncryptedOutputParameters {
|
||||
const usecase = new GenerateEncryptedParametersUseCase(this.crypto)
|
||||
return usecase.execute(payload, key, signingKeyPair)
|
||||
}
|
||||
|
||||
private authenticatedDataToString(attachedData: ItemAuthenticatedData) {
|
||||
return this.crypto.base64Encode(JSON.stringify(Utils.sortedCopy(Utils.omitUndefinedCopy(attachedData))))
|
||||
}
|
||||
|
||||
private stringToAuthenticatedData(
|
||||
rawAuthenticatedData: string,
|
||||
override?: Partial<ItemAuthenticatedData>,
|
||||
): RootKeyEncryptedAuthenticatedData | ItemAuthenticatedData {
|
||||
const base = JSON.parse(this.crypto.base64Decode(rawAuthenticatedData))
|
||||
return Utils.sortedCopy({
|
||||
...base,
|
||||
...override,
|
||||
})
|
||||
}
|
||||
|
||||
public generateEncryptedParametersSync(
|
||||
payload: Models.DecryptedPayloadInterface,
|
||||
key: ItemsKeyInterface | SNRootKey,
|
||||
): EncryptedParameters {
|
||||
const itemKey = this.crypto.generateRandomKey(V004Algorithm.EncryptionKeyLength)
|
||||
|
||||
const contentPlaintext = JSON.stringify(payload.content)
|
||||
const authenticatedData = this.generateAuthenticatedDataForPayload(payload, key)
|
||||
const encryptedContentString = this.generateEncryptedProtocolString(contentPlaintext, itemKey, authenticatedData)
|
||||
|
||||
const encryptedItemKey = this.generateEncryptedProtocolString(itemKey, key.itemsKey, authenticatedData)
|
||||
|
||||
return {
|
||||
uuid: payload.uuid,
|
||||
items_key_id: isItemsKey(key) ? key.uuid : undefined,
|
||||
content: encryptedContentString,
|
||||
enc_item_key: encryptedItemKey,
|
||||
version: this.version,
|
||||
}
|
||||
}
|
||||
|
||||
public generateDecryptedParametersSync<C extends ItemContent = ItemContent>(
|
||||
encrypted: EncryptedParameters,
|
||||
key: ItemsKeyInterface | SNRootKey,
|
||||
public generateDecryptedParameters<C extends ItemContent = ItemContent>(
|
||||
encrypted: EncryptedInputParameters,
|
||||
key: ItemsKeyInterface | KeySystemItemsKeyInterface | KeySystemRootKeyInterface | RootKeyInterface,
|
||||
): DecryptedParameters<C> | ErrorDecryptingParameters {
|
||||
const contentKeyComponents = this.deconstructEncryptedPayloadString(encrypted.enc_item_key)
|
||||
const authenticatedData = this.stringToAuthenticatedData(contentKeyComponents.authenticatedData, {
|
||||
u: encrypted.uuid,
|
||||
v: encrypted.version,
|
||||
})
|
||||
const usecase = new GenerateDecryptedParametersUseCase(this.crypto)
|
||||
return usecase.execute(encrypted, key)
|
||||
}
|
||||
|
||||
const useAuthenticatedString = this.authenticatedDataToString(authenticatedData)
|
||||
const contentKey = this.decryptString004(
|
||||
contentKeyComponents.ciphertext,
|
||||
key.itemsKey,
|
||||
contentKeyComponents.nonce,
|
||||
useAuthenticatedString,
|
||||
)
|
||||
public asymmetricEncrypt(dto: {
|
||||
stringToEncrypt: Utf8String
|
||||
senderKeyPair: PkcKeyPair
|
||||
senderSigningKeyPair: PkcKeyPair
|
||||
recipientPublicKey: HexString
|
||||
}): AsymmetricallyEncryptedString {
|
||||
const usecase = new AsymmetricEncryptUseCase(this.crypto)
|
||||
return usecase.execute(dto)
|
||||
}
|
||||
|
||||
if (!contentKey) {
|
||||
console.error('Error decrypting itemKey parameters', encrypted)
|
||||
return {
|
||||
uuid: encrypted.uuid,
|
||||
errorDecrypting: true,
|
||||
}
|
||||
}
|
||||
asymmetricDecrypt(dto: {
|
||||
stringToDecrypt: AsymmetricallyEncryptedString
|
||||
recipientSecretKey: HexString
|
||||
}): AsymmetricDecryptResult | null {
|
||||
const usecase = new AsymmetricDecryptUseCase(this.crypto)
|
||||
return usecase.execute(dto)
|
||||
}
|
||||
|
||||
const contentComponents = this.deconstructEncryptedPayloadString(encrypted.content)
|
||||
const content = this.decryptString004(
|
||||
contentComponents.ciphertext,
|
||||
contentKey,
|
||||
contentComponents.nonce,
|
||||
useAuthenticatedString,
|
||||
)
|
||||
asymmetricSignatureVerifyDetached(
|
||||
encryptedString: AsymmetricallyEncryptedString,
|
||||
): AsymmetricSignatureVerificationDetachedResult {
|
||||
const usecase = new AsymmetricSignatureVerificationDetachedUseCase(this.crypto)
|
||||
return usecase.execute({ encryptedString })
|
||||
}
|
||||
|
||||
if (!content) {
|
||||
return {
|
||||
uuid: encrypted.uuid,
|
||||
errorDecrypting: true,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
uuid: encrypted.uuid,
|
||||
content: JSON.parse(content),
|
||||
}
|
||||
getSenderPublicKeySetFromAsymmetricallyEncryptedString(string: AsymmetricallyEncryptedString): PublicKeySet {
|
||||
const [_, __, ___, additionalDataString] = <V004AsymmetricStringComponents>string.split(':')
|
||||
const parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||
const additionalData = parseBase64Usecase.execute<AsymmetricItemAdditionalData>(additionalDataString)
|
||||
return {
|
||||
encryption: additionalData.senderPublicKey,
|
||||
signing: additionalData.signingData.publicKey,
|
||||
}
|
||||
}
|
||||
|
||||
private async deriveKey(password: string, keyParams: SNRootKeyParams): Promise<SNRootKey> {
|
||||
const salt = await this.generateSalt004(keyParams.content004.identifier, keyParams.content004.pw_nonce)
|
||||
const derivedKey = this.crypto.argon2(
|
||||
password,
|
||||
salt,
|
||||
V004Algorithm.ArgonIterations,
|
||||
V004Algorithm.ArgonMemLimit,
|
||||
V004Algorithm.ArgonOutputKeyBytes,
|
||||
)
|
||||
|
||||
const partitions = Utils.splitString(derivedKey, 2)
|
||||
const masterKey = partitions[0]
|
||||
const serverPassword = partitions[1]
|
||||
|
||||
return CreateNewRootKey({
|
||||
masterKey,
|
||||
serverPassword,
|
||||
version: ProtocolVersion.V004,
|
||||
keyParams: keyParams.getPortableValue(),
|
||||
})
|
||||
versionForAsymmetricallyEncryptedString(string: string): ProtocolVersion {
|
||||
const [versionPrefix] = <V004AsymmetricStringComponents>string.split(':')
|
||||
const version = versionPrefix.split('_')[0]
|
||||
return version as ProtocolVersion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { PkcKeyPair, PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { getMockedCrypto } from '../../MockedCrypto'
|
||||
import { AsymmetricDecryptUseCase } from './AsymmetricDecrypt'
|
||||
import { AsymmetricEncryptUseCase } from './AsymmetricEncrypt'
|
||||
import { V004AsymmetricStringComponents } from '../../V004AlgorithmTypes'
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
|
||||
describe('asymmetric decrypt use case', () => {
|
||||
let crypto: PureCryptoInterface
|
||||
let usecase: AsymmetricDecryptUseCase
|
||||
let recipientKeyPair: PkcKeyPair
|
||||
let senderKeyPair: PkcKeyPair
|
||||
let senderSigningKeyPair: PkcKeyPair
|
||||
|
||||
beforeEach(() => {
|
||||
crypto = getMockedCrypto()
|
||||
usecase = new AsymmetricDecryptUseCase(crypto)
|
||||
recipientKeyPair = crypto.sodiumCryptoBoxSeedKeypair('recipient-seedling')
|
||||
senderKeyPair = crypto.sodiumCryptoBoxSeedKeypair('sender-seedling')
|
||||
senderSigningKeyPair = crypto.sodiumCryptoSignSeedKeypair('sender-signing-seedling')
|
||||
})
|
||||
|
||||
const getEncryptedString = () => {
|
||||
const encryptUsecase = new AsymmetricEncryptUseCase(crypto)
|
||||
|
||||
const result = encryptUsecase.execute({
|
||||
stringToEncrypt: 'foobar',
|
||||
senderKeyPair: senderKeyPair,
|
||||
senderSigningKeyPair: senderSigningKeyPair,
|
||||
recipientPublicKey: recipientKeyPair.publicKey,
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
it('should generate decrypted string', () => {
|
||||
const encryptedString = getEncryptedString()
|
||||
|
||||
const decrypted = usecase.execute({
|
||||
stringToDecrypt: encryptedString,
|
||||
recipientSecretKey: recipientKeyPair.privateKey,
|
||||
})
|
||||
|
||||
expect(decrypted).toEqual({
|
||||
plaintext: 'foobar',
|
||||
signatureVerified: true,
|
||||
signaturePublicKey: senderSigningKeyPair.publicKey,
|
||||
senderPublicKey: senderKeyPair.publicKey,
|
||||
})
|
||||
})
|
||||
|
||||
it('should fail signature verification if signature is changed', () => {
|
||||
const encryptedString = getEncryptedString()
|
||||
|
||||
const [version, nonce, ciphertext] = <V004AsymmetricStringComponents>encryptedString.split(':')
|
||||
|
||||
const corruptAdditionalData: AsymmetricItemAdditionalData = {
|
||||
signingData: {
|
||||
publicKey: senderSigningKeyPair.publicKey,
|
||||
signature: 'corrupt',
|
||||
},
|
||||
senderPublicKey: senderKeyPair.publicKey,
|
||||
}
|
||||
|
||||
const corruptedAdditionalDataString = crypto.base64Encode(JSON.stringify(corruptAdditionalData))
|
||||
|
||||
const corruptEncryptedString = [version, nonce, ciphertext, corruptedAdditionalDataString].join(':')
|
||||
|
||||
const decrypted = usecase.execute({
|
||||
stringToDecrypt: corruptEncryptedString,
|
||||
recipientSecretKey: recipientKeyPair.privateKey,
|
||||
})
|
||||
|
||||
expect(decrypted).toEqual({
|
||||
plaintext: 'foobar',
|
||||
signatureVerified: false,
|
||||
signaturePublicKey: senderSigningKeyPair.publicKey,
|
||||
senderPublicKey: senderKeyPair.publicKey,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,48 @@
|
||||
import { HexString, PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { AsymmetricallyEncryptedString } from '../../../Types/Types'
|
||||
import { V004AsymmetricStringComponents } from '../../V004AlgorithmTypes'
|
||||
import { ParseConsistentBase64JsonPayloadUseCase } from '../Utils/ParseConsistentBase64JsonPayload'
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
import { AsymmetricDecryptResult } from '../../../Types/AsymmetricDecryptResult'
|
||||
|
||||
export class AsymmetricDecryptUseCase {
|
||||
private parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||
|
||||
constructor(private readonly crypto: PureCryptoInterface) {}
|
||||
|
||||
execute(dto: {
|
||||
stringToDecrypt: AsymmetricallyEncryptedString
|
||||
recipientSecretKey: HexString
|
||||
}): AsymmetricDecryptResult | null {
|
||||
const [_, nonce, ciphertext, additionalDataString] = <V004AsymmetricStringComponents>dto.stringToDecrypt.split(':')
|
||||
|
||||
const additionalData = this.parseBase64Usecase.execute<AsymmetricItemAdditionalData>(additionalDataString)
|
||||
|
||||
try {
|
||||
const plaintext = this.crypto.sodiumCryptoBoxEasyDecrypt(
|
||||
ciphertext,
|
||||
nonce,
|
||||
additionalData.senderPublicKey,
|
||||
dto.recipientSecretKey,
|
||||
)
|
||||
if (!plaintext) {
|
||||
return null
|
||||
}
|
||||
|
||||
const signatureVerified = this.crypto.sodiumCryptoSignVerify(
|
||||
ciphertext,
|
||||
additionalData.signingData.signature,
|
||||
additionalData.signingData.publicKey,
|
||||
)
|
||||
|
||||
return {
|
||||
plaintext,
|
||||
signatureVerified,
|
||||
signaturePublicKey: additionalData.signingData.publicKey,
|
||||
senderPublicKey: additionalData.senderPublicKey,
|
||||
}
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { PkcKeyPair, PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { getMockedCrypto } from '../../MockedCrypto'
|
||||
import { AsymmetricEncryptUseCase } from './AsymmetricEncrypt'
|
||||
import { V004AsymmetricStringComponents } from '../../V004AlgorithmTypes'
|
||||
import { ParseConsistentBase64JsonPayloadUseCase } from '../Utils/ParseConsistentBase64JsonPayload'
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
|
||||
describe('asymmetric encrypt use case', () => {
|
||||
let crypto: PureCryptoInterface
|
||||
let usecase: AsymmetricEncryptUseCase
|
||||
let encryptionKeyPair: PkcKeyPair
|
||||
let signingKeyPair: PkcKeyPair
|
||||
let parseBase64Usecase: ParseConsistentBase64JsonPayloadUseCase
|
||||
|
||||
beforeEach(() => {
|
||||
crypto = getMockedCrypto()
|
||||
usecase = new AsymmetricEncryptUseCase(crypto)
|
||||
encryptionKeyPair = crypto.sodiumCryptoBoxSeedKeypair('seedling')
|
||||
signingKeyPair = crypto.sodiumCryptoSignSeedKeypair('seedling')
|
||||
parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(crypto)
|
||||
})
|
||||
|
||||
it('should generate encrypted string', () => {
|
||||
const recipientKeyPair = crypto.sodiumCryptoBoxSeedKeypair('recipient-seedling')
|
||||
|
||||
const result = usecase.execute({
|
||||
stringToEncrypt: 'foobar',
|
||||
senderKeyPair: encryptionKeyPair,
|
||||
senderSigningKeyPair: signingKeyPair,
|
||||
recipientPublicKey: recipientKeyPair.publicKey,
|
||||
})
|
||||
|
||||
const [version, nonce, ciphertext, additionalDataString] = <V004AsymmetricStringComponents>result.split(':')
|
||||
|
||||
expect(version).toEqual('004_Asym')
|
||||
expect(nonce).toEqual(expect.any(String))
|
||||
expect(ciphertext).toEqual(expect.any(String))
|
||||
expect(additionalDataString).toEqual(expect.any(String))
|
||||
|
||||
const additionalData = parseBase64Usecase.execute<AsymmetricItemAdditionalData>(additionalDataString)
|
||||
expect(additionalData.signingData.publicKey).toEqual(signingKeyPair.publicKey)
|
||||
expect(additionalData.signingData.signature).toEqual(expect.any(String))
|
||||
expect(additionalData.senderPublicKey).toEqual(encryptionKeyPair.publicKey)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,45 @@
|
||||
import { HexString, PkcKeyPair, PureCryptoInterface, Utf8String } from '@standardnotes/sncrypto-common'
|
||||
import { AsymmetricallyEncryptedString } from '../../../Types/Types'
|
||||
import { V004Algorithm } from '../../../../Algorithm'
|
||||
import { V004AsymmetricCiphertextPrefix, V004AsymmetricStringComponents } from '../../V004AlgorithmTypes'
|
||||
import { CreateConsistentBase64JsonPayloadUseCase } from '../Utils/CreateConsistentBase64JsonPayload'
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
|
||||
export class AsymmetricEncryptUseCase {
|
||||
private base64DataUsecase = new CreateConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||
|
||||
constructor(private readonly crypto: PureCryptoInterface) {}
|
||||
|
||||
execute(dto: {
|
||||
stringToEncrypt: Utf8String
|
||||
senderKeyPair: PkcKeyPair
|
||||
senderSigningKeyPair: PkcKeyPair
|
||||
recipientPublicKey: HexString
|
||||
}): AsymmetricallyEncryptedString {
|
||||
const nonce = this.crypto.generateRandomKey(V004Algorithm.AsymmetricEncryptionNonceLength)
|
||||
|
||||
const ciphertext = this.crypto.sodiumCryptoBoxEasyEncrypt(
|
||||
dto.stringToEncrypt,
|
||||
nonce,
|
||||
dto.senderKeyPair.privateKey,
|
||||
dto.recipientPublicKey,
|
||||
)
|
||||
|
||||
const additionalData: AsymmetricItemAdditionalData = {
|
||||
signingData: {
|
||||
publicKey: dto.senderSigningKeyPair.publicKey,
|
||||
signature: this.crypto.sodiumCryptoSign(ciphertext, dto.senderSigningKeyPair.privateKey),
|
||||
},
|
||||
senderPublicKey: dto.senderKeyPair.publicKey,
|
||||
}
|
||||
|
||||
const components: V004AsymmetricStringComponents = [
|
||||
V004AsymmetricCiphertextPrefix,
|
||||
nonce,
|
||||
ciphertext,
|
||||
this.base64DataUsecase.execute(additionalData),
|
||||
]
|
||||
|
||||
return components.join(':')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { AsymmetricallyEncryptedString } from '../../../Types/Types'
|
||||
import { V004AsymmetricStringComponents } from '../../V004AlgorithmTypes'
|
||||
import { ParseConsistentBase64JsonPayloadUseCase } from '../Utils/ParseConsistentBase64JsonPayload'
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
import { AsymmetricSignatureVerificationDetachedResult } from '../../../Types/AsymmetricSignatureVerificationDetachedResult'
|
||||
|
||||
export class AsymmetricSignatureVerificationDetachedUseCase {
|
||||
private parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||
|
||||
constructor(private readonly crypto: PureCryptoInterface) {}
|
||||
|
||||
execute(dto: { encryptedString: AsymmetricallyEncryptedString }): AsymmetricSignatureVerificationDetachedResult {
|
||||
const [_, __, ciphertext, additionalDataString] = <V004AsymmetricStringComponents>dto.encryptedString.split(':')
|
||||
|
||||
const additionalData = this.parseBase64Usecase.execute<AsymmetricItemAdditionalData>(additionalDataString)
|
||||
|
||||
try {
|
||||
const signatureVerified = this.crypto.sodiumCryptoSignVerify(
|
||||
ciphertext,
|
||||
additionalData.signingData.signature,
|
||||
additionalData.signingData.publicKey,
|
||||
)
|
||||
|
||||
return {
|
||||
signatureVerified,
|
||||
signaturePublicKey: additionalData.signingData.publicKey,
|
||||
senderPublicKey: additionalData.senderPublicKey,
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
signatureVerified: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user