@byok-sdk/core 0.30.0 → 0.30.2

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.
@@ -0,0 +1,61 @@
1
+ import { type WebCryptoKey } from '../webcrypto';
2
+ import { type P256PublicJwk } from './primitives';
3
+ export declare const MEMBER_KEY_RECORD_VERSION = 1;
4
+ /**
5
+ * Whose keys a store holds. A device member's binding names its SDK device
6
+ * id; a client member's has none, as in its bundle.
7
+ */
8
+ export interface MemberKeyBinding {
9
+ readonly tenantId: string;
10
+ readonly deviceId?: string;
11
+ }
12
+ export interface MemberKeys {
13
+ readonly binding: MemberKeyBinding;
14
+ /** Signs chain entries and key packages. The private key is not extractable. */
15
+ readonly signing: {
16
+ readonly publicJwk: P256PublicJwk;
17
+ readonly privateKey: WebCryptoKey;
18
+ };
19
+ /** Opens key packages. The private key is not extractable. `epoch` starts at 1. */
20
+ readonly kem: MemberKemKey;
21
+ /** The KEM key of a rotation that has begun and not finished, at `kem.epoch + 1`. */
22
+ readonly nextKem?: MemberKemKey;
23
+ }
24
+ export interface MemberKemKey {
25
+ readonly epoch: number;
26
+ readonly publicJwk: P256PublicJwk;
27
+ readonly privateKey: WebCryptoKey;
28
+ }
29
+ /** The custody port of one member's keys. Each store expects one writer. */
30
+ export interface MemberKeyStore {
31
+ /** The keys bound to `binding`, or `undefined` when the store holds none for it. A malformed record throws. */
32
+ load(binding: MemberKeyBinding): Promise<MemberKeys | undefined>;
33
+ /** The keys bound to `binding`. A store that holds none for it creates new ones, which replace any other binding's keys. */
34
+ loadOrCreate(binding: MemberKeyBinding): Promise<MemberKeys>;
35
+ }
36
+ /**
37
+ * A new member's record for `binding`: a fresh signing key and a fresh KEM
38
+ * key at epoch 1, as one canonical JSON text. The text holds both private
39
+ * scalars; only the member's own custody stores it.
40
+ */
41
+ export declare function generateMemberKeyRecord(binding: MemberKeyBinding): Promise<string>;
42
+ /**
43
+ * The keys of a stored record, imported non-extractable, or `undefined` when
44
+ * the text is not a canonical v1 record, or a private scalar does not match
45
+ * its public point. Never throws for hostile input.
46
+ */
47
+ export declare function openMemberKeyRecord(text: unknown): Promise<MemberKeys | undefined>;
48
+ /**
49
+ * Begin a KEM rotation: the record with a new KEM key at `kemEpoch + 1`
50
+ * beside the current one. A record whose rotation has begun comes back
51
+ * unchanged, so a resumed rotation keeps its key. A record that is not
52
+ * canonical v1 text throws.
53
+ */
54
+ export declare function beginMemberKemRotation(text: string): Promise<string>;
55
+ /**
56
+ * Finish the KEM rotation to `epoch`: the next key becomes current, and the
57
+ * old private key is gone from the record. A repeat after the rotation
58
+ * finished comes back unchanged. Any other `epoch` returns `undefined`. A
59
+ * record that is not canonical v1 text throws.
60
+ */
61
+ export declare function finishMemberKemRotation(text: string, epoch: number): string | undefined;
@@ -10,6 +10,9 @@ export declare const SCOPE_KEY_PACKAGE_VERSION = 1;
10
10
  export declare const SCOPE_KEY_HPKE_INFO = "byok-sdk.scope-key.v1";
11
11
  /** Prepended to the canonical package bytes the sender signs. */
12
12
  export declare const SCOPE_KEY_PACKAGE_DOMAIN_PREFIX = "byok-scope-key-package-v1\n";
13
+ export declare const SCOPE_KEY_PACKAGE_V2 = 2;
14
+ export declare const SCOPE_KEY_HPKE_INFO_V2 = "byok-sdk.scope-key.v2";
15
+ export declare const SCOPE_KEY_PACKAGE_V2_DOMAIN_PREFIX = "byok-scope-key-package-v2\n";
13
16
  /** A fresh scope key from the platform CSPRNG. */
14
17
  export declare function generateScopeKey(): Bytes;
15
18
  export declare function deriveScopeKeyId(scopeKey: Uint8Array): Promise<string>;
@@ -38,14 +41,22 @@ export interface ScopeKeyPackageV1 {
38
41
  /** Sender's ECDSA P-256 / SHA-256 signature, base64url of the 64-byte P1363 form. */
39
42
  readonly sig: string;
40
43
  }
44
+ export interface ScopeKeyPackageV2 extends Omit<ScopeKeyPackageV1, 'v'> {
45
+ readonly v: typeof SCOPE_KEY_PACKAGE_V2;
46
+ /** The KEM epoch of the recipient key the package is wrapped to. */
47
+ readonly recipientKemEpoch: number;
48
+ }
49
+ export type ScopeKeyPackage = ScopeKeyPackageV1 | ScopeKeyPackageV2;
41
50
  export interface SealScopeKeyPackageInput {
42
51
  readonly scopeKey: Uint8Array;
43
52
  readonly tenantId: string;
44
53
  readonly scopeId: string;
45
54
  readonly epoch: number;
55
+ /** The recipient's current KEM key, with its epoch from the verified member chain. */
46
56
  readonly recipient: {
47
57
  readonly memberId: string;
48
58
  readonly kemPublicJwk: P256PublicJwk;
59
+ readonly kemEpoch: number;
49
60
  };
50
61
  readonly sender: {
51
62
  readonly signingPublicJwk: P256PublicJwk;
@@ -53,11 +64,11 @@ export interface SealScopeKeyPackageInput {
53
64
  };
54
65
  }
55
66
  /**
56
- * Wrap one scope key to one member and sign the package. A signing private key
57
- * that does not pair with `sender.signingPublicJwk` throws
58
- * `sealed_secret_invalid`.
67
+ * Wrap one scope key to one member's KEM key of `recipient.kemEpoch` and sign
68
+ * the version 2 package. A signing private key that does not pair with
69
+ * `sender.signingPublicJwk` throws `sealed_secret_invalid`.
59
70
  */
60
- export declare function sealScopeKeyPackage(input: SealScopeKeyPackageInput): Promise<ScopeKeyPackageV1>;
71
+ export declare function sealScopeKeyPackage(input: SealScopeKeyPackageInput): Promise<ScopeKeyPackageV2>;
61
72
  export interface OpenScopeKeyPackageInput {
62
73
  readonly package: unknown;
63
74
  /** What the caller asked the Host for. A package for anything else is refused. */
@@ -70,6 +81,8 @@ export interface OpenScopeKeyPackageInput {
70
81
  readonly memberId: string;
71
82
  readonly kemPublicJwk: P256PublicJwk;
72
83
  readonly kemPrivateKey: WebCryptoKey;
84
+ /** The KEM epoch of this key pair. A version 2 package must name it. */
85
+ readonly kemEpoch: number;
73
86
  };
74
87
  /** The signing key the caller's verified chain holds for `package.sender`. */
75
88
  readonly senderSigningPublicJwk: P256PublicJwk;
@@ -84,7 +97,8 @@ export type ScopeKeyPackageOpenResult = {
84
97
  };
85
98
  /**
86
99
  * Verify and open one key package, in this order: shape; addressed to this
87
- * recipient for the expected tenant, scope and epoch; sender key matches the
100
+ * recipient (and, for version 2, its KEM epoch) for the expected tenant,
101
+ * scope and epoch; sender key matches the
88
102
  * sender id; sender signature; HPKE open; key length; and `keyId` equal to
89
103
  * the opened key's own id. Nothing here throws for hostile input.
90
104
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byok-sdk/core",
3
- "version": "0.30.0",
3
+ "version": "0.30.2",
4
4
  "description": "BYOK SDK platform contracts: branded tenant identity, tenant-first store ports, device proof canonicalization, and the composition conformance reference",
5
5
  "type": "module",
6
6
  "license": "MIT",