diff --git a/keeperapi/src/browser/platform.ts b/keeperapi/src/browser/platform.ts index c3a00dfa..c5b962bd 100644 --- a/keeperapi/src/browser/platform.ts +++ b/keeperapi/src/browser/platform.ts @@ -96,7 +96,8 @@ export const browserPlatform: Platform = class { if (storage.saveObject) { await storage.saveObject(this.getStorageKeyId(keyId, 'cbc'), cbcKey) await storage.saveObject(this.getStorageKeyId(keyId, 'gcm'), gcmKey) - } else { + } + if (extractable && storage.saveKeyBytes) { await storage.saveKeyBytes(keyId, key) } } @@ -106,15 +107,18 @@ export const browserPlatform: Platform = class { keyId: string, privateKey: Uint8Array, publicKey: Uint8Array, - storage?: KeyStorage + storage?: KeyStorage, + canExport?: boolean ): Promise { - const key = await this.importPrivateKeyEC(privateKey, publicKey) + const extractable = !!canExport + const key = await this.importPrivateKeyEC(privateKey, publicKey, extractable) cryptoKeysCache['ecc'][keyId] = key if (storage) { if (storage.saveObject) { await storage.saveObject(this.getStorageKeyId(keyId, 'ecc'), key) - } else { + } + if (extractable && storage.saveKeyBytes) { const jwk = await crypto.subtle.exportKey('jwk', key) const keyBytes = this.stringToBytes(JSON.stringify(jwk)) await storage.saveKeyBytes(keyId, keyBytes) @@ -168,10 +172,9 @@ export const browserPlatform: Platform = class { if (storage?.getObject) { const storageKeyId = this.getStorageKeyId(keyId, keyType) const storedKey = await storage.getObject(storageKeyId) - if (!storedKey) { - throw new Error('Unable to load crypto key ' + keyId) + if (storedKey) { + return storedKey } - return storedKey } const keyBytes = await this.loadKeyBytes(keyId, storage) @@ -298,7 +301,7 @@ export const browserPlatform: Platform = class { break // TODO: add something like this, need to find pub/priv key pair case 'ecc': - if (cryptoKeysCache['gcm'][keyId]) { + if (cryptoKeysCache['ecc'][keyId]) { return } @@ -394,7 +397,8 @@ export const browserPlatform: Platform = class { if (storage.saveObject) { await storage.saveObject(this.getStorageKeyId(keyId, 'cbc'), cbcKey) await storage.saveObject(this.getStorageKeyId(keyId, 'gcm'), gcmKey) - } else { + } + if (canExtract && storage.saveKeyBytes) { const keyBuffer = await crypto.subtle.exportKey('raw', gcmKey) await storage.saveKeyBytes(keyId, new Uint8Array(keyBuffer)) } @@ -590,7 +594,7 @@ export const browserPlatform: Platform = class { return this.privateDecryptECWebCrypto(data, privateKeyImport, id, useHKDF) } - static async importPrivateKeyEC(privateKey: Uint8Array, publicKey: Uint8Array) { + static async importPrivateKeyEC(privateKey: Uint8Array, publicKey: Uint8Array, extractable = true) { const x = webSafe64FromBytes(publicKey.subarray(1, 33)) const y = webSafe64FromBytes(publicKey.subarray(33, 65)) const d = webSafe64FromBytes(privateKey) @@ -598,18 +602,24 @@ export const browserPlatform: Platform = class { const jwk = { crv: 'P-256', d, - ext: true, + ext: extractable, key_ops: ['deriveBits'], kty: 'EC', x, y, } - return this.importECCJsonWebKey(jwk) + return this.importECCJsonWebKey(jwk, extractable) } - static async importECCJsonWebKey(jwk: JsonWebKey): Promise { - return await crypto.subtle.importKey('jwk', jwk, { name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveBits']) + static async importECCJsonWebKey(jwk: JsonWebKey, extractable = true): Promise { + return await crypto.subtle.importKey( + 'jwk', + jwk, + { name: 'ECDH', namedCurve: 'P-256' }, + extractable, + ['deriveBits'] + ) } static async ecdhComputeSharedSecret( diff --git a/keeperapi/src/commands.ts b/keeperapi/src/commands.ts index 6e566b34..f3ccd604 100644 --- a/keeperapi/src/commands.ts +++ b/keeperapi/src/commands.ts @@ -211,6 +211,27 @@ export const enterpriseAllocateIdsCommand = ( ): RestCommand => createCommand(request, 'enterprise_allocate_ids') +export type NodeEditRequest = { + node_id: number + parent_id: number + encrypted_data: string + license_id?: number + restrict_visibility?: number +} + +export const nodeAddCommand = (request: NodeEditRequest): RestCommand => + createCommand(request, 'node_add') + +export const nodeUpdateCommand = (request: NodeEditRequest): RestCommand => + createCommand(request, 'node_update') + +export type NodeDeleteRequest = { + node_id: number +} + +export const nodeDeleteCommand = (request: NodeDeleteRequest): RestCommand => + createCommand(request, 'node_delete') + export type RoleEditRequest = { role_id: number node_id: number @@ -232,6 +253,114 @@ export type RoleDeleteRequest = { export const roleDeleteCommand = (request: RoleDeleteRequest): RestCommand => createCommand(request, 'role_delete') +export type RoleUserAddRequest = { + role_id: number + enterprise_user_id: number + tree_key?: string + tree_key_type?: string | number + role_admin_key?: string + role_admin_key_type?: string | number +} + +export const roleUserAddCommand = (request: RoleUserAddRequest): RestCommand => + createCommand(request, 'role_user_add') + +export type RoleUserRemoveRequest = { + role_id: number + enterprise_user_id: number +} + +export const roleUserRemoveCommand = ( + request: RoleUserRemoveRequest +): RestCommand => createCommand(request, 'role_user_remove') + +export type RoleManagedNodeTreeKey = { + enterprise_user_id: number + tree_key: string + tree_key_type?: string | number +} + +export type RoleManagedNodeAddResponse = KeeperResponse & { + missing_tree_keys?: Record + missing_ecc_tree_keys?: Record +} + +export type RoleManagedNodeAddRequest = { + role_id: number + managed_node_id: number + cascade_node_management: boolean + tree_keys?: RoleManagedNodeTreeKey[] +} + +export const roleManagedNodeAddCommand = ( + request: RoleManagedNodeAddRequest +): RestCommand => createCommand(request, 'role_managed_node_add') + +export type RoleManagedNodeUpdateRequest = { + role_id: number + managed_node_id: number + cascade_node_management?: boolean +} + +export const roleManagedNodeUpdateCommand = ( + request: RoleManagedNodeUpdateRequest +): RestCommand => createCommand(request, 'role_managed_node_update') + +export type RoleManagedNodeRemoveRequest = { + role_id: number + managed_node_id: number +} + +export const roleManagedNodeRemoveCommand = ( + request: RoleManagedNodeRemoveRequest +): RestCommand => createCommand(request, 'role_managed_node_remove') + +export type ManagedNodeRoleKey = { + enterprise_user_id: number + role_key: string + tree_key_type?: string | number +} + +export type ManagedNodeMspKey = { + mc_enterprise_id: number + tree_key: string +} + +export type ManagedNodePrivilegeAddRequest = { + role_id: number + managed_node_id: number + privilege: string + role_key_enc_with_tree_key?: string + role_public_key?: string + role_private_key?: string + role_keys?: ManagedNodeRoleKey[] + msp_keys?: ManagedNodeMspKey[] +} + +export type ManagedNodePrivilegeAddResponse = KeeperResponse & { + status?: { + missing_keys?: Record + invalid_users?: number[] + cant_be_pending?: number[] + } +} + +export const managedNodePrivilegeAddCommand = ( + request: ManagedNodePrivilegeAddRequest +): RestCommand => + createCommand(request, 'managed_node_privilege_add') + +export type ManagedNodePrivilegeRemoveRequest = { + role_id: number + managed_node_id: number + privilege: string +} + +export const managedNodePrivilegeRemoveCommand = ( + request: ManagedNodePrivilegeRemoveRequest +): RestCommand => + createCommand(request, 'managed_node_privilege_remove') + export type MoveRequest = { to_type: 'user_folder' | 'shared_folder' | 'shared_folder_folder' to_uid?: string @@ -379,6 +508,16 @@ export const teamEnterpriseUserRemoveCommand = ( request: TeamUserCommandRequest ): RestCommand => createCommand(request, 'team_enterprise_user_remove') +export type TeamEnterpriseUserUpdateRequest = { + team_uid: string + enterprise_user_id: number + user_type: number +} + +export const teamEnterpriseUserUpdateCommand = ( + request: TeamEnterpriseUserUpdateRequest +): RestCommand => createCommand(request, 'team_enterprise_user_update') + export type TeamGetKeysRequest = { teams: string[] } diff --git a/keeperapi/src/node/platform.ts b/keeperapi/src/node/platform.ts index 54fdbc14..8f4be9d6 100644 --- a/keeperapi/src/node/platform.ts +++ b/keeperapi/src/node/platform.ts @@ -65,7 +65,8 @@ export const nodePlatform: Platform = class { keyId: string, privateKey: Uint8Array, _publicKey: Uint8Array, - storage?: KeyStorage + storage?: KeyStorage, + _canExport?: boolean ): Promise { this.importKey(keyId, privateKey, storage) } diff --git a/keeperapi/src/restMessages.ts b/keeperapi/src/restMessages.ts index e8301747..7fbd629d 100644 --- a/keeperapi/src/restMessages.ts +++ b/keeperapi/src/restMessages.ts @@ -640,6 +640,11 @@ export const enterpriseUsersLockMessage = ( ): RestMessage => createMessage(data, 'enterprise/enterprise_users_lock', Enterprise.LockUsersRequest, Enterprise.LockUsersResponse) +export const disableTwoFaMessage = ( + data: Enterprise.IEnterpriseUserIds +): RestInMessage => + createInMessage(data, 'enterprise/disable_two_fa', Enterprise.EnterpriseUserIds) + export const setV2AlternatePasswordMessage = ( data: Authentication.IUserAuthRequest ): RestInMessage => diff --git a/keeperapi/src/syncDown/utils.ts b/keeperapi/src/syncDown/utils.ts index 62cc8d9e..6cc7ab65 100644 --- a/keeperapi/src/syncDown/utils.ts +++ b/keeperapi/src/syncDown/utils.ts @@ -14,6 +14,10 @@ export const mapTeamKeyType = (keyType: Folder.EncryptedKeyType, teamUid: string keyId = teamUid encryptionType = 'cbc' break + case Folder.EncryptedKeyType.encrypted_by_data_key_gcm: + keyId = teamUid + encryptionType = 'gcm' + break case Folder.EncryptedKeyType.encrypted_by_public_key: keyId = `${teamUid}_priv` encryptionType = 'rsa'