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
3 changes: 3 additions & 0 deletions cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
OpenTDF,
DecoratedStream,
isPublicKeyAlgorithm,
PUBLIC_KEY_ALGORITHMS,
} from '@opentdf/sdk';
import { CLIError, Level, log } from './logger.js';
import * as assertions from '@opentdf/sdk/assertions';
Expand Down Expand Up @@ -462,6 +463,7 @@ export const handleArgs = (args: string[]) => {
group: 'Encrypt Options:',
desc: 'Key type for wrapping keys',
type: 'string',
choices: PUBLIC_KEY_ALGORITHMS,
default: 'rsa:2048',
},
mimeType: {
Expand All @@ -475,6 +477,7 @@ export const handleArgs = (args: string[]) => {
group: 'Decrypt Options:',
desc: 'Key type for rewrap',
type: 'string',
choices: PUBLIC_KEY_ALGORITHMS,
default: 'rsa:2048',
},
userId: {
Expand Down
18 changes: 9 additions & 9 deletions lib/src/access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import { type AuthConfig, resolveAuthConfig } from './auth/interceptors.js';
import { RewrapResponse } from './platform/kas/kas_pb.js';
import { getPlatformUrlFromKasEndpoint, validateSecureUrl } from './utils.js';
import { base64 } from './encodings/index.js';
import {
KEY_ALGORITHMS,
type KeyAlgorithm,
isKeyAlgorithm,
} from '../tdf3/src/crypto/declarations.js';

import {
fetchKasBasePubKey,
Expand Down Expand Up @@ -87,16 +92,11 @@ export const rewrapAdditionalContextHeader = (
return base64.encode(JSON.stringify(context));
};

export type KasPublicKeyAlgorithm =
| 'ec:secp256r1'
| 'ec:secp384r1'
| 'ec:secp521r1'
| 'rsa:2048'
| 'rsa:4096';
export const PUBLIC_KEY_ALGORITHMS = KEY_ALGORITHMS;

export const isPublicKeyAlgorithm = (a: string): a is KasPublicKeyAlgorithm => {
return a === 'ec:secp256r1' || a === 'rsa:2048';
};
export type KasPublicKeyAlgorithm = KeyAlgorithm;

export const isPublicKeyAlgorithm = (a: string): a is KasPublicKeyAlgorithm => isKeyAlgorithm(a);

export const keyAlgorithmToPublicKeyAlgorithm = (k: CryptoKey): KasPublicKeyAlgorithm => {
const a = k.algorithm;
Expand Down
6 changes: 4 additions & 2 deletions lib/src/auth/dpop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import type {
KeyPair,
PrivateKey,
AsymmetricSigningAlgorithm,
KeyAlgorithm,
} from '../../tdf3/src/crypto/declarations.js';
import { isRsaKeyAlgorithm } from '../../tdf3/src/crypto/declarations.js';

export type JsonObject = { [Key in string]?: JsonValue };
export type JsonArray = JsonValue[];
Expand Down Expand Up @@ -119,8 +121,8 @@ class UnsupportedOperationError extends Error {
/**
* Determines a supported JWS `alg` identifier from PublicKeyInfo algorithm string.
*/
function determineJWSAlgorithmFromKeyInfo(algorithm: string): JWSAlgorithm {
if (algorithm.startsWith('rsa:')) {
function determineJWSAlgorithmFromKeyInfo(algorithm: KeyAlgorithm): JWSAlgorithm {
if (isRsaKeyAlgorithm(algorithm)) {
return 'RS256';
}
switch (algorithm) {
Expand Down
2 changes: 2 additions & 0 deletions lib/src/opentdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import {
type KasPublicKeyAlgorithm,
OriginAllowList,
PUBLIC_KEY_ALGORITHMS,
fetchKeyAccessServers,
isPublicKeyAlgorithm,
} from './access.js';
Expand Down Expand Up @@ -45,6 +46,7 @@ export {
type Payload,
type Segment,
type SplitType,
PUBLIC_KEY_ALGORITHMS,
isPublicKeyAlgorithm,
};

Expand Down
25 changes: 12 additions & 13 deletions lib/tdf3/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ import {
} from '../../../src/access.js';
import { ConfigurationError } from '../../../src/errors.js';
import { AesGcmCipher } from '../ciphers/aes-gcm-cipher.js';
import { type KeyPair, type SymmetricKey } from '../crypto/declarations.js';
import {
isEcKeyAlgorithm,
isRsaKeyAlgorithm,
type KeyPair,
type SymmetricKey,
} from '../crypto/declarations.js';
import * as defaultCryptoService from '../crypto/index.js';
import {
type AttributeObject,
Expand Down Expand Up @@ -730,18 +735,12 @@ export class Client {
);
}
let type: KeyAccessType;
switch (algorithm) {
case 'rsa:2048':
case 'rsa:4096':
type = 'wrapped';
break;
case 'ec:secp384r1':
case 'ec:secp521r1':
case 'ec:secp256r1':
type = 'ec-wrapped';
break;
default:
throw new ConfigurationError(`Unsupported algorithm ${algorithm}`);
if (isRsaKeyAlgorithm(algorithm)) {
type = 'wrapped';
} else if (isEcKeyAlgorithm(algorithm)) {
type = 'ec-wrapped';
} else {
throw new ConfigurationError(`Unsupported algorithm ${algorithm}`);
}
return buildKeyAccess({
alg: algorithm,
Expand Down
35 changes: 9 additions & 26 deletions lib/tdf3/src/crypto/core/key-format.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import {
ecAlgorithmToCurve,
isEcKeyAlgorithm,
isRsaKeyAlgorithm,
type KeyAlgorithm,
type KeyOptions,
MIN_ASYMMETRIC_KEY_SIZE_BITS,
Expand Down Expand Up @@ -238,7 +241,7 @@ export async function importPublicKey(pem: string, options: KeyOptions): Promise
let cryptoAlgorithm: RsaHashedImportParams | EcKeyImportParams;
let keyUsages: KeyUsage[];

if (algorithm.startsWith('rsa:')) {
if (isRsaKeyAlgorithm(algorithm)) {
if (usage === 'encrypt') {
cryptoAlgorithm = rsaOaepSha1();
keyUsages = ['encrypt'];
Expand All @@ -248,18 +251,8 @@ export async function importPublicKey(pem: string, options: KeyOptions): Promise
} else {
throw new ConfigurationError('RSA keys only support usage: encrypt or sign');
}
} else if (algorithm.startsWith('ec:')) {
const curve = algorithm.split(':')[1];
const namedCurve =
curve === 'secp256r1'
? 'P-256'
: curve === 'secp384r1'
? 'P-384'
: curve === 'secp521r1'
? 'P-521'
: (() => {
throw new ConfigurationError(`Unsupported EC curve: ${curve}`);
})();
} else if (isEcKeyAlgorithm(algorithm)) {
const namedCurve = ecAlgorithmToCurve(algorithm);

if (usage === 'derive') {
cryptoAlgorithm = { name: 'ECDH', namedCurve };
Expand Down Expand Up @@ -344,7 +337,7 @@ export async function importPrivateKey(pem: string, options: KeyOptions): Promis
let cryptoAlgorithm: RsaHashedImportParams | EcKeyImportParams;
let keyUsages: KeyUsage[];

if (algorithm.startsWith('rsa:')) {
if (isRsaKeyAlgorithm(algorithm)) {
if (usage === 'encrypt') {
cryptoAlgorithm = rsaOaepSha1();
keyUsages = ['decrypt'];
Expand All @@ -354,18 +347,8 @@ export async function importPrivateKey(pem: string, options: KeyOptions): Promis
} else {
throw new ConfigurationError('RSA keys only support usage: encrypt or sign');
}
} else if (algorithm.startsWith('ec:')) {
const curve = algorithm.split(':')[1];
const namedCurve =
curve === 'secp256r1'
? 'P-256'
: curve === 'secp384r1'
? 'P-384'
: curve === 'secp521r1'
? 'P-521'
: (() => {
throw new ConfigurationError(`Unsupported EC curve: ${curve}`);
})();
} else if (isEcKeyAlgorithm(algorithm)) {
const namedCurve = ecAlgorithmToCurve(algorithm);

if (usage === 'derive') {
cryptoAlgorithm = { name: 'ECDH', namedCurve };
Expand Down
36 changes: 12 additions & 24 deletions lib/tdf3/src/crypto/core/keys.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import {
ecAlgorithmToCurve,
isEcKeyAlgorithm,
isRsaKeyAlgorithm,
type KeyAlgorithm,
type PrivateKey,
type PublicKey,
rsaAlgorithmToModulusBits,
type SymmetricKey,
} from '../declarations.js';

Expand All @@ -15,18 +19,10 @@ export function wrapPublicKey(key: CryptoKey, algorithm: KeyAlgorithm): PublicKe
algorithm,
_internal: key,
};
if (algorithm.startsWith('rsa:')) {
result.modulusBits = parseInt(algorithm.split(':')[1], 10);
} else if (algorithm.startsWith('ec:')) {
const curvePart = algorithm.split(':')[1];
result.curve =
curvePart === 'secp256r1'
? 'P-256'
: curvePart === 'secp384r1'
? 'P-384'
: curvePart === 'secp521r1'
? 'P-521'
: undefined;
if (isRsaKeyAlgorithm(algorithm)) {
result.modulusBits = rsaAlgorithmToModulusBits(algorithm);
} else if (isEcKeyAlgorithm(algorithm)) {
result.curve = ecAlgorithmToCurve(algorithm);
}
return result as PublicKey;
}
Expand All @@ -41,18 +37,10 @@ export function wrapPrivateKey(key: CryptoKey, algorithm: KeyAlgorithm): Private
algorithm,
_internal: key,
};
if (algorithm.startsWith('rsa:')) {
result.modulusBits = parseInt(algorithm.split(':')[1], 10);
} else if (algorithm.startsWith('ec:')) {
const curvePart = algorithm.split(':')[1];
result.curve =
curvePart === 'secp256r1'
? 'P-256'
: curvePart === 'secp384r1'
? 'P-384'
: curvePart === 'secp521r1'
? 'P-521'
: undefined;
if (isRsaKeyAlgorithm(algorithm)) {
result.modulusBits = rsaAlgorithmToModulusBits(algorithm);
} else if (isEcKeyAlgorithm(algorithm)) {
result.curve = ecAlgorithmToCurve(algorithm);
}
return result as PrivateKey;
}
Expand Down
41 changes: 34 additions & 7 deletions lib/tdf3/src/crypto/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,42 @@ export type PemKeyPair = {
privateKey: string;
};

export const EC_KEY_ALGORITHMS = ['ec:secp256r1', 'ec:secp384r1', 'ec:secp521r1'] as const;
export const RSA_KEY_ALGORITHMS = ['rsa:2048', 'rsa:4096'] as const;

/** Order is significant: re-exported as `PUBLIC_KEY_ALGORITHMS` in `access.ts` and consumed as an ordered list (e.g. CLI `--choices` output). */
export const KEY_ALGORITHMS = [...EC_KEY_ALGORITHMS, ...RSA_KEY_ALGORITHMS] as const;

export type EcKeyAlgorithm = (typeof EC_KEY_ALGORITHMS)[number];
export type RsaKeyAlgorithm = (typeof RSA_KEY_ALGORITHMS)[number];

/**
* Key algorithm identifier combining key type and parameters.
*/
export type KeyAlgorithm =
| 'rsa:2048'
| 'rsa:4096'
| 'ec:secp256r1'
| 'ec:secp384r1'
| 'ec:secp521r1';
export type KeyAlgorithm = EcKeyAlgorithm | RsaKeyAlgorithm;

export const isEcKeyAlgorithm = (a: string): a is EcKeyAlgorithm =>
(EC_KEY_ALGORITHMS as readonly string[]).includes(a);
export const isRsaKeyAlgorithm = (a: string): a is RsaKeyAlgorithm =>
(RSA_KEY_ALGORITHMS as readonly string[]).includes(a);
export const isKeyAlgorithm = (a: string): a is KeyAlgorithm =>
(KEY_ALGORITHMS as readonly string[]).includes(a);
Comment thread
dmihalcik-virtru marked this conversation as resolved.

const EC_ALGORITHM_CURVES: Record<EcKeyAlgorithm, ECCurve> = {
'ec:secp256r1': 'P-256',
'ec:secp384r1': 'P-384',
'ec:secp521r1': 'P-521',
};
/** The elliptic curve for an `ec:*` key algorithm. */
export const ecAlgorithmToCurve = (alg: EcKeyAlgorithm): ECCurve => EC_ALGORITHM_CURVES[alg];

const RSA_ALGORITHM_MODULUS_BITS: Record<RsaKeyAlgorithm, 2048 | 4096> = {
'rsa:2048': 2048,
'rsa:4096': 4096,
};
/** The modulus bit length for an `rsa:*` key algorithm. */
export const rsaAlgorithmToModulusBits = (alg: RsaKeyAlgorithm): 2048 | 4096 =>
RSA_ALGORITHM_MODULUS_BITS[alg];

/**
* Options for key generation and import.
Expand Down Expand Up @@ -156,7 +183,7 @@ export type HkdfParams = {
*/
export type PublicKeyInfo = {
/** Detected algorithm of the key. */
algorithm: 'rsa:2048' | 'rsa:4096' | 'ec:secp256r1' | 'ec:secp384r1' | 'ec:secp521r1';
algorithm: KeyAlgorithm;
/** Normalized PEM string. */
pem: string;
};
Expand Down
Loading