diff --git a/src/CryptoErrorCode.ts b/src/CryptoErrorCode.ts index d6f29b9..206bd8b 100644 --- a/src/CryptoErrorCode.ts +++ b/src/CryptoErrorCode.ts @@ -58,6 +58,7 @@ export enum CryptoErrorCode { CalProvidersAlreadyInitialized = "error.crypto.cal.providersAlreadyInitialized", CalImportOfKey = "error.crypto.cal.calImportOfKey", CalKeyDerivation = "error.crypto.cal.keyDerivation", + CalLoadKey = "error.crypto.cal.loadKey", DeserializeValidation = "error.deserialize.validation" } diff --git a/src/crypto-layer/CryptoDerivationHandle.ts b/src/crypto-layer/CryptoDerivationHandle.ts index 4234c3f..f4510ab 100644 --- a/src/crypto-layer/CryptoDerivationHandle.ts +++ b/src/crypto-layer/CryptoDerivationHandle.ts @@ -9,7 +9,9 @@ import { CryptoEncryptionAlgorithm } from "../encryption/CryptoEncryption"; import { CryptoHashAlgorithm } from "../hash/CryptoHash"; import { getProvider, ProviderIdentifier } from "./CryptoLayerProviders"; import { CryptoLayerUtils } from "./CryptoLayerUtils"; -import { BaseKeyHandle, BaseKeyHandleConstructor } from "./encryption/BaseKeyHandle"; +import { BaseDerivedKeyHandle } from "./encryption/BaseDerivedKeyHandle"; +import { BaseKeyHandle } from "./encryption/BaseKeyHandle"; +import { CryptoEncryptionHandle } from "./encryption/CryptoEncryptionHandle"; import { DeviceBoundDerivedKeyHandle } from "./encryption/DeviceBoundDerivedKeyHandle"; import { DeviceBoundKeyHandle } from "./encryption/DeviceBoundKeyHandle"; import { PortableDerivedKeyHandle } from "./encryption/PortableDerivedKeyHandle"; @@ -28,8 +30,8 @@ export interface DeriveKeyHandleFromPasswordParameters { } export class CryptoDerivationHandle { - private static async deriveKeyHandleFromPassword( - constructor: BaseKeyHandleConstructor, + private static async deriveKeyHandleFromPassword( + constructor: new () => T, derivationParameters: DeriveKeyHandleFromPasswordParameters, keySpecOfResultingKey: KeySpec ): Promise { @@ -62,7 +64,7 @@ export class CryptoDerivationHandle { ); } - return await constructor.fromProviderAndKeyHandle(provider, keyHandle); + return await CryptoEncryptionHandle._keyHandleFromProviderAndCalKeyHandle(constructor, provider, keyHandle); } /** @@ -105,8 +107,8 @@ export class CryptoDerivationHandle { ); } - private static async deriveKeyFromBaseKeyHandle( - constructor: BaseKeyHandleConstructor, + private static async deriveKeyFromBaseKeyHandle( + constructor: new () => R, baseKey: T, keyId: number, context: string @@ -126,7 +128,11 @@ export class CryptoDerivationHandle { ); } - return await constructor.fromProviderAndKeyHandle(baseKey.provider, keyHandle); + return await CryptoEncryptionHandle._keyHandleFromProviderAndCalKeyHandle( + constructor, + baseKey.provider, + keyHandle + ); } /** diff --git a/src/crypto-layer/encryption/BaseDerivedKeyHandle.ts b/src/crypto-layer/encryption/BaseDerivedKeyHandle.ts new file mode 100644 index 0000000..b5787b2 --- /dev/null +++ b/src/crypto-layer/encryption/BaseDerivedKeyHandle.ts @@ -0,0 +1,33 @@ +import { KeyHandle, Provider } from "@nmshd/rs-crypto-types"; +import { CryptoEncryptionAlgorithm } from "../../encryption/CryptoEncryption"; +import { CryptoHashAlgorithm } from "../../hash/CryptoHash"; +import { CryptoLayerUtils } from "../CryptoLayerUtils"; + +/** + * Variant of {@link BaseKeyHandle} without serialization and deserialization. + */ +export abstract class BaseDerivedKeyHandle { + public id: string; + public providerName: string; + + public provider: Provider; + public keyHandle: KeyHandle; + + public async encryptionAndHashAlgorithm(): Promise<[CryptoEncryptionAlgorithm, CryptoHashAlgorithm]> { + const spec = await this.keyHandle.spec(); + return [ + CryptoLayerUtils.cryptoEncryptionAlgorithmFromCipher(spec.cipher), + CryptoLayerUtils.cryptoHashAlgorithmFromCryptoHash(spec.signing_hash) + ]; + } + + public async encryptionAlgorithm(): Promise { + const spec = await this.keyHandle.spec(); + return CryptoLayerUtils.cryptoEncryptionAlgorithmFromCipher(spec.cipher); + } + + public async hashAlgorithm(): Promise { + const spec = await this.keyHandle.spec(); + return CryptoLayerUtils.cryptoHashAlgorithmFromCryptoHash(spec.signing_hash); + } +} diff --git a/src/crypto-layer/encryption/BaseKeyHandle.ts b/src/crypto-layer/encryption/BaseKeyHandle.ts index 9235329..8dc9656 100644 --- a/src/crypto-layer/encryption/BaseKeyHandle.ts +++ b/src/crypto-layer/encryption/BaseKeyHandle.ts @@ -1,34 +1,21 @@ import { ISerializable, ISerialized, SerializableAsync, serialize, type, validate } from "@js-soft/ts-serval"; -import { KeyHandle, KeySpec, Provider } from "@nmshd/rs-crypto-types"; -import { isKeySpec } from "@nmshd/rs-crypto-types/checks"; -import { CoreBuffer, ICoreBuffer } from "../../CoreBuffer"; +import { KeyHandle, Provider } from "@nmshd/rs-crypto-types"; import { CryptoError } from "../../CryptoError"; import { CryptoErrorCode } from "../../CryptoErrorCode"; import { CryptoSerializableAsync } from "../../CryptoSerializable"; -import { getProvider, ProviderIdentifier } from "../CryptoLayerProviders"; +import { CryptoEncryptionAlgorithm } from "../../encryption/CryptoEncryption"; +import { CryptoHashAlgorithm } from "../../hash/CryptoHash"; +import { getProvider } from "../CryptoLayerProviders"; +import { CryptoLayerUtils } from "../CryptoLayerUtils"; export interface IBaseKeyHandleSerialized extends ISerialized { kid: string; pnm: string; - spc: KeySpec; } export interface IBaseKeyHandle extends ISerializable { id: string; providerName: string; - spec: KeySpec; -} - -export interface BaseKeyHandleConstructor { - new (): T; - - deserialize(value: string): Promise; - fromProviderAndKeyHandle( - provider: Provider, - keyHandle: KeyHandle, - other?: { providerName?: string; keyId?: string; keySpec?: KeySpec } - ): Promise; - fromAny(value: any): Promise; } /** @@ -46,65 +33,25 @@ export abstract class BaseKeyHandle extends CryptoSerializableAsync implements I @serialize() public providerName: string; - @validate({ - customValidator: (value) => { - if (isKeySpec(value)) return undefined; - return "Is not of type KeySpec."; - } - }) - @serialize() - public spec: KeySpec; - public provider: Provider; public keyHandle: KeyHandle; - /** - * Deserializes an object representation of a {@link BaseKeyHandle}. - * - * This method is not able to import raw keys or {@link KeyHandle}. - */ - public static async from( - this: BaseKeyHandleConstructor, - value: BaseKeyHandleConstructor | IBaseKeyHandle | CoreBuffer - ): Promise { - return await this.fromAny(value); + public async encryptionAndHashAlgorithm(): Promise<[CryptoEncryptionAlgorithm, CryptoHashAlgorithm]> { + const spec = await this.keyHandle.spec(); + return [ + CryptoLayerUtils.cryptoEncryptionAlgorithmFromCipher(spec.cipher), + CryptoLayerUtils.cryptoHashAlgorithmFromCryptoHash(spec.signing_hash) + ]; } - /** @see from */ - public static async fromJSON( - this: BaseKeyHandleConstructor, - value: IBaseKeyHandleSerialized - ): Promise { - return await this.fromAny(value); + public async encryptionAlgorithm(): Promise { + const spec = await this.keyHandle.spec(); + return CryptoLayerUtils.cryptoEncryptionAlgorithmFromCipher(spec.cipher); } - /** @see from */ - public static async fromBase64( - this: BaseKeyHandleConstructor, - value: string - ): Promise { - return await this.deserialize(CoreBuffer.base64_utf8(value)); - } - - /** - * Creates a new {@link BaseKeyHandle} or its child from an existing {@link KeyHandle}. - */ - public static async fromProviderAndKeyHandle( - this: new () => T, - provider: Provider, - keyHandle: KeyHandle - ): Promise { - const result = new this(); - - [result.providerName, result.id, result.spec] = await Promise.all([ - provider.providerName(), - keyHandle.id(), - keyHandle.spec() - ]); - - result.provider = provider; - result.keyHandle = keyHandle; - return result; + public async hashAlgorithm(): Promise { + const spec = await this.keyHandle.spec(); + return CryptoLayerUtils.cryptoHashAlgorithmFromCryptoHash(spec.signing_hash); } protected static override preFrom(value: any): any { @@ -125,37 +72,21 @@ export abstract class BaseKeyHandle extends CryptoSerializableAsync implements I } const provider = getProvider({ providerName: value.providerName }); - const keyHandle = await provider.loadKey(value.id); - - value.keyHandle = keyHandle; - value.provider = provider; - return value; - } -} - -export abstract class ImportableBaseKeyHandle extends BaseKeyHandle { - /** - * Creates a new {@link BaseKeyHandle} or its child by importing a raw key into a provider. - */ - public static async fromRawKey( - this: BaseKeyHandleConstructor, - providerIdent: ProviderIdentifier, - rawKey: ICoreBuffer, - spec: KeySpec - ): Promise { - const provider = getProvider(providerIdent); - let keyHandle; + let keyHandle: KeyHandle; try { - keyHandle = await provider.importKey(spec, rawKey.buffer); + keyHandle = await provider.loadKey(value.id); } catch (e) { throw new CryptoError( - CryptoErrorCode.CalImportOfKey, - "Failed to import raw symmetric key.", + CryptoErrorCode.CalLoadKey, + "Failed to load key during deserialization.", undefined, e as Error, - ImportableBaseKeyHandle.fromRawKey + BaseKeyHandle.postFrom ); } - return await this.fromProviderAndKeyHandle(provider, keyHandle); + + value.keyHandle = keyHandle; + value.provider = provider; + return value; } } diff --git a/src/crypto-layer/encryption/CryptoEncryptionHandle.ts b/src/crypto-layer/encryption/CryptoEncryptionHandle.ts index ea9c812..5827f21 100644 --- a/src/crypto-layer/encryption/CryptoEncryptionHandle.ts +++ b/src/crypto-layer/encryption/CryptoEncryptionHandle.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { KeySpec, Provider } from "@nmshd/rs-crypto-types"; +import { KeyHandle, KeySpec, Provider } from "@nmshd/rs-crypto-types"; import { CoreBuffer } from "../../CoreBuffer"; import { CryptoError } from "../../CryptoError"; import { CryptoErrorCode } from "../../CryptoErrorCode"; @@ -10,22 +10,42 @@ import { CryptoSecretKey, ICryptoSecretKey } from "../../encryption/CryptoSecret import { CryptoHashAlgorithm } from "../../hash/CryptoHash"; import { getProvider, ProviderIdentifier } from "../CryptoLayerProviders"; import { CryptoLayerUtils } from "../CryptoLayerUtils"; -import { BaseKeyHandle, BaseKeyHandleConstructor } from "./BaseKeyHandle"; +import { BaseDerivedKeyHandle } from "./BaseDerivedKeyHandle"; +import { BaseKeyHandle } from "./BaseKeyHandle"; import { DeviceBoundKeyHandle } from "./DeviceBoundKeyHandle"; import { PortableDerivedKeyHandle } from "./PortableDerivedKeyHandle"; import { PortableKeyHandle } from "./PortableKeyHandle"; export class CryptoEncryptionHandle { + /** + * Creates a new {@link BaseKeyHandle} / {@link DerivedBaseKeyHandle} or its child from an existing {@link KeyHandle}. + */ + public static async _keyHandleFromProviderAndCalKeyHandle( + constructor: new () => T, + provider: Provider, + keyHandle: KeyHandle + ): Promise { + const result = new constructor(); + + [result.providerName, result.id] = await Promise.all([provider.providerName(), keyHandle.id()]); + + result.provider = provider; + result.keyHandle = keyHandle; + return result; + } + private static async generateKeyHandle( - constructor: BaseKeyHandleConstructor, + constructor: new () => T, providerIdent: ProviderIdentifier, spec: KeySpec ): Promise { const provider = getProvider(providerIdent); const keyHandle = await provider.createKey(spec); - const secretKeyHandle = await constructor.fromProviderAndKeyHandle(provider, keyHandle, { - keySpec: spec - }); + const secretKeyHandle = await CryptoEncryptionHandle._keyHandleFromProviderAndCalKeyHandle( + constructor, + provider, + keyHandle + ); return secretKeyHandle; } @@ -59,15 +79,15 @@ export class CryptoEncryptionHandle { return await this.generateKeyHandle(PortableKeyHandle, providerIdent, portableSpec); } - public static async encrypt( + public static async encrypt( plaintext: CoreBuffer, secretKeyHandle: T, nonce?: CoreBuffer ): Promise { - const encryptionAlgorithm = CryptoLayerUtils.cryptoEncryptionAlgorithmFromCipher(secretKeyHandle.spec.cipher); + const encryptionAlgorithm = await secretKeyHandle.encryptionAlgorithm(); if (nonce === undefined || nonce.buffer.length === 0) { - nonce = await this.createNonce(encryptionAlgorithm, secretKeyHandle.provider); + nonce = await this.createNonce({ providerName: secretKeyHandle.providerName }, encryptionAlgorithm); } else { CryptoValidation.checkNonceForAlgorithm(nonce, encryptionAlgorithm); } @@ -92,13 +112,13 @@ export class CryptoEncryptionHandle { }); } - public static async encryptWithCounter( + public static async encryptWithCounter( plaintext: CoreBuffer, secretKeyHandle: T, nonce: CoreBuffer, counter: number ): Promise { - const encryptionAlgorithm = CryptoLayerUtils.cryptoEncryptionAlgorithmFromCipher(secretKeyHandle.spec.cipher); + const encryptionAlgorithm = await secretKeyHandle.encryptionAlgorithm(); CryptoValidation.checkCounter(counter); CryptoValidation.checkNonceForAlgorithm(nonce, encryptionAlgorithm); @@ -125,12 +145,12 @@ export class CryptoEncryptionHandle { }); } - public static async decrypt( + public static async decrypt( cipher: CryptoCipher, secretKeyHandle: T, nonce?: CoreBuffer ): Promise { - const encryptionAlgorithm = CryptoLayerUtils.cryptoEncryptionAlgorithmFromCipher(secretKeyHandle.spec.cipher); + const encryptionAlgorithm = await secretKeyHandle.encryptionAlgorithm(); let publicNonce; if (nonce !== undefined) { @@ -159,13 +179,13 @@ export class CryptoEncryptionHandle { } } - public static async decryptWithCounter( + public static async decryptWithCounter( cipher: CryptoCipher, secretKeyHandle: T, nonce: CoreBuffer, counter: number ): Promise { - const encryptionAlgorithm = CryptoLayerUtils.cryptoEncryptionAlgorithmFromCipher(secretKeyHandle.spec.cipher); + const encryptionAlgorithm = await secretKeyHandle.encryptionAlgorithm(); CryptoValidation.checkCounter(counter); CryptoValidation.checkNonceForAlgorithm(nonce, encryptionAlgorithm); @@ -186,7 +206,12 @@ export class CryptoEncryptionHandle { } } - public static async createNonce(algorithm: CryptoEncryptionAlgorithm, provider: Provider): Promise { + public static async createNonce( + providerIdent: ProviderIdentifier, + algorithm: CryptoEncryptionAlgorithm + ): Promise { + const provider = getProvider(providerIdent); + let nonceLength; switch (algorithm) { case CryptoEncryptionAlgorithm.AES128_GCM: @@ -218,7 +243,7 @@ export class CryptoEncryptionHandle { portableKeyHandle: PortableKeyHandle | PortableDerivedKeyHandle ): Promise { const rawKeyPromise = CryptoEncryptionHandle.extractRawKey(portableKeyHandle); - const algorithm = CryptoLayerUtils.cryptoEncryptionAlgorithmFromCipher(portableKeyHandle.spec.cipher); + const algorithm = await portableKeyHandle.encryptionAlgorithm(); const cryptoSecretKeyObj: ICryptoSecretKey = { algorithm: algorithm, secretKey: await rawKeyPromise @@ -226,8 +251,8 @@ export class CryptoEncryptionHandle { return CryptoSecretKey.from(cryptoSecretKeyObj); } - private static async keyHandleFromCryptoSecretKey( - constructor: BaseKeyHandleConstructor, + private static async keyHandleFromCryptoSecretKey( + constructor: new () => T, providerIdent: ProviderIdentifier, cryptoSecretKey: CryptoSecretKey, signingHash: CryptoHashAlgorithm, @@ -242,9 +267,20 @@ export class CryptoEncryptionHandle { }; const provider = getProvider(providerIdent); - const keyHandle = await provider.importKey(keySpec, cryptoSecretKey.secretKey.buffer); + let keyHandle: KeyHandle; + try { + keyHandle = await provider.importKey(keySpec, cryptoSecretKey.secretKey.buffer); + } catch (e) { + throw new CryptoError( + CryptoErrorCode.CalImportOfKey, + "Failed to import crypto secret key.", + undefined, + e as Error, + CryptoEncryptionHandle.keyHandleFromCryptoSecretKey + ); + } - return await constructor.fromProviderAndKeyHandle(provider, keyHandle); + return await CryptoEncryptionHandle._keyHandleFromProviderAndCalKeyHandle(constructor, provider, keyHandle); } public static async portableKeyHandleFromCryptoSecretKey( diff --git a/src/crypto-layer/encryption/DeviceBoundDerivedKeyHandle.ts b/src/crypto-layer/encryption/DeviceBoundDerivedKeyHandle.ts index c5feba1..56764bd 100644 --- a/src/crypto-layer/encryption/DeviceBoundDerivedKeyHandle.ts +++ b/src/crypto-layer/encryption/DeviceBoundDerivedKeyHandle.ts @@ -1,23 +1,9 @@ import { type } from "@js-soft/ts-serval"; -import { CoreBuffer } from "../../CoreBuffer"; -import { BaseKeyHandle, IBaseKeyHandleSerialized } from "./BaseKeyHandle"; +import { BaseDerivedKeyHandle } from "./BaseDerivedKeyHandle"; /** Non exportable, ephemeral key handle that is derived from a device bound key handle. */ @type("DeviceBoundDerivedKeyHandle") -export class DeviceBoundDerivedKeyHandle extends BaseKeyHandle { +export class DeviceBoundDerivedKeyHandle extends BaseDerivedKeyHandle { // Phantom marker to make this type incompatible to other types that extend `BaseKeyHandle`. public readonly _isDeviceBoundDerivedKeyHandle = true; - - public override toJSON(verbose = true): IBaseKeyHandleSerialized { - return { - kid: this.id, - pnm: this.providerName, - spc: this.spec, - "@type": verbose ? "DeviceBoundDerivedKeyHandle" : undefined - }; - } - - public override toBase64(verbose = true): string { - return CoreBuffer.utf8_base64(this.serialize(verbose)); - } } diff --git a/src/crypto-layer/encryption/DeviceBoundKeyHandle.ts b/src/crypto-layer/encryption/DeviceBoundKeyHandle.ts index 9ff3c5d..8c5f04f 100644 --- a/src/crypto-layer/encryption/DeviceBoundKeyHandle.ts +++ b/src/crypto-layer/encryption/DeviceBoundKeyHandle.ts @@ -1,6 +1,6 @@ import { type } from "@js-soft/ts-serval"; import { CoreBuffer } from "../../CoreBuffer"; -import { BaseKeyHandle, IBaseKeyHandleSerialized } from "./BaseKeyHandle"; +import { BaseKeyHandle, IBaseKeyHandle, IBaseKeyHandleSerialized } from "./BaseKeyHandle"; /** Key handle that is non exportable (the key cannot be extracted). */ @type("DeviceBoundKeyHandle") @@ -12,7 +12,6 @@ export class DeviceBoundKeyHandle extends BaseKeyHandle { return { kid: this.id, pnm: this.providerName, - spc: this.spec, "@type": verbose ? "DeviceBoundKeyHandle" : undefined }; } @@ -20,4 +19,23 @@ export class DeviceBoundKeyHandle extends BaseKeyHandle { public override toBase64(verbose = true): string { return CoreBuffer.utf8_base64(this.serialize(verbose)); } + + /** + * Deserializes an object representation of a {@link DeviceBoundKeyHandle}. + * + * This method is not able to import raw keys or {@link KeyHandle}. + */ + public static async from(value: IBaseKeyHandle | CoreBuffer): Promise { + return await this.fromAny(value); + } + + /** @see from */ + public static async fromJSON(value: IBaseKeyHandleSerialized): Promise { + return await this.fromAny(value); + } + + /** @see from */ + public static async fromBase64(value: string): Promise { + return await this.deserialize(CoreBuffer.base64_utf8(value)); + } } diff --git a/src/crypto-layer/encryption/PortableDerivedKeyHandle.ts b/src/crypto-layer/encryption/PortableDerivedKeyHandle.ts index dc2117f..3d8dfa1 100644 --- a/src/crypto-layer/encryption/PortableDerivedKeyHandle.ts +++ b/src/crypto-layer/encryption/PortableDerivedKeyHandle.ts @@ -1,22 +1,8 @@ import { type } from "@js-soft/ts-serval"; -import { CoreBuffer } from "../../CoreBuffer"; -import { BaseKeyHandle, IBaseKeyHandleSerialized } from "./BaseKeyHandle"; +import { BaseDerivedKeyHandle } from "./BaseDerivedKeyHandle"; @type("PortableDerivedKeyHandle") -export class PortableDerivedKeyHandle extends BaseKeyHandle { +export class PortableDerivedKeyHandle extends BaseDerivedKeyHandle { // Phantom marker to make this type incompatible to other types that extend `BaseKeyHandle`. public readonly _isPortableDeriveKeyHandle = true; - - public override toJSON(verbose = true): IBaseKeyHandleSerialized { - return { - kid: this.id, - pnm: this.providerName, - spc: this.spec, - "@type": verbose ? "PortableDerivedKeyHandle" : undefined - }; - } - - public override toBase64(verbose = true): string { - return CoreBuffer.utf8_base64(this.serialize(verbose)); - } } diff --git a/src/crypto-layer/encryption/PortableKeyHandle.ts b/src/crypto-layer/encryption/PortableKeyHandle.ts index 8ae971d..0bf64f0 100644 --- a/src/crypto-layer/encryption/PortableKeyHandle.ts +++ b/src/crypto-layer/encryption/PortableKeyHandle.ts @@ -1,10 +1,10 @@ import { type } from "@js-soft/ts-serval"; import { CoreBuffer } from "../../CoreBuffer"; -import { IBaseKeyHandleSerialized, ImportableBaseKeyHandle } from "./BaseKeyHandle"; +import { BaseKeyHandle, IBaseKeyHandleSerialized } from "./BaseKeyHandle"; /** Key handle that is exportable and can also be created by importing a key. */ @type("PortableKeyHandle") -export class PortableKeyHandle extends ImportableBaseKeyHandle { +export class PortableKeyHandle extends BaseKeyHandle { // Phantom marker to make this type incompatible to other types that extend `BaseKeyHandle`. public readonly _isPortableKeyHandle = true; @@ -12,7 +12,6 @@ export class PortableKeyHandle extends ImportableBaseKeyHandle { return { kid: this.id, pnm: this.providerName, - spc: this.spec, "@type": verbose ? "PortableKeyHandle" : undefined }; } @@ -20,4 +19,23 @@ export class PortableKeyHandle extends ImportableBaseKeyHandle { public override toBase64(verbose = true): string { return CoreBuffer.utf8_base64(this.serialize(verbose)); } + + /** + * Deserializes an object representation of a {@link PortableKeyHandle}. + * + * This method is not able to import raw keys or {@link KeyHandle}. + */ + public static async from(value: IBaseKeyHandleSerialized | CoreBuffer): Promise { + return await this.fromAny(value); + } + + /** @see from */ + public static async fromJSON(value: IBaseKeyHandleSerialized): Promise { + return await this.fromAny(value); + } + + /** @see from */ + public static async fromBase64(value: string): Promise { + return await this.deserialize(CoreBuffer.base64_utf8(value)); + } } diff --git a/src/crypto-layer/index.ts b/src/crypto-layer/index.ts index 6d42790..7234409 100644 --- a/src/crypto-layer/index.ts +++ b/src/crypto-layer/index.ts @@ -2,6 +2,7 @@ export * from "./CryptoDerivationHandle"; export * from "./CryptoLayerConfig"; export * from "./CryptoLayerProviders"; export * from "./CryptoLayerUtils"; +export * from "./encryption/BaseDerivedKeyHandle"; export * from "./encryption/BaseKeyHandle"; export * from "./encryption/CryptoEncryptionHandle"; export * from "./encryption/DeviceBoundDerivedKeyHandle"; diff --git a/test/cal/KeyValidation.ts b/test/cal/KeyValidation.ts index ae67819..54217a8 100644 --- a/test/cal/KeyValidation.ts +++ b/test/cal/KeyValidation.ts @@ -1,62 +1,63 @@ import { + BaseDerivedKeyHandle, BaseKeyHandle, CoreBuffer, CryptoCipher, CryptoEncryptionHandle, - CryptoLayerUtils, DeviceBoundDerivedKeyHandle, DeviceBoundKeyHandle, PortableDerivedKeyHandle, PortableKeyHandle } from "@nmshd/crypto"; -import { assertKeyHandle, assertKeySpec, assertProvider } from "@nmshd/rs-crypto-types/checks"; +import { assertKeyHandle, assertProvider } from "@nmshd/rs-crypto-types/checks"; import { expect } from "chai"; /** * Tests SecretKeyHandle for validity and executes the id function. */ -export async function assertSecretKeyHandleValid(handle: T): Promise { +export async function assertSecretKeyHandleValid( + handle: T +): Promise { expect(handle).to.exist; expect(handle.id).to.exist.and.to.be.a("string"); expect(handle.keyHandle).to.exist; expect(handle.provider).to.exist; - expect(handle.spec).to.exist; expect(handle.providerName).to.exist.and.to.be.a("string"); assertKeyHandle(handle.keyHandle); assertProvider(handle.provider); - assertKeySpec(handle.spec); expect(await handle.keyHandle.id()).to.exist.and.to.be.a("string").and.to.be.not.empty; - expect(await handle.keyHandle.spec()).to.exist.and.to.deep.equal(handle.spec); + const spec = await handle.keyHandle.spec(); if (handle instanceof DeviceBoundKeyHandle) { - expect(handle.spec.ephemeral).to.be.false; - expect(handle.spec.non_exportable).to.be.true; + expect(spec.ephemeral).to.be.false; + expect(spec.non_exportable).to.be.true; } else if (handle instanceof DeviceBoundDerivedKeyHandle) { - expect(handle.spec.ephemeral).to.be.true; - expect(handle.spec.non_exportable).to.be.true; + expect(spec.ephemeral).to.be.true; + expect(spec.non_exportable).to.be.true; } else if (handle instanceof PortableKeyHandle) { - expect(handle.spec.ephemeral).to.be.false; - expect(handle.spec.non_exportable).to.be.false; + expect(spec.ephemeral).to.be.false; + expect(spec.non_exportable).to.be.false; } else if (handle instanceof PortableDerivedKeyHandle) { - expect(handle.spec.ephemeral).to.be.true; - expect(handle.spec.non_exportable).to.be.false; + expect(spec.ephemeral).to.be.true; + expect(spec.non_exportable).to.be.false; } else { throw new Error("Test: unknown key handle instance."); } } -async function testDecryptEncryptIsIdentityFunction(before: T, after: T): Promise { +async function testDecryptEncryptIsIdentityFunction( + before: T, + after: T +): Promise { const payload = CoreBuffer.fromUtf8("Hello World!"); const encryptedPayload = await CryptoEncryptionHandle.encrypt(payload, before); expect(encryptedPayload).to.exist; expect(encryptedPayload).to.be.instanceOf(CryptoCipher); - expect(encryptedPayload.algorithm).to.be.equal( - CryptoLayerUtils.cryptoEncryptionAlgorithmFromCipher(before.spec.cipher) - ); + expect(encryptedPayload.algorithm).to.be.equal(await before.encryptionAlgorithm()); expect(encryptedPayload.counter).to.not.exist; expect(encryptedPayload.nonce).to.exist; expect(encryptedPayload.nonce?.buffer.byteLength).to.be.greaterThanOrEqual(12); @@ -69,8 +70,12 @@ async function testDecryptEncryptIsIdentityFunction(bef /** * Test that the content of two SecretKeys match. */ -export async function assertSecretKeyHandleEqual(before: T, after: T): Promise { - expect(before.spec).to.deep.equal(after.spec); +export async function assertSecretKeyHandleEqual( + before: T, + after: T +): Promise { + const [beforeSpec, afterSpec] = await Promise.all([before.keyHandle.spec(), after.keyHandle.spec()]); + expect(beforeSpec).to.deep.equal(afterSpec); if ( (before instanceof PortableKeyHandle && after instanceof PortableKeyHandle) || (before instanceof PortableDerivedKeyHandle && after instanceof PortableDerivedKeyHandle) diff --git a/test/cal/encryption/CryptoEncryptionHandle.test.ts b/test/cal/encryption/CryptoEncryptionHandle.test.ts index d55b077..7e0f214 100644 --- a/test/cal/encryption/CryptoEncryptionHandle.test.ts +++ b/test/cal/encryption/CryptoEncryptionHandle.test.ts @@ -8,7 +8,6 @@ import { CryptoEncryptionHandle, CryptoError, CryptoHashAlgorithm, - CryptoLayerUtils, CryptoSecretKey, ICryptoSecretKeySerialized, PortableKeyHandle @@ -50,9 +49,7 @@ export class CryptoEncryptionHandleTest { const data = new CoreBuffer("0123456789ABCDEF"); const encrypted = await CryptoEncryptionHandle.encrypt(data, cryptoSecretKeyHandle); expect(encrypted).to.be.ok.and.to.be.instanceOf(CryptoCipher); - expect(encrypted.algorithm).to.equal( - CryptoLayerUtils.cryptoEncryptionAlgorithmFromCipher(cryptoSecretKeyHandle.spec.cipher) - ); + expect(encrypted.algorithm).to.equal(await cryptoSecretKeyHandle.encryptionAlgorithm()); const decrypted = await CryptoEncryptionHandle.decrypt(encrypted, cryptoSecretKeyHandle); @@ -67,13 +64,11 @@ export class CryptoEncryptionHandleTest { crypto, hash ); - const cryptoEncryptionAlgorithm = CryptoLayerUtils.cryptoEncryptionAlgorithmFromCipher( - cryptoSecretKeyHandle.spec.cipher - ); + const cryptoEncryptionAlgorithm = await cryptoSecretKeyHandle.encryptionAlgorithm(); const nonce = await CryptoEncryptionHandle.createNonce( - cryptoEncryptionAlgorithm, - cryptoSecretKeyHandle.provider + TEST_PROVIDER_IDENT, + cryptoEncryptionAlgorithm ); const data = new CoreBuffer("0123456789ABCDEF"); @@ -106,13 +101,11 @@ export class CryptoEncryptionHandleTest { hash ); - const cryptoEncryptionAlgorithm = CryptoLayerUtils.cryptoEncryptionAlgorithmFromCipher( - cryptoSecretKeyHandle.spec.cipher - ); + const cryptoEncryptionAlgorithm = await cryptoSecretKeyHandle.encryptionAlgorithm(); const nonce = await CryptoEncryptionHandle.createNonce( - cryptoEncryptionAlgorithm, - cryptoSecretKeyHandle.provider + TEST_PROVIDER_IDENT, + cryptoEncryptionAlgorithm ); const data = new CoreBuffer("0123456789ABCDEF"); @@ -158,7 +151,7 @@ export class CryptoEncryptionHandleTest { ); const keyBufferPromise = CryptoEncryptionHandle.extractRawKey(key); - const algorithm = CryptoLayerUtils.cryptoEncryptionAlgorithmFromCipher(key.spec.cipher); + const algorithm = await key.encryptionAlgorithm(); const keyObject: ICryptoSecretKeySerialized = { alg: algorithm, key: (await keyBufferPromise).toBase64URL() @@ -176,23 +169,33 @@ export class CryptoEncryptionHandleTest { expect(decrypted.buffer).to.deep.equal(payload.buffer); }); - it("libsodium key should be importable", async function () { + it("libsodium key should be importable as portable key handle", async function () { const libsodiumKey = await CryptoEncryption.generateKey( CryptoEncryptionAlgorithm.XCHACHA20_POLY1305 ); - const spec: KeySpec = { - cipher: CryptoLayerUtils.cipherFromCryptoEncryptionAlgorithm(libsodiumKey.algorithm), - signing_hash: "Sha2_256", - ephemeral: false, - non_exportable: false - }; + const keyHandle = await CryptoEncryptionHandle.portableKeyHandleFromCryptoSecretKey( + TEST_PROVIDER_IDENT, + libsodiumKey, + CryptoHashAlgorithm.SHA256 + ); + + await assertSecretKeyHandleValid(keyHandle); + + expect(await keyHandle.keyHandle.extractKey()).to.deep.equal(libsodiumKey.secretKey.buffer); + }); - const keyHandle = await PortableKeyHandle.fromRawKey( + it("libsodium key should be importable as portable derived key handle", async function () { + const libsodiumKey = await CryptoEncryption.generateKey( + CryptoEncryptionAlgorithm.XCHACHA20_POLY1305 + ); + + const keyHandle = await CryptoEncryptionHandle.portableDerivedKeyHandleFromCryptoSecretKey( TEST_PROVIDER_IDENT, - libsodiumKey.secretKey, - spec + libsodiumKey, + CryptoHashAlgorithm.SHA256 ); + await assertSecretKeyHandleValid(keyHandle); expect(await keyHandle.keyHandle.extractKey()).to.deep.equal(libsodiumKey.secretKey.buffer); @@ -220,12 +223,11 @@ export class CryptoEncryptionHandleTest { it("should return a SecretKey", function () { expect(key).to.exist; expect(key).to.be.instanceOf(PortableKeyHandle); - expect(key.spec).to.exist; expect(key.keyHandle).to.exist; }); - it("should return a correct algorithm in the key", function () { - expect(key.spec).to.deep.equal(spec); + it("should return a correct algorithm in the key", async function () { + expect(await key.keyHandle.spec()).to.deep.equal(spec); }); it("should serialize and deserialize the key correctly", async function () { @@ -248,20 +250,24 @@ export class CryptoEncryptionHandleTest { expect(deserialized.id).to.equal(key.id); }); - it("should not deserialize a wrong KeySpec", async function () { + it("should not deserialize a non existing key", async function () { await expectThrows(() => { return PortableKeyHandle.fromJSON({ - kid: "3KpnHNPtcG", + kid: "this_key_does_not_exist", pnm: "SoftwareProvider", - spc: { - cipher: "XChaCha20Poly1305", - signing_hash: "Sha2_256a", - ephemeral: false, - non_exportable: false - }, "@type": "PortableKeyHandle" } as any); - }, "PortableKeyHandle.spec:Object :: Is not of type KeySpec."); + }, /.*error\.crypto\.cal\.loadKey.*Failed to load key during deserialization.*/); + }); + + it("should not deserialize with a non initialized provider", async function () { + await expectThrows(() => { + return PortableKeyHandle.fromJSON({ + kid: "3KpnHNPtcG", + pnm: "ProviderThatDoesNotExist", + "@type": "PortableKeyHandle" + } as any); + }, /.*error\.crypto\.cal\.thisProviderNotInitialized.*/); }); it("should serialize and deserialize the key using base64 correctly", async function () { diff --git a/test/cal/encryption/CryptoSecretKeyHandle.test.ts b/test/cal/encryption/CryptoSecretKeyHandle.test.ts index 7d988e3..fc50191 100644 --- a/test/cal/encryption/CryptoSecretKeyHandle.test.ts +++ b/test/cal/encryption/CryptoSecretKeyHandle.test.ts @@ -54,7 +54,6 @@ export class CryptoSecretKeyHandleTest { const loadedKeyHandle = await DeviceBoundKeyHandle.from({ id: keyHandle.id, - spec: keyHandle.spec, providerName: keyHandle.providerName });