Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 24 additions & 14 deletions keeperapi/src/browser/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand All @@ -106,15 +107,18 @@ export const browserPlatform: Platform = class {
keyId: string,
privateKey: Uint8Array,
publicKey: Uint8Array,
storage?: KeyStorage
storage?: KeyStorage,
canExport?: boolean
): Promise<void> {
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)
Expand Down Expand Up @@ -168,10 +172,9 @@ export const browserPlatform: Platform = class {
if (storage?.getObject) {
const storageKeyId = this.getStorageKeyId(keyId, keyType)
const storedKey = await storage.getObject<CryptoKey>(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)
Expand Down Expand Up @@ -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]) {
Comment thread
tylerccarson marked this conversation as resolved.
return
}

Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -590,26 +594,32 @@ 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)

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<CryptoKey> {
return await crypto.subtle.importKey('jwk', jwk, { name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveBits'])
static async importECCJsonWebKey(jwk: JsonWebKey, extractable = true): Promise<CryptoKey> {
return await crypto.subtle.importKey(
'jwk',
jwk,
{ name: 'ECDH', namedCurve: 'P-256' },
extractable,
['deriveBits']
)
}

static async ecdhComputeSharedSecret(
Expand Down
139 changes: 139 additions & 0 deletions keeperapi/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,27 @@ export const enterpriseAllocateIdsCommand = (
): RestCommand<EnterpriseAllocateIdsRequest, KeeperResponse & { base_id: number; number_allocated: number }> =>
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<NodeEditRequest, KeeperResponse> =>
createCommand(request, 'node_add')

export const nodeUpdateCommand = (request: NodeEditRequest): RestCommand<NodeEditRequest, KeeperResponse> =>
createCommand(request, 'node_update')

export type NodeDeleteRequest = {
node_id: number
}

export const nodeDeleteCommand = (request: NodeDeleteRequest): RestCommand<NodeDeleteRequest, KeeperResponse> =>
createCommand(request, 'node_delete')

export type RoleEditRequest = {
role_id: number
node_id: number
Expand All @@ -232,6 +253,114 @@ export type RoleDeleteRequest = {
export const roleDeleteCommand = (request: RoleDeleteRequest): RestCommand<RoleDeleteRequest, KeeperResponse> =>
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<RoleUserAddRequest, KeeperResponse> =>
createCommand(request, 'role_user_add')

export type RoleUserRemoveRequest = {
role_id: number
enterprise_user_id: number
}

export const roleUserRemoveCommand = (
request: RoleUserRemoveRequest
): RestCommand<RoleUserRemoveRequest, KeeperResponse> => 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<string, string>
missing_ecc_tree_keys?: Record<string, string>
}

export type RoleManagedNodeAddRequest = {
role_id: number
managed_node_id: number
cascade_node_management: boolean
tree_keys?: RoleManagedNodeTreeKey[]
}

export const roleManagedNodeAddCommand = (
request: RoleManagedNodeAddRequest
): RestCommand<RoleManagedNodeAddRequest, RoleManagedNodeAddResponse> => 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<RoleManagedNodeUpdateRequest, KeeperResponse> => createCommand(request, 'role_managed_node_update')

export type RoleManagedNodeRemoveRequest = {
role_id: number
managed_node_id: number
}

export const roleManagedNodeRemoveCommand = (
request: RoleManagedNodeRemoveRequest
): RestCommand<RoleManagedNodeRemoveRequest, KeeperResponse> => 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<string, string>
invalid_users?: number[]
cant_be_pending?: number[]
}
}

export const managedNodePrivilegeAddCommand = (
request: ManagedNodePrivilegeAddRequest
): RestCommand<ManagedNodePrivilegeAddRequest, ManagedNodePrivilegeAddResponse> =>
createCommand(request, 'managed_node_privilege_add')

export type ManagedNodePrivilegeRemoveRequest = {
role_id: number
managed_node_id: number
privilege: string
}

export const managedNodePrivilegeRemoveCommand = (
request: ManagedNodePrivilegeRemoveRequest
): RestCommand<ManagedNodePrivilegeRemoveRequest, KeeperResponse> =>
createCommand(request, 'managed_node_privilege_remove')

export type MoveRequest = {
to_type: 'user_folder' | 'shared_folder' | 'shared_folder_folder'
to_uid?: string
Expand Down Expand Up @@ -379,6 +508,16 @@ export const teamEnterpriseUserRemoveCommand = (
request: TeamUserCommandRequest
): RestCommand<TeamUserCommandRequest, KeeperResponse> => 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<TeamEnterpriseUserUpdateRequest, KeeperResponse> => createCommand(request, 'team_enterprise_user_update')

export type TeamGetKeysRequest = {
teams: string[]
}
Expand Down
3 changes: 2 additions & 1 deletion keeperapi/src/node/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ export const nodePlatform: Platform = class {
keyId: string,
privateKey: Uint8Array,
_publicKey: Uint8Array,
storage?: KeyStorage
storage?: KeyStorage,
_canExport?: boolean
): Promise<void> {
this.importKey(keyId, privateKey, storage)
}
Expand Down
5 changes: 5 additions & 0 deletions keeperapi/src/restMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,11 @@ export const enterpriseUsersLockMessage = (
): RestMessage<Enterprise.ILockUsersRequest, Enterprise.ILockUsersResponse> =>
createMessage(data, 'enterprise/enterprise_users_lock', Enterprise.LockUsersRequest, Enterprise.LockUsersResponse)

export const disableTwoFaMessage = (
data: Enterprise.IEnterpriseUserIds
): RestInMessage<Enterprise.IEnterpriseUserIds> =>
createInMessage(data, 'enterprise/disable_two_fa', Enterprise.EnterpriseUserIds)

export const setV2AlternatePasswordMessage = (
data: Authentication.IUserAuthRequest
): RestInMessage<Authentication.IUserAuthRequest> =>
Expand Down
4 changes: 4 additions & 0 deletions keeperapi/src/syncDown/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Loading