@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
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
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 =
|
|
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:
|
|
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(
|
|
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(
|
|
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"),
|