@motebit/crypto 1.1.0 → 1.2.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/dist/artifacts.d.ts +1 -1
- package/dist/credential-anchor.d.ts.map +1 -1
- package/dist/deletion-certificate.d.ts +256 -0
- package/dist/deletion-certificate.d.ts.map +1 -0
- package/dist/index.d.ts +120 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +878 -13
- package/dist/merkle.d.ts +34 -0
- package/dist/merkle.d.ts.map +1 -0
- package/dist/skills.d.ts +95 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/witness-omission-dispute.d.ts +98 -0
- package/dist/witness-omission-dispute.d.ts.map +1 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3747,6 +3747,135 @@ function isScopeNarrowed(parentScope, childScope) {
|
|
|
3747
3747
|
return true;
|
|
3748
3748
|
}
|
|
3749
3749
|
|
|
3750
|
+
// src/skills.ts
|
|
3751
|
+
var SKILL_SIGNATURE_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
3752
|
+
function signatureWithoutValue(sig) {
|
|
3753
|
+
return { suite: sig.suite, public_key: sig.public_key };
|
|
3754
|
+
}
|
|
3755
|
+
function canonicalizeSkillManifestBytes(manifest, body) {
|
|
3756
|
+
const motebitForCanonical = manifest.motebit.signature ? {
|
|
3757
|
+
...manifest.motebit,
|
|
3758
|
+
signature: signatureWithoutValue(manifest.motebit.signature)
|
|
3759
|
+
} : manifest.motebit;
|
|
3760
|
+
const manifestForCanonical = { ...manifest, motebit: motebitForCanonical };
|
|
3761
|
+
const canonical = canonicalJson(manifestForCanonical);
|
|
3762
|
+
const manifestBytes = new TextEncoder().encode(canonical);
|
|
3763
|
+
const out = new Uint8Array(manifestBytes.length + 1 + body.length);
|
|
3764
|
+
out.set(manifestBytes, 0);
|
|
3765
|
+
out[manifestBytes.length] = 10;
|
|
3766
|
+
out.set(body, manifestBytes.length + 1);
|
|
3767
|
+
return out;
|
|
3768
|
+
}
|
|
3769
|
+
function canonicalizeSkillEnvelopeBytes(envelope) {
|
|
3770
|
+
const envelopeForCanonical = {
|
|
3771
|
+
...envelope,
|
|
3772
|
+
signature: signatureWithoutValue(envelope.signature)
|
|
3773
|
+
};
|
|
3774
|
+
const canonical = canonicalJson(envelopeForCanonical);
|
|
3775
|
+
return new TextEncoder().encode(canonical);
|
|
3776
|
+
}
|
|
3777
|
+
async function signSkillManifest(unsigned, privateKey, publicKey, body) {
|
|
3778
|
+
const publicKeyHex = bytesToLowerHex(publicKey);
|
|
3779
|
+
const unsignedWithSig = {
|
|
3780
|
+
...unsigned,
|
|
3781
|
+
motebit: {
|
|
3782
|
+
...unsigned.motebit,
|
|
3783
|
+
signature: {
|
|
3784
|
+
suite: SKILL_SIGNATURE_SUITE,
|
|
3785
|
+
public_key: publicKeyHex,
|
|
3786
|
+
value: ""
|
|
3787
|
+
// placeholder; stripped by canonicalize
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
};
|
|
3791
|
+
const message = canonicalizeSkillManifestBytes(unsignedWithSig, body);
|
|
3792
|
+
const sig = await signBySuite(SKILL_SIGNATURE_SUITE, message, privateKey);
|
|
3793
|
+
return {
|
|
3794
|
+
...unsigned,
|
|
3795
|
+
motebit: {
|
|
3796
|
+
...unsigned.motebit,
|
|
3797
|
+
signature: {
|
|
3798
|
+
suite: SKILL_SIGNATURE_SUITE,
|
|
3799
|
+
public_key: publicKeyHex,
|
|
3800
|
+
value: toBase64Url(sig)
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
};
|
|
3804
|
+
}
|
|
3805
|
+
async function signSkillEnvelope(unsigned, privateKey, publicKey) {
|
|
3806
|
+
const publicKeyHex = bytesToLowerHex(publicKey);
|
|
3807
|
+
const unsignedWithSig = {
|
|
3808
|
+
...unsigned,
|
|
3809
|
+
signature: {
|
|
3810
|
+
suite: SKILL_SIGNATURE_SUITE,
|
|
3811
|
+
public_key: publicKeyHex,
|
|
3812
|
+
value: ""
|
|
3813
|
+
// placeholder; stripped by canonicalize
|
|
3814
|
+
}
|
|
3815
|
+
};
|
|
3816
|
+
const message = canonicalizeSkillEnvelopeBytes(unsignedWithSig);
|
|
3817
|
+
const sig = await signBySuite(SKILL_SIGNATURE_SUITE, message, privateKey);
|
|
3818
|
+
return {
|
|
3819
|
+
...unsigned,
|
|
3820
|
+
signature: {
|
|
3821
|
+
suite: SKILL_SIGNATURE_SUITE,
|
|
3822
|
+
public_key: publicKeyHex,
|
|
3823
|
+
value: toBase64Url(sig)
|
|
3824
|
+
}
|
|
3825
|
+
};
|
|
3826
|
+
}
|
|
3827
|
+
async function verifySkillManifest(manifest, body, publicKey) {
|
|
3828
|
+
return (await verifySkillManifestDetailed(manifest, body, publicKey)).valid;
|
|
3829
|
+
}
|
|
3830
|
+
async function verifySkillManifestDetailed(manifest, body, publicKey) {
|
|
3831
|
+
const sig = manifest.motebit.signature;
|
|
3832
|
+
if (!sig) return { valid: false, reason: "no_signature" };
|
|
3833
|
+
if (sig.suite !== SKILL_SIGNATURE_SUITE) return { valid: false, reason: "wrong_suite" };
|
|
3834
|
+
const publicKeyHex = bytesToLowerHex(publicKey);
|
|
3835
|
+
if (sig.public_key.toLowerCase() !== publicKeyHex) {
|
|
3836
|
+
return { valid: false, reason: "bad_public_key" };
|
|
3837
|
+
}
|
|
3838
|
+
let sigBytes;
|
|
3839
|
+
try {
|
|
3840
|
+
sigBytes = fromBase64Url(sig.value);
|
|
3841
|
+
} catch {
|
|
3842
|
+
return { valid: false, reason: "bad_signature_value" };
|
|
3843
|
+
}
|
|
3844
|
+
const message = canonicalizeSkillManifestBytes(manifest, body);
|
|
3845
|
+
const valid = await verifyBySuite(SKILL_SIGNATURE_SUITE, message, sigBytes, publicKey);
|
|
3846
|
+
return { valid, reason: valid ? "ok" : "ed25519_mismatch" };
|
|
3847
|
+
}
|
|
3848
|
+
async function verifySkillEnvelope(envelope, publicKey) {
|
|
3849
|
+
return (await verifySkillEnvelopeDetailed(envelope, publicKey)).valid;
|
|
3850
|
+
}
|
|
3851
|
+
async function verifySkillEnvelopeDetailed(envelope, publicKey) {
|
|
3852
|
+
const sig = envelope.signature;
|
|
3853
|
+
if (sig.suite !== SKILL_SIGNATURE_SUITE) return { valid: false, reason: "wrong_suite" };
|
|
3854
|
+
const publicKeyHex = bytesToLowerHex(publicKey);
|
|
3855
|
+
if (sig.public_key.toLowerCase() !== publicKeyHex) {
|
|
3856
|
+
return { valid: false, reason: "bad_public_key" };
|
|
3857
|
+
}
|
|
3858
|
+
let sigBytes;
|
|
3859
|
+
try {
|
|
3860
|
+
sigBytes = fromBase64Url(sig.value);
|
|
3861
|
+
} catch {
|
|
3862
|
+
return { valid: false, reason: "bad_signature_value" };
|
|
3863
|
+
}
|
|
3864
|
+
const message = canonicalizeSkillEnvelopeBytes(envelope);
|
|
3865
|
+
const valid = await verifyBySuite(SKILL_SIGNATURE_SUITE, message, sigBytes, publicKey);
|
|
3866
|
+
return { valid, reason: valid ? "ok" : "ed25519_mismatch" };
|
|
3867
|
+
}
|
|
3868
|
+
function bytesToLowerHex(bytes) {
|
|
3869
|
+
let hex = "";
|
|
3870
|
+
for (const b of bytes) {
|
|
3871
|
+
hex += b.toString(16).padStart(2, "0");
|
|
3872
|
+
}
|
|
3873
|
+
return hex;
|
|
3874
|
+
}
|
|
3875
|
+
function decodeSkillSignaturePublicKey(sig) {
|
|
3876
|
+
return hexToBytes4(sig.public_key);
|
|
3877
|
+
}
|
|
3878
|
+
|
|
3750
3879
|
// src/hardware-attestation.ts
|
|
3751
3880
|
function verifyHardwareAttestationClaim(claim, expectedIdentityPublicKeyHex, verifiers, deviceCheckContext) {
|
|
3752
3881
|
const platform = claim.platform;
|
|
@@ -4934,11 +5063,7 @@ async function createPresentation(credentials, privateKey, publicKey) {
|
|
|
4934
5063
|
return signVerifiablePresentation(unsignedVP, privateKey, publicKey);
|
|
4935
5064
|
}
|
|
4936
5065
|
|
|
4937
|
-
// src/
|
|
4938
|
-
var CREDENTIAL_ANCHOR_SUITE = "motebit-jcs-ed25519-hex-v1";
|
|
4939
|
-
function toHex(bytes) {
|
|
4940
|
-
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
4941
|
-
}
|
|
5066
|
+
// src/merkle.ts
|
|
4942
5067
|
function fromHex(hex) {
|
|
4943
5068
|
const bytes = new Uint8Array(hex.length / 2);
|
|
4944
5069
|
for (let i = 0; i < hex.length; i += 2) {
|
|
@@ -4946,19 +5071,22 @@ function fromHex(hex) {
|
|
|
4946
5071
|
}
|
|
4947
5072
|
return bytes;
|
|
4948
5073
|
}
|
|
5074
|
+
function toHex(bytes) {
|
|
5075
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
5076
|
+
}
|
|
4949
5077
|
function concat(a, b) {
|
|
4950
5078
|
const out = new Uint8Array(a.length + b.length);
|
|
4951
5079
|
out.set(a);
|
|
4952
5080
|
out.set(b, a.length);
|
|
4953
5081
|
return out;
|
|
4954
5082
|
}
|
|
4955
|
-
async function computeCredentialLeaf(credential) {
|
|
4956
|
-
const canonical = canonicalJson(credential);
|
|
4957
|
-
const hash2 = await sha2563(new TextEncoder().encode(canonical));
|
|
4958
|
-
return toHex(hash2);
|
|
4959
|
-
}
|
|
4960
5083
|
async function verifyMerkleInclusion(leaf, index, siblings, layerSizes, expectedRoot) {
|
|
4961
|
-
let current
|
|
5084
|
+
let current;
|
|
5085
|
+
try {
|
|
5086
|
+
current = fromHex(leaf);
|
|
5087
|
+
} catch {
|
|
5088
|
+
return false;
|
|
5089
|
+
}
|
|
4962
5090
|
let idx = index;
|
|
4963
5091
|
let sibIdx = 0;
|
|
4964
5092
|
for (const layerSize of layerSizes) {
|
|
@@ -4966,7 +5094,12 @@ async function verifyMerkleInclusion(leaf, index, siblings, layerSizes, expected
|
|
|
4966
5094
|
const hasSibling = siblingPos >= 0 && siblingPos < layerSize;
|
|
4967
5095
|
if (hasSibling) {
|
|
4968
5096
|
if (sibIdx >= siblings.length) return false;
|
|
4969
|
-
|
|
5097
|
+
let siblingBytes;
|
|
5098
|
+
try {
|
|
5099
|
+
siblingBytes = fromHex(siblings[sibIdx]);
|
|
5100
|
+
} catch {
|
|
5101
|
+
return false;
|
|
5102
|
+
}
|
|
4970
5103
|
const combined = idx % 2 === 0 ? concat(current, siblingBytes) : concat(siblingBytes, current);
|
|
4971
5104
|
current = await sha2563(combined);
|
|
4972
5105
|
sibIdx++;
|
|
@@ -4975,6 +5108,14 @@ async function verifyMerkleInclusion(leaf, index, siblings, layerSizes, expected
|
|
|
4975
5108
|
}
|
|
4976
5109
|
return toHex(current) === expectedRoot;
|
|
4977
5110
|
}
|
|
5111
|
+
|
|
5112
|
+
// src/credential-anchor.ts
|
|
5113
|
+
var CREDENTIAL_ANCHOR_SUITE = "motebit-jcs-ed25519-hex-v1";
|
|
5114
|
+
async function computeCredentialLeaf(credential) {
|
|
5115
|
+
const canonical = canonicalJson(credential);
|
|
5116
|
+
const hash2 = await sha2563(new TextEncoder().encode(canonical));
|
|
5117
|
+
return bytesToHex3(hash2);
|
|
5118
|
+
}
|
|
4978
5119
|
async function verifyCredentialAnchor(credential, anchorProof, chainVerifier) {
|
|
4979
5120
|
const errors = [];
|
|
4980
5121
|
const computedHash = await computeCredentialLeaf(credential);
|
|
@@ -5116,6 +5257,537 @@ async function verifyRevocationAnchor(proof, revocationPayload, chainVerifier) {
|
|
|
5116
5257
|
};
|
|
5117
5258
|
}
|
|
5118
5259
|
|
|
5260
|
+
// src/deletion-certificate.ts
|
|
5261
|
+
var DELETION_CERTIFICATE_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
5262
|
+
var WITNESS_OMISSION_DISPUTE_WINDOW_MS = 24 * 60 * 60 * 1e3;
|
|
5263
|
+
var EMPTY_FEDERATION_GRAPH_ANCHOR_ROOT = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
|
|
5264
|
+
var REASON_TABLE = Object.freeze({
|
|
5265
|
+
user_request: {
|
|
5266
|
+
required: "subject",
|
|
5267
|
+
optional: ["operator"],
|
|
5268
|
+
forbidden: [],
|
|
5269
|
+
modes: ["sovereign", "mediated", "enterprise"]
|
|
5270
|
+
},
|
|
5271
|
+
retention_enforcement: {
|
|
5272
|
+
required: "operator",
|
|
5273
|
+
optional: ["subject"],
|
|
5274
|
+
forbidden: [],
|
|
5275
|
+
modes: ["mediated", "enterprise"]
|
|
5276
|
+
},
|
|
5277
|
+
retention_enforcement_post_classification: {
|
|
5278
|
+
required: "operator",
|
|
5279
|
+
optional: ["subject"],
|
|
5280
|
+
forbidden: [],
|
|
5281
|
+
modes: ["mediated", "enterprise"]
|
|
5282
|
+
},
|
|
5283
|
+
operator_request: {
|
|
5284
|
+
required: "operator",
|
|
5285
|
+
optional: [],
|
|
5286
|
+
forbidden: ["subject"],
|
|
5287
|
+
modes: ["mediated", "enterprise"]
|
|
5288
|
+
},
|
|
5289
|
+
delegated_request: {
|
|
5290
|
+
required: "delegate",
|
|
5291
|
+
optional: ["operator"],
|
|
5292
|
+
forbidden: [],
|
|
5293
|
+
modes: ["mediated", "enterprise"]
|
|
5294
|
+
},
|
|
5295
|
+
self_enforcement: {
|
|
5296
|
+
required: "subject",
|
|
5297
|
+
optional: [],
|
|
5298
|
+
forbidden: ["operator"],
|
|
5299
|
+
// Admitted in every mode: the subject's runtime can drive its own
|
|
5300
|
+
// retention policy whether or not an operator exists. The
|
|
5301
|
+
// distinction from `retention_enforcement` is who signs — subject
|
|
5302
|
+
// for self_enforcement, operator for retention_enforcement.
|
|
5303
|
+
modes: ["sovereign", "mediated", "enterprise"]
|
|
5304
|
+
},
|
|
5305
|
+
guardian_request: {
|
|
5306
|
+
required: "guardian",
|
|
5307
|
+
optional: ["operator"],
|
|
5308
|
+
forbidden: [],
|
|
5309
|
+
modes: ["enterprise"]
|
|
5310
|
+
}
|
|
5311
|
+
});
|
|
5312
|
+
function canonicalizeMultiSignatureCert(cert) {
|
|
5313
|
+
const { subject_signature, operator_signature, delegate_signature, guardian_signature, ...body } = cert;
|
|
5314
|
+
void subject_signature;
|
|
5315
|
+
void operator_signature;
|
|
5316
|
+
void delegate_signature;
|
|
5317
|
+
void guardian_signature;
|
|
5318
|
+
return new TextEncoder().encode(canonicalJson(body));
|
|
5319
|
+
}
|
|
5320
|
+
function canonicalizeHorizonCert(cert) {
|
|
5321
|
+
const { signature, ...body } = cert;
|
|
5322
|
+
void signature;
|
|
5323
|
+
return new TextEncoder().encode(canonicalJson(body));
|
|
5324
|
+
}
|
|
5325
|
+
function canonicalizeHorizonCertForWitness(cert) {
|
|
5326
|
+
const { signature, witnessed_by, ...body } = cert;
|
|
5327
|
+
void signature;
|
|
5328
|
+
void witnessed_by;
|
|
5329
|
+
return new TextEncoder().encode(canonicalJson(body));
|
|
5330
|
+
}
|
|
5331
|
+
async function signCertAsSubject(cert, motebitId, privateKey) {
|
|
5332
|
+
const bytes = canonicalizeMultiSignatureCert(cert);
|
|
5333
|
+
const sig = await signBySuite(DELETION_CERTIFICATE_SUITE, bytes, privateKey);
|
|
5334
|
+
return {
|
|
5335
|
+
...cert,
|
|
5336
|
+
subject_signature: {
|
|
5337
|
+
motebit_id: motebitId,
|
|
5338
|
+
suite: DELETION_CERTIFICATE_SUITE,
|
|
5339
|
+
signature: toBase64Url(sig)
|
|
5340
|
+
}
|
|
5341
|
+
};
|
|
5342
|
+
}
|
|
5343
|
+
async function signCertAsOperator(cert, operatorId, privateKey) {
|
|
5344
|
+
const bytes = canonicalizeMultiSignatureCert(cert);
|
|
5345
|
+
const sig = await signBySuite(DELETION_CERTIFICATE_SUITE, bytes, privateKey);
|
|
5346
|
+
return {
|
|
5347
|
+
...cert,
|
|
5348
|
+
operator_signature: {
|
|
5349
|
+
operator_id: operatorId,
|
|
5350
|
+
suite: DELETION_CERTIFICATE_SUITE,
|
|
5351
|
+
signature: toBase64Url(sig)
|
|
5352
|
+
}
|
|
5353
|
+
};
|
|
5354
|
+
}
|
|
5355
|
+
async function signCertAsDelegate(cert, delegateMotebitId, delegationReceiptId, privateKey) {
|
|
5356
|
+
const bytes = canonicalizeMultiSignatureCert(cert);
|
|
5357
|
+
const sig = await signBySuite(DELETION_CERTIFICATE_SUITE, bytes, privateKey);
|
|
5358
|
+
return {
|
|
5359
|
+
...cert,
|
|
5360
|
+
delegate_signature: {
|
|
5361
|
+
motebit_id: delegateMotebitId,
|
|
5362
|
+
delegation_receipt_id: delegationReceiptId,
|
|
5363
|
+
suite: DELETION_CERTIFICATE_SUITE,
|
|
5364
|
+
signature: toBase64Url(sig)
|
|
5365
|
+
}
|
|
5366
|
+
};
|
|
5367
|
+
}
|
|
5368
|
+
async function signCertAsGuardian(cert, guardianPublicKey, privateKey) {
|
|
5369
|
+
const bytes = canonicalizeMultiSignatureCert(cert);
|
|
5370
|
+
const sig = await signBySuite(DELETION_CERTIFICATE_SUITE, bytes, privateKey);
|
|
5371
|
+
return {
|
|
5372
|
+
...cert,
|
|
5373
|
+
guardian_signature: {
|
|
5374
|
+
guardian_public_key: bytesToHex3(guardianPublicKey),
|
|
5375
|
+
suite: DELETION_CERTIFICATE_SUITE,
|
|
5376
|
+
signature: toBase64Url(sig)
|
|
5377
|
+
}
|
|
5378
|
+
};
|
|
5379
|
+
}
|
|
5380
|
+
async function signHorizonCertAsIssuer(cert, privateKey) {
|
|
5381
|
+
const withSuite = { ...cert, suite: DELETION_CERTIFICATE_SUITE, signature: "" };
|
|
5382
|
+
const bytes = canonicalizeHorizonCert(withSuite);
|
|
5383
|
+
const sig = await signBySuite(DELETION_CERTIFICATE_SUITE, bytes, privateKey);
|
|
5384
|
+
return { ...withSuite, signature: toBase64Url(sig) };
|
|
5385
|
+
}
|
|
5386
|
+
async function signHorizonWitness(cert, witnessMotebitId, privateKey, inclusionProof) {
|
|
5387
|
+
const bytes = canonicalizeHorizonCertForWitness(cert);
|
|
5388
|
+
const sig = await signBySuite(DELETION_CERTIFICATE_SUITE, bytes, privateKey);
|
|
5389
|
+
const witness = {
|
|
5390
|
+
motebit_id: witnessMotebitId,
|
|
5391
|
+
signature: toBase64Url(sig),
|
|
5392
|
+
...inclusionProof !== void 0 ? { inclusion_proof: inclusionProof } : {}
|
|
5393
|
+
};
|
|
5394
|
+
return witness;
|
|
5395
|
+
}
|
|
5396
|
+
function canonicalizeHorizonWitnessRequestBody(body) {
|
|
5397
|
+
return canonicalizeHorizonCertForWitness({
|
|
5398
|
+
...body,
|
|
5399
|
+
witnessed_by: [],
|
|
5400
|
+
signature: ""
|
|
5401
|
+
});
|
|
5402
|
+
}
|
|
5403
|
+
async function signHorizonWitnessRequestBody(body, privateKey) {
|
|
5404
|
+
const bytes = canonicalizeHorizonWitnessRequestBody(body);
|
|
5405
|
+
const sig = await signBySuite(DELETION_CERTIFICATE_SUITE, bytes, privateKey);
|
|
5406
|
+
return toBase64Url(sig);
|
|
5407
|
+
}
|
|
5408
|
+
async function verifyHorizonWitnessRequestSignature(body, signatureBase64Url, issuerPublicKey) {
|
|
5409
|
+
let sigBytes;
|
|
5410
|
+
try {
|
|
5411
|
+
sigBytes = fromBase64Url(signatureBase64Url);
|
|
5412
|
+
} catch {
|
|
5413
|
+
return false;
|
|
5414
|
+
}
|
|
5415
|
+
if (sigBytes.length === 0) return false;
|
|
5416
|
+
const bytes = canonicalizeHorizonWitnessRequestBody(body);
|
|
5417
|
+
return verifyBySuite(DELETION_CERTIFICATE_SUITE, bytes, sigBytes, issuerPublicKey);
|
|
5418
|
+
}
|
|
5419
|
+
async function verifyDeletionCertificate(cert, ctx) {
|
|
5420
|
+
switch (cert.kind) {
|
|
5421
|
+
case "mutable_pruning":
|
|
5422
|
+
case "consolidation_flush":
|
|
5423
|
+
return verifyMultiSignatureCert(cert, ctx);
|
|
5424
|
+
case "append_only_horizon":
|
|
5425
|
+
return verifyHorizonCert(cert, ctx);
|
|
5426
|
+
}
|
|
5427
|
+
}
|
|
5428
|
+
async function verifyMultiSignatureCert(cert, ctx) {
|
|
5429
|
+
const errors = [];
|
|
5430
|
+
const rule = REASON_TABLE[cert.reason];
|
|
5431
|
+
if (rule === void 0) {
|
|
5432
|
+
return failResult(`unknown reason: ${cert.reason}`);
|
|
5433
|
+
}
|
|
5434
|
+
if (ctx.deploymentMode !== void 0 && !rule.modes.includes(ctx.deploymentMode)) {
|
|
5435
|
+
errors.push(`reason "${cert.reason}" not admitted in deployment mode "${ctx.deploymentMode}"`);
|
|
5436
|
+
}
|
|
5437
|
+
const present = {
|
|
5438
|
+
subject: cert.subject_signature !== void 0,
|
|
5439
|
+
operator: cert.operator_signature !== void 0,
|
|
5440
|
+
delegate: cert.delegate_signature !== void 0,
|
|
5441
|
+
guardian: cert.guardian_signature !== void 0
|
|
5442
|
+
};
|
|
5443
|
+
if (!present[rule.required]) {
|
|
5444
|
+
errors.push(`reason "${cert.reason}" requires ${rule.required}_signature, not present`);
|
|
5445
|
+
}
|
|
5446
|
+
for (const f of rule.forbidden) {
|
|
5447
|
+
if (present[f]) {
|
|
5448
|
+
errors.push(`reason "${cert.reason}" forbids ${f}_signature, present`);
|
|
5449
|
+
}
|
|
5450
|
+
}
|
|
5451
|
+
const bytes = canonicalizeMultiSignatureCert(cert);
|
|
5452
|
+
let subjectValid = null;
|
|
5453
|
+
if (cert.subject_signature !== void 0) {
|
|
5454
|
+
subjectValid = await verifyOneSignature(
|
|
5455
|
+
bytes,
|
|
5456
|
+
cert.subject_signature.signature,
|
|
5457
|
+
cert.subject_signature.suite,
|
|
5458
|
+
await ctx.resolveMotebitPublicKey(cert.subject_signature.motebit_id)
|
|
5459
|
+
);
|
|
5460
|
+
if (!subjectValid) errors.push("subject_signature invalid");
|
|
5461
|
+
}
|
|
5462
|
+
let operatorValid = null;
|
|
5463
|
+
if (cert.operator_signature !== void 0) {
|
|
5464
|
+
operatorValid = await verifyOneSignature(
|
|
5465
|
+
bytes,
|
|
5466
|
+
cert.operator_signature.signature,
|
|
5467
|
+
cert.operator_signature.suite,
|
|
5468
|
+
await ctx.resolveOperatorPublicKey(cert.operator_signature.operator_id)
|
|
5469
|
+
);
|
|
5470
|
+
if (!operatorValid) errors.push("operator_signature invalid");
|
|
5471
|
+
}
|
|
5472
|
+
let delegateValid = null;
|
|
5473
|
+
if (cert.delegate_signature !== void 0) {
|
|
5474
|
+
delegateValid = await verifyOneSignature(
|
|
5475
|
+
bytes,
|
|
5476
|
+
cert.delegate_signature.signature,
|
|
5477
|
+
cert.delegate_signature.suite,
|
|
5478
|
+
await ctx.resolveMotebitPublicKey(cert.delegate_signature.motebit_id)
|
|
5479
|
+
);
|
|
5480
|
+
if (!delegateValid) errors.push("delegate_signature invalid");
|
|
5481
|
+
}
|
|
5482
|
+
let guardianValid = null;
|
|
5483
|
+
if (cert.guardian_signature !== void 0) {
|
|
5484
|
+
let guardianKey;
|
|
5485
|
+
try {
|
|
5486
|
+
guardianKey = hexToBytes4(cert.guardian_signature.guardian_public_key);
|
|
5487
|
+
} catch {
|
|
5488
|
+
guardianKey = null;
|
|
5489
|
+
errors.push("guardian_public_key not valid hex");
|
|
5490
|
+
}
|
|
5491
|
+
if (ctx.validateGuardianBinding !== void 0) {
|
|
5492
|
+
const subjectMotebitId = cert.subject_signature !== void 0 ? cert.subject_signature.motebit_id : void 0;
|
|
5493
|
+
const ok = await ctx.validateGuardianBinding(
|
|
5494
|
+
subjectMotebitId,
|
|
5495
|
+
cert.guardian_signature.guardian_public_key
|
|
5496
|
+
);
|
|
5497
|
+
if (!ok) errors.push("guardian_public_key not bound to subject motebit");
|
|
5498
|
+
}
|
|
5499
|
+
guardianValid = await verifyOneSignature(
|
|
5500
|
+
bytes,
|
|
5501
|
+
cert.guardian_signature.signature,
|
|
5502
|
+
cert.guardian_signature.suite,
|
|
5503
|
+
guardianKey
|
|
5504
|
+
);
|
|
5505
|
+
if (!guardianValid) errors.push("guardian_signature invalid");
|
|
5506
|
+
}
|
|
5507
|
+
return {
|
|
5508
|
+
valid: errors.length === 0,
|
|
5509
|
+
errors,
|
|
5510
|
+
steps: {
|
|
5511
|
+
reason_table_satisfied: errors.length === 0,
|
|
5512
|
+
subject_signature_valid: subjectValid,
|
|
5513
|
+
operator_signature_valid: operatorValid,
|
|
5514
|
+
delegate_signature_valid: delegateValid,
|
|
5515
|
+
guardian_signature_valid: guardianValid,
|
|
5516
|
+
horizon_issuer_signature_valid: null,
|
|
5517
|
+
horizon_witnesses_valid_count: null,
|
|
5518
|
+
horizon_witnesses_present_count: null
|
|
5519
|
+
}
|
|
5520
|
+
};
|
|
5521
|
+
}
|
|
5522
|
+
async function verifyHorizonCert(cert, ctx) {
|
|
5523
|
+
const errors = [];
|
|
5524
|
+
const anchor = cert.federation_graph_anchor;
|
|
5525
|
+
if (anchor !== void 0) {
|
|
5526
|
+
if (anchor.leaf_count === 0 && anchor.merkle_root !== EMPTY_FEDERATION_GRAPH_ANCHOR_ROOT) {
|
|
5527
|
+
errors.push("federation_graph_anchor.leaf_count=0 requires the empty-tree merkle_root");
|
|
5528
|
+
}
|
|
5529
|
+
if (anchor.leaf_count < 0 || !Number.isInteger(anchor.leaf_count)) {
|
|
5530
|
+
errors.push("federation_graph_anchor.leaf_count must be a non-negative integer");
|
|
5531
|
+
}
|
|
5532
|
+
}
|
|
5533
|
+
const issuerBytes = canonicalizeHorizonCert(cert);
|
|
5534
|
+
const witnessBytes = canonicalizeHorizonCertForWitness(cert);
|
|
5535
|
+
const issuerKey = cert.subject.kind === "motebit" ? await ctx.resolveMotebitPublicKey(cert.subject.motebit_id) : await ctx.resolveOperatorPublicKey(cert.subject.operator_id);
|
|
5536
|
+
const issuerValid = await verifyOneSignature(issuerBytes, cert.signature, cert.suite, issuerKey);
|
|
5537
|
+
if (!issuerValid) errors.push("horizon issuer signature invalid");
|
|
5538
|
+
let witnessesValid = 0;
|
|
5539
|
+
for (const w of cert.witnessed_by) {
|
|
5540
|
+
const key = await ctx.resolveMotebitPublicKey(w.motebit_id);
|
|
5541
|
+
const ok = await verifyOneSignature(witnessBytes, w.signature, cert.suite, key);
|
|
5542
|
+
if (ok) witnessesValid++;
|
|
5543
|
+
else errors.push(`witness ${w.motebit_id} signature invalid`);
|
|
5544
|
+
}
|
|
5545
|
+
return {
|
|
5546
|
+
valid: errors.length === 0,
|
|
5547
|
+
errors,
|
|
5548
|
+
steps: {
|
|
5549
|
+
reason_table_satisfied: true,
|
|
5550
|
+
subject_signature_valid: null,
|
|
5551
|
+
operator_signature_valid: null,
|
|
5552
|
+
delegate_signature_valid: null,
|
|
5553
|
+
guardian_signature_valid: null,
|
|
5554
|
+
horizon_issuer_signature_valid: issuerValid,
|
|
5555
|
+
horizon_witnesses_valid_count: witnessesValid,
|
|
5556
|
+
horizon_witnesses_present_count: cert.witnessed_by.length
|
|
5557
|
+
}
|
|
5558
|
+
};
|
|
5559
|
+
}
|
|
5560
|
+
async function verifyOneSignature(canonicalBytes, signatureBase64Url, suite, publicKey) {
|
|
5561
|
+
if (publicKey === null) return false;
|
|
5562
|
+
let sigBytes;
|
|
5563
|
+
try {
|
|
5564
|
+
sigBytes = fromBase64Url(signatureBase64Url);
|
|
5565
|
+
} catch {
|
|
5566
|
+
return false;
|
|
5567
|
+
}
|
|
5568
|
+
return verifyBySuite(suite, canonicalBytes, sigBytes, publicKey);
|
|
5569
|
+
}
|
|
5570
|
+
function failResult(message) {
|
|
5571
|
+
return {
|
|
5572
|
+
valid: false,
|
|
5573
|
+
errors: [message],
|
|
5574
|
+
steps: {
|
|
5575
|
+
reason_table_satisfied: false,
|
|
5576
|
+
subject_signature_valid: null,
|
|
5577
|
+
operator_signature_valid: null,
|
|
5578
|
+
delegate_signature_valid: null,
|
|
5579
|
+
guardian_signature_valid: null,
|
|
5580
|
+
horizon_issuer_signature_valid: null,
|
|
5581
|
+
horizon_witnesses_valid_count: null,
|
|
5582
|
+
horizon_witnesses_present_count: null
|
|
5583
|
+
}
|
|
5584
|
+
};
|
|
5585
|
+
}
|
|
5586
|
+
async function verifyRetentionManifest(manifest, operatorPublicKey) {
|
|
5587
|
+
const errors = [];
|
|
5588
|
+
if (manifest.spec !== "motebit/retention-manifest@1") {
|
|
5589
|
+
errors.push(`unexpected spec: ${String(manifest.spec)}`);
|
|
5590
|
+
}
|
|
5591
|
+
if (manifest.suite !== "motebit-jcs-ed25519-hex-v1") {
|
|
5592
|
+
errors.push(`unexpected suite: ${manifest.suite}`);
|
|
5593
|
+
}
|
|
5594
|
+
if (errors.length > 0) {
|
|
5595
|
+
return { valid: false, errors, manifest: null };
|
|
5596
|
+
}
|
|
5597
|
+
const { signature, ...body } = manifest;
|
|
5598
|
+
const canonical = canonicalJson(body);
|
|
5599
|
+
const canonicalBytes = new TextEncoder().encode(canonical);
|
|
5600
|
+
let signatureBytes;
|
|
5601
|
+
try {
|
|
5602
|
+
if (signature.length !== 128 || !/^[0-9a-f]+$/i.test(signature)) {
|
|
5603
|
+
errors.push("signature is not 128-char hex");
|
|
5604
|
+
return { valid: false, errors, manifest: null };
|
|
5605
|
+
}
|
|
5606
|
+
const out = new Uint8Array(64);
|
|
5607
|
+
for (let i = 0; i < 64; i++) {
|
|
5608
|
+
out[i] = parseInt(signature.slice(i * 2, i * 2 + 2), 16);
|
|
5609
|
+
}
|
|
5610
|
+
signatureBytes = out;
|
|
5611
|
+
} catch {
|
|
5612
|
+
errors.push("signature decode failed");
|
|
5613
|
+
return { valid: false, errors, manifest: null };
|
|
5614
|
+
}
|
|
5615
|
+
const ok = await verifyBySuite(
|
|
5616
|
+
"motebit-jcs-ed25519-hex-v1",
|
|
5617
|
+
canonicalBytes,
|
|
5618
|
+
signatureBytes,
|
|
5619
|
+
operatorPublicKey
|
|
5620
|
+
);
|
|
5621
|
+
if (!ok) {
|
|
5622
|
+
errors.push("manifest signature does not verify against operator_public_key");
|
|
5623
|
+
return { valid: false, errors, manifest: null };
|
|
5624
|
+
}
|
|
5625
|
+
return { valid: true, errors: [], manifest };
|
|
5626
|
+
}
|
|
5627
|
+
|
|
5628
|
+
// src/witness-omission-dispute.ts
|
|
5629
|
+
var WITNESS_OMISSION_DISPUTE_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
5630
|
+
var FEDERATION_HEARTBEAT_SUITE = "motebit-concat-ed25519-hex-v1";
|
|
5631
|
+
var HEARTBEAT_FRESHNESS_WINDOW_MS = 5 * 60 * 1e3;
|
|
5632
|
+
function canonicalizeWitnessOmissionDispute(dispute) {
|
|
5633
|
+
const { signature, ...body } = dispute;
|
|
5634
|
+
void signature;
|
|
5635
|
+
return new TextEncoder().encode(canonicalJson(body));
|
|
5636
|
+
}
|
|
5637
|
+
async function signWitnessOmissionDispute(body, privateKey) {
|
|
5638
|
+
const withSuite = {
|
|
5639
|
+
...body,
|
|
5640
|
+
suite: WITNESS_OMISSION_DISPUTE_SUITE,
|
|
5641
|
+
signature: ""
|
|
5642
|
+
};
|
|
5643
|
+
const bytes = canonicalizeWitnessOmissionDispute(withSuite);
|
|
5644
|
+
const sig = await signBySuite(WITNESS_OMISSION_DISPUTE_SUITE, bytes, privateKey);
|
|
5645
|
+
return { ...withSuite, signature: toBase64Url(sig) };
|
|
5646
|
+
}
|
|
5647
|
+
async function verifyWitnessOmissionDispute(dispute, ctx) {
|
|
5648
|
+
const errors = [];
|
|
5649
|
+
const { cert } = ctx;
|
|
5650
|
+
const windowEnd = cert.issued_at + WITNESS_OMISSION_DISPUTE_WINDOW_MS;
|
|
5651
|
+
const wallClockOpen = ctx.now <= windowEnd;
|
|
5652
|
+
const filedAtInRange = dispute.filed_at >= cert.issued_at && dispute.filed_at <= windowEnd;
|
|
5653
|
+
const windowOpen = wallClockOpen && filedAtInRange;
|
|
5654
|
+
if (!wallClockOpen) {
|
|
5655
|
+
errors.push(
|
|
5656
|
+
`dispute window expired: now (${ctx.now}) > cert.issued_at + ${WITNESS_OMISSION_DISPUTE_WINDOW_MS}ms`
|
|
5657
|
+
);
|
|
5658
|
+
}
|
|
5659
|
+
if (!filedAtInRange) {
|
|
5660
|
+
errors.push(
|
|
5661
|
+
"dispute.filed_at outside [cert.issued_at, cert.issued_at + WINDOW] \u2014 disputant-attested clock cannot widen window"
|
|
5662
|
+
);
|
|
5663
|
+
}
|
|
5664
|
+
let certBindingValid = true;
|
|
5665
|
+
if (dispute.cert_signature !== cert.signature) {
|
|
5666
|
+
errors.push("dispute.cert_signature does not match cert.signature");
|
|
5667
|
+
certBindingValid = false;
|
|
5668
|
+
}
|
|
5669
|
+
const certSubjectId = cert.subject.kind === "motebit" ? cert.subject.motebit_id : cert.subject.operator_id;
|
|
5670
|
+
if (dispute.cert_issuer !== certSubjectId) {
|
|
5671
|
+
errors.push(
|
|
5672
|
+
`dispute.cert_issuer (${dispute.cert_issuer}) does not match cert subject (${certSubjectId})`
|
|
5673
|
+
);
|
|
5674
|
+
certBindingValid = false;
|
|
5675
|
+
}
|
|
5676
|
+
let disputantSignatureValid = false;
|
|
5677
|
+
if (ctx.disputantPublicKey === null) {
|
|
5678
|
+
errors.push("disputant public key not resolvable");
|
|
5679
|
+
} else if (dispute.suite !== WITNESS_OMISSION_DISPUTE_SUITE) {
|
|
5680
|
+
errors.push(`unexpected suite: ${String(dispute.suite)}`);
|
|
5681
|
+
} else {
|
|
5682
|
+
const bytes = canonicalizeWitnessOmissionDispute(dispute);
|
|
5683
|
+
let sigBytes;
|
|
5684
|
+
try {
|
|
5685
|
+
sigBytes = fromBase64Url(dispute.signature);
|
|
5686
|
+
} catch {
|
|
5687
|
+
sigBytes = new Uint8Array(0);
|
|
5688
|
+
}
|
|
5689
|
+
if (sigBytes.length === 0) {
|
|
5690
|
+
errors.push("dispute.signature decode failed");
|
|
5691
|
+
} else {
|
|
5692
|
+
disputantSignatureValid = await verifyBySuite(
|
|
5693
|
+
dispute.suite,
|
|
5694
|
+
bytes,
|
|
5695
|
+
sigBytes,
|
|
5696
|
+
ctx.disputantPublicKey
|
|
5697
|
+
);
|
|
5698
|
+
if (!disputantSignatureValid) errors.push("dispute.signature does not verify");
|
|
5699
|
+
}
|
|
5700
|
+
}
|
|
5701
|
+
if (!windowOpen || !certBindingValid || !disputantSignatureValid) {
|
|
5702
|
+
return {
|
|
5703
|
+
valid: false,
|
|
5704
|
+
errors,
|
|
5705
|
+
steps: {
|
|
5706
|
+
window_open: windowOpen,
|
|
5707
|
+
cert_binding_valid: certBindingValid,
|
|
5708
|
+
disputant_signature_valid: disputantSignatureValid,
|
|
5709
|
+
evidence_valid: null
|
|
5710
|
+
}
|
|
5711
|
+
};
|
|
5712
|
+
}
|
|
5713
|
+
const evidenceValid = await verifyEvidence(dispute.evidence, cert, ctx, errors);
|
|
5714
|
+
return {
|
|
5715
|
+
valid: errors.length === 0,
|
|
5716
|
+
errors,
|
|
5717
|
+
steps: {
|
|
5718
|
+
window_open: windowOpen,
|
|
5719
|
+
cert_binding_valid: certBindingValid,
|
|
5720
|
+
disputant_signature_valid: disputantSignatureValid,
|
|
5721
|
+
evidence_valid: evidenceValid
|
|
5722
|
+
}
|
|
5723
|
+
};
|
|
5724
|
+
}
|
|
5725
|
+
async function verifyEvidence(evidence, cert, ctx, errors) {
|
|
5726
|
+
if (evidence.kind === "inclusion_proof") {
|
|
5727
|
+
const anchor = cert.federation_graph_anchor;
|
|
5728
|
+
if (anchor === void 0) {
|
|
5729
|
+
errors.push("inclusion_proof evidence requires cert.federation_graph_anchor \u2014 none present");
|
|
5730
|
+
return false;
|
|
5731
|
+
}
|
|
5732
|
+
if (anchor.leaf_count === 0) {
|
|
5733
|
+
errors.push(
|
|
5734
|
+
"inclusion_proof evidence rejected: cert is self-witnessed (anchor.leaf_count=0)"
|
|
5735
|
+
);
|
|
5736
|
+
return false;
|
|
5737
|
+
}
|
|
5738
|
+
const ok = await verifyMerkleInclusion(
|
|
5739
|
+
evidence.leaf_hash,
|
|
5740
|
+
evidence.proof.leaf_index,
|
|
5741
|
+
evidence.proof.siblings,
|
|
5742
|
+
evidence.proof.layer_sizes,
|
|
5743
|
+
anchor.merkle_root
|
|
5744
|
+
);
|
|
5745
|
+
if (!ok) errors.push("inclusion proof does not reconstruct to anchor.merkle_root");
|
|
5746
|
+
return ok;
|
|
5747
|
+
}
|
|
5748
|
+
return verifyAlternativePeeringArtifact(evidence.peering_artifact, cert, ctx, errors);
|
|
5749
|
+
}
|
|
5750
|
+
async function verifyAlternativePeeringArtifact(artifact, cert, ctx, errors) {
|
|
5751
|
+
const relayId = artifact["relay_id"];
|
|
5752
|
+
const timestamp = artifact["timestamp"];
|
|
5753
|
+
const signatureHex = artifact["signature"];
|
|
5754
|
+
if (typeof relayId !== "string" || typeof timestamp !== "number" || typeof signatureHex !== "string") {
|
|
5755
|
+
errors.push(
|
|
5756
|
+
"alternative_peering artifact unrecognized \u2014 expected federation Heartbeat shape (relay_id, timestamp, signature)"
|
|
5757
|
+
);
|
|
5758
|
+
return false;
|
|
5759
|
+
}
|
|
5760
|
+
const certSubjectId = cert.subject.kind === "motebit" ? cert.subject.motebit_id : cert.subject.operator_id;
|
|
5761
|
+
if (relayId !== certSubjectId) {
|
|
5762
|
+
errors.push(
|
|
5763
|
+
`peering artifact relay_id (${relayId}) does not match cert issuer (${certSubjectId})`
|
|
5764
|
+
);
|
|
5765
|
+
return false;
|
|
5766
|
+
}
|
|
5767
|
+
if (Math.abs(timestamp - cert.horizon_ts) > HEARTBEAT_FRESHNESS_WINDOW_MS) {
|
|
5768
|
+
errors.push(
|
|
5769
|
+
`peering artifact timestamp (${timestamp}) outside \xB1${HEARTBEAT_FRESHNESS_WINDOW_MS}ms of cert.horizon_ts (${cert.horizon_ts})`
|
|
5770
|
+
);
|
|
5771
|
+
return false;
|
|
5772
|
+
}
|
|
5773
|
+
let sigBytes;
|
|
5774
|
+
try {
|
|
5775
|
+
sigBytes = hexToBytes4(signatureHex);
|
|
5776
|
+
} catch {
|
|
5777
|
+
errors.push("peering artifact signature is not valid hex");
|
|
5778
|
+
return false;
|
|
5779
|
+
}
|
|
5780
|
+
const payload = new TextEncoder().encode(`${relayId}|${timestamp}|${FEDERATION_HEARTBEAT_SUITE}`);
|
|
5781
|
+
const ok = await verifyBySuite(
|
|
5782
|
+
FEDERATION_HEARTBEAT_SUITE,
|
|
5783
|
+
payload,
|
|
5784
|
+
sigBytes,
|
|
5785
|
+
ctx.issuerPublicKey
|
|
5786
|
+
);
|
|
5787
|
+
if (!ok) errors.push("peering artifact signature does not verify against cert issuer pubkey");
|
|
5788
|
+
return ok;
|
|
5789
|
+
}
|
|
5790
|
+
|
|
5119
5791
|
// src/index.ts
|
|
5120
5792
|
function parseYamlValue(raw) {
|
|
5121
5793
|
const trimmed = raw.trim();
|
|
@@ -5314,6 +5986,11 @@ async function sha2564(data) {
|
|
|
5314
5986
|
const buf = await crypto.subtle.digest("SHA-256", data);
|
|
5315
5987
|
return new Uint8Array(buf);
|
|
5316
5988
|
}
|
|
5989
|
+
function bytesToLowerHex2(bytes) {
|
|
5990
|
+
let out = "";
|
|
5991
|
+
for (const b of bytes) out += b.toString(16).padStart(2, "0");
|
|
5992
|
+
return out;
|
|
5993
|
+
}
|
|
5317
5994
|
var IDENTITY_FILE_SUITE = "motebit-jcs-ed25519-hex-v1";
|
|
5318
5995
|
var SIG_PREFIX = `<!-- motebit:sig:${IDENTITY_FILE_SUITE}:`;
|
|
5319
5996
|
var SIG_SUFFIX = " -->";
|
|
@@ -5340,6 +6017,9 @@ function detectArtifactType(artifact) {
|
|
|
5340
6017
|
if ("task_id" in obj && "motebit_id" in obj && "signature" in obj && "prompt_hash" in obj) {
|
|
5341
6018
|
return "receipt";
|
|
5342
6019
|
}
|
|
6020
|
+
if ("spec_version" in obj && "skill" in obj && "manifest" in obj && "body_hash" in obj && "signature" in obj) {
|
|
6021
|
+
return "skill";
|
|
6022
|
+
}
|
|
5343
6023
|
return null;
|
|
5344
6024
|
}
|
|
5345
6025
|
function parse(content) {
|
|
@@ -5695,6 +6375,134 @@ async function verifyDataIntegrity(document, proof) {
|
|
|
5695
6375
|
}
|
|
5696
6376
|
return verifyBySuite("eddsa-jcs-2022", combined, signature, publicKey);
|
|
5697
6377
|
}
|
|
6378
|
+
async function verifySkillBundle(input) {
|
|
6379
|
+
const { envelope, body, files = {} } = input;
|
|
6380
|
+
let publicKey;
|
|
6381
|
+
try {
|
|
6382
|
+
publicKey = decodeSkillSignaturePublicKey(envelope.signature);
|
|
6383
|
+
} catch (err2) {
|
|
6384
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
6385
|
+
return {
|
|
6386
|
+
type: "skill",
|
|
6387
|
+
valid: false,
|
|
6388
|
+
envelope,
|
|
6389
|
+
skill: `${envelope.skill.name}@${envelope.skill.version}`,
|
|
6390
|
+
steps: {
|
|
6391
|
+
envelope: { valid: false, reason: "bad_public_key" },
|
|
6392
|
+
body_hash: null,
|
|
6393
|
+
files: []
|
|
6394
|
+
},
|
|
6395
|
+
errors: [{ message: `public key decode failed: ${msg}`, path: "signature.public_key" }]
|
|
6396
|
+
};
|
|
6397
|
+
}
|
|
6398
|
+
const sigDetail = await verifySkillEnvelopeDetailed(envelope, publicKey);
|
|
6399
|
+
const envelopeStep = { valid: sigDetail.valid, reason: sigDetail.reason };
|
|
6400
|
+
const bodyHashActual = bytesToLowerHex2(await sha2564(body));
|
|
6401
|
+
const bodyHashStep = {
|
|
6402
|
+
valid: bodyHashActual === envelope.body_hash.toLowerCase(),
|
|
6403
|
+
expected: envelope.body_hash,
|
|
6404
|
+
actual: bodyHashActual
|
|
6405
|
+
};
|
|
6406
|
+
const fileSteps = [];
|
|
6407
|
+
for (const entry of envelope.files) {
|
|
6408
|
+
const fileBytes = files[entry.path];
|
|
6409
|
+
if (fileBytes === void 0) {
|
|
6410
|
+
fileSteps.push({
|
|
6411
|
+
path: entry.path,
|
|
6412
|
+
valid: false,
|
|
6413
|
+
expected: entry.hash,
|
|
6414
|
+
actual: null,
|
|
6415
|
+
reason: "missing"
|
|
6416
|
+
});
|
|
6417
|
+
continue;
|
|
6418
|
+
}
|
|
6419
|
+
const actual = bytesToLowerHex2(await sha2564(fileBytes));
|
|
6420
|
+
fileSteps.push({
|
|
6421
|
+
path: entry.path,
|
|
6422
|
+
valid: actual === entry.hash.toLowerCase(),
|
|
6423
|
+
expected: entry.hash,
|
|
6424
|
+
actual,
|
|
6425
|
+
reason: actual === entry.hash.toLowerCase() ? "ok" : "hash_mismatch"
|
|
6426
|
+
});
|
|
6427
|
+
}
|
|
6428
|
+
const filesAllOk = fileSteps.every((f) => f.valid);
|
|
6429
|
+
const valid = envelopeStep.valid && bodyHashStep.valid && filesAllOk;
|
|
6430
|
+
const errors = [];
|
|
6431
|
+
if (!envelopeStep.valid) {
|
|
6432
|
+
errors.push({
|
|
6433
|
+
message: `envelope signature verification failed (${envelopeStep.reason})`,
|
|
6434
|
+
path: "signature"
|
|
6435
|
+
});
|
|
6436
|
+
}
|
|
6437
|
+
if (!bodyHashStep.valid) {
|
|
6438
|
+
errors.push({
|
|
6439
|
+
message: `body_hash mismatch \u2014 expected ${bodyHashStep.expected}, got ${bodyHashStep.actual}`,
|
|
6440
|
+
path: "body_hash"
|
|
6441
|
+
});
|
|
6442
|
+
}
|
|
6443
|
+
for (const f of fileSteps) {
|
|
6444
|
+
if (!f.valid) {
|
|
6445
|
+
errors.push({
|
|
6446
|
+
message: f.reason === "missing" ? `file declared in envelope.files[] but not provided in bundle: ${f.path}` : `file hash mismatch for ${f.path} \u2014 expected ${f.expected}, got ${f.actual ?? "<missing>"}`,
|
|
6447
|
+
path: `files[${f.path}]`
|
|
6448
|
+
});
|
|
6449
|
+
}
|
|
6450
|
+
}
|
|
6451
|
+
return {
|
|
6452
|
+
type: "skill",
|
|
6453
|
+
valid,
|
|
6454
|
+
envelope,
|
|
6455
|
+
skill: `${envelope.skill.name}@${envelope.skill.version}`,
|
|
6456
|
+
signer: envelope.signature.public_key,
|
|
6457
|
+
steps: { envelope: envelopeStep, body_hash: bodyHashStep, files: fileSteps },
|
|
6458
|
+
...errors.length > 0 ? { errors } : {}
|
|
6459
|
+
};
|
|
6460
|
+
}
|
|
6461
|
+
async function verifySkillEnvelopeArtifact(envelope) {
|
|
6462
|
+
let publicKey;
|
|
6463
|
+
try {
|
|
6464
|
+
publicKey = decodeSkillSignaturePublicKey(envelope.signature);
|
|
6465
|
+
} catch (err2) {
|
|
6466
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
6467
|
+
return {
|
|
6468
|
+
type: "skill",
|
|
6469
|
+
valid: false,
|
|
6470
|
+
envelope,
|
|
6471
|
+
skill: `${envelope.skill.name}@${envelope.skill.version}`,
|
|
6472
|
+
steps: {
|
|
6473
|
+
envelope: { valid: false, reason: "bad_public_key" },
|
|
6474
|
+
body_hash: null,
|
|
6475
|
+
files: []
|
|
6476
|
+
},
|
|
6477
|
+
errors: [{ message: `public key decode failed: ${msg}`, path: "signature.public_key" }]
|
|
6478
|
+
};
|
|
6479
|
+
}
|
|
6480
|
+
const detail = await verifySkillEnvelopeDetailed(envelope, publicKey);
|
|
6481
|
+
const errors = [];
|
|
6482
|
+
if (!detail.valid) {
|
|
6483
|
+
errors.push({
|
|
6484
|
+
message: `envelope signature verification failed (${detail.reason})`,
|
|
6485
|
+
path: "signature"
|
|
6486
|
+
});
|
|
6487
|
+
}
|
|
6488
|
+
errors.push({
|
|
6489
|
+
message: "body_hash and files[] cross-check were not attempted \u2014 verifying a bare envelope JSON only checks the envelope signature. Use `verifySkillDirectory` from @motebit/verifier (or `motebit-verify <skill-directory>`) for full verification.",
|
|
6490
|
+
path: "body_hash"
|
|
6491
|
+
});
|
|
6492
|
+
return {
|
|
6493
|
+
type: "skill",
|
|
6494
|
+
valid: false,
|
|
6495
|
+
envelope,
|
|
6496
|
+
skill: `${envelope.skill.name}@${envelope.skill.version}`,
|
|
6497
|
+
signer: envelope.signature.public_key,
|
|
6498
|
+
steps: {
|
|
6499
|
+
envelope: { valid: detail.valid, reason: detail.reason },
|
|
6500
|
+
body_hash: null,
|
|
6501
|
+
files: []
|
|
6502
|
+
},
|
|
6503
|
+
errors
|
|
6504
|
+
};
|
|
6505
|
+
}
|
|
5698
6506
|
var DEFAULT_CLOCK_SKEW_SECONDS = 60;
|
|
5699
6507
|
async function verifyCredential(vc, clockSkewSeconds = DEFAULT_CLOCK_SKEW_SECONDS, hardwareAttestationVerifiers) {
|
|
5700
6508
|
const errors = [];
|
|
@@ -5781,6 +6589,14 @@ async function verify(artifact, options) {
|
|
|
5781
6589
|
...fallbackType === "receipt" ? { receipt: null } : {},
|
|
5782
6590
|
...fallbackType === "credential" ? { credential: null } : {},
|
|
5783
6591
|
...fallbackType === "presentation" ? { presentation: null } : {},
|
|
6592
|
+
...fallbackType === "skill" ? {
|
|
6593
|
+
envelope: null,
|
|
6594
|
+
steps: {
|
|
6595
|
+
envelope: { valid: false, reason: "wrong_suite" },
|
|
6596
|
+
body_hash: null,
|
|
6597
|
+
files: []
|
|
6598
|
+
}
|
|
6599
|
+
} : {},
|
|
5784
6600
|
errors: [{ message: "Unrecognized artifact format" }]
|
|
5785
6601
|
};
|
|
5786
6602
|
}
|
|
@@ -5792,6 +6608,14 @@ async function verify(artifact, options) {
|
|
|
5792
6608
|
...detected === "receipt" ? { receipt: null } : {},
|
|
5793
6609
|
...detected === "credential" ? { credential: null } : {},
|
|
5794
6610
|
...detected === "presentation" ? { presentation: null } : {},
|
|
6611
|
+
...detected === "skill" ? {
|
|
6612
|
+
envelope: null,
|
|
6613
|
+
steps: {
|
|
6614
|
+
envelope: { valid: false, reason: "wrong_suite" },
|
|
6615
|
+
body_hash: null,
|
|
6616
|
+
files: []
|
|
6617
|
+
}
|
|
6618
|
+
} : {},
|
|
5795
6619
|
errors: [{ message: `Expected type "${options.expectedType}" but detected "${detected}"` }]
|
|
5796
6620
|
};
|
|
5797
6621
|
}
|
|
@@ -5806,6 +6630,14 @@ async function verify(artifact, options) {
|
|
|
5806
6630
|
...detected === "receipt" ? { receipt: null } : {},
|
|
5807
6631
|
...detected === "credential" ? { credential: null } : {},
|
|
5808
6632
|
...detected === "presentation" ? { presentation: null } : {},
|
|
6633
|
+
...detected === "skill" ? {
|
|
6634
|
+
envelope: null,
|
|
6635
|
+
steps: {
|
|
6636
|
+
envelope: { valid: false, reason: "wrong_suite" },
|
|
6637
|
+
body_hash: null,
|
|
6638
|
+
files: []
|
|
6639
|
+
}
|
|
6640
|
+
} : {},
|
|
5809
6641
|
errors: [{ message: "Failed to parse JSON" }]
|
|
5810
6642
|
};
|
|
5811
6643
|
}
|
|
@@ -5827,6 +6659,8 @@ async function verify(artifact, options) {
|
|
|
5827
6659
|
options?.clockSkewSeconds,
|
|
5828
6660
|
options?.hardwareAttestation
|
|
5829
6661
|
);
|
|
6662
|
+
case "skill":
|
|
6663
|
+
return verifySkillEnvelopeArtifact(resolved);
|
|
5830
6664
|
}
|
|
5831
6665
|
}
|
|
5832
6666
|
async function verifyIdentityFile(content) {
|
|
@@ -5845,6 +6679,7 @@ export {
|
|
|
5845
6679
|
COLLABORATIVE_RECEIPT_SUITE,
|
|
5846
6680
|
CONSOLIDATION_RECEIPT_SUITE,
|
|
5847
6681
|
DELEGATION_TOKEN_SUITE,
|
|
6682
|
+
DELETION_CERTIFICATE_SUITE,
|
|
5848
6683
|
DEVICE_REGISTRATION_MAX_AGE_MS,
|
|
5849
6684
|
DEVICE_REGISTRATION_SUITE,
|
|
5850
6685
|
DISPUTE_APPEAL_SUITE,
|
|
@@ -5856,16 +6691,26 @@ export {
|
|
|
5856
6691
|
KEY_SUCCESSION_SUITE,
|
|
5857
6692
|
SETTLEMENT_RECORD_SUITE,
|
|
5858
6693
|
SIGNED_TOKEN_SUITE,
|
|
6694
|
+
SKILL_SIGNATURE_SUITE,
|
|
5859
6695
|
TOOL_INVOCATION_RECEIPT_SUITE,
|
|
6696
|
+
WITNESS_OMISSION_DISPUTE_WINDOW_MS,
|
|
5860
6697
|
base58btcDecode,
|
|
5861
6698
|
base58btcEncode,
|
|
5862
6699
|
bytesToHex3 as bytesToHex,
|
|
5863
6700
|
canonicalJson,
|
|
5864
6701
|
canonicalSecureEnclaveBodyForTest,
|
|
5865
6702
|
canonicalSha256,
|
|
6703
|
+
canonicalizeHorizonCert,
|
|
6704
|
+
canonicalizeHorizonCertForWitness,
|
|
6705
|
+
canonicalizeHorizonWitnessRequestBody,
|
|
6706
|
+
canonicalizeMultiSignatureCert,
|
|
6707
|
+
canonicalizeSkillEnvelopeBytes,
|
|
6708
|
+
canonicalizeSkillManifestBytes,
|
|
6709
|
+
canonicalizeWitnessOmissionDispute,
|
|
5866
6710
|
computeCredentialLeaf,
|
|
5867
6711
|
createPresentation,
|
|
5868
6712
|
createSignedToken,
|
|
6713
|
+
decodeSkillSignaturePublicKey,
|
|
5869
6714
|
didKeyToPublicKey,
|
|
5870
6715
|
ed25519Sign,
|
|
5871
6716
|
ed25519Verify,
|
|
@@ -5890,6 +6735,10 @@ export {
|
|
|
5890
6735
|
signAdjudicatorVote,
|
|
5891
6736
|
signBalanceWaiver,
|
|
5892
6737
|
signBySuite,
|
|
6738
|
+
signCertAsDelegate,
|
|
6739
|
+
signCertAsGuardian,
|
|
6740
|
+
signCertAsOperator,
|
|
6741
|
+
signCertAsSubject,
|
|
5893
6742
|
signCollaborativeReceipt,
|
|
5894
6743
|
signConsolidationReceipt,
|
|
5895
6744
|
signDelegation,
|
|
@@ -5901,12 +6750,18 @@ export {
|
|
|
5901
6750
|
signExecutionReceipt,
|
|
5902
6751
|
signGuardianRecoverySuccession,
|
|
5903
6752
|
signGuardianRevocation,
|
|
6753
|
+
signHorizonCertAsIssuer,
|
|
6754
|
+
signHorizonWitness,
|
|
6755
|
+
signHorizonWitnessRequestBody,
|
|
5904
6756
|
signKeySuccession,
|
|
5905
6757
|
signSettlement,
|
|
6758
|
+
signSkillEnvelope,
|
|
6759
|
+
signSkillManifest,
|
|
5906
6760
|
signSovereignPaymentReceipt,
|
|
5907
6761
|
signToolInvocationReceipt,
|
|
5908
6762
|
signVerifiableCredential,
|
|
5909
6763
|
signVerifiablePresentation,
|
|
6764
|
+
signWitnessOmissionDispute,
|
|
5910
6765
|
toBase64Url,
|
|
5911
6766
|
verify,
|
|
5912
6767
|
verifyAdjudicatorVote,
|
|
@@ -5917,6 +6772,7 @@ export {
|
|
|
5917
6772
|
verifyCredentialAnchor,
|
|
5918
6773
|
verifyDelegation,
|
|
5919
6774
|
verifyDelegationChain,
|
|
6775
|
+
verifyDeletionCertificate,
|
|
5920
6776
|
verifyDeviceRegistration,
|
|
5921
6777
|
verifyDisputeAppeal,
|
|
5922
6778
|
verifyDisputeEvidence,
|
|
@@ -5926,17 +6782,26 @@ export {
|
|
|
5926
6782
|
verifyExecutionReceiptDetailed,
|
|
5927
6783
|
verifyGuardianRevocation,
|
|
5928
6784
|
verifyHardwareAttestationClaim,
|
|
6785
|
+
verifyHorizonWitnessRequestSignature,
|
|
5929
6786
|
verifyIdentityFile,
|
|
5930
6787
|
verifyKeySuccession,
|
|
6788
|
+
verifyMerkleInclusion,
|
|
5931
6789
|
verifyReceiptChain,
|
|
5932
6790
|
verifyReceiptSequence,
|
|
6791
|
+
verifyRetentionManifest,
|
|
5933
6792
|
verifyRevocationAnchor,
|
|
5934
6793
|
verifySettlement,
|
|
5935
6794
|
verifySignedToken,
|
|
6795
|
+
verifySkillBundle,
|
|
6796
|
+
verifySkillEnvelope,
|
|
6797
|
+
verifySkillEnvelopeDetailed,
|
|
6798
|
+
verifySkillManifest,
|
|
6799
|
+
verifySkillManifestDetailed,
|
|
5936
6800
|
verifySuccessionChain,
|
|
5937
6801
|
verifyToolInvocationReceipt,
|
|
5938
6802
|
verifyVerifiableCredential,
|
|
5939
|
-
verifyVerifiablePresentation
|
|
6803
|
+
verifyVerifiablePresentation,
|
|
6804
|
+
verifyWitnessOmissionDispute
|
|
5940
6805
|
};
|
|
5941
6806
|
/*! Bundled license information:
|
|
5942
6807
|
|