Skip to content

Commit

Permalink
refactor!: fixed eslint warnings and refactored crypto layer provider…
Browse files Browse the repository at this point in the history
… to be more readable

BREAKING CHANGE:
* Renamed `CryptoLayerProviderConfig` to `CryptoLayerProviderFilter`.
* Renamed `providers` key of `CryptoLayerConfig` to `providersToBeInitialized`.
* Refactored `CryptoLayerConfig` to be an interface.
  • Loading branch information
WyvernIXTL committed Feb 18, 2025
1 parent 23dab29 commit bab59c5
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 55 deletions.
16 changes: 10 additions & 6 deletions src/CryptoLayerConfig.ts
Original file line number Diff line number Diff line change
@@ -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<AdditionalConfig, { KVStoreConfig: any } | { FileStoreConfig: any }>;
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;
}
Expand Down
117 changes: 70 additions & 47 deletions src/CryptoLayerProviders.ts
Original file line number Diff line number Diff line change
@@ -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<SecurityLevel, Provider[]> | undefined = undefined;
let PROVIDERS_BY_NAME: Map<string, Provider> | undefined = undefined;
Expand All @@ -16,6 +23,56 @@ const DEFAULT_PROVIDER_CONFIG: ProviderConfig = {
supported_hashes: ["Sha2_256", "Sha2_512"]
};

async function providerBySecurityMapFromProviderByNameMap(
providersByName: Map<string, Provider>
): Promise<Map<SecurityLevel, Provider[]>> {
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<Provider | undefined> {
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.
*
Expand All @@ -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<string, Provider> = 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<string, Provider> = 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.`);
Expand All @@ -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 {
Expand Down Expand Up @@ -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}`);
Expand Down
91 changes: 91 additions & 0 deletions src/CryptoPrivateKeyHandle.ts
Original file line number Diff line number Diff line change
@@ -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<ICryptoPrivateKeyHandle>;
fromString(
value: string,
algorithm: CryptoExchangeAlgorithm | CryptoSignatureAlgorithm,
encoding: Encoding
): Promise<ICryptoPrivateKeyHandle>;
fromNativeKey(
key: any,
algorithm: CryptoExchangeAlgorithm | CryptoSignatureAlgorithm
): Promise<ICryptoPrivateKeyHandle>;
}

@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));
}
}
3 changes: 1 addition & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -42,4 +42,3 @@ export * from "./state/CryptoStateType";
export * from "./stream/CryptoStream";
export * from "./stream/CryptoStreamAddress";
export * from "./stream/CryptoStreamState";

0 comments on commit bab59c5

Please sign in to comment.