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: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: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",
|
"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",
|
"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",
|
"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\"",
|
"release:prod": "lerna version --conventional-commits --yes -m \"chore(release): publish\"",
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
"typescript": "*"
|
"typescript": "*"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@standardnotes/common": "^1.46.6",
|
"@standardnotes/common": "^1.48.3",
|
||||||
"@standardnotes/domain-core": "^1.12.0",
|
"@standardnotes/domain-core": "^1.12.0",
|
||||||
"@standardnotes/encryption": "workspace:*",
|
"@standardnotes/encryption": "workspace:*",
|
||||||
"@standardnotes/models": "workspace:*",
|
"@standardnotes/models": "workspace:*",
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { ErrorMessage } from '../../Error/ErrorMessage'
|
import { ErrorMessage } from '../../Error/ErrorMessage'
|
||||||
import { ApiCallError } from '../../Error/ApiCallError'
|
import { ApiCallError } from '../../Error/ApiCallError'
|
||||||
import { ApiVersion } from '../../Api/ApiVersion'
|
import { ApiVersion } from '../../Api/ApiVersion'
|
||||||
import { ApiEndpointParam } from '../../Request/ApiEndpointParam'
|
|
||||||
import { SubscriptionServerInterface } from '../../Server/Subscription/SubscriptionServerInterface'
|
import { SubscriptionServerInterface } from '../../Server/Subscription/SubscriptionServerInterface'
|
||||||
import { AppleIAPConfirmResponseBody } from './../../Response/Subscription/AppleIAPConfirmResponseBody'
|
import { AppleIAPConfirmResponseBody } from './../../Response/Subscription/AppleIAPConfirmResponseBody'
|
||||||
import { SubscriptionInviteAcceptResponseBody } from '../../Response/Subscription/SubscriptionInviteAcceptResponseBody'
|
import { SubscriptionInviteAcceptResponseBody } from '../../Response/Subscription/SubscriptionInviteAcceptResponseBody'
|
||||||
import { SubscriptionInviteCancelResponseBody } from '../../Response/Subscription/SubscriptionInviteCancelResponseBody'
|
import { SubscriptionInviteCancelResponseBody } from '../../Response/Subscription/SubscriptionInviteCancelResponseBody'
|
||||||
import { SubscriptionInviteListResponseBody } from '../../Response/Subscription/SubscriptionInviteListResponseBody'
|
import { SubscriptionInviteListResponseBody } from '../../Response/Subscription/SubscriptionInviteListResponseBody'
|
||||||
import { SubscriptionInviteResponseBody } from '../../Response/Subscription/SubscriptionInviteResponseBody'
|
import { SubscriptionInviteResponseBody } from '../../Response/Subscription/SubscriptionInviteResponseBody'
|
||||||
import { HttpResponse } from '@standardnotes/responses'
|
import { HttpResponse, ApiEndpointParam } from '@standardnotes/responses'
|
||||||
|
|
||||||
import { SubscriptionApiServiceInterface } from './SubscriptionApiServiceInterface'
|
import { SubscriptionApiServiceInterface } from './SubscriptionApiServiceInterface'
|
||||||
import { SubscriptionApiOperations } from './SubscriptionApiOperations'
|
import { SubscriptionApiOperations } from './SubscriptionApiOperations'
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ export enum UserApiOperations {
|
|||||||
Registering,
|
Registering,
|
||||||
SubmittingRequest,
|
SubmittingRequest,
|
||||||
DeletingAccount,
|
DeletingAccount,
|
||||||
|
UpdatingUser,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ import { ErrorMessage } from '../../Error/ErrorMessage'
|
|||||||
import { ApiCallError } from '../../Error/ApiCallError'
|
import { ApiCallError } from '../../Error/ApiCallError'
|
||||||
import { UserServerInterface } from '../../Server/User/UserServerInterface'
|
import { UserServerInterface } from '../../Server/User/UserServerInterface'
|
||||||
import { ApiVersion } from '../../Api/ApiVersion'
|
import { ApiVersion } from '../../Api/ApiVersion'
|
||||||
import { ApiEndpointParam } from '../../Request/ApiEndpointParam'
|
import { HttpResponse, ApiEndpointParam } from '@standardnotes/responses'
|
||||||
import { HttpResponse } from '@standardnotes/responses'
|
|
||||||
|
|
||||||
import { UserDeletionResponseBody } from '../../Response/User/UserDeletionResponseBody'
|
import { UserDeletionResponseBody } from '../../Response/User/UserDeletionResponseBody'
|
||||||
import { UserRegistrationResponseBody } from '../../Response/User/UserRegistrationResponseBody'
|
import { UserRegistrationResponseBody } from '../../Response/User/UserRegistrationResponseBody'
|
||||||
@@ -15,6 +14,7 @@ import { UserRequestServerInterface } from '../../Server/UserRequest/UserRequest
|
|||||||
|
|
||||||
import { UserApiOperations } from './UserApiOperations'
|
import { UserApiOperations } from './UserApiOperations'
|
||||||
import { UserApiServiceInterface } from './UserApiServiceInterface'
|
import { UserApiServiceInterface } from './UserApiServiceInterface'
|
||||||
|
import { UserUpdateResponse } from '../../Response/User/UserUpdateResponse'
|
||||||
|
|
||||||
export class UserApiService implements UserApiServiceInterface {
|
export class UserApiService implements UserApiServiceInterface {
|
||||||
private operationsInProgress: Map<UserApiOperations, boolean>
|
private operationsInProgress: Map<UserApiOperations, boolean>
|
||||||
@@ -35,7 +35,7 @@ export class UserApiService implements UserApiServiceInterface {
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
} catch (error) {
|
} 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 {
|
private lockOperation(operation: UserApiOperations): void {
|
||||||
if (this.operationsInProgress.get(operation)) {
|
if (this.operationsInProgress.get(operation)) {
|
||||||
throw new ApiCallError(ErrorMessage.GenericInProgress)
|
throw new ApiCallError(ErrorMessage.GenericInProgress)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { HttpResponse } from '@standardnotes/responses'
|
|||||||
import { UserDeletionResponseBody } from '../../Response/User/UserDeletionResponseBody'
|
import { UserDeletionResponseBody } from '../../Response/User/UserDeletionResponseBody'
|
||||||
import { UserRegistrationResponseBody } from '../../Response/User/UserRegistrationResponseBody'
|
import { UserRegistrationResponseBody } from '../../Response/User/UserRegistrationResponseBody'
|
||||||
import { UserRequestResponseBody } from '../../Response/UserRequest/UserRequestResponseBody'
|
import { UserRequestResponseBody } from '../../Response/UserRequest/UserRequestResponseBody'
|
||||||
|
import { UserUpdateResponse } from '../../Response/User/UserUpdateResponse'
|
||||||
|
|
||||||
export interface UserApiServiceInterface {
|
export interface UserApiServiceInterface {
|
||||||
register(registerDTO: {
|
register(registerDTO: {
|
||||||
@@ -13,9 +14,12 @@ export interface UserApiServiceInterface {
|
|||||||
keyParams: RootKeyParamsInterface
|
keyParams: RootKeyParamsInterface
|
||||||
ephemeral: boolean
|
ephemeral: boolean
|
||||||
}): Promise<HttpResponse<UserRegistrationResponseBody>>
|
}): Promise<HttpResponse<UserRegistrationResponseBody>>
|
||||||
|
updateUser(updateDTO: { userUuid: string }): Promise<HttpResponse<UserUpdateResponse>>
|
||||||
|
|
||||||
submitUserRequest(dto: {
|
submitUserRequest(dto: {
|
||||||
userUuid: string
|
userUuid: string
|
||||||
requestType: UserRequestType
|
requestType: UserRequestType
|
||||||
}): Promise<HttpResponse<UserRequestResponseBody>>
|
}): Promise<HttpResponse<UserRequestResponseBody>>
|
||||||
|
|
||||||
deleteAccount(userUuid: string): Promise<HttpResponse<UserDeletionResponseBody>>
|
deleteAccount(userUuid: string): Promise<HttpResponse<UserDeletionResponseBody>>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,15 @@ export class HttpService implements HttpServiceInterface {
|
|||||||
this.host = host
|
this.host = host
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getHost(): string {
|
||||||
|
return this.host
|
||||||
|
}
|
||||||
|
|
||||||
async get<T>(path: string, params?: HttpRequestParams, authentication?: string): Promise<HttpResponse<T>> {
|
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({
|
return this.runHttp({
|
||||||
url: joinPaths(this.host, path),
|
url: joinPaths(this.host, path),
|
||||||
params,
|
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>> {
|
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({
|
return this.runHttp({
|
||||||
url: joinPaths(this.host, path),
|
url: joinPaths(this.host, path),
|
||||||
params,
|
params,
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ import { HttpRequest, HttpRequestParams, HttpResponse, HttpResponseMeta } from '
|
|||||||
|
|
||||||
export interface HttpServiceInterface {
|
export interface HttpServiceInterface {
|
||||||
setHost(host: string): void
|
setHost(host: string): void
|
||||||
|
getHost(): string
|
||||||
setSession(session: Session): void
|
setSession(session: Session): void
|
||||||
get<T>(path: string, params?: HttpRequestParams, authentication?: string): Promise<HttpResponse<T>>
|
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>>
|
post<T>(path: string, params?: HttpRequestParams, authentication?: string): Promise<HttpResponse<T>>
|
||||||
put<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>>
|
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'
|
import { ApiVersion } from '../../Api/ApiVersion'
|
||||||
|
|
||||||
export type SubscriptionInviteCancelRequestParams = {
|
export type SubscriptionInviteCancelRequestParams = {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ApiEndpointParam } from '../ApiEndpointParam'
|
import { ApiEndpointParam } from '@standardnotes/responses'
|
||||||
import { ApiVersion } from '../../Api/ApiVersion'
|
import { ApiVersion } from '../../Api/ApiVersion'
|
||||||
|
|
||||||
export type SubscriptionInviteDeclineRequestParams = {
|
export type SubscriptionInviteDeclineRequestParams = {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ApiEndpointParam } from '../ApiEndpointParam'
|
import { ApiEndpointParam } from '@standardnotes/responses'
|
||||||
import { ApiVersion } from '../../Api/ApiVersion'
|
import { ApiVersion } from '../../Api/ApiVersion'
|
||||||
|
|
||||||
export type SubscriptionInviteListRequestParams = {
|
export type SubscriptionInviteListRequestParams = {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ApiEndpointParam } from '../ApiEndpointParam'
|
import { ApiEndpointParam } from '@standardnotes/responses'
|
||||||
import { ApiVersion } from '../../Api/ApiVersion'
|
import { ApiVersion } from '../../Api/ApiVersion'
|
||||||
|
|
||||||
export type SubscriptionInviteRequestParams = {
|
export type SubscriptionInviteRequestParams = {
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import { AnyKeyParamsContent } from '@standardnotes/common'
|
import { AnyKeyParamsContent } from '@standardnotes/common'
|
||||||
import { ApiEndpointParam } from '../ApiEndpointParam'
|
import { ApiEndpointParam } from '@standardnotes/responses'
|
||||||
import { ApiVersion } from '../../Api/ApiVersion'
|
import { ApiVersion } from '../../Api/ApiVersion'
|
||||||
|
|
||||||
export type UserRegistrationRequestParams = AnyKeyParamsContent & {
|
export type UserRegistrationRequestParams = AnyKeyParamsContent & {
|
||||||
[ApiEndpointParam.ApiVersion]: ApiVersion.v0
|
[ApiEndpointParam.ApiVersion]: ApiVersion.v0
|
||||||
|
[additionalParam: string]: unknown
|
||||||
password: string
|
password: string
|
||||||
email: string
|
email: string
|
||||||
ephemeral: boolean
|
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/DeleteAuthenticatorRequestParams'
|
||||||
export * from './Authenticator/GenerateAuthenticatorAuthenticationOptionsRequestParams'
|
export * from './Authenticator/GenerateAuthenticatorAuthenticationOptionsRequestParams'
|
||||||
export * from './Authenticator/ListAuthenticatorsRequestParams'
|
export * from './Authenticator/ListAuthenticatorsRequestParams'
|
||||||
@@ -17,3 +16,4 @@ export * from './Subscription/SubscriptionInviteRequestParams'
|
|||||||
export * from './User/UserRegistrationRequestParams'
|
export * from './User/UserRegistrationRequestParams'
|
||||||
export * from './UserRequest/UserRequestRequestParams'
|
export * from './UserRequest/UserRequestRequestParams'
|
||||||
export * from './WebSocket/WebSocketConnectionTokenRequestParams'
|
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
|
items_key_id: string | null
|
||||||
enc_item_key: string | null
|
enc_item_key: string | null
|
||||||
auth_hash: string | null
|
auth_hash: string | null
|
||||||
|
user_uuid: string
|
||||||
|
key_system_identifier: string | null
|
||||||
|
shared_vault_uuid: string | null
|
||||||
created_at: string
|
created_at: string
|
||||||
updated_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/SubscriptionInviteDeclineResponseBody'
|
||||||
export * from './Subscription/SubscriptionInviteListResponseBody'
|
export * from './Subscription/SubscriptionInviteListResponseBody'
|
||||||
export * from './Subscription/SubscriptionInviteResponseBody'
|
export * from './Subscription/SubscriptionInviteResponseBody'
|
||||||
|
|
||||||
export * from './User/UserDeletionResponseBody'
|
export * from './User/UserDeletionResponseBody'
|
||||||
export * from './User/UserRegistrationResponseBody'
|
export * from './User/UserRegistrationResponseBody'
|
||||||
|
|
||||||
export * from './UserRequest/UserRequestResponseBody'
|
export * from './UserRequest/UserRequestResponseBody'
|
||||||
export * from './WebSocket/WebSocketConnectionTokenResponseBody'
|
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 = {
|
const UserPaths = {
|
||||||
register: '/v1/users',
|
register: '/v1/users',
|
||||||
|
updateAccount: (userUuid: string) => `/v1/users/${userUuid}`,
|
||||||
deleteAccount: (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 { HttpServiceInterface } from '../../Http/HttpServiceInterface'
|
||||||
import { UserDeletionRequestParams } from '../../Request/User/UserDeletionRequestParams'
|
import { UserDeletionRequestParams } from '../../Request/User/UserDeletionRequestParams'
|
||||||
import { UserRegistrationRequestParams } from '../../Request/User/UserRegistrationRequestParams'
|
import { UserRegistrationRequestParams } from '../../Request/User/UserRegistrationRequestParams'
|
||||||
@@ -6,6 +7,7 @@ import { UserDeletionResponseBody } from '../../Response/User/UserDeletionRespon
|
|||||||
import { UserRegistrationResponseBody } from '../../Response/User/UserRegistrationResponseBody'
|
import { UserRegistrationResponseBody } from '../../Response/User/UserRegistrationResponseBody'
|
||||||
import { Paths } from './Paths'
|
import { Paths } from './Paths'
|
||||||
import { UserServerInterface } from './UserServerInterface'
|
import { UserServerInterface } from './UserServerInterface'
|
||||||
|
import { UserUpdateRequestParams } from '../../Request/User/UserUpdateRequestParams'
|
||||||
|
|
||||||
export class UserServer implements UserServerInterface {
|
export class UserServer implements UserServerInterface {
|
||||||
constructor(private httpService: HttpServiceInterface) {}
|
constructor(private httpService: HttpServiceInterface) {}
|
||||||
@@ -17,4 +19,8 @@ export class UserServer implements UserServerInterface {
|
|||||||
async register(params: UserRegistrationRequestParams): Promise<HttpResponse<UserRegistrationResponseBody>> {
|
async register(params: UserRegistrationRequestParams): Promise<HttpResponse<UserRegistrationResponseBody>> {
|
||||||
return this.httpService.post(Paths.v1.register, params)
|
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 { UserRegistrationRequestParams } from '../../Request/User/UserRegistrationRequestParams'
|
||||||
import { UserDeletionResponseBody } from '../../Response/User/UserDeletionResponseBody'
|
import { UserDeletionResponseBody } from '../../Response/User/UserDeletionResponseBody'
|
||||||
import { UserRegistrationResponseBody } from '../../Response/User/UserRegistrationResponseBody'
|
import { UserRegistrationResponseBody } from '../../Response/User/UserRegistrationResponseBody'
|
||||||
|
import { UserUpdateResponse } from '../../Response/User/UserUpdateResponse'
|
||||||
|
import { UserUpdateRequestParams } from '../../Request/User/UserUpdateRequestParams'
|
||||||
|
|
||||||
export interface UserServerInterface {
|
export interface UserServerInterface {
|
||||||
register(params: UserRegistrationRequestParams): Promise<HttpResponse<UserRegistrationResponseBody>>
|
register(params: UserRegistrationRequestParams): Promise<HttpResponse<UserRegistrationResponseBody>>
|
||||||
deleteAccount(params: UserDeletionRequestParams): Promise<HttpResponse<UserDeletionResponseBody>>
|
deleteAccount(params: UserDeletionRequestParams): Promise<HttpResponse<UserDeletionResponseBody>>
|
||||||
|
update(params: UserUpdateRequestParams): Promise<HttpResponse<UserUpdateResponse>>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,32 @@
|
|||||||
export * from './Auth/AuthServer'
|
export * from './Auth/AuthServer'
|
||||||
export * from './Auth/AuthServerInterface'
|
export * from './Auth/AuthServerInterface'
|
||||||
|
|
||||||
export * from './Authenticator/AuthenticatorServer'
|
export * from './Authenticator/AuthenticatorServer'
|
||||||
export * from './Authenticator/AuthenticatorServerInterface'
|
export * from './Authenticator/AuthenticatorServerInterface'
|
||||||
|
|
||||||
export * from './Revision/RevisionServer'
|
export * from './Revision/RevisionServer'
|
||||||
export * from './Revision/RevisionServerInterface'
|
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/SubscriptionServer'
|
||||||
export * from './Subscription/SubscriptionServerInterface'
|
export * from './Subscription/SubscriptionServerInterface'
|
||||||
|
|
||||||
|
export * from './SharedVaultInvites/SharedVaultInvitesServer'
|
||||||
|
export * from './SharedVaultInvites/SharedVaultInvitesServerInterface'
|
||||||
|
|
||||||
export * from './User/UserServer'
|
export * from './User/UserServer'
|
||||||
export * from './User/UserServerInterface'
|
export * from './User/UserServerInterface'
|
||||||
|
|
||||||
export * from './UserRequest/UserRequestServer'
|
export * from './UserRequest/UserRequestServer'
|
||||||
export * from './UserRequest/UserRequestServerInterface'
|
export * from './UserRequest/UserRequestServerInterface'
|
||||||
|
|
||||||
export * from './WebSocket/WebSocketServer'
|
export * from './WebSocket/WebSocketServer'
|
||||||
export * from './WebSocket/WebSocketServerInterface'
|
export * from './WebSocket/WebSocketServerInterface'
|
||||||
|
|||||||
@@ -5,22 +5,15 @@
|
|||||||
"node": ">=16.0.0 <17.0.0"
|
"node": ">=16.0.0 <17.0.0"
|
||||||
},
|
},
|
||||||
"description": "Payload encryption used in SNJS library",
|
"description": "Payload encryption used in SNJS library",
|
||||||
"main": "dist/index.js",
|
"main": "./src/index.ts",
|
||||||
|
"private": true,
|
||||||
"author": "Standard Notes",
|
"author": "Standard Notes",
|
||||||
"types": "dist/index.d.ts",
|
|
||||||
"files": [
|
|
||||||
"dist"
|
|
||||||
],
|
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"scripts": {
|
"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",
|
"lint": "eslint src --ext .ts",
|
||||||
"format": "prettier --write src",
|
"format": "prettier --write src",
|
||||||
"test": "jest"
|
"test": "jest",
|
||||||
|
"build": "echo 'Empty build script required for yarn topological install'"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@standardnotes/config": "2.4.3",
|
"@standardnotes/config": "2.4.3",
|
||||||
@@ -35,7 +28,7 @@
|
|||||||
"typescript": "*"
|
"typescript": "*"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@standardnotes/common": "^1.46.6",
|
"@standardnotes/common": "^1.48.3",
|
||||||
"@standardnotes/models": "workspace:*",
|
"@standardnotes/models": "workspace:*",
|
||||||
"@standardnotes/responses": "workspace:*",
|
"@standardnotes/responses": "workspace:*",
|
||||||
"@standardnotes/sncrypto-common": "workspace:*",
|
"@standardnotes/sncrypto-common": "workspace:*",
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { SodiumConstant } from '@standardnotes/sncrypto-common'
|
||||||
|
|
||||||
export const V001Algorithm = Object.freeze({
|
export const V001Algorithm = Object.freeze({
|
||||||
SaltSeedLength: 128,
|
SaltSeedLength: 128,
|
||||||
/**
|
/**
|
||||||
@@ -41,11 +43,21 @@ export enum V004Algorithm {
|
|||||||
ArgonIterations = 5,
|
ArgonIterations = 5,
|
||||||
ArgonMemLimit = 67108864,
|
ArgonMemLimit = 67108864,
|
||||||
ArgonOutputKeyBytes = 64,
|
ArgonOutputKeyBytes = 64,
|
||||||
|
|
||||||
EncryptionKeyLength = 256,
|
EncryptionKeyLength = 256,
|
||||||
EncryptionNonceLength = 192,
|
EncryptionNonceLength = 192,
|
||||||
}
|
|
||||||
|
|
||||||
export enum V005Algorithm {
|
|
||||||
AsymmetricEncryptionNonceLength = 192,
|
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,
|
HistoryEntryInterface,
|
||||||
ItemsKeyContent,
|
ItemsKeyContent,
|
||||||
ItemsKeyInterface,
|
ItemsKeyInterface,
|
||||||
RootKeyInterface,
|
|
||||||
} from '@standardnotes/models'
|
} from '@standardnotes/models'
|
||||||
|
|
||||||
export function isItemsKey(x: ItemsKeyInterface | RootKeyInterface): x is ItemsKeyInterface {
|
export function isItemsKey(x: unknown): x is ItemsKeyInterface {
|
||||||
return x.content_type === ContentType.ItemsKey
|
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,
|
PayloadTimestampDefaults,
|
||||||
RootKeyContent,
|
RootKeyContent,
|
||||||
RootKeyContentSpecialized,
|
RootKeyContentSpecialized,
|
||||||
|
RootKeyInterface,
|
||||||
} from '@standardnotes/models'
|
} from '@standardnotes/models'
|
||||||
import { UuidGenerator } from '@standardnotes/utils'
|
import { UuidGenerator } from '@standardnotes/utils'
|
||||||
import { SNRootKey } from './RootKey'
|
import { SNRootKey } from './RootKey'
|
||||||
|
|
||||||
export function CreateNewRootKey(content: RootKeyContentSpecialized): SNRootKey {
|
export function CreateNewRootKey<K extends RootKeyInterface>(content: RootKeyContentSpecialized): K {
|
||||||
const uuid = UuidGenerator.GenerateUuid()
|
const uuid = UuidGenerator.GenerateUuid()
|
||||||
|
|
||||||
const payload = new DecryptedPayload<RootKeyContent>({
|
const payload = new DecryptedPayload<RootKeyContent>({
|
||||||
@@ -19,7 +20,7 @@ export function CreateNewRootKey(content: RootKeyContentSpecialized): SNRootKey
|
|||||||
...PayloadTimestampDefaults(),
|
...PayloadTimestampDefaults(),
|
||||||
})
|
})
|
||||||
|
|
||||||
return new SNRootKey(payload)
|
return new SNRootKey(payload) as K
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FillRootKeyContent(content: Partial<RootKeyContentSpecialized>): RootKeyContent {
|
export function FillRootKeyContent(content: Partial<RootKeyContentSpecialized>): RootKeyContent {
|
||||||
@@ -37,15 +38,3 @@ export function FillRootKeyContent(content: Partial<RootKeyContentSpecialized>):
|
|||||||
|
|
||||||
return FillItemContentSpecialized(content)
|
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,
|
RootKeyContentInStorage,
|
||||||
RootKeyInterface,
|
RootKeyInterface,
|
||||||
} from '@standardnotes/models'
|
} from '@standardnotes/models'
|
||||||
import { timingSafeEqual } from '@standardnotes/sncrypto-common'
|
import { PkcKeyPair, timingSafeEqual } from '@standardnotes/sncrypto-common'
|
||||||
import { SNRootKeyParams } from './RootKeyParams'
|
import { SNRootKeyParams } from './RootKeyParams'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,6 +47,14 @@ export class SNRootKey extends DecryptedItem<RootKeyContent> implements RootKeyI
|
|||||||
return this.content.serverPassword
|
return this.content.serverPassword
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get encryptionKeyPair(): PkcKeyPair | undefined {
|
||||||
|
return this.content.encryptionKeyPair
|
||||||
|
}
|
||||||
|
|
||||||
|
get signingKeyPair(): PkcKeyPair | undefined {
|
||||||
|
return this.content.signingKeyPair
|
||||||
|
}
|
||||||
|
|
||||||
/** 003 and below only. */
|
/** 003 and below only. */
|
||||||
public get dataAuthenticationKey(): string | undefined {
|
public get dataAuthenticationKey(): string | undefined {
|
||||||
return this.content.dataAuthenticationKey
|
return this.content.dataAuthenticationKey
|
||||||
@@ -84,6 +92,8 @@ export class SNRootKey extends DecryptedItem<RootKeyContent> implements RootKeyI
|
|||||||
const values: NamespacedRootKeyInKeychain = {
|
const values: NamespacedRootKeyInKeychain = {
|
||||||
version: this.keyVersion,
|
version: this.keyVersion,
|
||||||
masterKey: this.masterKey,
|
masterKey: this.masterKey,
|
||||||
|
encryptionKeyPair: this.encryptionKeyPair,
|
||||||
|
signingKeyPair: this.signingKeyPair,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.dataAuthenticationKey) {
|
if (this.dataAuthenticationKey) {
|
||||||
|
|||||||
@@ -8,8 +8,13 @@ import {
|
|||||||
ItemsKeyContent,
|
ItemsKeyContent,
|
||||||
ItemsKeyInterface,
|
ItemsKeyInterface,
|
||||||
PayloadTimestampDefaults,
|
PayloadTimestampDefaults,
|
||||||
|
KeySystemItemsKeyInterface,
|
||||||
|
KeySystemIdentifier,
|
||||||
|
KeySystemRootKeyInterface,
|
||||||
|
RootKeyInterface,
|
||||||
|
KeySystemRootKeyParamsInterface,
|
||||||
} from '@standardnotes/models'
|
} 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 { firstHalfOfString, secondHalfOfString, splitString, UuidGenerator } from '@standardnotes/utils'
|
||||||
import { V001Algorithm } from '../../Algorithm'
|
import { V001Algorithm } from '../../Algorithm'
|
||||||
import { isItemsKey } from '../../Keys/ItemsKey/ItemsKey'
|
import { isItemsKey } from '../../Keys/ItemsKey/ItemsKey'
|
||||||
@@ -17,11 +22,16 @@ import { CreateNewRootKey } from '../../Keys/RootKey/Functions'
|
|||||||
import { Create001KeyParams } from '../../Keys/RootKey/KeyParamsFunctions'
|
import { Create001KeyParams } from '../../Keys/RootKey/KeyParamsFunctions'
|
||||||
import { SNRootKey } from '../../Keys/RootKey/RootKey'
|
import { SNRootKey } from '../../Keys/RootKey/RootKey'
|
||||||
import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
|
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 { ItemAuthenticatedData } from '../../Types/ItemAuthenticatedData'
|
||||||
import { LegacyAttachedData } from '../../Types/LegacyAttachedData'
|
import { LegacyAttachedData } from '../../Types/LegacyAttachedData'
|
||||||
import { RootKeyEncryptedAuthenticatedData } from '../../Types/RootKeyEncryptedAuthenticatedData'
|
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'
|
const NO_IV = '00000000000000000000000000000000'
|
||||||
|
|
||||||
@@ -29,7 +39,7 @@ const NO_IV = '00000000000000000000000000000000'
|
|||||||
* @deprecated
|
* @deprecated
|
||||||
* A legacy operator no longer used to generate new accounts
|
* 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
|
protected readonly crypto: PureCryptoInterface
|
||||||
|
|
||||||
constructor(crypto: PureCryptoInterface) {
|
constructor(crypto: PureCryptoInterface) {
|
||||||
@@ -68,11 +78,11 @@ export class SNProtocolOperator001 implements AsynchronousOperator {
|
|||||||
return CreateDecryptedItemFromPayload(payload)
|
return CreateDecryptedItemFromPayload(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createRootKey(
|
public async createRootKey<K extends RootKeyInterface>(
|
||||||
identifier: string,
|
identifier: string,
|
||||||
password: string,
|
password: string,
|
||||||
origination: KeyParamsOrigination,
|
origination: KeyParamsOrigination,
|
||||||
): Promise<SNRootKey> {
|
): Promise<K> {
|
||||||
const pwCost = V001Algorithm.PbkdfMinCost as number
|
const pwCost = V001Algorithm.PbkdfMinCost as number
|
||||||
const pwNonce = this.crypto.generateRandomKey(V001Algorithm.SaltSeedLength)
|
const pwNonce = this.crypto.generateRandomKey(V001Algorithm.SaltSeedLength)
|
||||||
const pwSalt = await this.crypto.unsafeSha1(identifier + 'SN' + pwNonce)
|
const pwSalt = await this.crypto.unsafeSha1(identifier + 'SN' + pwNonce)
|
||||||
@@ -90,13 +100,13 @@ export class SNProtocolOperator001 implements AsynchronousOperator {
|
|||||||
return this.deriveKey(password, keyParams)
|
return this.deriveKey(password, keyParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPayloadAuthenticatedData(
|
public getPayloadAuthenticatedDataForExternalUse(
|
||||||
_encrypted: EncryptedParameters,
|
_encrypted: EncryptedOutputParameters,
|
||||||
): RootKeyEncryptedAuthenticatedData | ItemAuthenticatedData | LegacyAttachedData | undefined {
|
): RootKeyEncryptedAuthenticatedData | ItemAuthenticatedData | LegacyAttachedData | undefined {
|
||||||
return 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)
|
return this.deriveKey(password, keyParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +121,7 @@ export class SNProtocolOperator001 implements AsynchronousOperator {
|
|||||||
public async generateEncryptedParametersAsync(
|
public async generateEncryptedParametersAsync(
|
||||||
payload: DecryptedPayloadInterface,
|
payload: DecryptedPayloadInterface,
|
||||||
key: ItemsKeyInterface | SNRootKey,
|
key: ItemsKeyInterface | SNRootKey,
|
||||||
): Promise<EncryptedParameters> {
|
): Promise<EncryptedOutputParameters> {
|
||||||
/**
|
/**
|
||||||
* Generate new item key that is double the key size.
|
* Generate new item key that is double the key size.
|
||||||
* Will be split to create encryption key and authentication key.
|
* Will be split to create encryption key and authentication key.
|
||||||
@@ -132,16 +142,19 @@ export class SNProtocolOperator001 implements AsynchronousOperator {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
uuid: payload.uuid,
|
uuid: payload.uuid,
|
||||||
|
content_type: payload.content_type,
|
||||||
items_key_id: isItemsKey(key) ? key.uuid : undefined,
|
items_key_id: isItemsKey(key) ? key.uuid : undefined,
|
||||||
content: ciphertext,
|
content: ciphertext,
|
||||||
enc_item_key: encItemKey,
|
enc_item_key: encItemKey,
|
||||||
auth_hash: authHash,
|
auth_hash: authHash,
|
||||||
version: this.version,
|
version: this.version,
|
||||||
|
key_system_identifier: payload.key_system_identifier,
|
||||||
|
shared_vault_uuid: payload.shared_vault_uuid,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async generateDecryptedParametersAsync<C extends ItemContent = ItemContent>(
|
public async generateDecryptedParametersAsync<C extends ItemContent = ItemContent>(
|
||||||
encrypted: EncryptedParameters,
|
encrypted: EncryptedOutputParameters,
|
||||||
key: ItemsKeyInterface | SNRootKey,
|
key: ItemsKeyInterface | SNRootKey,
|
||||||
): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> {
|
): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> {
|
||||||
if (!encrypted.enc_item_key) {
|
if (!encrypted.enc_item_key) {
|
||||||
@@ -178,6 +191,7 @@ export class SNProtocolOperator001 implements AsynchronousOperator {
|
|||||||
return {
|
return {
|
||||||
uuid: encrypted.uuid,
|
uuid: encrypted.uuid,
|
||||||
content: JSON.parse(content),
|
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(
|
const derivedKey = await this.crypto.pbkdf2(
|
||||||
password,
|
password,
|
||||||
keyParams.content001.pw_salt,
|
keyParams.content001.pw_salt,
|
||||||
@@ -205,11 +219,63 @@ export class SNProtocolOperator001 implements AsynchronousOperator {
|
|||||||
|
|
||||||
const partitions = splitString(derivedKey, 2)
|
const partitions = splitString(derivedKey, 2)
|
||||||
|
|
||||||
return CreateNewRootKey({
|
return CreateNewRootKey<K>({
|
||||||
serverPassword: partitions[0],
|
serverPassword: partitions[0],
|
||||||
masterKey: partitions[1],
|
masterKey: partitions[1],
|
||||||
version: ProtocolVersion.V001,
|
version: ProtocolVersion.V001,
|
||||||
keyParams: keyParams.getPortableValue(),
|
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 { Create002KeyParams } from '../../Keys/RootKey/KeyParamsFunctions'
|
||||||
import { SNRootKey } from '../../Keys/RootKey/RootKey'
|
import { SNRootKey } from '../../Keys/RootKey/RootKey'
|
||||||
import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
|
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 { ItemAuthenticatedData } from '../../Types/ItemAuthenticatedData'
|
||||||
import { LegacyAttachedData } from '../../Types/LegacyAttachedData'
|
import { LegacyAttachedData } from '../../Types/LegacyAttachedData'
|
||||||
import { RootKeyEncryptedAuthenticatedData } from '../../Types/RootKeyEncryptedAuthenticatedData'
|
import { RootKeyEncryptedAuthenticatedData } from '../../Types/RootKeyEncryptedAuthenticatedData'
|
||||||
@@ -50,11 +51,11 @@ export class SNProtocolOperator002 extends SNProtocolOperator001 {
|
|||||||
return Models.CreateDecryptedItemFromPayload(payload)
|
return Models.CreateDecryptedItemFromPayload(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async createRootKey(
|
public override async createRootKey<K extends Models.RootKeyInterface>(
|
||||||
identifier: string,
|
identifier: string,
|
||||||
password: string,
|
password: string,
|
||||||
origination: Common.KeyParamsOrigination,
|
origination: Common.KeyParamsOrigination,
|
||||||
): Promise<SNRootKey> {
|
): Promise<K> {
|
||||||
const pwCost = Utils.lastElement(V002Algorithm.PbkdfCostsUsed) as number
|
const pwCost = Utils.lastElement(V002Algorithm.PbkdfCostsUsed) as number
|
||||||
const pwNonce = this.crypto.generateRandomKey(V002Algorithm.SaltSeedLength)
|
const pwNonce = this.crypto.generateRandomKey(V002Algorithm.SaltSeedLength)
|
||||||
const pwSalt = await this.crypto.unsafeSha1(identifier + ':' + pwNonce)
|
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
|
* 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.
|
* 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)
|
return this.deriveKey(password, keyParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,8 +145,8 @@ export class SNProtocolOperator002 extends SNProtocolOperator001 {
|
|||||||
return this.decryptString002(contentCiphertext, encryptionKey, iv)
|
return this.decryptString002(contentCiphertext, encryptionKey, iv)
|
||||||
}
|
}
|
||||||
|
|
||||||
public override getPayloadAuthenticatedData(
|
public override getPayloadAuthenticatedDataForExternalUse(
|
||||||
encrypted: EncryptedParameters,
|
encrypted: EncryptedOutputParameters,
|
||||||
): RootKeyEncryptedAuthenticatedData | ItemAuthenticatedData | LegacyAttachedData | undefined {
|
): RootKeyEncryptedAuthenticatedData | ItemAuthenticatedData | LegacyAttachedData | undefined {
|
||||||
const itemKeyComponents = this.encryptionComponentsFromString002(encrypted.enc_item_key)
|
const itemKeyComponents = this.encryptionComponentsFromString002(encrypted.enc_item_key)
|
||||||
const authenticatedData = itemKeyComponents.keyParams
|
const authenticatedData = itemKeyComponents.keyParams
|
||||||
@@ -161,7 +165,7 @@ export class SNProtocolOperator002 extends SNProtocolOperator001 {
|
|||||||
public override async generateEncryptedParametersAsync(
|
public override async generateEncryptedParametersAsync(
|
||||||
payload: Models.DecryptedPayloadInterface,
|
payload: Models.DecryptedPayloadInterface,
|
||||||
key: Models.ItemsKeyInterface | SNRootKey,
|
key: Models.ItemsKeyInterface | SNRootKey,
|
||||||
): Promise<EncryptedParameters> {
|
): Promise<EncryptedOutputParameters> {
|
||||||
/**
|
/**
|
||||||
* Generate new item key that is double the key size.
|
* Generate new item key that is double the key size.
|
||||||
* Will be split to create encryption key and authentication key.
|
* Will be split to create encryption key and authentication key.
|
||||||
@@ -189,15 +193,18 @@ export class SNProtocolOperator002 extends SNProtocolOperator001 {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
uuid: payload.uuid,
|
uuid: payload.uuid,
|
||||||
|
content_type: payload.content_type,
|
||||||
items_key_id: isItemsKey(key) ? key.uuid : undefined,
|
items_key_id: isItemsKey(key) ? key.uuid : undefined,
|
||||||
content: ciphertext,
|
content: ciphertext,
|
||||||
enc_item_key: encItemKey,
|
enc_item_key: encItemKey,
|
||||||
version: this.version,
|
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>(
|
public override async generateDecryptedParametersAsync<C extends ItemContent = ItemContent>(
|
||||||
encrypted: EncryptedParameters,
|
encrypted: EncryptedOutputParameters,
|
||||||
key: Models.ItemsKeyInterface | SNRootKey,
|
key: Models.ItemsKeyInterface | SNRootKey,
|
||||||
): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> {
|
): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> {
|
||||||
if (!encrypted.enc_item_key) {
|
if (!encrypted.enc_item_key) {
|
||||||
@@ -252,11 +259,15 @@ export class SNProtocolOperator002 extends SNProtocolOperator001 {
|
|||||||
return {
|
return {
|
||||||
uuid: encrypted.uuid,
|
uuid: encrypted.uuid,
|
||||||
content: JSON.parse(content),
|
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(
|
const derivedKey = await this.crypto.pbkdf2(
|
||||||
password,
|
password,
|
||||||
keyParams.content002.pw_salt,
|
keyParams.content002.pw_salt,
|
||||||
@@ -270,7 +281,7 @@ export class SNProtocolOperator002 extends SNProtocolOperator001 {
|
|||||||
|
|
||||||
const partitions = Utils.splitString(derivedKey, 3)
|
const partitions = Utils.splitString(derivedKey, 3)
|
||||||
|
|
||||||
return CreateNewRootKey({
|
return CreateNewRootKey<K>({
|
||||||
serverPassword: partitions[0],
|
serverPassword: partitions[0],
|
||||||
masterKey: partitions[1],
|
masterKey: partitions[1],
|
||||||
dataAuthenticationKey: partitions[2],
|
dataAuthenticationKey: partitions[2],
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ import {
|
|||||||
ItemsKeyContent,
|
ItemsKeyContent,
|
||||||
ItemsKeyInterface,
|
ItemsKeyInterface,
|
||||||
PayloadTimestampDefaults,
|
PayloadTimestampDefaults,
|
||||||
|
RootKeyInterface,
|
||||||
} from '@standardnotes/models'
|
} from '@standardnotes/models'
|
||||||
import { splitString, UuidGenerator } from '@standardnotes/utils'
|
import { splitString, UuidGenerator } from '@standardnotes/utils'
|
||||||
import { V003Algorithm } from '../../Algorithm'
|
import { V003Algorithm } from '../../Algorithm'
|
||||||
import { CreateNewRootKey } from '../../Keys/RootKey/Functions'
|
import { CreateNewRootKey } from '../../Keys/RootKey/Functions'
|
||||||
import { Create003KeyParams } from '../../Keys/RootKey/KeyParamsFunctions'
|
import { Create003KeyParams } from '../../Keys/RootKey/KeyParamsFunctions'
|
||||||
import { SNRootKey } from '../../Keys/RootKey/RootKey'
|
|
||||||
import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
|
import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
|
||||||
import { SNProtocolOperator002 } from '../002/Operator002'
|
import { SNProtocolOperator002 } from '../002/Operator002'
|
||||||
|
|
||||||
@@ -53,11 +53,17 @@ export class SNProtocolOperator003 extends SNProtocolOperator002 {
|
|||||||
return CreateDecryptedItemFromPayload(payload)
|
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)
|
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(
|
const salt = await this.generateSalt(
|
||||||
keyParams.content003.identifier,
|
keyParams.content003.identifier,
|
||||||
ProtocolVersion.V003,
|
ProtocolVersion.V003,
|
||||||
@@ -78,7 +84,7 @@ export class SNProtocolOperator003 extends SNProtocolOperator002 {
|
|||||||
|
|
||||||
const partitions = splitString(derivedKey, 3)
|
const partitions = splitString(derivedKey, 3)
|
||||||
|
|
||||||
return CreateNewRootKey({
|
return CreateNewRootKey<K>({
|
||||||
serverPassword: partitions[0],
|
serverPassword: partitions[0],
|
||||||
masterKey: partitions[1],
|
masterKey: partitions[1],
|
||||||
dataAuthenticationKey: partitions[2],
|
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,
|
identifier: string,
|
||||||
password: string,
|
password: string,
|
||||||
origination: KeyParamsOrigination,
|
origination: KeyParamsOrigination,
|
||||||
): Promise<SNRootKey> {
|
): Promise<K> {
|
||||||
const version = ProtocolVersion.V003
|
const version = ProtocolVersion.V003
|
||||||
const pwNonce = this.crypto.generateRandomKey(V003Algorithm.SaltSeedLength)
|
const pwNonce = this.crypto.generateRandomKey(V003Algorithm.SaltSeedLength)
|
||||||
const keyParams = Create003KeyParams({
|
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 { ContentType, ProtocolVersion } from '@standardnotes/common'
|
||||||
import { DecryptedPayload, ItemContent, ItemsKeyContent, PayloadTimestampDefaults } from '@standardnotes/models'
|
import { DecryptedPayload, ItemContent, ItemsKeyContent, PayloadTimestampDefaults } from '@standardnotes/models'
|
||||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
|
||||||
import { SNItemsKey } from '../../Keys/ItemsKey/ItemsKey'
|
import { SNItemsKey } from '../../Keys/ItemsKey/ItemsKey'
|
||||||
import { ItemAuthenticatedData } from '../../Types/ItemAuthenticatedData'
|
|
||||||
import { SNProtocolOperator004 } from './Operator004'
|
import { SNProtocolOperator004 } from './Operator004'
|
||||||
|
import { getMockedCrypto } from './MockedCrypto'
|
||||||
const b64 = (text: string): string => {
|
import { deconstructEncryptedPayloadString } from './V004AlgorithmHelpers'
|
||||||
return Buffer.from(text).toString('base64')
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('operator 004', () => {
|
describe('operator 004', () => {
|
||||||
let crypto: PureCryptoInterface
|
const crypto = getMockedCrypto()
|
||||||
|
|
||||||
let operator: SNProtocolOperator004
|
let operator: SNProtocolOperator004
|
||||||
|
|
||||||
beforeEach(() => {
|
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)
|
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', () => {
|
it('should deconstructEncryptedPayloadString', () => {
|
||||||
const string = '004:noncy:<e>foo<e>:eyJ1IjoiMTIzIiwidiI6IjAwNCJ9'
|
const string = '004:noncy:<e>foo<e>:eyJ1IjoiMTIzIiwidiI6IjAwNCJ9'
|
||||||
|
|
||||||
const result = operator.deconstructEncryptedPayloadString(string)
|
const result = deconstructEncryptedPayloadString(string)
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
version: '004',
|
version: '004',
|
||||||
nonce: 'noncy',
|
nonce: 'noncy',
|
||||||
ciphertext: '<e>foo<e>',
|
ciphertext: '<e>foo<e>',
|
||||||
authenticatedData: 'eyJ1IjoiMTIzIiwidiI6IjAwNCJ9',
|
authenticatedData: 'eyJ1IjoiMTIzIiwidiI6IjAwNCJ9',
|
||||||
|
additionalData: 'e30=',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should generateEncryptedParametersSync', () => {
|
it('should generateEncryptedParameters', () => {
|
||||||
const payload = {
|
const payload = {
|
||||||
uuid: '123',
|
uuid: '123',
|
||||||
content_type: ContentType.Note,
|
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({
|
expect(result).toEqual({
|
||||||
uuid: '123',
|
uuid: '123',
|
||||||
items_key_id: 'key-456',
|
items_key_id: 'key-456',
|
||||||
content: '004:random-string:<e>{"foo":"bar"}<e>:eyJ1IjoiMTIzIiwidiI6IjAwNCJ9',
|
key_system_identifier: undefined,
|
||||||
enc_item_key: '004:random-string:<e>random-string<e>:eyJ1IjoiMTIzIiwidiI6IjAwNCJ9',
|
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',
|
version: '004',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,44 +1,56 @@
|
|||||||
import { ContentType, KeyParamsOrigination, ProtocolVersion } from '@standardnotes/common'
|
|
||||||
import * as Models from '@standardnotes/models'
|
|
||||||
import {
|
import {
|
||||||
CreateDecryptedItemFromPayload,
|
CreateDecryptedItemFromPayload,
|
||||||
FillItemContent,
|
|
||||||
ItemContent,
|
ItemContent,
|
||||||
ItemsKeyContent,
|
|
||||||
ItemsKeyInterface,
|
ItemsKeyInterface,
|
||||||
PayloadTimestampDefaults,
|
PayloadTimestampDefaults,
|
||||||
|
DecryptedPayload,
|
||||||
|
DecryptedPayloadInterface,
|
||||||
|
KeySystemItemsKeyInterface,
|
||||||
|
KeySystemRootKeyInterface,
|
||||||
|
FillItemContentSpecialized,
|
||||||
|
ItemsKeyContentSpecialized,
|
||||||
|
KeySystemIdentifier,
|
||||||
|
RootKeyInterface,
|
||||||
|
KeySystemRootKeyParamsInterface,
|
||||||
} from '@standardnotes/models'
|
} from '@standardnotes/models'
|
||||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
import { ContentType, KeyParamsOrigination, ProtocolVersion } from '@standardnotes/common'
|
||||||
import * as Utils from '@standardnotes/utils'
|
import { HexString, PkcKeyPair, PureCryptoInterface, Utf8String } from '@standardnotes/sncrypto-common'
|
||||||
import { V004Algorithm } from '../../Algorithm'
|
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 { 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 { ItemAuthenticatedData } from '../../Types/ItemAuthenticatedData'
|
||||||
import { LegacyAttachedData } from '../../Types/LegacyAttachedData'
|
import { LegacyAttachedData } from '../../Types/LegacyAttachedData'
|
||||||
import { RootKeyEncryptedAuthenticatedData } from '../../Types/RootKeyEncryptedAuthenticatedData'
|
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]
|
export class SNProtocolOperator004 implements OperatorInterface, SyncOperatorInterface {
|
||||||
|
constructor(protected readonly crypto: PureCryptoInterface) {}
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
public getEncryptionDisplayName(): string {
|
public getEncryptionDisplayName(): string {
|
||||||
return 'XChaCha20-Poly1305'
|
return 'XChaCha20-Poly1305'
|
||||||
@@ -50,7 +62,7 @@ export class SNProtocolOperator004 implements SynchronousOperator {
|
|||||||
|
|
||||||
private generateNewItemsKeyContent() {
|
private generateNewItemsKeyContent() {
|
||||||
const itemsKey = this.crypto.generateRandomKey(V004Algorithm.EncryptionKeyLength)
|
const itemsKey = this.crypto.generateRandomKey(V004Algorithm.EncryptionKeyLength)
|
||||||
const response = FillItemContent<ItemsKeyContent>({
|
const response = FillItemContentSpecialized<ItemsKeyContentSpecialized>({
|
||||||
itemsKey: itemsKey,
|
itemsKey: itemsKey,
|
||||||
version: ProtocolVersion.V004,
|
version: ProtocolVersion.V004,
|
||||||
})
|
})
|
||||||
@@ -62,260 +74,130 @@ export class SNProtocolOperator004 implements SynchronousOperator {
|
|||||||
* The consumer must save/sync this item.
|
* The consumer must save/sync this item.
|
||||||
*/
|
*/
|
||||||
public createItemsKey(): ItemsKeyInterface {
|
public createItemsKey(): ItemsKeyInterface {
|
||||||
const payload = new Models.DecryptedPayload({
|
const payload = new DecryptedPayload({
|
||||||
uuid: Utils.UuidGenerator.GenerateUuid(),
|
uuid: UuidGenerator.GenerateUuid(),
|
||||||
content_type: ContentType.ItemsKey,
|
content_type: ContentType.ItemsKey,
|
||||||
content: this.generateNewItemsKeyContent(),
|
content: this.generateNewItemsKeyContent(),
|
||||||
|
key_system_identifier: undefined,
|
||||||
|
shared_vault_uuid: undefined,
|
||||||
...PayloadTimestampDefaults(),
|
...PayloadTimestampDefaults(),
|
||||||
})
|
})
|
||||||
return CreateDecryptedItemFromPayload(payload)
|
return CreateDecryptedItemFromPayload(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
createRandomizedKeySystemRootKey(dto: { systemIdentifier: KeySystemIdentifier }): KeySystemRootKeyInterface {
|
||||||
* We require both a client-side component and a server-side component in generating a
|
const usecase = new CreateRandomKeySystemRootKey(this.crypto)
|
||||||
* salt. This way, a comprimised server cannot benefit from sending the same seed value
|
return usecase.execute(dto)
|
||||||
* 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
createUserInputtedKeySystemRootKey(dto: {
|
||||||
* Computes a root key given a passworf
|
systemIdentifier: KeySystemIdentifier
|
||||||
* qwd and previous keyParams
|
userInputtedPassword: string
|
||||||
* @param password - Plain string representing raw user password
|
}): KeySystemRootKeyInterface {
|
||||||
* @param keyParams - KeyParams object
|
const usecase = new CreateUserInputKeySystemRootKey(this.crypto)
|
||||||
*/
|
return usecase.execute(dto)
|
||||||
public async computeRootKey(password: string, keyParams: SNRootKeyParams): Promise<SNRootKey> {
|
|
||||||
return this.deriveKey(password, keyParams)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
deriveUserInputtedKeySystemRootKey(dto: {
|
||||||
* Creates a new root key given an identifier and a user password
|
keyParams: KeySystemRootKeyParamsInterface
|
||||||
* @param identifier - Plain string representing a unique identifier
|
userInputtedPassword: string
|
||||||
* @param password - Plain string representing raw user password
|
}): KeySystemRootKeyInterface {
|
||||||
*/
|
const usecase = new DeriveKeySystemRootKeyUseCase(this.crypto)
|
||||||
public async createRootKey(
|
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,
|
identifier: string,
|
||||||
password: string,
|
password: Utf8String,
|
||||||
origination: KeyParamsOrigination,
|
origination: KeyParamsOrigination,
|
||||||
): Promise<SNRootKey> {
|
): Promise<K> {
|
||||||
const version = ProtocolVersion.V004
|
const usecase = new CreateRootKeyUseCase(this.crypto)
|
||||||
const seed = this.crypto.generateRandomKey(V004Algorithm.ArgonSaltSeedLength)
|
return usecase.execute(identifier, password, origination)
|
||||||
const keyParams = Create004KeyParams({
|
|
||||||
identifier: identifier,
|
|
||||||
pw_nonce: seed,
|
|
||||||
version: version,
|
|
||||||
origination: origination,
|
|
||||||
created: `${Date.now()}`,
|
|
||||||
})
|
|
||||||
return this.deriveKey(password, keyParams)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public getPayloadAuthenticatedDataForExternalUse(
|
||||||
* @param plaintext - The plaintext to encrypt.
|
encrypted: EncryptedOutputParameters,
|
||||||
* @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,
|
|
||||||
): RootKeyEncryptedAuthenticatedData | ItemAuthenticatedData | LegacyAttachedData | undefined {
|
): RootKeyEncryptedAuthenticatedData | ItemAuthenticatedData | LegacyAttachedData | undefined {
|
||||||
const itemKeyComponents = this.deconstructEncryptedPayloadString(encrypted.enc_item_key)
|
const usecase = new GetPayloadAuthenticatedDataDetachedUseCase(this.crypto)
|
||||||
const authenticatedDataString = itemKeyComponents.authenticatedData
|
return usecase.execute(encrypted)
|
||||||
const result = this.stringToAuthenticatedData(authenticatedDataString)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public generateEncryptedParameters(
|
||||||
* For items that are encrypted with a root key, we append the root key's key params, so
|
payload: DecryptedPayloadInterface,
|
||||||
* that in the event the client/user loses a reference to their root key, they may still
|
key: ItemsKeyInterface | KeySystemItemsKeyInterface | KeySystemRootKeyInterface | RootKeyInterface,
|
||||||
* decrypt data by regenerating the key based on the attached key params.
|
signingKeyPair?: PkcKeyPair,
|
||||||
*/
|
): EncryptedOutputParameters {
|
||||||
private generateAuthenticatedDataForPayload(
|
const usecase = new GenerateEncryptedParametersUseCase(this.crypto)
|
||||||
payload: Models.DecryptedPayloadInterface,
|
return usecase.execute(payload, key, signingKeyPair)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private authenticatedDataToString(attachedData: ItemAuthenticatedData) {
|
public generateDecryptedParameters<C extends ItemContent = ItemContent>(
|
||||||
return this.crypto.base64Encode(JSON.stringify(Utils.sortedCopy(Utils.omitUndefinedCopy(attachedData))))
|
encrypted: EncryptedInputParameters,
|
||||||
}
|
key: ItemsKeyInterface | KeySystemItemsKeyInterface | KeySystemRootKeyInterface | RootKeyInterface,
|
||||||
|
|
||||||
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,
|
|
||||||
): DecryptedParameters<C> | ErrorDecryptingParameters {
|
): DecryptedParameters<C> | ErrorDecryptingParameters {
|
||||||
const contentKeyComponents = this.deconstructEncryptedPayloadString(encrypted.enc_item_key)
|
const usecase = new GenerateDecryptedParametersUseCase(this.crypto)
|
||||||
const authenticatedData = this.stringToAuthenticatedData(contentKeyComponents.authenticatedData, {
|
return usecase.execute(encrypted, key)
|
||||||
u: encrypted.uuid,
|
}
|
||||||
v: encrypted.version,
|
|
||||||
})
|
|
||||||
|
|
||||||
const useAuthenticatedString = this.authenticatedDataToString(authenticatedData)
|
public asymmetricEncrypt(dto: {
|
||||||
const contentKey = this.decryptString004(
|
stringToEncrypt: Utf8String
|
||||||
contentKeyComponents.ciphertext,
|
senderKeyPair: PkcKeyPair
|
||||||
key.itemsKey,
|
senderSigningKeyPair: PkcKeyPair
|
||||||
contentKeyComponents.nonce,
|
recipientPublicKey: HexString
|
||||||
useAuthenticatedString,
|
}): AsymmetricallyEncryptedString {
|
||||||
)
|
const usecase = new AsymmetricEncryptUseCase(this.crypto)
|
||||||
|
return usecase.execute(dto)
|
||||||
|
}
|
||||||
|
|
||||||
if (!contentKey) {
|
asymmetricDecrypt(dto: {
|
||||||
console.error('Error decrypting itemKey parameters', encrypted)
|
stringToDecrypt: AsymmetricallyEncryptedString
|
||||||
return {
|
recipientSecretKey: HexString
|
||||||
uuid: encrypted.uuid,
|
}): AsymmetricDecryptResult | null {
|
||||||
errorDecrypting: true,
|
const usecase = new AsymmetricDecryptUseCase(this.crypto)
|
||||||
}
|
return usecase.execute(dto)
|
||||||
}
|
}
|
||||||
|
|
||||||
const contentComponents = this.deconstructEncryptedPayloadString(encrypted.content)
|
asymmetricSignatureVerifyDetached(
|
||||||
const content = this.decryptString004(
|
encryptedString: AsymmetricallyEncryptedString,
|
||||||
contentComponents.ciphertext,
|
): AsymmetricSignatureVerificationDetachedResult {
|
||||||
contentKey,
|
const usecase = new AsymmetricSignatureVerificationDetachedUseCase(this.crypto)
|
||||||
contentComponents.nonce,
|
return usecase.execute({ encryptedString })
|
||||||
useAuthenticatedString,
|
}
|
||||||
)
|
|
||||||
|
|
||||||
if (!content) {
|
getSenderPublicKeySetFromAsymmetricallyEncryptedString(string: AsymmetricallyEncryptedString): PublicKeySet {
|
||||||
return {
|
const [_, __, ___, additionalDataString] = <V004AsymmetricStringComponents>string.split(':')
|
||||||
uuid: encrypted.uuid,
|
const parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||||
errorDecrypting: true,
|
const additionalData = parseBase64Usecase.execute<AsymmetricItemAdditionalData>(additionalDataString)
|
||||||
}
|
return {
|
||||||
} else {
|
encryption: additionalData.senderPublicKey,
|
||||||
return {
|
signing: additionalData.signingData.publicKey,
|
||||||
uuid: encrypted.uuid,
|
|
||||||
content: JSON.parse(content),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async deriveKey(password: string, keyParams: SNRootKeyParams): Promise<SNRootKey> {
|
versionForAsymmetricallyEncryptedString(string: string): ProtocolVersion {
|
||||||
const salt = await this.generateSalt004(keyParams.content004.identifier, keyParams.content004.pw_nonce)
|
const [versionPrefix] = <V004AsymmetricStringComponents>string.split(':')
|
||||||
const derivedKey = this.crypto.argon2(
|
const version = versionPrefix.split('_')[0]
|
||||||
password,
|
return version as ProtocolVersion
|
||||||
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(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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