-
Notifications
You must be signed in to change notification settings - Fork 1
Integration of rust crypto-layer for secure element support #636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
3ae30b0
00b3f96
8ca4087
8d4e955
2e246b9
aaaa1b1
0725b74
141ad07
4cbc88b
6328603
1dfbc32
c656ce0
3edecd4
3889bfa
b2e6042
814885a
33b9484
7efe1fe
e7c916a
b1dd485
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| tsconfig.tsbuildinfo | ||
| node_modules | ||
| packages/*/coverage | ||
| nmshd-runtime.code-workspace | ||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| /* eslint-disable @typescript-eslint/naming-convention */ | ||
| import { DeviceBoundKeyHandle, hasProviderForSecurityLevel, PortableKeyHandle } from "@nmshd/crypto"; | ||
| import { SecurityLevel } from "@nmshd/rs-crypto-types"; | ||
| import { CoreCrypto } from "./CoreCrypto"; | ||
|
|
||
| export const CryptoProviderTypes = { | ||
| Software: "Software", | ||
| Hardware: "Hardware", | ||
| Network: "Network", | ||
| LEGACY: "LEGACY" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is LEGACY Uppercase? |
||
| } as const; | ||
|
|
||
| export const CryptoPurpose = { | ||
| DeviceKeyPair: "deviceKeyPair", | ||
| BaseKey: "baseKey", | ||
| Default: "default" | ||
| } as const; | ||
| type CryptoPurpose = (typeof CryptoPurpose)[keyof typeof CryptoPurpose]; | ||
|
|
||
| export const CryptoKeyType = { | ||
| Signature: "signature", | ||
| Encryption: "encryption", | ||
| Derivation: "derivation", | ||
| Exchange: "exchange" | ||
| } as const; | ||
| type CryptoKeyType = (typeof CryptoKeyType)[keyof typeof CryptoKeyType]; | ||
|
|
||
| export const CryptoObject = { | ||
| AccountController: "AccountController", | ||
| AnonymousTokenController: "AnonymousTokenController", | ||
| Certificate: "Certificate", | ||
| DeviceController: "DeviceController", | ||
| DeviceSecretController: "DeviceSecretController", | ||
| IdentityController: "IdentityController", | ||
| FileController: "FileController", | ||
| MessageController: "MessageController", | ||
| RelationshipTemplateController: "RelationshipTemplateController", | ||
| RelationshipsController: "RelationshipsController", | ||
| RelationshipSecretController: "RelationshipSecretController", | ||
| SecretController: "SecretController", | ||
| TokenController: "TokenController" | ||
| } as const; | ||
| type CryptoObject = (typeof CryptoObject)[keyof typeof CryptoObject]; | ||
|
|
||
| export const ALL_CRYPTO_PROVIDERS = ["SoftwareProvider", "AndroidProvider"]; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't get the AndroidProvider here. Shouldn't this information come either from CAL or the app? |
||
|
|
||
| const CRYPTO_OPERATION_OBJECT_MAP: Partial<Record<CryptoObject, CryptoKeyType[]>> = { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For me, this is a duplicate of OBJECT_OPERATION_PREFERENCES with less details :) |
||
| [CryptoObject.AccountController]: [CryptoKeyType.Encryption, CryptoKeyType.Signature], | ||
| [CryptoObject.AnonymousTokenController]: [CryptoKeyType.Derivation], | ||
| [CryptoObject.Certificate]: [CryptoKeyType.Encryption], | ||
| [CryptoObject.DeviceController]: [CryptoKeyType.Signature, CryptoKeyType.Encryption], | ||
| [CryptoObject.DeviceSecretController]: [CryptoKeyType.Derivation], | ||
| [CryptoObject.FileController]: [CryptoKeyType.Encryption], | ||
| [CryptoObject.IdentityController]: [CryptoKeyType.Encryption], | ||
| [CryptoObject.MessageController]: [CryptoKeyType.Encryption], | ||
| [CryptoObject.RelationshipTemplateController]: [CryptoKeyType.Derivation, CryptoKeyType.Encryption], | ||
| [CryptoObject.RelationshipsController]: [CryptoKeyType.Encryption], | ||
| [CryptoObject.RelationshipSecretController]: [CryptoKeyType.Encryption], | ||
| [CryptoObject.SecretController]: [CryptoKeyType.Encryption], | ||
| [CryptoObject.TokenController]: [CryptoKeyType.Encryption] | ||
| }; | ||
|
|
||
| const OBJECT_OPERATION_PREFERENCES: Partial< | ||
| Record<CryptoObject, Partial<Record<CryptoKeyType, SecurityLevel | Partial<Record<Exclude<CryptoPurpose, undefined>, SecurityLevel>>>>> | ||
| > = { | ||
| [CryptoObject.AccountController]: { | ||
| [CryptoKeyType.Signature]: { | ||
| [CryptoPurpose.DeviceKeyPair]: CryptoProviderTypes.Hardware, | ||
| [CryptoPurpose.Default]: CryptoProviderTypes.Software | ||
| }, | ||
| [CryptoKeyType.Encryption]: { | ||
| [CryptoPurpose.BaseKey]: CryptoProviderTypes.Hardware, | ||
| [CryptoPurpose.Default]: CryptoProviderTypes.Software | ||
| } | ||
| }, | ||
| [CryptoObject.AnonymousTokenController]: { | ||
| [CryptoKeyType.Derivation]: CryptoProviderTypes.Software | ||
| }, | ||
| [CryptoObject.Certificate]: {}, | ||
| [CryptoObject.DeviceController]: {}, | ||
| [CryptoObject.DeviceSecretController]: { | ||
| [CryptoKeyType.Encryption]: CryptoProviderTypes.Hardware | ||
| }, | ||
| [CryptoObject.FileController]: { | ||
| [CryptoKeyType.Encryption]: CryptoProviderTypes.Software | ||
| }, | ||
| [CryptoObject.IdentityController]: {}, | ||
| [CryptoObject.MessageController]: { | ||
| [CryptoKeyType.Encryption]: CryptoProviderTypes.Software | ||
| }, | ||
| [CryptoObject.RelationshipTemplateController]: { | ||
| [CryptoKeyType.Encryption]: CryptoProviderTypes.Software | ||
| }, | ||
| [CryptoObject.RelationshipsController]: {}, | ||
| [CryptoObject.RelationshipSecretController]: {}, | ||
| [CryptoObject.SecretController]: { | ||
| [CryptoKeyType.Exchange]: CryptoProviderTypes.Software | ||
| }, | ||
| [CryptoObject.TokenController]: { | ||
| [CryptoKeyType.Encryption]: CryptoProviderTypes.Software | ||
| } | ||
| }; | ||
|
|
||
| const DEFAULT_OPERATION_PREFERENCES: Partial<Record<CryptoKeyType, SecurityLevel>> = { | ||
| [CryptoKeyType.Derivation]: CryptoProviderTypes.Software, | ||
| [CryptoKeyType.Signature]: CryptoProviderTypes.Hardware, | ||
| [CryptoKeyType.Encryption]: CryptoProviderTypes.Software, | ||
| [CryptoKeyType.Exchange]: CryptoProviderTypes.Software | ||
| }; | ||
|
|
||
| const FALLBACK_PREFERENCE: SecurityLevel = CryptoProviderTypes.Software; | ||
|
|
||
| export function getPreferredProviderLevel(cryptoObject: CryptoObject, cryptoOperation: CryptoKeyType, purpose?: Exclude<CryptoPurpose, undefined>): SecurityLevel { | ||
| const allowedOps = CRYPTO_OPERATION_OBJECT_MAP[cryptoObject]; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you need this autorization on code level base? |
||
| if (allowedOps && !allowedOps.includes(cryptoOperation)) { | ||
| throw new Error(`Operation '${cryptoOperation}' is not supported for object '${cryptoObject}'.`); | ||
| } | ||
|
|
||
| let chosenSecurityLevel: SecurityLevel | undefined; | ||
| const objectPrefs = OBJECT_OPERATION_PREFERENCES[cryptoObject]; | ||
| if (objectPrefs) { | ||
| const operationPrefOrMap = objectPrefs[cryptoOperation]; | ||
| if (operationPrefOrMap) { | ||
| if (typeof operationPrefOrMap === "object") { | ||
| if (purpose && operationPrefOrMap[purpose]) { | ||
| chosenSecurityLevel = operationPrefOrMap[purpose]; | ||
| } | ||
| } else { | ||
| chosenSecurityLevel = operationPrefOrMap; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (!chosenSecurityLevel) { | ||
| const operationPref = DEFAULT_OPERATION_PREFERENCES[cryptoOperation]; | ||
| chosenSecurityLevel = operationPref ?? FALLBACK_PREFERENCE; | ||
| } | ||
|
|
||
| if (!hasProviderForSecurityLevel(chosenSecurityLevel)) { | ||
| if (chosenSecurityLevel !== FALLBACK_PREFERENCE && !hasProviderForSecurityLevel(FALLBACK_PREFERENCE)) { | ||
| throw new Error(`No provider available for either ${chosenSecurityLevel} or fallback ${FALLBACK_PREFERENCE} for operation ${cryptoOperation}.`); | ||
| } | ||
| return FALLBACK_PREFERENCE; | ||
| } | ||
|
|
||
| return chosenSecurityLevel; | ||
| } | ||
|
|
||
| export async function getPreferredProviderKeyHandle( | ||
| cryptoObject: CryptoObject, | ||
| cryptoOperation: CryptoKeyType, | ||
| isPortable: boolean, | ||
| purpose?: CryptoPurpose | ||
| ): Promise<DeviceBoundKeyHandle | PortableKeyHandle> { | ||
| const securityLevel = getPreferredProviderLevel(cryptoObject, cryptoOperation, purpose); | ||
|
|
||
| switch (securityLevel) { | ||
| case CryptoProviderTypes.Hardware: | ||
| if (isPortable) { | ||
| return await CoreCrypto.generatePortableKeyHandle({ securityLevel }); | ||
| } | ||
| return await CoreCrypto.generateDeviceBoundKeyHandle({ securityLevel }); | ||
|
|
||
| case CryptoProviderTypes.Software: | ||
| if (isPortable) { | ||
| return await CoreCrypto.generatePortableKeyHandle({ securityLevel }); | ||
| } | ||
| return await CoreCrypto.generateDeviceBoundKeyHandle({ securityLevel }); | ||
|
|
||
| default: | ||
| throw new Error(`Unsupported SecurityLevel '${securityLevel}' encountered for key handle generation.`); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| import { ILogger, ILoggerFactory } from "@js-soft/logging-abstractions"; | ||
| import { SimpleLoggerFactory } from "@js-soft/simple-logger"; | ||
| import { EventBus } from "@js-soft/ts-utils"; | ||
| import { SodiumWrapper } from "@nmshd/crypto"; | ||
| import { CryptoLayerConfig, initCryptoLayerProviders, SodiumWrapper } from "@nmshd/crypto"; | ||
| import { AgentOptions } from "http"; | ||
| import { AgentOptions as HTTPSAgentOptions } from "https"; | ||
| import _ from "lodash"; | ||
|
|
@@ -86,7 +86,8 @@ export class Transport { | |
| customConfig: IConfigOverwrite, | ||
| public readonly eventBus: EventBus, | ||
| loggerFactory: ILoggerFactory = new SimpleLoggerFactory(), | ||
| public readonly correlator?: ICorrelator | ||
| public readonly correlator?: ICorrelator, | ||
| public readonly cryptoLayerConfig?: CryptoLayerConfig | ||
| ) { | ||
| this._config = _.defaultsDeep({}, customConfig, Transport.defaultConfig); | ||
|
|
||
|
|
@@ -120,7 +121,20 @@ export class Transport { | |
|
|
||
| public async init(): Promise<Transport> { | ||
| log.trace("Initializing Libsodium..."); | ||
| await SodiumWrapper.ready(); | ||
| const sodium = SodiumWrapper.ready(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did you split the await of SodiumWrapper? |
||
|
|
||
| if (this.cryptoLayerConfig) { | ||
| log.trace("Initializing Crypto Layer..."); | ||
| try { | ||
| await initCryptoLayerProviders(this.cryptoLayerConfig); | ||
| log.trace("Crypto Layer initialized successfully"); | ||
| } catch (error) { | ||
| log.warn("Failed to initialize Crypto Layer, continuing without it.", error); | ||
| } | ||
| log.trace("Crypto Layer initialized"); | ||
| } | ||
|
|
||
| await sodium; | ||
| log.trace("Libsodium initialized"); | ||
|
|
||
| log.info("Transport initialized"); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undo that please.