@byok-sdk/core 0.30.1 → 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.
@@ -10,7 +10,7 @@
10
10
  export { SEALED_CATEGORIES, SEALED_CONTENT_SUITE, SEALED_CONTENT_VERSION, SEALED_DIRECTIONS, SealedContentHeaderV1Schema, openSealedContent, sealContent, } from './sealed-content';
11
11
  export type { OpenSealedContentInput, SealContentInput, SealedBinding, SealedCategory, SealedContent, SealedContentHeaderV1, SealedContentOpenFailure, SealedContentOpenResult, SealedDirection, SealedScopePolicy, } from './sealed-content';
12
12
  export { deriveScopeKeyId, generateScopeKey, openScopeKeyPackage, sealScopeKeyPackage } from './scope-key';
13
- export type { OpenScopeKeyPackageInput, ScopeKeyPackageOpenResult, ScopeKeyPackageV1, SealScopeKeyPackageInput, } from './scope-key';
13
+ export type { OpenScopeKeyPackageInput, ScopeKeyPackage, ScopeKeyPackageOpenResult, ScopeKeyPackageV1, ScopeKeyPackageV2, SealScopeKeyPackageInput, } from './scope-key';
14
14
  export { MEMBER_CHAIN_OPS, deriveMemberId, memberChainEntryHash, signMemberChainEntry, verifyMemberChainEntry, } from './member-chain';
15
15
  export type { MemberChainEntryV1, MemberChainEntryVerifyResult, MemberChainOp, UnsignedMemberChainEntryV1, } from './member-chain';
16
16
  export { MEMBER_KEY_BUNDLE_VERSION, createMemberKeyBundle, parseMemberKeyBundle, verifyMemberKem } from './member-bundle';
@@ -679,6 +679,87 @@ async function verifyMemberChainEntry(entry, signerPublicJwk) {
679
679
  }
680
680
  return { ok: true, entry: parsed.data, hash: `sha256:${hex(await sha256(input))}` };
681
681
  }
682
+ var MEMBER_KEY_BUNDLE_VERSION = 1;
683
+ var MEMBER_KEM_DOMAIN_PREFIX = "byok-member-kem-v1\n";
684
+ var KemEpochSchema = z.number().int().positive().max(Number.MAX_SAFE_INTEGER);
685
+ var KemSigSchema = base64UrlBytes(64);
686
+ var MemberKeyBundleV1Schema = z.strictObject({
687
+ v: z.literal(MEMBER_KEY_BUNDLE_VERSION),
688
+ memberId: z.string().regex(MEMBER_ID_PATTERN),
689
+ signingPublicJwk: P256PublicJwkSchema,
690
+ kemPublicJwk: P256PublicJwkSchema,
691
+ kemEpoch: KemEpochSchema,
692
+ kemSig: KemSigSchema,
693
+ deviceId: z.string().min(1).optional()
694
+ });
695
+ var CreateInputSchema = z.object({
696
+ signingPublicJwk: P256PublicJwkSchema,
697
+ signingPrivateKey: z.custom(),
698
+ kemPublicJwk: P256PublicJwkSchema,
699
+ kemEpoch: KemEpochSchema,
700
+ deviceId: z.string().min(1).optional()
701
+ });
702
+ var KemSignerSchema = z.object({ memberId: z.string().regex(MEMBER_ID_PATTERN), signingPublicJwk: P256PublicJwkSchema });
703
+ var KemStatementSchema = z.object({ kemPublicJwk: P256PublicJwkSchema, kemEpoch: KemEpochSchema, kemSig: KemSigSchema });
704
+ function memberKemSigningInput(memberId, kemPublicJwk, kemEpoch) {
705
+ return concat2(
706
+ utf82(MEMBER_KEM_DOMAIN_PREFIX),
707
+ canonicalizeJsonBytes({ memberId, kemPublicKey: { x: kemPublicJwk.x, y: kemPublicJwk.y }, kemEpoch })
708
+ );
709
+ }
710
+ async function createMemberKeyBundle(input) {
711
+ const parsed = safeParseTotal(CreateInputSchema, input);
712
+ if (!parsed.success) throw invalidInput("Member key bundle input is invalid");
713
+ const { signingPublicJwk, signingPrivateKey, kemPublicJwk, kemEpoch, deviceId } = parsed.data;
714
+ const memberId = await deriveMemberId(signingPublicJwk);
715
+ if (!await isValidMemberKemKey(kemPublicJwk, signingPublicJwk)) throw invalidInput("Member KEM key is invalid");
716
+ const kemSig = await ecdsaSign(signingPublicJwk, signingPrivateKey, memberKemSigningInput(memberId, kemPublicJwk, kemEpoch));
717
+ return {
718
+ v: MEMBER_KEY_BUNDLE_VERSION,
719
+ memberId,
720
+ signingPublicJwk,
721
+ kemPublicJwk,
722
+ kemEpoch,
723
+ kemSig,
724
+ ...deviceId === void 0 ? {} : { deviceId }
725
+ };
726
+ }
727
+ async function isValidMemberKemKey(kemPublicJwk, signingPublicJwk) {
728
+ if (kemPublicJwk.x === signingPublicJwk.x && kemPublicJwk.y === signingPublicJwk.y) return false;
729
+ try {
730
+ await p256PublicKeyBytes(kemPublicJwk);
731
+ return true;
732
+ } catch {
733
+ return false;
734
+ }
735
+ }
736
+ async function verifyMemberKem(member, kem) {
737
+ const signer = safeParseTotal(KemSignerSchema, member);
738
+ const statement = safeParseTotal(KemStatementSchema, kem);
739
+ if (!signer.success || !statement.success) return false;
740
+ const { memberId, signingPublicJwk } = signer.data;
741
+ const { kemPublicJwk, kemEpoch, kemSig } = statement.data;
742
+ if (!await isValidMemberKemKey(kemPublicJwk, signingPublicJwk)) return false;
743
+ try {
744
+ return await ecdsaVerify(signingPublicJwk, kemSig, memberKemSigningInput(memberId, kemPublicJwk, kemEpoch));
745
+ } catch {
746
+ return false;
747
+ }
748
+ }
749
+ async function parseMemberKeyBundle(value) {
750
+ const parsed = safeParseTotal(MemberKeyBundleV1Schema, value);
751
+ if (!parsed.success) return void 0;
752
+ const bundle = parsed.data;
753
+ try {
754
+ if (await deriveMemberId(bundle.signingPublicJwk) !== bundle.memberId) return void 0;
755
+ } catch {
756
+ return void 0;
757
+ }
758
+ if (!await verifyMemberKem(bundle, bundle)) return void 0;
759
+ return bundle;
760
+ }
761
+
762
+ // src/sealed-payload/scope-key.ts
682
763
  var SCOPE_KEY_BYTES = 32;
683
764
  var SCOPE_KEY_ID_PATTERN = /^[A-Za-z0-9_-]{22}$/u;
684
765
  var KEY_ID_MESSAGE = "byok-scope-key-id-v1";
@@ -688,6 +769,9 @@ var SEALED_SALT_BYTES = 32;
688
769
  var SCOPE_KEY_PACKAGE_VERSION = 1;
689
770
  var SCOPE_KEY_HPKE_INFO = "byok-sdk.scope-key.v1";
690
771
  var SCOPE_KEY_PACKAGE_DOMAIN_PREFIX = "byok-scope-key-package-v1\n";
772
+ var SCOPE_KEY_PACKAGE_V2 = 2;
773
+ var SCOPE_KEY_HPKE_INFO_V2 = "byok-sdk.scope-key.v2";
774
+ var SCOPE_KEY_PACKAGE_V2_DOMAIN_PREFIX = "byok-scope-key-package-v2\n";
691
775
  var P256_POINT_BYTES = 65;
692
776
  var AEAD_TAG_BYTES = 16;
693
777
  function assertScopeKey(scopeKey) {
@@ -722,49 +806,35 @@ var ScopeKeyPackageV1Schema = z.strictObject({
722
806
  ciphertext: base64UrlBytes(SCOPE_KEY_BYTES + AEAD_TAG_BYTES),
723
807
  sig: base64UrlBytes(64)
724
808
  });
725
- function packageAad(identity) {
726
- return canonicalizeJsonBytes({
727
- v: identity.v,
728
- tenantId: identity.tenantId,
729
- scopeId: identity.scopeId,
730
- epoch: identity.epoch,
731
- keyId: identity.keyId,
732
- recipient: identity.recipient,
733
- sender: identity.sender
734
- });
809
+ var ScopeKeyPackageV2Schema = z.strictObject({
810
+ ...ScopeKeyPackageV1Schema.shape,
811
+ v: z.literal(SCOPE_KEY_PACKAGE_V2),
812
+ recipientKemEpoch: KemEpochSchema
813
+ });
814
+ var ScopeKeyPackageSchema = z.union([ScopeKeyPackageV1Schema, ScopeKeyPackageV2Schema]);
815
+ function packageAad({ enc: _enc, ciphertext: _ciphertext, sig: _sig, ...identity }) {
816
+ return canonicalizeJsonBytes(identity);
735
817
  }
736
- function packageSigningInput(pkg) {
737
- return concat2(
738
- utf82(SCOPE_KEY_PACKAGE_DOMAIN_PREFIX),
739
- canonicalizeJsonBytes({
740
- v: pkg.v,
741
- tenantId: pkg.tenantId,
742
- scopeId: pkg.scopeId,
743
- epoch: pkg.epoch,
744
- keyId: pkg.keyId,
745
- recipient: pkg.recipient,
746
- sender: pkg.sender,
747
- enc: pkg.enc,
748
- ciphertext: pkg.ciphertext
749
- })
750
- );
818
+ function packageSigningInput({ sig: _sig, ...pkg }) {
819
+ return concat2(utf82(pkg.v === SCOPE_KEY_PACKAGE_V2 ? SCOPE_KEY_PACKAGE_V2_DOMAIN_PREFIX : SCOPE_KEY_PACKAGE_DOMAIN_PREFIX), canonicalizeJsonBytes(pkg));
751
820
  }
752
- var PackageIdentitySchema = ScopeKeyPackageV1Schema.omit({ enc: true, ciphertext: true, sig: true });
821
+ var PackageIdentitySchema = ScopeKeyPackageV2Schema.omit({ enc: true, ciphertext: true, sig: true });
753
822
  async function sealScopeKeyPackage(input) {
754
823
  const parsed = safeParseTotal(PackageIdentitySchema, {
755
- v: SCOPE_KEY_PACKAGE_VERSION,
824
+ v: SCOPE_KEY_PACKAGE_V2,
756
825
  tenantId: input.tenantId,
757
826
  scopeId: input.scopeId,
758
827
  epoch: input.epoch,
759
828
  keyId: await deriveScopeKeyId(input.scopeKey),
760
829
  recipient: input.recipient.memberId,
830
+ recipientKemEpoch: input.recipient.kemEpoch,
761
831
  sender: await deriveMemberId(input.sender.signingPublicJwk)
762
832
  });
763
833
  if (!parsed.success) throw invalidInput("Scope key package input is invalid");
764
834
  const identity = parsed.data;
765
835
  const { enc, ciphertext } = await hpkeSealBase({
766
836
  recipientPublicKey: await p256PublicKeyBytes(input.recipient.kemPublicJwk),
767
- info: utf82(SCOPE_KEY_HPKE_INFO),
837
+ info: utf82(SCOPE_KEY_HPKE_INFO_V2),
768
838
  aad: packageAad(identity),
769
839
  plaintext: input.scopeKey
770
840
  });
@@ -772,10 +842,10 @@ async function sealScopeKeyPackage(input) {
772
842
  return { ...unsigned, sig: await ecdsaSign(input.sender.signingPublicJwk, input.sender.signingPrivateKey, packageSigningInput(unsigned)) };
773
843
  }
774
844
  async function openScopeKeyPackage(input) {
775
- const parsed = safeParseTotal(ScopeKeyPackageV1Schema, input.package);
845
+ const parsed = safeParseTotal(ScopeKeyPackageSchema, input.package);
776
846
  if (!parsed.success) return { ok: false, reason: "key_package_invalid" };
777
847
  const pkg = parsed.data;
778
- if (pkg.recipient !== input.recipient.memberId || pkg.tenantId !== input.expected.tenantId || pkg.scopeId !== input.expected.scopeId || pkg.epoch !== input.expected.epoch || pkg.sender !== await deriveMemberId(input.senderSigningPublicJwk)) {
848
+ if (pkg.recipient !== input.recipient.memberId || pkg.v === SCOPE_KEY_PACKAGE_V2 && pkg.recipientKemEpoch !== input.recipient.kemEpoch || pkg.tenantId !== input.expected.tenantId || pkg.scopeId !== input.expected.scopeId || pkg.epoch !== input.expected.epoch || pkg.sender !== await deriveMemberId(input.senderSigningPublicJwk)) {
779
849
  return { ok: false, reason: "key_package_invalid" };
780
850
  }
781
851
  if (!await ecdsaVerify(input.senderSigningPublicJwk, pkg.sig, packageSigningInput(pkg))) {
@@ -787,7 +857,7 @@ async function openScopeKeyPackage(input) {
787
857
  recipientPrivateKey: input.recipient.kemPrivateKey,
788
858
  recipientPublicKey: await p256PublicKeyBytes(input.recipient.kemPublicJwk),
789
859
  enc: decodeBase64Url(pkg.enc, "enc"),
790
- info: utf82(SCOPE_KEY_HPKE_INFO),
860
+ info: utf82(pkg.v === SCOPE_KEY_PACKAGE_V2 ? SCOPE_KEY_HPKE_INFO_V2 : SCOPE_KEY_HPKE_INFO),
791
861
  aad: packageAad(pkg),
792
862
  ciphertext: decodeBase64Url(pkg.ciphertext, "ciphertext")
793
863
  });
@@ -1086,85 +1156,6 @@ async function openSealedContent(input) {
1086
1156
  frame.fill(0);
1087
1157
  }
1088
1158
  }
1089
- var MEMBER_KEY_BUNDLE_VERSION = 1;
1090
- var MEMBER_KEM_DOMAIN_PREFIX = "byok-member-kem-v1\n";
1091
- var KemEpochSchema = z.number().int().positive().max(Number.MAX_SAFE_INTEGER);
1092
- var KemSigSchema = base64UrlBytes(64);
1093
- var MemberKeyBundleV1Schema = z.strictObject({
1094
- v: z.literal(MEMBER_KEY_BUNDLE_VERSION),
1095
- memberId: z.string().regex(MEMBER_ID_PATTERN),
1096
- signingPublicJwk: P256PublicJwkSchema,
1097
- kemPublicJwk: P256PublicJwkSchema,
1098
- kemEpoch: KemEpochSchema,
1099
- kemSig: KemSigSchema,
1100
- deviceId: z.string().min(1).optional()
1101
- });
1102
- var CreateInputSchema = z.object({
1103
- signingPublicJwk: P256PublicJwkSchema,
1104
- signingPrivateKey: z.custom(),
1105
- kemPublicJwk: P256PublicJwkSchema,
1106
- kemEpoch: KemEpochSchema,
1107
- deviceId: z.string().min(1).optional()
1108
- });
1109
- var KemSignerSchema = z.object({ memberId: z.string().regex(MEMBER_ID_PATTERN), signingPublicJwk: P256PublicJwkSchema });
1110
- var KemStatementSchema = z.object({ kemPublicJwk: P256PublicJwkSchema, kemEpoch: KemEpochSchema, kemSig: KemSigSchema });
1111
- function memberKemSigningInput(memberId, kemPublicJwk, kemEpoch) {
1112
- return concat2(
1113
- utf82(MEMBER_KEM_DOMAIN_PREFIX),
1114
- canonicalizeJsonBytes({ memberId, kemPublicKey: { x: kemPublicJwk.x, y: kemPublicJwk.y }, kemEpoch })
1115
- );
1116
- }
1117
- async function createMemberKeyBundle(input) {
1118
- const parsed = safeParseTotal(CreateInputSchema, input);
1119
- if (!parsed.success) throw invalidInput("Member key bundle input is invalid");
1120
- const { signingPublicJwk, signingPrivateKey, kemPublicJwk, kemEpoch, deviceId } = parsed.data;
1121
- const memberId = await deriveMemberId(signingPublicJwk);
1122
- if (!await isValidMemberKemKey(kemPublicJwk, signingPublicJwk)) throw invalidInput("Member KEM key is invalid");
1123
- const kemSig = await ecdsaSign(signingPublicJwk, signingPrivateKey, memberKemSigningInput(memberId, kemPublicJwk, kemEpoch));
1124
- return {
1125
- v: MEMBER_KEY_BUNDLE_VERSION,
1126
- memberId,
1127
- signingPublicJwk,
1128
- kemPublicJwk,
1129
- kemEpoch,
1130
- kemSig,
1131
- ...deviceId === void 0 ? {} : { deviceId }
1132
- };
1133
- }
1134
- async function isValidMemberKemKey(kemPublicJwk, signingPublicJwk) {
1135
- if (kemPublicJwk.x === signingPublicJwk.x && kemPublicJwk.y === signingPublicJwk.y) return false;
1136
- try {
1137
- await p256PublicKeyBytes(kemPublicJwk);
1138
- return true;
1139
- } catch {
1140
- return false;
1141
- }
1142
- }
1143
- async function verifyMemberKem(member, kem) {
1144
- const signer = safeParseTotal(KemSignerSchema, member);
1145
- const statement = safeParseTotal(KemStatementSchema, kem);
1146
- if (!signer.success || !statement.success) return false;
1147
- const { memberId, signingPublicJwk } = signer.data;
1148
- const { kemPublicJwk, kemEpoch, kemSig } = statement.data;
1149
- if (!await isValidMemberKemKey(kemPublicJwk, signingPublicJwk)) return false;
1150
- try {
1151
- return await ecdsaVerify(signingPublicJwk, kemSig, memberKemSigningInput(memberId, kemPublicJwk, kemEpoch));
1152
- } catch {
1153
- return false;
1154
- }
1155
- }
1156
- async function parseMemberKeyBundle(value) {
1157
- const parsed = safeParseTotal(MemberKeyBundleV1Schema, value);
1158
- if (!parsed.success) return void 0;
1159
- const bundle = parsed.data;
1160
- try {
1161
- if (await deriveMemberId(bundle.signingPublicJwk) !== bundle.memberId) return void 0;
1162
- } catch {
1163
- return void 0;
1164
- }
1165
- if (!await verifyMemberKem(bundle, bundle)) return void 0;
1166
- return bundle;
1167
- }
1168
1159
  var MEMBER_KEY_RECORD_VERSION = 1;
1169
1160
  var PrivateJwkSchema = z.strictObject({
1170
1161
  kty: z.literal("EC"),