@enbox/crypto 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.mjs +1 -1
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/algorithms/aes-ctr.js +1 -1
- package/dist/esm/algorithms/aes-gcm.js +34 -1
- package/dist/esm/algorithms/aes-gcm.js.map +1 -1
- package/dist/esm/algorithms/aes-kw.js +154 -0
- package/dist/esm/algorithms/aes-kw.js.map +1 -0
- package/dist/esm/algorithms/ecdsa.js +110 -1
- package/dist/esm/algorithms/ecdsa.js.map +1 -1
- package/dist/esm/algorithms/eddsa.js +90 -1
- package/dist/esm/algorithms/eddsa.js.map +1 -1
- package/dist/esm/algorithms/hkdf.js +53 -0
- package/dist/esm/algorithms/hkdf.js.map +1 -0
- package/dist/esm/algorithms/pbkdf2.js +55 -0
- package/dist/esm/algorithms/pbkdf2.js.map +1 -0
- package/dist/esm/algorithms/sha-2.js +1 -1
- package/dist/esm/algorithms/x25519.js +125 -0
- package/dist/esm/algorithms/x25519.js.map +1 -0
- package/dist/esm/cose/cbor.js +35 -0
- package/dist/esm/cose/cbor.js.map +1 -0
- package/dist/esm/cose/cose-key.js +312 -0
- package/dist/esm/cose/cose-key.js.map +1 -0
- package/dist/esm/cose/cose-sign1.js +283 -0
- package/dist/esm/cose/cose-sign1.js.map +1 -0
- package/dist/esm/cose/eat.js +254 -0
- package/dist/esm/cose/eat.js.map +1 -0
- package/dist/esm/crypto-error.js +4 -0
- package/dist/esm/crypto-error.js.map +1 -1
- package/dist/esm/index.js +9 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/local-key-manager.js +6 -1
- package/dist/esm/local-key-manager.js.map +1 -1
- package/dist/esm/primitives/ecies-secp256k1.js +79 -0
- package/dist/esm/primitives/ecies-secp256k1.js.map +1 -0
- package/dist/esm/primitives/x25519.js +9 -16
- package/dist/esm/primitives/x25519.js.map +1 -1
- package/dist/esm/utils.js +30 -0
- package/dist/esm/utils.js.map +1 -1
- package/dist/types/algorithms/aes-ctr.d.ts +1 -1
- package/dist/types/algorithms/aes-gcm.d.ts +23 -3
- package/dist/types/algorithms/aes-gcm.d.ts.map +1 -1
- package/dist/types/algorithms/aes-kw.d.ts +129 -0
- package/dist/types/algorithms/aes-kw.d.ts.map +1 -0
- package/dist/types/algorithms/ecdsa.d.ts +48 -3
- package/dist/types/algorithms/ecdsa.d.ts.map +1 -1
- package/dist/types/algorithms/eddsa.d.ts +48 -3
- package/dist/types/algorithms/eddsa.d.ts.map +1 -1
- package/dist/types/algorithms/hkdf.d.ts +35 -0
- package/dist/types/algorithms/hkdf.d.ts.map +1 -0
- package/dist/types/algorithms/pbkdf2.d.ts +35 -0
- package/dist/types/algorithms/pbkdf2.d.ts.map +1 -0
- package/dist/types/algorithms/sha-2.d.ts +1 -1
- package/dist/types/algorithms/x25519.d.ts +76 -0
- package/dist/types/algorithms/x25519.d.ts.map +1 -0
- package/dist/types/cose/cbor.d.ts +30 -0
- package/dist/types/cose/cbor.d.ts.map +1 -0
- package/dist/types/cose/cose-key.d.ts +106 -0
- package/dist/types/cose/cose-key.d.ts.map +1 -0
- package/dist/types/cose/cose-sign1.d.ts +195 -0
- package/dist/types/cose/cose-sign1.d.ts.map +1 -0
- package/dist/types/cose/eat.d.ts +203 -0
- package/dist/types/cose/eat.d.ts.map +1 -0
- package/dist/types/crypto-error.d.ts +4 -0
- package/dist/types/crypto-error.d.ts.map +1 -1
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/local-key-manager.d.ts +4 -4
- package/dist/types/local-key-manager.d.ts.map +1 -1
- package/dist/types/primitives/ecies-secp256k1.d.ts +53 -0
- package/dist/types/primitives/ecies-secp256k1.d.ts.map +1 -0
- package/dist/types/primitives/x25519.d.ts +9 -16
- package/dist/types/primitives/x25519.d.ts.map +1 -1
- package/dist/types/types/crypto-api.d.ts +52 -4
- package/dist/types/types/crypto-api.d.ts.map +1 -1
- package/dist/types/types/key-converter.d.ts +37 -15
- package/dist/types/types/key-converter.d.ts.map +1 -1
- package/dist/types/types/key-deriver.d.ts +41 -0
- package/dist/types/types/key-deriver.d.ts.map +1 -1
- package/dist/types/types/key-io.d.ts +37 -0
- package/dist/types/types/key-io.d.ts.map +1 -1
- package/dist/types/types/params-direct.d.ts +17 -0
- package/dist/types/types/params-direct.d.ts.map +1 -1
- package/dist/types/types/params-kms.d.ts +55 -0
- package/dist/types/types/params-kms.d.ts.map +1 -1
- package/dist/types/utils.d.ts +19 -0
- package/dist/types/utils.d.ts.map +1 -1
- package/dist/utils.js +1 -1
- package/dist/utils.js.map +3 -3
- package/package.json +12 -14
- package/src/algorithms/aes-ctr.ts +1 -1
- package/src/algorithms/aes-gcm.ts +38 -2
- package/src/algorithms/aes-kw.ts +182 -0
- package/src/algorithms/ecdsa.ts +132 -1
- package/src/algorithms/eddsa.ts +108 -1
- package/src/algorithms/hkdf.ts +54 -0
- package/src/algorithms/pbkdf2.ts +57 -0
- package/src/algorithms/sha-2.ts +1 -1
- package/src/algorithms/x25519.ts +153 -0
- package/src/cose/cbor.ts +36 -0
- package/src/cose/cose-key.ts +344 -0
- package/src/cose/cose-sign1.ts +473 -0
- package/src/cose/eat.ts +368 -0
- package/src/crypto-error.ts +6 -0
- package/src/index.ts +10 -0
- package/src/local-key-manager.ts +9 -4
- package/src/primitives/ecies-secp256k1.ts +113 -0
- package/src/primitives/x25519.ts +9 -16
- package/src/types/crypto-api.ts +124 -6
- package/src/types/key-converter.ts +33 -7
- package/src/types/key-deriver.ts +49 -0
- package/src/types/key-io.ts +40 -0
- package/src/types/params-direct.ts +21 -0
- package/src/types/params-kms.ts +67 -0
- package/src/utils.ts +53 -0
- package/dist/browser.js +0 -60
- package/dist/browser.js.map +0 -7
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { AsymmetricKeyGenerator } from '../types/key-generator.js';
|
|
2
|
+
import type { Jwk } from '../jose/jwk.js';
|
|
3
|
+
import type { KeyConverter } from '../types/key-converter.js';
|
|
4
|
+
import type {
|
|
5
|
+
BytesToPrivateKeyParams,
|
|
6
|
+
ComputePublicKeyParams,
|
|
7
|
+
GenerateKeyParams,
|
|
8
|
+
GetPublicKeyParams,
|
|
9
|
+
PrivateKeyToBytesParams,
|
|
10
|
+
} from '../types/params-direct.js';
|
|
11
|
+
|
|
12
|
+
import { CryptoAlgorithm } from './crypto-algorithm.js';
|
|
13
|
+
import { isOkpPrivateJwk } from '../jose/jwk.js';
|
|
14
|
+
import { X25519 } from '../primitives/x25519.js';
|
|
15
|
+
|
|
16
|
+
import { CryptoError, CryptoErrorCode } from '../crypto-error.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The `X25519GenerateKeyParams` interface defines the algorithm-specific parameters that should be
|
|
20
|
+
* passed into the `generateKey()` method when using the X25519 key agreement algorithm.
|
|
21
|
+
*/
|
|
22
|
+
export interface X25519GenerateKeyParams extends GenerateKeyParams {
|
|
23
|
+
/**
|
|
24
|
+
* A string defining the type of key to generate. The value must be:
|
|
25
|
+
* - `"X25519"`: Elliptic-curve Diffie-Hellman (ECDH) using Curve25519.
|
|
26
|
+
*/
|
|
27
|
+
algorithm: 'X25519';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The `X25519Algorithm` class provides a concrete implementation for key generation,
|
|
32
|
+
* public key derivation, and key conversion using the X25519 elliptic curve. X25519 is a
|
|
33
|
+
* key agreement curve (not a signature curve) used for ECDH key exchange in JWE encryption.
|
|
34
|
+
*
|
|
35
|
+
* This class implements the {@link AsymmetricKeyGenerator | `AsymmetricKeyGenerator`} and
|
|
36
|
+
* {@link KeyConverter | `KeyConverter`} interfaces, providing private key generation,
|
|
37
|
+
* public key derivation, and byte/JWK conversion.
|
|
38
|
+
*/
|
|
39
|
+
export class X25519Algorithm extends CryptoAlgorithm
|
|
40
|
+
implements AsymmetricKeyGenerator<X25519GenerateKeyParams, Jwk, GetPublicKeyParams>,
|
|
41
|
+
KeyConverter {
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Converts a raw private key in bytes to its corresponding JWK format.
|
|
45
|
+
*
|
|
46
|
+
* @param params - The parameters for the private key conversion.
|
|
47
|
+
* @param params.algorithm - Must be `'X25519'`.
|
|
48
|
+
* @param params.privateKeyBytes - The raw private key as a Uint8Array.
|
|
49
|
+
*
|
|
50
|
+
* @returns A Promise that resolves to the private key in JWK format.
|
|
51
|
+
*/
|
|
52
|
+
public async bytesToPrivateKey({ algorithm, privateKeyBytes }:
|
|
53
|
+
BytesToPrivateKeyParams & { algorithm: 'X25519' }
|
|
54
|
+
): Promise<Jwk> {
|
|
55
|
+
switch (algorithm) {
|
|
56
|
+
|
|
57
|
+
case 'X25519': {
|
|
58
|
+
return X25519.bytesToPrivateKey({ privateKeyBytes });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
default: {
|
|
62
|
+
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `Algorithm not supported: ${algorithm}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Derives the public key in JWK format from a given X25519 private key.
|
|
69
|
+
*
|
|
70
|
+
* @param params - The parameters for the public key derivation.
|
|
71
|
+
* @param params.key - The private key in JWK format from which to derive the public key.
|
|
72
|
+
*
|
|
73
|
+
* @returns A Promise that resolves to the derived public key in JWK format.
|
|
74
|
+
*/
|
|
75
|
+
public async computePublicKey({ key }:
|
|
76
|
+
ComputePublicKeyParams
|
|
77
|
+
): Promise<Jwk> {
|
|
78
|
+
if (!isOkpPrivateJwk(key)) {throw new TypeError('Invalid key provided. Must be an octet key pair (OKP) private key.');}
|
|
79
|
+
|
|
80
|
+
switch (key.crv) {
|
|
81
|
+
|
|
82
|
+
case 'X25519': {
|
|
83
|
+
return X25519.computePublicKey({ key });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
default: {
|
|
87
|
+
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `Unsupported curve: ${key.crv}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Generates a new X25519 private key in JWK format.
|
|
94
|
+
*
|
|
95
|
+
* @param params - The parameters for key generation.
|
|
96
|
+
* @param params.algorithm - Must be `'X25519'`.
|
|
97
|
+
*
|
|
98
|
+
* @returns A Promise that resolves to the generated private key in JWK format.
|
|
99
|
+
*/
|
|
100
|
+
async generateKey({ algorithm }:
|
|
101
|
+
X25519GenerateKeyParams
|
|
102
|
+
): Promise<Jwk> {
|
|
103
|
+
switch (algorithm) {
|
|
104
|
+
|
|
105
|
+
case 'X25519': {
|
|
106
|
+
return X25519.generateKey();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
default: {
|
|
110
|
+
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `Algorithm not supported: ${algorithm}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Retrieves the public key properties from a given X25519 private key in JWK format.
|
|
117
|
+
*
|
|
118
|
+
* @param params - The parameters for retrieving the public key properties.
|
|
119
|
+
* @param params.key - The private key in JWK format.
|
|
120
|
+
*
|
|
121
|
+
* @returns A Promise that resolves to the public key in JWK format.
|
|
122
|
+
*/
|
|
123
|
+
public async getPublicKey({ key }:
|
|
124
|
+
GetPublicKeyParams
|
|
125
|
+
): Promise<Jwk> {
|
|
126
|
+
if (!isOkpPrivateJwk(key)) {throw new TypeError('Invalid key provided. Must be an octet key pair (OKP) private key.');}
|
|
127
|
+
|
|
128
|
+
switch (key.crv) {
|
|
129
|
+
|
|
130
|
+
case 'X25519': {
|
|
131
|
+
return X25519.getPublicKey({ key });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
default: {
|
|
135
|
+
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `Unsupported curve: ${key.crv}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Converts a private key from JWK format to a byte array.
|
|
142
|
+
*
|
|
143
|
+
* @param params - The parameters for the private key conversion.
|
|
144
|
+
* @param params.privateKey - The private key in JWK format.
|
|
145
|
+
*
|
|
146
|
+
* @returns A Promise that resolves to the private key as a Uint8Array.
|
|
147
|
+
*/
|
|
148
|
+
public async privateKeyToBytes({ privateKey }:
|
|
149
|
+
PrivateKeyToBytesParams
|
|
150
|
+
): Promise<Uint8Array> {
|
|
151
|
+
return X25519.privateKeyToBytes({ privateKey });
|
|
152
|
+
}
|
|
153
|
+
}
|
package/src/cose/cbor.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { decode as cborDecode, encode as cborEncode } from 'cborg';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CBOR (Concise Binary Object Representation) encoding and decoding utilities.
|
|
5
|
+
*
|
|
6
|
+
* Provides a thin wrapper around the `cborg` library, exposing `encode` and `decode`
|
|
7
|
+
* operations for use by COSE and EAT implementations.
|
|
8
|
+
*
|
|
9
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8949 | RFC 8949 — CBOR}
|
|
10
|
+
*/
|
|
11
|
+
export class Cbor {
|
|
12
|
+
/**
|
|
13
|
+
* Encodes a JavaScript value to a CBOR byte string.
|
|
14
|
+
*
|
|
15
|
+
* @param value - The value to encode. Supports objects, arrays, strings, numbers,
|
|
16
|
+
* booleans, null, undefined, Uint8Array (encoded as CBOR byte string), and Map.
|
|
17
|
+
* @returns The CBOR-encoded bytes.
|
|
18
|
+
*/
|
|
19
|
+
public static encode(value: unknown): Uint8Array {
|
|
20
|
+
return cborEncode(value);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Decodes a CBOR byte string to a JavaScript value.
|
|
25
|
+
*
|
|
26
|
+
* CBOR maps are decoded as JavaScript `Map` instances to support integer keys,
|
|
27
|
+
* which is required by COSE (RFC 9052) and EAT (RFC 9711).
|
|
28
|
+
*
|
|
29
|
+
* @param data - The CBOR-encoded bytes to decode.
|
|
30
|
+
* @returns The decoded JavaScript value.
|
|
31
|
+
* @throws If the input is not valid CBOR.
|
|
32
|
+
*/
|
|
33
|
+
public static decode<T = unknown>(data: Uint8Array): T {
|
|
34
|
+
return cborDecode(data, { useMaps: true }) as T;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import type { Jwk } from '../jose/jwk.js';
|
|
2
|
+
|
|
3
|
+
import { Convert } from '@enbox/common';
|
|
4
|
+
|
|
5
|
+
import { CryptoError, CryptoErrorCode } from '../crypto-error.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* COSE Key Type values (RFC 9052, Section 7).
|
|
9
|
+
*
|
|
10
|
+
* @see {@link https://www.iana.org/assignments/cose/cose.xhtml#key-type | IANA COSE Key Types}
|
|
11
|
+
*/
|
|
12
|
+
export enum CoseKeyType {
|
|
13
|
+
/** Octet Key Pair (e.g., Ed25519, X25519) */
|
|
14
|
+
OKP = 1,
|
|
15
|
+
/** Elliptic Curve (e.g., P-256, P-384, P-521) */
|
|
16
|
+
EC2 = 2,
|
|
17
|
+
/** Symmetric key */
|
|
18
|
+
Symmetric = 4,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* COSE Elliptic Curve identifiers (RFC 9053, Section 7.1).
|
|
23
|
+
*
|
|
24
|
+
* @see {@link https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves | IANA COSE Elliptic Curves}
|
|
25
|
+
*/
|
|
26
|
+
export enum CoseEllipticCurve {
|
|
27
|
+
/** NIST P-256 (secp256r1) */
|
|
28
|
+
P256 = 1,
|
|
29
|
+
/** NIST P-384 (secp384r1) */
|
|
30
|
+
P384 = 2,
|
|
31
|
+
/** NIST P-521 (secp521r1) */
|
|
32
|
+
P521 = 3,
|
|
33
|
+
/** X25519 for ECDH */
|
|
34
|
+
X25519 = 4,
|
|
35
|
+
/** X448 for ECDH */
|
|
36
|
+
X448 = 5,
|
|
37
|
+
/** Ed25519 for EdDSA */
|
|
38
|
+
Ed25519 = 6,
|
|
39
|
+
/** Ed448 for EdDSA */
|
|
40
|
+
Ed448 = 7,
|
|
41
|
+
/** secp256k1 */
|
|
42
|
+
Secp256k1 = 8,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* COSE Algorithm identifiers (RFC 9053).
|
|
47
|
+
*
|
|
48
|
+
* Only includes algorithms relevant to Enbox confidential compute.
|
|
49
|
+
*
|
|
50
|
+
* @see {@link https://www.iana.org/assignments/cose/cose.xhtml#algorithms | IANA COSE Algorithms}
|
|
51
|
+
*/
|
|
52
|
+
export enum CoseAlgorithm {
|
|
53
|
+
/** EdDSA (Ed25519 or Ed448) */
|
|
54
|
+
EdDSA = -8,
|
|
55
|
+
/** ECDSA with SHA-256 (P-256) */
|
|
56
|
+
ES256 = -7,
|
|
57
|
+
/** ECDSA with SHA-384 (P-384) */
|
|
58
|
+
ES384 = -35,
|
|
59
|
+
/** ECDSA with SHA-512 (P-521) */
|
|
60
|
+
ES512 = -36,
|
|
61
|
+
/** ECDSA with SHA-256 (secp256k1) */
|
|
62
|
+
ES256K = -47,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* COSE Key common parameter labels (RFC 9052, Section 7.1).
|
|
67
|
+
*/
|
|
68
|
+
enum CoseKeyLabel {
|
|
69
|
+
/** Key Type (kty) */
|
|
70
|
+
Kty = 1,
|
|
71
|
+
/** Key ID (kid) */
|
|
72
|
+
Kid = 2,
|
|
73
|
+
/** Algorithm */
|
|
74
|
+
Alg = 3,
|
|
75
|
+
/** Key Operations */
|
|
76
|
+
KeyOps = 4,
|
|
77
|
+
/** Base IV */
|
|
78
|
+
BaseIv = 5,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* COSE Key type-specific parameter labels.
|
|
83
|
+
*
|
|
84
|
+
* For OKP and EC2 keys, the curve and coordinate labels share the same
|
|
85
|
+
* negative-integer label space (RFC 9053, Section 7.1-7.2).
|
|
86
|
+
*/
|
|
87
|
+
enum CoseKeyParamLabel {
|
|
88
|
+
/** Curve identifier (OKP and EC2) */
|
|
89
|
+
Crv = -1,
|
|
90
|
+
/** X coordinate (OKP public key or EC2 x-coordinate) */
|
|
91
|
+
X = -2,
|
|
92
|
+
/** Y coordinate (EC2 only) */
|
|
93
|
+
Y = -3,
|
|
94
|
+
/** Private key (OKP d value or EC2 d value) */
|
|
95
|
+
D = -4,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Maps JWK curve names to COSE elliptic curve identifiers.
|
|
100
|
+
*/
|
|
101
|
+
const jwkCrvToCose: Record<string, CoseEllipticCurve> = {
|
|
102
|
+
'P-256' : CoseEllipticCurve.P256,
|
|
103
|
+
'P-384' : CoseEllipticCurve.P384,
|
|
104
|
+
'P-521' : CoseEllipticCurve.P521,
|
|
105
|
+
'X25519' : CoseEllipticCurve.X25519,
|
|
106
|
+
'Ed25519' : CoseEllipticCurve.Ed25519,
|
|
107
|
+
'Ed448' : CoseEllipticCurve.Ed448,
|
|
108
|
+
'secp256k1' : CoseEllipticCurve.Secp256k1,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Maps COSE elliptic curve identifiers to JWK curve names.
|
|
113
|
+
*/
|
|
114
|
+
const coseCrvToJwk: Record<number, string> = {
|
|
115
|
+
[CoseEllipticCurve.P256] : 'P-256',
|
|
116
|
+
[CoseEllipticCurve.P384] : 'P-384',
|
|
117
|
+
[CoseEllipticCurve.P521] : 'P-521',
|
|
118
|
+
[CoseEllipticCurve.X25519] : 'X25519',
|
|
119
|
+
[CoseEllipticCurve.Ed25519] : 'Ed25519',
|
|
120
|
+
[CoseEllipticCurve.Ed448] : 'Ed448',
|
|
121
|
+
[CoseEllipticCurve.Secp256k1] : 'secp256k1',
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Maps JWK algorithm names to COSE algorithm identifiers.
|
|
126
|
+
*/
|
|
127
|
+
const jwkAlgToCose: Record<string, CoseAlgorithm> = {
|
|
128
|
+
'EdDSA' : CoseAlgorithm.EdDSA,
|
|
129
|
+
'ES256' : CoseAlgorithm.ES256,
|
|
130
|
+
'ES384' : CoseAlgorithm.ES384,
|
|
131
|
+
'ES512' : CoseAlgorithm.ES512,
|
|
132
|
+
'ES256K' : CoseAlgorithm.ES256K,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Maps COSE algorithm identifiers to JWK algorithm names.
|
|
137
|
+
*/
|
|
138
|
+
const coseAlgToJwk: Record<number, string> = {
|
|
139
|
+
[CoseAlgorithm.EdDSA] : 'EdDSA',
|
|
140
|
+
[CoseAlgorithm.ES256] : 'ES256',
|
|
141
|
+
[CoseAlgorithm.ES384] : 'ES384',
|
|
142
|
+
[CoseAlgorithm.ES512] : 'ES512',
|
|
143
|
+
[CoseAlgorithm.ES256K] : 'ES256K',
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Utilities for converting between JWK and COSE key representations.
|
|
148
|
+
*
|
|
149
|
+
* COSE keys use integer labels and CBOR encoding, while JWK uses string
|
|
150
|
+
* property names and JSON. This class provides bidirectional conversion.
|
|
151
|
+
*
|
|
152
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc9052#section-7 | RFC 9052, Section 7}
|
|
153
|
+
*/
|
|
154
|
+
export class CoseKey {
|
|
155
|
+
/**
|
|
156
|
+
* Converts a JWK to a COSE key represented as a Map.
|
|
157
|
+
*
|
|
158
|
+
* @param jwk - The JWK to convert.
|
|
159
|
+
* @returns A Map with integer labels as keys, suitable for CBOR encoding.
|
|
160
|
+
* @throws {CryptoError} If the JWK key type or curve is not supported.
|
|
161
|
+
*/
|
|
162
|
+
public static fromJwk(jwk: Jwk): Map<number, unknown> {
|
|
163
|
+
const coseKey = new Map<number, unknown>();
|
|
164
|
+
|
|
165
|
+
if (jwk.kty === 'OKP') {
|
|
166
|
+
coseKey.set(CoseKeyLabel.Kty, CoseKeyType.OKP);
|
|
167
|
+
|
|
168
|
+
const crv = jwk.crv;
|
|
169
|
+
if (crv === undefined || !(crv in jwkCrvToCose)) {
|
|
170
|
+
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `CoseKey: unsupported OKP curve '${crv}'`);
|
|
171
|
+
}
|
|
172
|
+
coseKey.set(CoseKeyParamLabel.Crv, jwkCrvToCose[crv]);
|
|
173
|
+
|
|
174
|
+
if (jwk.x !== undefined) {
|
|
175
|
+
coseKey.set(CoseKeyParamLabel.X, Convert.base64Url(jwk.x as string).toUint8Array());
|
|
176
|
+
}
|
|
177
|
+
if (jwk.d !== undefined) {
|
|
178
|
+
coseKey.set(CoseKeyParamLabel.D, Convert.base64Url(jwk.d as string).toUint8Array());
|
|
179
|
+
}
|
|
180
|
+
} else if (jwk.kty === 'EC') {
|
|
181
|
+
coseKey.set(CoseKeyLabel.Kty, CoseKeyType.EC2);
|
|
182
|
+
|
|
183
|
+
const crv = jwk.crv;
|
|
184
|
+
if (crv === undefined || !(crv in jwkCrvToCose)) {
|
|
185
|
+
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `CoseKey: unsupported EC curve '${crv}'`);
|
|
186
|
+
}
|
|
187
|
+
coseKey.set(CoseKeyParamLabel.Crv, jwkCrvToCose[crv]);
|
|
188
|
+
|
|
189
|
+
if (jwk.x !== undefined) {
|
|
190
|
+
coseKey.set(CoseKeyParamLabel.X, Convert.base64Url(jwk.x as string).toUint8Array());
|
|
191
|
+
}
|
|
192
|
+
if (jwk.y !== undefined) {
|
|
193
|
+
coseKey.set(CoseKeyParamLabel.Y, Convert.base64Url(jwk.y as string).toUint8Array());
|
|
194
|
+
}
|
|
195
|
+
if (jwk.d !== undefined) {
|
|
196
|
+
coseKey.set(CoseKeyParamLabel.D, Convert.base64Url(jwk.d as string).toUint8Array());
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `CoseKey: unsupported key type '${jwk.kty}'`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (jwk.kid !== undefined) {
|
|
203
|
+
coseKey.set(CoseKeyLabel.Kid, Convert.string(jwk.kid).toUint8Array());
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (jwk.alg !== undefined && jwk.alg in jwkAlgToCose) {
|
|
207
|
+
coseKey.set(CoseKeyLabel.Alg, jwkAlgToCose[jwk.alg]);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return coseKey;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Converts a COSE key Map to a JWK.
|
|
215
|
+
*
|
|
216
|
+
* @param coseKey - A Map with integer labels as keys (from CBOR decoding).
|
|
217
|
+
* @returns The equivalent JWK.
|
|
218
|
+
* @throws {CryptoError} If the COSE key type or curve is not supported.
|
|
219
|
+
*/
|
|
220
|
+
public static toJwk(coseKey: Map<number, unknown>): Jwk {
|
|
221
|
+
const kty = coseKey.get(CoseKeyLabel.Kty) as number;
|
|
222
|
+
|
|
223
|
+
if (kty === CoseKeyType.OKP) {
|
|
224
|
+
const crv = coseKey.get(CoseKeyParamLabel.Crv) as number;
|
|
225
|
+
if (!(crv in coseCrvToJwk)) {
|
|
226
|
+
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `CoseKey: unsupported COSE OKP curve ${crv}`);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const jwk: Jwk = {
|
|
230
|
+
kty : 'OKP',
|
|
231
|
+
crv : coseCrvToJwk[crv],
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const x = coseKey.get(CoseKeyParamLabel.X) as Uint8Array | undefined;
|
|
235
|
+
if (x !== undefined) {
|
|
236
|
+
jwk.x = Convert.uint8Array(x).toBase64Url();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const d = coseKey.get(CoseKeyParamLabel.D) as Uint8Array | undefined;
|
|
240
|
+
if (d !== undefined) {
|
|
241
|
+
jwk.d = Convert.uint8Array(d).toBase64Url();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
CoseKey.applyCommonFields(coseKey, jwk);
|
|
245
|
+
return jwk;
|
|
246
|
+
|
|
247
|
+
} else if (kty === CoseKeyType.EC2) {
|
|
248
|
+
const crv = coseKey.get(CoseKeyParamLabel.Crv) as number;
|
|
249
|
+
if (!(crv in coseCrvToJwk)) {
|
|
250
|
+
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `CoseKey: unsupported COSE EC2 curve ${crv}`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const jwk: Jwk = {
|
|
254
|
+
kty : 'EC',
|
|
255
|
+
crv : coseCrvToJwk[crv],
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const x = coseKey.get(CoseKeyParamLabel.X) as Uint8Array | undefined;
|
|
259
|
+
if (x !== undefined) {
|
|
260
|
+
jwk.x = Convert.uint8Array(x).toBase64Url();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const y = coseKey.get(CoseKeyParamLabel.Y) as Uint8Array | undefined;
|
|
264
|
+
if (y !== undefined) {
|
|
265
|
+
jwk.y = Convert.uint8Array(y).toBase64Url();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const d = coseKey.get(CoseKeyParamLabel.D) as Uint8Array | undefined;
|
|
269
|
+
if (d !== undefined) {
|
|
270
|
+
jwk.d = Convert.uint8Array(d).toBase64Url();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
CoseKey.applyCommonFields(coseKey, jwk);
|
|
274
|
+
return jwk;
|
|
275
|
+
|
|
276
|
+
} else {
|
|
277
|
+
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `CoseKey: unsupported COSE key type ${kty}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Infers the COSE algorithm identifier from a JWK.
|
|
283
|
+
*
|
|
284
|
+
* If the JWK has an `alg` field, it is used directly. Otherwise, the algorithm
|
|
285
|
+
* is inferred from the key type and curve.
|
|
286
|
+
*
|
|
287
|
+
* @param jwk - The JWK to infer the algorithm from.
|
|
288
|
+
* @returns The COSE algorithm identifier.
|
|
289
|
+
* @throws {CryptoError} If the algorithm cannot be determined.
|
|
290
|
+
*/
|
|
291
|
+
public static algorithmFromJwk(jwk: Jwk): CoseAlgorithm {
|
|
292
|
+
if (jwk.alg !== undefined && jwk.alg in jwkAlgToCose) {
|
|
293
|
+
return jwkAlgToCose[jwk.alg];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Infer from key type and curve.
|
|
297
|
+
if (jwk.kty === 'OKP') {
|
|
298
|
+
if (jwk.crv === 'Ed25519' || jwk.crv === 'Ed448') {
|
|
299
|
+
return CoseAlgorithm.EdDSA;
|
|
300
|
+
}
|
|
301
|
+
} else if (jwk.kty === 'EC') {
|
|
302
|
+
switch (jwk.crv) {
|
|
303
|
+
case 'P-256': return CoseAlgorithm.ES256;
|
|
304
|
+
case 'P-384': return CoseAlgorithm.ES384;
|
|
305
|
+
case 'P-521': return CoseAlgorithm.ES512;
|
|
306
|
+
case 'secp256k1': return CoseAlgorithm.ES256K;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
throw new CryptoError(
|
|
311
|
+
CryptoErrorCode.AlgorithmNotSupported,
|
|
312
|
+
`CoseKey: cannot determine COSE algorithm for key type '${jwk.kty}' curve '${jwk.crv}'`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Maps a COSE algorithm identifier to a JWK algorithm name.
|
|
318
|
+
*
|
|
319
|
+
* @param alg - The COSE algorithm identifier.
|
|
320
|
+
* @returns The JWK algorithm name.
|
|
321
|
+
* @throws {CryptoError} If the algorithm is not supported.
|
|
322
|
+
*/
|
|
323
|
+
public static algorithmToJwk(alg: CoseAlgorithm): string {
|
|
324
|
+
if (alg in coseAlgToJwk) {
|
|
325
|
+
return coseAlgToJwk[alg];
|
|
326
|
+
}
|
|
327
|
+
throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `CoseKey: unsupported COSE algorithm ${alg}`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Applies common COSE key fields (kid, alg) to a JWK.
|
|
332
|
+
*/
|
|
333
|
+
private static applyCommonFields(coseKey: Map<number, unknown>, jwk: Jwk): void {
|
|
334
|
+
const kid = coseKey.get(CoseKeyLabel.Kid) as Uint8Array | undefined;
|
|
335
|
+
if (kid !== undefined) {
|
|
336
|
+
jwk.kid = Convert.uint8Array(kid).toString();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const alg = coseKey.get(CoseKeyLabel.Alg) as number | undefined;
|
|
340
|
+
if (alg !== undefined && alg in coseAlgToJwk) {
|
|
341
|
+
jwk.alg = coseAlgToJwk[alg];
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|