@hpke/cose 0.0.0-stage → 0.0.1
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/LICENSE +21 -0
- package/README.md +69 -2
- package/esm/mod.d.ts +16 -0
- package/esm/mod.d.ts.map +1 -0
- package/esm/mod.js +11 -0
- package/esm/package.json +3 -0
- package/esm/src/alg.d.ts +60 -0
- package/esm/src/alg.d.ts.map +1 -0
- package/esm/src/alg.js +96 -0
- package/esm/src/cbor/decoder.d.ts +4 -0
- package/esm/src/cbor/decoder.d.ts.map +1 -0
- package/esm/src/cbor/decoder.js +148 -0
- package/esm/src/cbor/encoder.d.ts +6 -0
- package/esm/src/cbor/encoder.d.ts.map +1 -0
- package/esm/src/cbor/encoder.js +109 -0
- package/esm/src/cbor/types.d.ts +8 -0
- package/esm/src/cbor/types.d.ts.map +1 -0
- package/esm/src/cbor/types.js +1 -0
- package/esm/src/contentAesGcm.d.ts +3 -0
- package/esm/src/contentAesGcm.d.ts.map +1 -0
- package/esm/src/contentAesGcm.js +30 -0
- package/esm/src/coseKey.d.ts +56 -0
- package/esm/src/coseKey.d.ts.map +1 -0
- package/esm/src/coseKey.js +185 -0
- package/esm/src/encrypt.d.ts +155 -0
- package/esm/src/encrypt.d.ts.map +1 -0
- package/esm/src/encrypt.js +317 -0
- package/esm/src/encrypt0.d.ts +91 -0
- package/esm/src/encrypt0.d.ts.map +1 -0
- package/esm/src/encrypt0.js +203 -0
- package/esm/src/errors.d.ts +5 -0
- package/esm/src/errors.d.ts.map +1 -0
- package/esm/src/errors.js +7 -0
- package/esm/src/hpke0.d.ts +8 -0
- package/esm/src/hpke0.d.ts.map +1 -0
- package/esm/src/hpke0.js +23 -0
- package/esm/src/hpke1.d.ts +8 -0
- package/esm/src/hpke1.d.ts.map +1 -0
- package/esm/src/hpke1.js +23 -0
- package/esm/src/hpke2.d.ts +8 -0
- package/esm/src/hpke2.d.ts.map +1 -0
- package/esm/src/hpke2.js +23 -0
- package/esm/src/hpke3.d.ts +8 -0
- package/esm/src/hpke3.d.ts.map +1 -0
- package/esm/src/hpke3.js +23 -0
- package/esm/src/hpke4.d.ts +8 -0
- package/esm/src/hpke4.d.ts.map +1 -0
- package/esm/src/hpke4.js +39 -0
- package/esm/src/hpke5.d.ts +8 -0
- package/esm/src/hpke5.d.ts.map +1 -0
- package/esm/src/hpke5.js +24 -0
- package/esm/src/hpke6.d.ts +8 -0
- package/esm/src/hpke6.d.ts.map +1 -0
- package/esm/src/hpke6.js +40 -0
- package/esm/src/hpke7.d.ts +8 -0
- package/esm/src/hpke7.d.ts.map +1 -0
- package/esm/src/hpke7.js +23 -0
- package/esm/src/structures.d.ts +41 -0
- package/esm/src/structures.d.ts.map +1 -0
- package/esm/src/structures.js +63 -0
- package/package.json +48 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Ajitomi Daisuke
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,70 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @hpke/cose
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A TypeScript implementation of
|
|
4
|
+
[COSE-HPKE (draft-ietf-cose-hpke)](https://datatracker.ietf.org/doc/draft-ietf-cose-hpke/)
|
|
5
|
+
built on top of
|
|
6
|
+
[@hpke/core](https://github.com/dajiaji/hpke-js/tree/main/packages/core). It is
|
|
7
|
+
tested against the test vectors in
|
|
8
|
+
[draft-ietf-cose-hpke-27](https://datatracker.ietf.org/doc/html/draft-ietf-cose-hpke-27).
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **COSE_Encrypt0** (Integrated Encryption) — single-recipient HPKE encryption
|
|
13
|
+
directly into the COSE_Encrypt0 structure.
|
|
14
|
+
- **COSE_Encrypt** (Key Encryption) — multi-recipient HPKE key wrapping with a
|
|
15
|
+
content encryption key.
|
|
16
|
+
|
|
17
|
+
## Supported Environments
|
|
18
|
+
|
|
19
|
+
This package is distributed as ESM only. On Node.js, version 20 or later is
|
|
20
|
+
required.
|
|
21
|
+
|
|
22
|
+
## Supported Algorithms
|
|
23
|
+
|
|
24
|
+
| Name | COSE Value | KEM | KDF | AEAD |
|
|
25
|
+
| ------ | ---------- | -------------------------- | ----------- | ---------------- |
|
|
26
|
+
| HPKE-0 | 35 | DHKEM(P-256, HKDF-SHA256) | HKDF-SHA256 | AES-128-GCM |
|
|
27
|
+
| HPKE-1 | 37 | DHKEM(P-384, HKDF-SHA384) | HKDF-SHA384 | AES-256-GCM |
|
|
28
|
+
| HPKE-2 | 39 | DHKEM(P-521, HKDF-SHA512) | HKDF-SHA512 | AES-256-GCM |
|
|
29
|
+
| HPKE-3 | 41 | DHKEM(X25519, HKDF-SHA256) | HKDF-SHA256 | AES-128-GCM |
|
|
30
|
+
| HPKE-4 | 42 | DHKEM(X25519, HKDF-SHA256) | HKDF-SHA256 | ChaCha20Poly1305 |
|
|
31
|
+
| HPKE-5 | 43 | DHKEM(X448, HKDF-SHA512) | HKDF-SHA512 | AES-256-GCM |
|
|
32
|
+
| HPKE-6 | 44 | DHKEM(X448, HKDF-SHA512) | HKDF-SHA512 | ChaCha20Poly1305 |
|
|
33
|
+
| HPKE-7 | 45 | DHKEM(P-256, HKDF-SHA256) | HKDF-SHA256 | AES-256-GCM |
|
|
34
|
+
|
|
35
|
+
Key Encryption variants (HPKE-0-KE through HPKE-7-KE) use COSE values 46–53.
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### COSE_Encrypt0 (Integrated Encryption)
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { createHpke3 } from "@hpke/cose";
|
|
43
|
+
|
|
44
|
+
const enc0 = createHpke3();
|
|
45
|
+
const rkp = await enc0.suite.kem.generateKeyPair();
|
|
46
|
+
|
|
47
|
+
const ct = await enc0.seal(rkp.publicKey, new TextEncoder().encode("hello"));
|
|
48
|
+
const pt = await enc0.open(rkp, ct);
|
|
49
|
+
// new TextDecoder().decode(pt) === "hello"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### COSE_Encrypt (Key Encryption)
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { ContentAlg, createHpke3Ke } from "@hpke/cose";
|
|
56
|
+
|
|
57
|
+
const enc = createHpke3Ke(ContentAlg.A128GCM);
|
|
58
|
+
const rkp = await enc.generateKemKeyPair();
|
|
59
|
+
|
|
60
|
+
const ct = await enc.seal(
|
|
61
|
+
[{ recipientPublicKey: rkp.publicKey }],
|
|
62
|
+
new TextEncoder().encode("hello"),
|
|
63
|
+
);
|
|
64
|
+
const pt = await enc.open(rkp, ct);
|
|
65
|
+
// new TextDecoder().decode(pt) === "hello"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
MIT
|
package/esm/mod.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { ContentAlg, CoseHpkeAlg } from "./src/alg.js";
|
|
2
|
+
export type { CoseEncrypt0, CoseEncrypt0DetachedResult, CoseEncrypt0Options, } from "./src/encrypt0.js";
|
|
3
|
+
export type { CoseEncrypt } from "./src/encrypt.js";
|
|
4
|
+
export type { ContentCrypto, CoseEncryptDetachedResult, CoseEncryptOpenOptions, CoseEncryptSealOptions, CoseRecipient, } from "./src/encrypt.js";
|
|
5
|
+
export { CoseError } from "./src/errors.js";
|
|
6
|
+
export { buildEc2CoseKey, buildOkpCoseKey, CoseCrv, CoseKty, extractCurve, extractPrivateKeyBytes, extractPublicKeyBytes, } from "./src/coseKey.js";
|
|
7
|
+
export type { CoseKeyBuildOptions } from "./src/coseKey.js";
|
|
8
|
+
export { createHpke0, createHpke0Ke } from "./src/hpke0.js";
|
|
9
|
+
export { createHpke1, createHpke1Ke } from "./src/hpke1.js";
|
|
10
|
+
export { createHpke2, createHpke2Ke } from "./src/hpke2.js";
|
|
11
|
+
export { createHpke3, createHpke3Ke } from "./src/hpke3.js";
|
|
12
|
+
export { createHpke4, createHpke4Ke } from "./src/hpke4.js";
|
|
13
|
+
export { createHpke5, createHpke5Ke } from "./src/hpke5.js";
|
|
14
|
+
export { createHpke6, createHpke6Ke } from "./src/hpke6.js";
|
|
15
|
+
export { createHpke7, createHpke7Ke } from "./src/hpke7.js";
|
|
16
|
+
//# sourceMappingURL=mod.d.ts.map
|
package/esm/mod.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EACV,YAAY,EACZ,0BAA0B,EAC1B,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,YAAY,EACV,aAAa,EACb,yBAAyB,EACzB,sBAAsB,EACtB,sBAAsB,EACtB,aAAa,GACd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EACL,eAAe,EACf,eAAe,EACf,OAAO,EACP,OAAO,EACP,YAAY,EACZ,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE5D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
package/esm/mod.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { ContentAlg, CoseHpkeAlg } from "./src/alg.js";
|
|
2
|
+
export { CoseError } from "./src/errors.js";
|
|
3
|
+
export { buildEc2CoseKey, buildOkpCoseKey, CoseCrv, CoseKty, extractCurve, extractPrivateKeyBytes, extractPublicKeyBytes, } from "./src/coseKey.js";
|
|
4
|
+
export { createHpke0, createHpke0Ke } from "./src/hpke0.js";
|
|
5
|
+
export { createHpke1, createHpke1Ke } from "./src/hpke1.js";
|
|
6
|
+
export { createHpke2, createHpke2Ke } from "./src/hpke2.js";
|
|
7
|
+
export { createHpke3, createHpke3Ke } from "./src/hpke3.js";
|
|
8
|
+
export { createHpke4, createHpke4Ke } from "./src/hpke4.js";
|
|
9
|
+
export { createHpke5, createHpke5Ke } from "./src/hpke5.js";
|
|
10
|
+
export { createHpke6, createHpke6Ke } from "./src/hpke6.js";
|
|
11
|
+
export { createHpke7, createHpke7Ke } from "./src/hpke7.js";
|
package/esm/package.json
ADDED
package/esm/src/alg.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COSE HPKE algorithm identifiers from draft-ietf-cose-hpke-27.
|
|
3
|
+
*
|
|
4
|
+
* Integrated Encryption (COSE_Encrypt0): values 35–45.
|
|
5
|
+
* Key Encryption (COSE_Encrypt): values 46–53.
|
|
6
|
+
*/
|
|
7
|
+
export declare const CoseHpkeAlg: {
|
|
8
|
+
/** DHKEM(P-256), HKDF-SHA256, AES-128-GCM */
|
|
9
|
+
readonly HPKE_0: 35;
|
|
10
|
+
/** DHKEM(P-384), HKDF-SHA384, AES-256-GCM */
|
|
11
|
+
readonly HPKE_1: 37;
|
|
12
|
+
/** DHKEM(P-521), HKDF-SHA512, AES-256-GCM */
|
|
13
|
+
readonly HPKE_2: 39;
|
|
14
|
+
/** DHKEM(X25519), HKDF-SHA256, AES-128-GCM */
|
|
15
|
+
readonly HPKE_3: 41;
|
|
16
|
+
/** DHKEM(X25519), HKDF-SHA256, ChaCha20Poly1305 */
|
|
17
|
+
readonly HPKE_4: 42;
|
|
18
|
+
/** DHKEM(X448), HKDF-SHA512, AES-256-GCM */
|
|
19
|
+
readonly HPKE_5: 43;
|
|
20
|
+
/** DHKEM(X448), HKDF-SHA512, ChaCha20Poly1305 */
|
|
21
|
+
readonly HPKE_6: 44;
|
|
22
|
+
/** DHKEM(P-256), HKDF-SHA256, AES-256-GCM */
|
|
23
|
+
readonly HPKE_7: 45;
|
|
24
|
+
/** Key Encryption: DHKEM(P-256), HKDF-SHA256, AES-128-GCM */
|
|
25
|
+
readonly HPKE_0_KE: 46;
|
|
26
|
+
/** Key Encryption: DHKEM(P-384), HKDF-SHA384, AES-256-GCM */
|
|
27
|
+
readonly HPKE_1_KE: 47;
|
|
28
|
+
/** Key Encryption: DHKEM(P-521), HKDF-SHA512, AES-256-GCM */
|
|
29
|
+
readonly HPKE_2_KE: 48;
|
|
30
|
+
/** Key Encryption: DHKEM(X25519), HKDF-SHA256, AES-128-GCM */
|
|
31
|
+
readonly HPKE_3_KE: 49;
|
|
32
|
+
/** Key Encryption: DHKEM(X25519), HKDF-SHA256, ChaCha20Poly1305 */
|
|
33
|
+
readonly HPKE_4_KE: 50;
|
|
34
|
+
/** Key Encryption: DHKEM(X448), HKDF-SHA512, AES-256-GCM */
|
|
35
|
+
readonly HPKE_5_KE: 51;
|
|
36
|
+
/** Key Encryption: DHKEM(X448), HKDF-SHA512, ChaCha20Poly1305 */
|
|
37
|
+
readonly HPKE_6_KE: 52;
|
|
38
|
+
/** Key Encryption: DHKEM(P-256), HKDF-SHA256, AES-256-GCM */
|
|
39
|
+
readonly HPKE_7_KE: 53;
|
|
40
|
+
};
|
|
41
|
+
export type CoseHpkeAlg = (typeof CoseHpkeAlg)[keyof typeof CoseHpkeAlg];
|
|
42
|
+
/** COSE content-encryption algorithm identifiers. */
|
|
43
|
+
export declare const ContentAlg: {
|
|
44
|
+
/** AES-128-GCM (COSE alg value 1) */
|
|
45
|
+
readonly A128GCM: 1;
|
|
46
|
+
/** AES-256-GCM (COSE alg value 3) */
|
|
47
|
+
readonly A256GCM: 3;
|
|
48
|
+
/** ChaCha20/Poly1305 (COSE alg value 24) */
|
|
49
|
+
readonly CHACHA20POLY1305: 24;
|
|
50
|
+
};
|
|
51
|
+
export type ContentAlg = (typeof ContentAlg)[keyof typeof ContentAlg];
|
|
52
|
+
/** Content key sizes in bytes for each ContentAlg. */
|
|
53
|
+
export declare function contentKeySize(alg: ContentAlg): number;
|
|
54
|
+
/** Content nonce sizes in bytes for each ContentAlg. */
|
|
55
|
+
export declare function contentNonceSize(_alg: ContentAlg): number;
|
|
56
|
+
/** Whether the algorithm is for Key Encryption (COSE_Encrypt). */
|
|
57
|
+
export declare function isKeyEncryption(alg: CoseHpkeAlg): boolean;
|
|
58
|
+
/** Whether the algorithm is for Integrated Encryption (COSE_Encrypt0). */
|
|
59
|
+
export declare function isIntegratedEncryption(alg: CoseHpkeAlg): boolean;
|
|
60
|
+
//# sourceMappingURL=alg.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alg.d.ts","sourceRoot":"","sources":["../../src/src/alg.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,WAAW;IACtB,6CAA6C;;IAE7C,6CAA6C;;IAE7C,6CAA6C;;IAE7C,8CAA8C;;IAE9C,mDAAmD;;IAEnD,4CAA4C;;IAE5C,iDAAiD;;IAEjD,6CAA6C;;IAE7C,6DAA6D;;IAE7D,6DAA6D;;IAE7D,6DAA6D;;IAE7D,8DAA8D;;IAE9D,mEAAmE;;IAEnE,4DAA4D;;IAE5D,iEAAiE;;IAEjE,6DAA6D;;CAErD,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC;AAEzE,qDAAqD;AACrD,eAAO,MAAM,UAAU;IACrB,qCAAqC;;IAErC,qCAAqC;;IAErC,4CAA4C;;CAEpC,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC;AAEtE,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAWtD;AAED,wDAAwD;AACxD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAGzD;AAuBD,kEAAkE;AAClE,wBAAgB,eAAe,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAEzD;AAED,0EAA0E;AAC1E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAEhE"}
|
package/esm/src/alg.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { CoseError } from "./errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* COSE HPKE algorithm identifiers from draft-ietf-cose-hpke-27.
|
|
4
|
+
*
|
|
5
|
+
* Integrated Encryption (COSE_Encrypt0): values 35–45.
|
|
6
|
+
* Key Encryption (COSE_Encrypt): values 46–53.
|
|
7
|
+
*/
|
|
8
|
+
export const CoseHpkeAlg = {
|
|
9
|
+
/** DHKEM(P-256), HKDF-SHA256, AES-128-GCM */
|
|
10
|
+
HPKE_0: 35,
|
|
11
|
+
/** DHKEM(P-384), HKDF-SHA384, AES-256-GCM */
|
|
12
|
+
HPKE_1: 37,
|
|
13
|
+
/** DHKEM(P-521), HKDF-SHA512, AES-256-GCM */
|
|
14
|
+
HPKE_2: 39,
|
|
15
|
+
/** DHKEM(X25519), HKDF-SHA256, AES-128-GCM */
|
|
16
|
+
HPKE_3: 41,
|
|
17
|
+
/** DHKEM(X25519), HKDF-SHA256, ChaCha20Poly1305 */
|
|
18
|
+
HPKE_4: 42,
|
|
19
|
+
/** DHKEM(X448), HKDF-SHA512, AES-256-GCM */
|
|
20
|
+
HPKE_5: 43,
|
|
21
|
+
/** DHKEM(X448), HKDF-SHA512, ChaCha20Poly1305 */
|
|
22
|
+
HPKE_6: 44,
|
|
23
|
+
/** DHKEM(P-256), HKDF-SHA256, AES-256-GCM */
|
|
24
|
+
HPKE_7: 45,
|
|
25
|
+
/** Key Encryption: DHKEM(P-256), HKDF-SHA256, AES-128-GCM */
|
|
26
|
+
HPKE_0_KE: 46,
|
|
27
|
+
/** Key Encryption: DHKEM(P-384), HKDF-SHA384, AES-256-GCM */
|
|
28
|
+
HPKE_1_KE: 47,
|
|
29
|
+
/** Key Encryption: DHKEM(P-521), HKDF-SHA512, AES-256-GCM */
|
|
30
|
+
HPKE_2_KE: 48,
|
|
31
|
+
/** Key Encryption: DHKEM(X25519), HKDF-SHA256, AES-128-GCM */
|
|
32
|
+
HPKE_3_KE: 49,
|
|
33
|
+
/** Key Encryption: DHKEM(X25519), HKDF-SHA256, ChaCha20Poly1305 */
|
|
34
|
+
HPKE_4_KE: 50,
|
|
35
|
+
/** Key Encryption: DHKEM(X448), HKDF-SHA512, AES-256-GCM */
|
|
36
|
+
HPKE_5_KE: 51,
|
|
37
|
+
/** Key Encryption: DHKEM(X448), HKDF-SHA512, ChaCha20Poly1305 */
|
|
38
|
+
HPKE_6_KE: 52,
|
|
39
|
+
/** Key Encryption: DHKEM(P-256), HKDF-SHA256, AES-256-GCM */
|
|
40
|
+
HPKE_7_KE: 53,
|
|
41
|
+
};
|
|
42
|
+
/** COSE content-encryption algorithm identifiers. */
|
|
43
|
+
export const ContentAlg = {
|
|
44
|
+
/** AES-128-GCM (COSE alg value 1) */
|
|
45
|
+
A128GCM: 1,
|
|
46
|
+
/** AES-256-GCM (COSE alg value 3) */
|
|
47
|
+
A256GCM: 3,
|
|
48
|
+
/** ChaCha20/Poly1305 (COSE alg value 24) */
|
|
49
|
+
CHACHA20POLY1305: 24,
|
|
50
|
+
};
|
|
51
|
+
/** Content key sizes in bytes for each ContentAlg. */
|
|
52
|
+
export function contentKeySize(alg) {
|
|
53
|
+
switch (alg) {
|
|
54
|
+
case ContentAlg.A128GCM:
|
|
55
|
+
return 16;
|
|
56
|
+
case ContentAlg.A256GCM:
|
|
57
|
+
return 32;
|
|
58
|
+
case ContentAlg.CHACHA20POLY1305:
|
|
59
|
+
return 32;
|
|
60
|
+
default:
|
|
61
|
+
throw new CoseError(`Unsupported content algorithm: ${alg}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Content nonce sizes in bytes for each ContentAlg. */
|
|
65
|
+
export function contentNonceSize(_alg) {
|
|
66
|
+
// AES-128-GCM, AES-256-GCM, and ChaCha20/Poly1305 all use 12-byte nonces
|
|
67
|
+
return 12;
|
|
68
|
+
}
|
|
69
|
+
const KEY_ENCRYPTION_ALGS = new Set([
|
|
70
|
+
46,
|
|
71
|
+
47,
|
|
72
|
+
48,
|
|
73
|
+
49,
|
|
74
|
+
50,
|
|
75
|
+
51,
|
|
76
|
+
52,
|
|
77
|
+
53,
|
|
78
|
+
]);
|
|
79
|
+
const INTEGRATED_ENCRYPTION_ALGS = new Set([
|
|
80
|
+
35,
|
|
81
|
+
37,
|
|
82
|
+
39,
|
|
83
|
+
41,
|
|
84
|
+
42,
|
|
85
|
+
43,
|
|
86
|
+
44,
|
|
87
|
+
45,
|
|
88
|
+
]);
|
|
89
|
+
/** Whether the algorithm is for Key Encryption (COSE_Encrypt). */
|
|
90
|
+
export function isKeyEncryption(alg) {
|
|
91
|
+
return KEY_ENCRYPTION_ALGS.has(alg);
|
|
92
|
+
}
|
|
93
|
+
/** Whether the algorithm is for Integrated Encryption (COSE_Encrypt0). */
|
|
94
|
+
export function isIntegratedEncryption(alg) {
|
|
95
|
+
return INTEGRATED_ENCRYPTION_ALGS.has(alg);
|
|
96
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decoder.d.ts","sourceRoot":"","sources":["../../../src/src/cbor/decoder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AA+I5C,kDAAkD;AAClD,wBAAgB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,CAOlD"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/** Maximum nesting depth of data items. COSE structures stay far below this. */
|
|
2
|
+
const MAX_NESTING_DEPTH = 32;
|
|
3
|
+
class Decoder {
|
|
4
|
+
constructor(data) {
|
|
5
|
+
Object.defineProperty(this, "_data", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
value: void 0
|
|
10
|
+
});
|
|
11
|
+
Object.defineProperty(this, "_pos", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: void 0
|
|
16
|
+
});
|
|
17
|
+
Object.defineProperty(this, "_depth", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: void 0
|
|
22
|
+
});
|
|
23
|
+
this._data = data;
|
|
24
|
+
this._pos = 0;
|
|
25
|
+
this._depth = 0;
|
|
26
|
+
}
|
|
27
|
+
get position() {
|
|
28
|
+
return this._pos;
|
|
29
|
+
}
|
|
30
|
+
decode() {
|
|
31
|
+
if (this._depth >= MAX_NESTING_DEPTH) {
|
|
32
|
+
throw new Error("CBOR: nesting too deep");
|
|
33
|
+
}
|
|
34
|
+
this._depth++;
|
|
35
|
+
const value = this._decodeItem();
|
|
36
|
+
this._depth--;
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
_decodeItem() {
|
|
40
|
+
if (this._pos >= this._data.length) {
|
|
41
|
+
throw new Error("CBOR: unexpected end of input");
|
|
42
|
+
}
|
|
43
|
+
const initial = this._data[this._pos++];
|
|
44
|
+
const major = initial >> 5;
|
|
45
|
+
const additional = initial & 0x1f;
|
|
46
|
+
switch (major) {
|
|
47
|
+
case 0: // unsigned int
|
|
48
|
+
return this._readUint(additional);
|
|
49
|
+
case 1: // negative int
|
|
50
|
+
return -1 - this._readUint(additional);
|
|
51
|
+
case 2: // byte string
|
|
52
|
+
return this._readBytes(this._readUint(additional));
|
|
53
|
+
case 3: // text string
|
|
54
|
+
return this._readText(this._readUint(additional));
|
|
55
|
+
case 4: // array
|
|
56
|
+
return this._readArray(this._readUint(additional));
|
|
57
|
+
case 5: // map
|
|
58
|
+
return this._readMap(this._readUint(additional));
|
|
59
|
+
case 6: // tag (skip tag number, return inner value)
|
|
60
|
+
this._readUint(additional);
|
|
61
|
+
return this.decode();
|
|
62
|
+
case 7: // simple/float
|
|
63
|
+
if (additional === 22) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
throw new Error(`CBOR: unsupported simple value ${additional}`);
|
|
67
|
+
default:
|
|
68
|
+
throw new Error(`CBOR: unsupported major type ${major}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
_readUint(additional) {
|
|
72
|
+
if (additional < 24) {
|
|
73
|
+
return additional;
|
|
74
|
+
}
|
|
75
|
+
if (additional === 24) {
|
|
76
|
+
return this._data[this._pos++];
|
|
77
|
+
}
|
|
78
|
+
if (additional === 25) {
|
|
79
|
+
const val = (this._data[this._pos] << 8) | this._data[this._pos + 1];
|
|
80
|
+
this._pos += 2;
|
|
81
|
+
return val;
|
|
82
|
+
}
|
|
83
|
+
if (additional === 26) {
|
|
84
|
+
const val = (this._data[this._pos] << 24) |
|
|
85
|
+
(this._data[this._pos + 1] << 16) |
|
|
86
|
+
(this._data[this._pos + 2] << 8) |
|
|
87
|
+
this._data[this._pos + 3];
|
|
88
|
+
this._pos += 4;
|
|
89
|
+
return val >>> 0; // unsigned
|
|
90
|
+
}
|
|
91
|
+
if (additional === 27) {
|
|
92
|
+
const dv = new DataView(this._data.buffer, this._data.byteOffset + this._pos, 8);
|
|
93
|
+
const val = dv.getBigUint64(0);
|
|
94
|
+
this._pos += 8;
|
|
95
|
+
if (val > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
96
|
+
throw new Error("CBOR: integer too large for number");
|
|
97
|
+
}
|
|
98
|
+
return Number(val);
|
|
99
|
+
}
|
|
100
|
+
throw new Error(`CBOR: unsupported additional info ${additional}`);
|
|
101
|
+
}
|
|
102
|
+
_readBytes(len) {
|
|
103
|
+
const slice = this._data.slice(this._pos, this._pos + len);
|
|
104
|
+
this._pos += len;
|
|
105
|
+
return slice;
|
|
106
|
+
}
|
|
107
|
+
_readText(len) {
|
|
108
|
+
const bytes = this._readBytes(len);
|
|
109
|
+
try {
|
|
110
|
+
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
throw new Error("CBOR: invalid UTF-8 in text string");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
_readArray(len) {
|
|
117
|
+
const arr = [];
|
|
118
|
+
for (let i = 0; i < len; i++) {
|
|
119
|
+
arr.push(this.decode());
|
|
120
|
+
}
|
|
121
|
+
return arr;
|
|
122
|
+
}
|
|
123
|
+
_readMap(len) {
|
|
124
|
+
const map = new Map();
|
|
125
|
+
for (let i = 0; i < len; i++) {
|
|
126
|
+
const key = this.decode();
|
|
127
|
+
if (typeof key !== "number" && typeof key !== "string") {
|
|
128
|
+
throw new Error("CBOR: unsupported map key type");
|
|
129
|
+
}
|
|
130
|
+
// RFC 9052 Section 3: a message with a repeated label MUST be rejected.
|
|
131
|
+
if (map.has(key)) {
|
|
132
|
+
throw new Error("CBOR: duplicate map key");
|
|
133
|
+
}
|
|
134
|
+
const val = this.decode();
|
|
135
|
+
map.set(key, val);
|
|
136
|
+
}
|
|
137
|
+
return map;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/** Decode a CBOR byte string into a CborValue. */
|
|
141
|
+
export function decode(data) {
|
|
142
|
+
const decoder = new Decoder(data);
|
|
143
|
+
const value = decoder.decode();
|
|
144
|
+
if (decoder.position !== data.length) {
|
|
145
|
+
throw new Error("CBOR: trailing bytes after decoded value");
|
|
146
|
+
}
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CborValue } from "./types.js";
|
|
2
|
+
/** Encode a CborValue into a CBOR byte string. */
|
|
3
|
+
export declare function encode(value: CborValue): Uint8Array;
|
|
4
|
+
/** Encode a CborValue wrapped in a CBOR tag (major type 6). */
|
|
5
|
+
export declare function encodeTagged(tag: number, value: CborValue): Uint8Array;
|
|
6
|
+
//# sourceMappingURL=encoder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encoder.d.ts","sourceRoot":"","sources":["../../../src/src/cbor/encoder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAkH5C,kDAAkD;AAClD,wBAAgB,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,CAEnD;AAED,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,UAAU,CAEtE"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
function encodeHead(major, value) {
|
|
2
|
+
const mt = major << 5;
|
|
3
|
+
if (value < 24) {
|
|
4
|
+
return new Uint8Array([mt | value]);
|
|
5
|
+
}
|
|
6
|
+
if (value < 0x100) {
|
|
7
|
+
return new Uint8Array([mt | 24, value]);
|
|
8
|
+
}
|
|
9
|
+
if (value < 0x10000) {
|
|
10
|
+
const buf = new Uint8Array(3);
|
|
11
|
+
buf[0] = mt | 25;
|
|
12
|
+
buf[1] = (value >> 8) & 0xff;
|
|
13
|
+
buf[2] = value & 0xff;
|
|
14
|
+
return buf;
|
|
15
|
+
}
|
|
16
|
+
if (value < 0x100000000) {
|
|
17
|
+
const buf = new Uint8Array(5);
|
|
18
|
+
buf[0] = mt | 26;
|
|
19
|
+
buf[1] = (value >> 24) & 0xff;
|
|
20
|
+
buf[2] = (value >> 16) & 0xff;
|
|
21
|
+
buf[3] = (value >> 8) & 0xff;
|
|
22
|
+
buf[4] = value & 0xff;
|
|
23
|
+
return buf;
|
|
24
|
+
}
|
|
25
|
+
// 64-bit: use DataView for values > 32 bits
|
|
26
|
+
const buf = new Uint8Array(9);
|
|
27
|
+
buf[0] = mt | 27;
|
|
28
|
+
const dv = new DataView(buf.buffer);
|
|
29
|
+
dv.setBigUint64(1, BigInt(value));
|
|
30
|
+
return buf;
|
|
31
|
+
}
|
|
32
|
+
function encodeValue(value) {
|
|
33
|
+
if (value === null) {
|
|
34
|
+
return [new Uint8Array([0xf6])]; // major 7, simple value 22
|
|
35
|
+
}
|
|
36
|
+
if (typeof value === "number") {
|
|
37
|
+
if (!Number.isInteger(value)) {
|
|
38
|
+
throw new Error("CBOR: float encoding is not supported");
|
|
39
|
+
}
|
|
40
|
+
if (value >= 0) {
|
|
41
|
+
return [encodeHead(0, value)]; // major 0: unsigned int
|
|
42
|
+
}
|
|
43
|
+
// negative int: major 1, value = -1 - n
|
|
44
|
+
return [encodeHead(1, -1 - value)];
|
|
45
|
+
}
|
|
46
|
+
if (typeof value === "string") {
|
|
47
|
+
const encoded = new TextEncoder().encode(value);
|
|
48
|
+
return [encodeHead(3, encoded.length), encoded]; // major 3: text string
|
|
49
|
+
}
|
|
50
|
+
if (value instanceof Uint8Array) {
|
|
51
|
+
return [encodeHead(2, value.length), value]; // major 2: byte string
|
|
52
|
+
}
|
|
53
|
+
if (Array.isArray(value)) {
|
|
54
|
+
const parts = [encodeHead(4, value.length)]; // major 4: array
|
|
55
|
+
for (const item of value) {
|
|
56
|
+
parts.push(...encodeValue(item));
|
|
57
|
+
}
|
|
58
|
+
return parts;
|
|
59
|
+
}
|
|
60
|
+
if (value instanceof Map) {
|
|
61
|
+
// COSE deterministic encoding: sort by encoded key bytes (lexicographic)
|
|
62
|
+
const entries = [];
|
|
63
|
+
for (const [k, v] of value) {
|
|
64
|
+
const keyParts = encodeValue(k);
|
|
65
|
+
const keyBytes = concat(keyParts);
|
|
66
|
+
entries.push({ keyBytes, valParts: encodeValue(v) });
|
|
67
|
+
}
|
|
68
|
+
entries.sort((a, b) => compareBytes(a.keyBytes, b.keyBytes));
|
|
69
|
+
const parts = [encodeHead(5, entries.length)]; // major 5: map
|
|
70
|
+
for (const entry of entries) {
|
|
71
|
+
parts.push(entry.keyBytes);
|
|
72
|
+
for (const vp of entry.valParts) {
|
|
73
|
+
parts.push(vp);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return parts;
|
|
77
|
+
}
|
|
78
|
+
throw new Error("CBOR: unsupported value type");
|
|
79
|
+
}
|
|
80
|
+
function compareBytes(a, b) {
|
|
81
|
+
const len = Math.min(a.length, b.length);
|
|
82
|
+
for (let i = 0; i < len; i++) {
|
|
83
|
+
if (a[i] !== b[i]) {
|
|
84
|
+
return a[i] - b[i];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return a.length - b.length;
|
|
88
|
+
}
|
|
89
|
+
function concat(parts) {
|
|
90
|
+
let total = 0;
|
|
91
|
+
for (const p of parts) {
|
|
92
|
+
total += p.length;
|
|
93
|
+
}
|
|
94
|
+
const result = new Uint8Array(total);
|
|
95
|
+
let offset = 0;
|
|
96
|
+
for (const p of parts) {
|
|
97
|
+
result.set(p, offset);
|
|
98
|
+
offset += p.length;
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
/** Encode a CborValue into a CBOR byte string. */
|
|
103
|
+
export function encode(value) {
|
|
104
|
+
return concat(encodeValue(value));
|
|
105
|
+
}
|
|
106
|
+
/** Encode a CborValue wrapped in a CBOR tag (major type 6). */
|
|
107
|
+
export function encodeTagged(tag, value) {
|
|
108
|
+
return concat([encodeHead(6, tag), ...encodeValue(value)]);
|
|
109
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A CBOR value that can be encoded/decoded by the minimal CBOR codec.
|
|
3
|
+
*
|
|
4
|
+
* Supported types: unsigned integer, negative integer, byte string,
|
|
5
|
+
* text string, array, map (with integer or text keys), and null.
|
|
6
|
+
*/
|
|
7
|
+
export type CborValue = number | Uint8Array | string | CborValue[] | Map<CborValue, CborValue> | null;
|
|
8
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/src/cbor/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,UAAU,GACV,MAAM,GACN,SAAS,EAAE,GACX,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,GACzB,IAAI,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contentAesGcm.d.ts","sourceRoot":"","sources":["../../src/src/contentAesGcm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAUlD,eAAO,MAAM,mBAAmB,EAAE,aAqCjC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Convert Uint8Array → ArrayBuffer (handles subarrays). */
|
|
2
|
+
function ab(u) {
|
|
3
|
+
return u.buffer.slice(u.byteOffset, u.byteOffset + u.byteLength);
|
|
4
|
+
}
|
|
5
|
+
export const aesGcmContentCrypto = {
|
|
6
|
+
async seal(key, nonce, plaintext, aad) {
|
|
7
|
+
const s = globalThis.crypto.subtle;
|
|
8
|
+
const k = await s.importKey("raw", ab(key), "AES-GCM", false, [
|
|
9
|
+
"encrypt",
|
|
10
|
+
]);
|
|
11
|
+
return new Uint8Array(await s.encrypt({
|
|
12
|
+
name: "AES-GCM",
|
|
13
|
+
iv: ab(nonce),
|
|
14
|
+
additionalData: ab(aad),
|
|
15
|
+
tagLength: 128,
|
|
16
|
+
}, k, ab(plaintext)));
|
|
17
|
+
},
|
|
18
|
+
async open(key, nonce, ciphertext, aad) {
|
|
19
|
+
const s = globalThis.crypto.subtle;
|
|
20
|
+
const k = await s.importKey("raw", ab(key), "AES-GCM", false, [
|
|
21
|
+
"decrypt",
|
|
22
|
+
]);
|
|
23
|
+
return new Uint8Array(await s.decrypt({
|
|
24
|
+
name: "AES-GCM",
|
|
25
|
+
iv: ab(nonce),
|
|
26
|
+
additionalData: ab(aad),
|
|
27
|
+
tagLength: 128,
|
|
28
|
+
}, k, ab(ciphertext)));
|
|
29
|
+
},
|
|
30
|
+
};
|