diff --git a/src/CryptoLayerConfig.ts b/src/CryptoLayerConfig.ts index 7d022bc..d3548c3 100644 --- a/src/CryptoLayerConfig.ts +++ b/src/CryptoLayerConfig.ts @@ -1,24 +1,28 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import { AdditionalConfig, ProviderConfig, ProviderFactoryFunctions, SecurityLevel } from "@nmshd/rs-crypto-types"; /** - * Type holding functions for listing and creating providers and configuration for initializing the key meta data store. + * Interface holding functions for listing and creating providers and configuration for initializing the key meta data store. * * @property factoryFunctions - Functions that list providers or create providers. * @property keyMetadataStoreConfig - Configuration needed for saving key metadata. * @property keyMetadataStoreAuth - ~~Optional~~ configuration for authenticating key metadata. - * @property providers - Array of providers to initalize. + * @property providersToBeInitialized - Array of providers to initalize. */ -export type CryptoLayerConfig = { +export interface CryptoLayerConfig { factoryFunctions: ProviderFactoryFunctions; keyMetadataStoreConfig: Extract; keyMetadataStoreAuth?: Extract< AdditionalConfig, { StorageConfigHMAC: any } | { StorageConfigDSA: any } | { StorageConfigPass: any } >; - providers: CryptoLayerProviderConfig[]; -}; + providersToBeInitialized: CryptoLayerProviderFilter[]; +} -export type CryptoLayerProviderConfig = +/** + * Reference to a specific provider, if a name is supplied, or any provider that fullfills the other requirements. + */ +export type CryptoLayerProviderFilter = | { providerName: string; } diff --git a/src/CryptoLayerProviders.ts b/src/CryptoLayerProviders.ts index 3767cd6..8b2fc8e 100644 --- a/src/CryptoLayerProviders.ts +++ b/src/CryptoLayerProviders.ts @@ -1,9 +1,16 @@ -import { Provider, ProviderConfig, ProviderImplConfig, SecurityLevel } from "@nmshd/rs-crypto-types"; +/* eslint-disable @typescript-eslint/naming-convention */ +import { + Provider, + ProviderConfig, + ProviderFactoryFunctions, + ProviderImplConfig, + SecurityLevel +} from "@nmshd/rs-crypto-types"; import { defaults } from "lodash"; import { CryptoError } from "./CryptoError"; import { CryptoErrorCode } from "./CryptoErrorCode"; -import { CryptoLayerConfig } from "./CryptoLayerConfig"; +import { CryptoLayerConfig, CryptoLayerProviderFilter } from "./CryptoLayerConfig"; let PROVIDERS_BY_SECURITY: Map | undefined = undefined; let PROVIDERS_BY_NAME: Map | undefined = undefined; @@ -16,6 +23,56 @@ const DEFAULT_PROVIDER_CONFIG: ProviderConfig = { supported_hashes: ["Sha2_256", "Sha2_512"] }; +async function providerBySecurityMapFromProviderByNameMap( + providersByName: Map +): Promise> { + const providersBySecurity = new Map(); + for (const [_key, value] of providersByName) { + const caps = await value.getCapabilities(); + if (!caps?.min_security_level) { + continue; + } + const securityLevel = caps.min_security_level; + + if (!providersBySecurity.has(securityLevel)) { + providersBySecurity.set(securityLevel, []); + } + + providersBySecurity.get(securityLevel)!.push(value); + } + return providersBySecurity; +} + +/** + * Creates a provider if possible with the given provider filter. This means, that the provider created must adhere to the filter. + * + * If a `SecurityLevel` is given, the default provider config (`DEFAULT_PROVIDER_CONFIG`) will be used to fill in the rest for the selection. + */ +async function createProviderFromProviderFilter( + providerToBeInitialized: CryptoLayerProviderFilter, + factoryFunctions: ProviderFactoryFunctions, + providerImplConfig: ProviderImplConfig +): Promise { + if ("providerName" in providerToBeInitialized) { + return await factoryFunctions.createProviderFromName(providerToBeInitialized.providerName, providerImplConfig); + } + if ("securityLevel" in providerToBeInitialized) { + const providerConfig: ProviderConfig = defaults( + { + max_security_level: providerToBeInitialized.securityLevel, + min_security_level: providerToBeInitialized.securityLevel + }, + DEFAULT_PROVIDER_CONFIG + ); + return await factoryFunctions.createProvider(providerConfig, providerImplConfig); + } + if ("providerConfig" in providerToBeInitialized) { + return await factoryFunctions.createProvider(providerToBeInitialized.providerConfig, providerImplConfig); + } + + throw new CryptoError(CryptoErrorCode.WrongParameters); +} + /** * Intializes global providers with the given configuration. * @@ -28,37 +85,19 @@ export async function initCryptoLayerProviders(config: CryptoLayerConfig): Promi return; } - let providerImplConfig: ProviderImplConfig = { additional_config: [config.keyMetadataStoreConfig] }; + const providerImplConfig: ProviderImplConfig = { additional_config: [config.keyMetadataStoreConfig] }; if (config.keyMetadataStoreAuth) { providerImplConfig.additional_config.push(config.keyMetadataStoreAuth); } - let providers: Map = new Map(); - - for (const providerInitalizationConfig of config.providers) { - let provider: Provider | undefined; - if ("providerName" in providerInitalizationConfig) { - provider = await config.factoryFunctions.createProviderFromName( - providerInitalizationConfig.providerName, - providerImplConfig - ); - } else if ("securityLevel" in providerInitalizationConfig) { - let providerConfig: ProviderConfig = defaults( - { - max_security_level: providerInitalizationConfig.securityLevel, - min_security_level: providerInitalizationConfig.securityLevel - }, - DEFAULT_PROVIDER_CONFIG - ); - provider = await config.factoryFunctions.createProvider(providerConfig, providerImplConfig); - } else if ("providerConfig" in providerInitalizationConfig) { - provider = await config.factoryFunctions.createProvider( - providerInitalizationConfig.providerConfig, - providerImplConfig - ); - } else { - throw new CryptoError(CryptoErrorCode.WrongParameters); - } + const providers: Map = new Map(); + + for (const providerFilter of config.providersToBeInitialized) { + const provider = await createProviderFromProviderFilter( + providerFilter, + config.factoryFunctions, + providerImplConfig + ); if (!provider) { throw new CryptoError(CryptoErrorCode.CalFailedLoadingProvider, `Failed loading provider.`); @@ -68,23 +107,7 @@ export async function initCryptoLayerProviders(config: CryptoLayerConfig): Promi } PROVIDERS_BY_NAME = providers; - - let providers_by_security = new Map(); - for (const [key, value] of providers) { - let caps = await value.getCapabilities(); - if (!caps?.min_security_level) { - continue; - } - let securityLevel = caps.min_security_level; - - if (!providers_by_security.has(securityLevel)) { - providers_by_security.set(securityLevel, []); - } - - providers_by_security.get(securityLevel)!.push(value); - } - - PROVIDERS_BY_SECURITY = providers_by_security; + PROVIDERS_BY_SECURITY = await providerBySecurityMapFromProviderByNameMap(PROVIDERS_BY_NAME); } function isSecurityLevel(value: string): value is SecurityLevel { @@ -113,7 +136,7 @@ export function getProvider(key: string | SecurityLevel | undefined): Provider | return undefined; } - let provider = isSecurityLevel(key) ? PROVIDERS_BY_SECURITY.get(key)?.[0] : PROVIDERS_BY_NAME.get(key); + const provider = isSecurityLevel(key) ? PROVIDERS_BY_SECURITY.get(key)?.[0] : PROVIDERS_BY_NAME.get(key); if (!provider) { throw new CryptoError(CryptoErrorCode.WrongParameters, `No such provider with name or security level: ${key}`); diff --git a/src/CryptoPrivateKeyHandle.ts b/src/CryptoPrivateKeyHandle.ts new file mode 100644 index 0000000..9e824f7 --- /dev/null +++ b/src/CryptoPrivateKeyHandle.ts @@ -0,0 +1,91 @@ +import { serialize, type, validate } from "@js-soft/ts-serval"; +import { CoreBuffer, Encoding, ICoreBuffer } from "./CoreBuffer"; +import { CryptoSerializable } from "./CryptoSerializable"; +import { CryptoExchangeAlgorithm } from "./exchange/CryptoExchange"; +import { CryptoSignatureAlgorithm } from "./signature/CryptoSignatureAlgorithm"; + +export interface ICryptoPrivateKeyHandle { + privateKey: ICoreBuffer; + algorithm: CryptoExchangeAlgorithm | CryptoSignatureAlgorithm; + toString(): string; + toPEM(): string; +} + +export interface ICryptoPrivateKeyHandleStatic { + new (): ICryptoPrivateKeyHandle; + fromPEM( + pem: string, + algorithm: CryptoExchangeAlgorithm | CryptoSignatureAlgorithm + ): Promise; + fromString( + value: string, + algorithm: CryptoExchangeAlgorithm | CryptoSignatureAlgorithm, + encoding: Encoding + ): Promise; + fromNativeKey( + key: any, + algorithm: CryptoExchangeAlgorithm | CryptoSignatureAlgorithm + ): Promise; +} + +@type("CryptoPrivateKeyHandle") +export class CryptoPrivateKeyHandle extends CryptoSerializable implements ICryptoPrivateKeyHandle { + @validate() + @serialize() + public algorithm: CryptoExchangeAlgorithm | CryptoSignatureAlgorithm; + + @validate() + @serialize() + public privateKey: CoreBuffer; + + public toPEM(): string { + return this.privateKey.toString(Encoding.Pem, "PRIVATE KEY"); + } + + public override toString(): string { + return this.privateKey.toString(Encoding.Base64_UrlSafe_NoPadding); + } + + protected static stripPEM(pem: string): string { + pem = pem.replace(/-----BEGIN [\w ]* KEY-----/, ""); + pem = pem.replace(/-----END [\w ]* KEY-----/, ""); + pem = pem.replace(/----- BEGIN [\w ]* KEY -----/, ""); + pem = pem.replace(/----- END [\w ]* KEY -----/, ""); + pem = pem.replace(/(?:\r\n|\r|\n)/g, ""); + return pem; + } + + public static fromString( + value: string, + algorithm: CryptoExchangeAlgorithm | CryptoSignatureAlgorithm, + encoding: Encoding = Encoding.Base64_UrlSafe_NoPadding + ): CryptoPrivateKeyHandle { + const buffer: CoreBuffer = CoreBuffer.fromString(value, encoding); + return this.fromAny({ algorithm, privateKey: buffer }); + } + + public static fromObject( + value: any, + algorithm: CryptoExchangeAlgorithm | CryptoSignatureAlgorithm + ): CryptoPrivateKeyHandle { + const buffer: ICoreBuffer = CoreBuffer.fromObject(value); + + return this.fromAny({ algorithm, privateKey: buffer }); + } + + public static fromPEM( + pem: string, + algorithm: CryptoExchangeAlgorithm | CryptoSignatureAlgorithm + ): CryptoPrivateKeyHandle { + const value = this.stripPEM(pem); + return this.fromString(value, algorithm, Encoding.Base64); + } + + public static from(value: any): CryptoPrivateKeyHandle { + return this.fromAny(value); + } + + public static fromBase64(value: string): CryptoPrivateKeyHandle { + return this.deserialize(CoreBuffer.base64_utf8(value)); + } +} diff --git a/src/index.ts b/src/index.ts index da2e229..a729a50 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,8 +5,8 @@ export * from "./CryptoDerivation"; export * from "./CryptoError"; export * from "./CryptoErrorCode"; export * from "./CryptoLayerConfig"; -//export * from "./CryptoLayerProviders"; export * from "./CryptoPrivateKey"; +export * from "./CryptoPrivateKeyHandle"; export * from "./CryptoPublicKey"; export * from "./CryptoValidation"; export * from "./encryption/CryptoCipher"; @@ -42,4 +42,3 @@ export * from "./state/CryptoStateType"; export * from "./stream/CryptoStream"; export * from "./stream/CryptoStreamAddress"; export * from "./stream/CryptoStreamState"; -