xoonya 3.0.1 → 3.1.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/.env.example +8 -0
- package/README.md +107 -1
- package/RELEASE_NOTES.md +12 -9
- package/SERVICE_CONTRACT.md +12 -0
- package/START_HERE.md +9 -0
- package/XOONYA_NATIVE_PROOF_FORMAT.md +52 -9
- package/index.js +12 -3
- package/lib/adapter_certification_contract.js +90 -0
- package/lib/authority_binding_lifecycle_contract.js +182 -0
- package/lib/crypto_agility_matrix.js +125 -0
- package/lib/machine_identity_registry_contract.js +199 -0
- package/lib/offline_verifiability_recovery_contract.js +155 -0
- package/lib/operational_receipt_contract.js +165 -0
- package/lib/public_surface_policy_contract.js +111 -0
- package/lib/signed_audit_provenance_contract.js +93 -0
- package/lib/trust_portability_boundary_contract.js +66 -0
- package/package.json +5 -2
- package/proof_sdk.js +327 -31
- package/reference/python/__pycache__/xnya_reference.cpython-314.pyc +0 -0
- package/scripts/verify_control_plane_contracts.js +34 -0
- package/scripts/verify_control_plane_routes.js +70 -0
- package/scripts/verify_forgery_resistance.js +281 -0
- package/scripts/verify_service.js +98 -0
- package/server.js +242 -16
package/proof_sdk.js
CHANGED
|
@@ -154,20 +154,29 @@ const NATIVE_SIGNATURE_SUITES = Object.freeze({
|
|
|
154
154
|
dilithium: Object.freeze({
|
|
155
155
|
id: "dilithium",
|
|
156
156
|
maturity: "planned_pqc",
|
|
157
|
+
status: "deprecated_placeholder",
|
|
158
|
+
deprecated_alias_of: "ml-dsa-65",
|
|
157
159
|
public_key_format: "suite_defined",
|
|
158
|
-
verification_posture: "registry_declared_not_yet_verified"
|
|
160
|
+
verification_posture: "registry_declared_not_yet_verified",
|
|
161
|
+
description: "Legacy pre-standard placeholder name. Use the implemented FIPS 204 suite `ml-dsa-65` instead; this entry never verifies."
|
|
159
162
|
}),
|
|
160
163
|
falcon: Object.freeze({
|
|
161
164
|
id: "falcon",
|
|
162
165
|
maturity: "planned_pqc",
|
|
166
|
+
status: "deprecated_placeholder",
|
|
167
|
+
deprecated_alias_of: null,
|
|
163
168
|
public_key_format: "suite_defined",
|
|
164
|
-
verification_posture: "registry_declared_not_yet_verified"
|
|
169
|
+
verification_posture: "registry_declared_not_yet_verified",
|
|
170
|
+
description: "Legacy pre-standard placeholder name (FN-DSA is not yet registered). This entry never verifies."
|
|
165
171
|
}),
|
|
166
172
|
"sphincs-plus": Object.freeze({
|
|
167
173
|
id: "sphincs-plus",
|
|
168
174
|
maturity: "planned_pqc",
|
|
175
|
+
status: "deprecated_placeholder",
|
|
176
|
+
deprecated_alias_of: "slh-dsa-sha2-128f",
|
|
169
177
|
public_key_format: "suite_defined",
|
|
170
|
-
verification_posture: "registry_declared_not_yet_verified"
|
|
178
|
+
verification_posture: "registry_declared_not_yet_verified",
|
|
179
|
+
description: "Legacy pre-standard placeholder name. Use the implemented FIPS 205 suite `slh-dsa-sha2-128f` instead; this entry never verifies."
|
|
171
180
|
}),
|
|
172
181
|
"ml-dsa-65": Object.freeze({
|
|
173
182
|
id: "ml-dsa-65",
|
|
@@ -208,17 +217,23 @@ const XOONYA_NATIVE_IDENTITY_TYPES = Object.freeze({
|
|
|
208
217
|
"05": Object.freeze({
|
|
209
218
|
prefix: "05",
|
|
210
219
|
id: "dilithium-public-key",
|
|
211
|
-
canonical_material: "suite-defined-pqc-public-key-bytes"
|
|
220
|
+
canonical_material: "suite-defined-pqc-public-key-bytes",
|
|
221
|
+
status: "legacy_placeholder",
|
|
222
|
+
description: "Legacy placeholder prefix reserved before FIPS 204 finalization; the implemented PQ suite is `ml-dsa-65`. No verifier binds this prefix to a live suite."
|
|
212
223
|
}),
|
|
213
224
|
"06": Object.freeze({
|
|
214
225
|
prefix: "06",
|
|
215
226
|
id: "falcon-public-key",
|
|
216
|
-
canonical_material: "suite-defined-pqc-public-key-bytes"
|
|
227
|
+
canonical_material: "suite-defined-pqc-public-key-bytes",
|
|
228
|
+
status: "legacy_placeholder",
|
|
229
|
+
description: "Legacy placeholder prefix reserved before FN-DSA standardization. No verifier binds this prefix to a live suite."
|
|
217
230
|
}),
|
|
218
231
|
"07": Object.freeze({
|
|
219
232
|
prefix: "07",
|
|
220
233
|
id: "sphincs-plus-public-key",
|
|
221
|
-
canonical_material: "suite-defined-pqc-public-key-bytes"
|
|
234
|
+
canonical_material: "suite-defined-pqc-public-key-bytes",
|
|
235
|
+
status: "legacy_placeholder",
|
|
236
|
+
description: "Legacy placeholder prefix reserved before FIPS 205 finalization; the implemented PQ suite is `slh-dsa-sha2-128f`. No verifier binds this prefix to a live suite."
|
|
222
237
|
})
|
|
223
238
|
});
|
|
224
239
|
const EXTERNAL_PROOF_IMPORT_METHODS = Object.freeze([
|
|
@@ -229,6 +244,53 @@ const EXTERNAL_PROOF_IMPORT_METHODS = Object.freeze([
|
|
|
229
244
|
SIGNED_ENVELOPE_METHOD,
|
|
230
245
|
PARTNER_PROOF_ENVELOPE_METHOD
|
|
231
246
|
]);
|
|
247
|
+
const VERIFICATION_CLASS_CRYPTOGRAPHIC = "cryptographic";
|
|
248
|
+
const VERIFICATION_CLASS_DIGEST = "digest";
|
|
249
|
+
const VERIFICATION_CLASS_STRUCTURAL = "structural";
|
|
250
|
+
const VERIFICATION_CLASS_ANCHORED_BINDING = "anchored_binding";
|
|
251
|
+
const PROOF_VERIFICATION_CLASSES = Object.freeze({
|
|
252
|
+
[VERIFICATION_CLASS_CRYPTOGRAPHIC]: Object.freeze({
|
|
253
|
+
id: VERIFICATION_CLASS_CRYPTOGRAPHIC,
|
|
254
|
+
cryptographically_verified: true,
|
|
255
|
+
strict_mode_accepted: true,
|
|
256
|
+
methods: Object.freeze([XOONYA_NATIVE_SIGNED_METHOD, XOONYA_NATIVE_HYBRID_METHOD]),
|
|
257
|
+
description: "A native classical and/or PQ signature was checked against the public key carried in the proof. Identity binding still requires a verifier-supplied trust root."
|
|
258
|
+
}),
|
|
259
|
+
[VERIFICATION_CLASS_DIGEST]: Object.freeze({
|
|
260
|
+
id: VERIFICATION_CLASS_DIGEST,
|
|
261
|
+
cryptographically_verified: false,
|
|
262
|
+
strict_mode_accepted: false,
|
|
263
|
+
methods: Object.freeze([XOONYA_NATIVE_SHA256_METHOD, LEGACY_OPRETURN_METHOD]),
|
|
264
|
+
description: "Only an unkeyed deterministic digest was recomputed. Anyone can produce a valid digest; this proves integrity and replayability, not authorship."
|
|
265
|
+
}),
|
|
266
|
+
[VERIFICATION_CLASS_ANCHORED_BINDING]: Object.freeze({
|
|
267
|
+
id: VERIFICATION_CLASS_ANCHORED_BINDING,
|
|
268
|
+
cryptographically_verified: false,
|
|
269
|
+
strict_mode_accepted: false,
|
|
270
|
+
methods: Object.freeze([XOONYA_NATIVE_ANCHORED_METHOD]),
|
|
271
|
+
description: "An unkeyed digest binding to declared anchor refs was recomputed. External anchor truth and authorship are not verified by xoonya."
|
|
272
|
+
}),
|
|
273
|
+
[VERIFICATION_CLASS_STRUCTURAL]: Object.freeze({
|
|
274
|
+
id: VERIFICATION_CLASS_STRUCTURAL,
|
|
275
|
+
cryptographically_verified: false,
|
|
276
|
+
strict_mode_accepted: false,
|
|
277
|
+
methods: Object.freeze(EXTERNAL_PROOF_IMPORT_METHODS.slice()),
|
|
278
|
+
description: "The envelope shape, method registration, and canonical receipt binding were checked. The external signature itself must be verified by the external verifier stack."
|
|
279
|
+
})
|
|
280
|
+
});
|
|
281
|
+
const STRICT_DIGEST_ONLY_REASON = "digest_only_not_cryptographically_verified";
|
|
282
|
+
const STRICT_EXTERNAL_VERIFICATION_REASON = "external_verification_required";
|
|
283
|
+
const NATIVE_PUBLIC_KEY_NOT_TRUSTED_REASON = "native_public_key_not_trusted";
|
|
284
|
+
const NATIVE_HYBRID_PUBLIC_KEY_NOT_TRUSTED_REASON = "native_hybrid_public_key_not_trusted";
|
|
285
|
+
const PROOF_VERIFICATION_STRICT_REJECTION_CODES = Object.freeze([
|
|
286
|
+
STRICT_DIGEST_ONLY_REASON,
|
|
287
|
+
STRICT_EXTERNAL_VERIFICATION_REASON
|
|
288
|
+
]);
|
|
289
|
+
const PROOF_VERIFICATION_TRUST_ROOT_REJECTION_CODES = Object.freeze([
|
|
290
|
+
NATIVE_PUBLIC_KEY_NOT_TRUSTED_REASON,
|
|
291
|
+
NATIVE_HYBRID_PUBLIC_KEY_NOT_TRUSTED_REASON
|
|
292
|
+
]);
|
|
293
|
+
const STRICT_VERIFY_ENV = "XOONYA_STRICT_VERIFY";
|
|
232
294
|
|
|
233
295
|
function sha256Hex(buf) {
|
|
234
296
|
return crypto.createHash("sha256").update(buf).digest("hex");
|
|
@@ -874,7 +936,19 @@ function buildNativeHybridProofMaterial(receipt, canonicalEvent, proofDescriptor
|
|
|
874
936
|
};
|
|
875
937
|
}
|
|
876
938
|
|
|
939
|
+
const NATIVE_HYBRID_RUNTIME_SUPPORT_CACHE = new Map();
|
|
940
|
+
|
|
877
941
|
function runtimeSupportsNativeHybridSuite(hybridSuiteId) {
|
|
942
|
+
const cacheKey = normalizeString(hybridSuiteId);
|
|
943
|
+
if (NATIVE_HYBRID_RUNTIME_SUPPORT_CACHE.has(cacheKey)) {
|
|
944
|
+
return NATIVE_HYBRID_RUNTIME_SUPPORT_CACHE.get(cacheKey);
|
|
945
|
+
}
|
|
946
|
+
const supported = probeNativeHybridRuntimeSupport(cacheKey);
|
|
947
|
+
NATIVE_HYBRID_RUNTIME_SUPPORT_CACHE.set(cacheKey, supported);
|
|
948
|
+
return supported;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
function probeNativeHybridRuntimeSupport(hybridSuiteId) {
|
|
878
952
|
try {
|
|
879
953
|
const hybridSuite = parseNativeHybridSuite(hybridSuiteId);
|
|
880
954
|
const classicalPair = hybridSuite.classical_suite.id === "secp256k1"
|
|
@@ -1053,7 +1127,162 @@ function buildNativeAnchoredReceiptProof(receipt, canonicalEvent, options = {})
|
|
|
1053
1127
|
};
|
|
1054
1128
|
}
|
|
1055
1129
|
|
|
1056
|
-
function
|
|
1130
|
+
function strictVerifyEnabledByEnvironment() {
|
|
1131
|
+
const raw = normalizeString(process.env[STRICT_VERIFY_ENV]).toLowerCase();
|
|
1132
|
+
return raw === "1" || raw === "true";
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
function resolveProofVerificationOptions(options) {
|
|
1136
|
+
const source = options && typeof options === "object" && !Array.isArray(options) ? options : {};
|
|
1137
|
+
const trustedPublicKeyRefs = source.trusted_public_key_refs &&
|
|
1138
|
+
typeof source.trusted_public_key_refs === "object" &&
|
|
1139
|
+
!Array.isArray(source.trusted_public_key_refs)
|
|
1140
|
+
? source.trusted_public_key_refs
|
|
1141
|
+
: null;
|
|
1142
|
+
const expectedPublicKeyRef = normalizeString(source.expected_public_key_ref);
|
|
1143
|
+
const expectedClassicalPublicKeyRef = normalizeString(source.expected_classical_public_key_ref);
|
|
1144
|
+
const expectedPqPublicKeyRef = normalizeString(source.expected_pq_public_key_ref);
|
|
1145
|
+
const expectedHybridPublicKeyRef = normalizeString(source.expected_hybrid_public_key_ref);
|
|
1146
|
+
return {
|
|
1147
|
+
__resolved_verification_options: true,
|
|
1148
|
+
strict: source.strict === true || strictVerifyEnabledByEnvironment(),
|
|
1149
|
+
trust_root_applied: Boolean(
|
|
1150
|
+
trustedPublicKeyRefs ||
|
|
1151
|
+
expectedPublicKeyRef ||
|
|
1152
|
+
expectedClassicalPublicKeyRef ||
|
|
1153
|
+
expectedPqPublicKeyRef ||
|
|
1154
|
+
expectedHybridPublicKeyRef
|
|
1155
|
+
),
|
|
1156
|
+
expected_public_key_ref: expectedPublicKeyRef,
|
|
1157
|
+
expected_classical_public_key_ref: expectedClassicalPublicKeyRef,
|
|
1158
|
+
expected_pq_public_key_ref: expectedPqPublicKeyRef,
|
|
1159
|
+
expected_hybrid_public_key_ref: expectedHybridPublicKeyRef,
|
|
1160
|
+
trusted_public_key_refs: trustedPublicKeyRefs
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
function normalizeTrustedPublicKeyRefList(value) {
|
|
1165
|
+
if (value == null) return [];
|
|
1166
|
+
const list = Array.isArray(value) ? value : [value];
|
|
1167
|
+
return list.map((entry) => normalizeString(entry)).filter(Boolean);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
function lookupTrustedPublicKeyEntry(trustedPublicKeyRefs, identityRef, agentIdentity) {
|
|
1171
|
+
if (!trustedPublicKeyRefs) return undefined;
|
|
1172
|
+
for (const candidate of [normalizeString(identityRef), normalizeString(agentIdentity)]) {
|
|
1173
|
+
if (candidate && Object.prototype.hasOwnProperty.call(trustedPublicKeyRefs, candidate)) {
|
|
1174
|
+
return trustedPublicKeyRefs[candidate];
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
return undefined;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
function trustedPublicKeyRefsForLane(entry, lane) {
|
|
1181
|
+
if (entry == null) return { declared: false, refs: [] };
|
|
1182
|
+
if (typeof entry === "string" || Array.isArray(entry)) {
|
|
1183
|
+
return lane === "classical"
|
|
1184
|
+
? { declared: true, refs: normalizeTrustedPublicKeyRefList(entry) }
|
|
1185
|
+
: { declared: false, refs: [] };
|
|
1186
|
+
}
|
|
1187
|
+
if (typeof entry !== "object") return { declared: false, refs: [] };
|
|
1188
|
+
const laneValue = lane === "classical"
|
|
1189
|
+
? (entry.classical != null ? entry.classical : (entry.classical_public_key_ref != null ? entry.classical_public_key_ref : entry.public_key_ref))
|
|
1190
|
+
: lane === "pq"
|
|
1191
|
+
? (entry.pq != null ? entry.pq : entry.pq_public_key_ref)
|
|
1192
|
+
: (entry.hybrid != null ? entry.hybrid : entry.hybrid_public_key_ref);
|
|
1193
|
+
if (laneValue == null) return { declared: false, refs: [] };
|
|
1194
|
+
return { declared: true, refs: normalizeTrustedPublicKeyRefList(laneValue) };
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
function nativeSignedPublicKeyTrusted(verification, publicKeyRef, identityRef, agentIdentity) {
|
|
1198
|
+
if (!verification || !verification.trust_root_applied) return true;
|
|
1199
|
+
let constrained = false;
|
|
1200
|
+
if (verification.expected_public_key_ref) {
|
|
1201
|
+
constrained = true;
|
|
1202
|
+
if (publicKeyRef !== verification.expected_public_key_ref) return false;
|
|
1203
|
+
}
|
|
1204
|
+
if (verification.expected_classical_public_key_ref) {
|
|
1205
|
+
constrained = true;
|
|
1206
|
+
if (publicKeyRef !== verification.expected_classical_public_key_ref) return false;
|
|
1207
|
+
}
|
|
1208
|
+
if (verification.trusted_public_key_refs) {
|
|
1209
|
+
constrained = true;
|
|
1210
|
+
const entry = lookupTrustedPublicKeyEntry(verification.trusted_public_key_refs, identityRef, agentIdentity);
|
|
1211
|
+
const lane = trustedPublicKeyRefsForLane(entry, "classical");
|
|
1212
|
+
if (!lane.declared || !lane.refs.includes(publicKeyRef)) return false;
|
|
1213
|
+
}
|
|
1214
|
+
return constrained;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
function nativeHybridPublicKeyTrusted(verification, refs, identityRef, agentIdentity) {
|
|
1218
|
+
if (!verification || !verification.trust_root_applied) return true;
|
|
1219
|
+
let constrained = false;
|
|
1220
|
+
if (verification.expected_public_key_ref) {
|
|
1221
|
+
constrained = true;
|
|
1222
|
+
if (refs.classical !== verification.expected_public_key_ref && refs.hybrid !== verification.expected_public_key_ref) return false;
|
|
1223
|
+
}
|
|
1224
|
+
if (verification.expected_classical_public_key_ref) {
|
|
1225
|
+
constrained = true;
|
|
1226
|
+
if (refs.classical !== verification.expected_classical_public_key_ref) return false;
|
|
1227
|
+
}
|
|
1228
|
+
if (verification.expected_pq_public_key_ref) {
|
|
1229
|
+
constrained = true;
|
|
1230
|
+
if (refs.pq !== verification.expected_pq_public_key_ref) return false;
|
|
1231
|
+
}
|
|
1232
|
+
if (verification.expected_hybrid_public_key_ref) {
|
|
1233
|
+
constrained = true;
|
|
1234
|
+
if (refs.hybrid !== verification.expected_hybrid_public_key_ref) return false;
|
|
1235
|
+
}
|
|
1236
|
+
if (verification.trusted_public_key_refs) {
|
|
1237
|
+
const entry = lookupTrustedPublicKeyEntry(verification.trusted_public_key_refs, identityRef, agentIdentity);
|
|
1238
|
+
const classicalLane = trustedPublicKeyRefsForLane(entry, "classical");
|
|
1239
|
+
const pqLane = trustedPublicKeyRefsForLane(entry, "pq");
|
|
1240
|
+
const hybridLane = trustedPublicKeyRefsForLane(entry, "hybrid");
|
|
1241
|
+
if (!classicalLane.declared && !pqLane.declared && !hybridLane.declared) return false;
|
|
1242
|
+
if (classicalLane.declared && !classicalLane.refs.includes(refs.classical) && !classicalLane.refs.includes(refs.hybrid)) return false;
|
|
1243
|
+
if (pqLane.declared && !pqLane.refs.includes(refs.pq)) return false;
|
|
1244
|
+
if (hybridLane.declared && !hybridLane.refs.includes(refs.hybrid)) return false;
|
|
1245
|
+
constrained = true;
|
|
1246
|
+
}
|
|
1247
|
+
return constrained;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
function verificationClassForMethod(algorithm) {
|
|
1251
|
+
const normalized = normalizeString(algorithm);
|
|
1252
|
+
for (const entry of Object.values(PROOF_VERIFICATION_CLASSES)) {
|
|
1253
|
+
if (entry.methods.includes(normalized)) return entry.id;
|
|
1254
|
+
}
|
|
1255
|
+
return null;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
function listProofVerificationClasses() {
|
|
1259
|
+
return Object.values(PROOF_VERIFICATION_CLASSES).map((entry) => ({ ...entry, methods: entry.methods.slice() }));
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
function finalizeProofMethodVerification(result, algorithm, verification) {
|
|
1263
|
+
const verificationClass = verificationClassForMethod(algorithm);
|
|
1264
|
+
const classEntry = verificationClass ? PROOF_VERIFICATION_CLASSES[verificationClass] : null;
|
|
1265
|
+
const decorated = {
|
|
1266
|
+
...result,
|
|
1267
|
+
verification_class: verificationClass,
|
|
1268
|
+
cryptographically_verified: result.valid === true && Boolean(classEntry && classEntry.cryptographically_verified),
|
|
1269
|
+
strict: verification.strict === true,
|
|
1270
|
+
trust_root_applied: verification.trust_root_applied === true
|
|
1271
|
+
};
|
|
1272
|
+
if (decorated.valid === true && decorated.strict && classEntry && !classEntry.strict_mode_accepted) {
|
|
1273
|
+
return {
|
|
1274
|
+
...decorated,
|
|
1275
|
+
valid: false,
|
|
1276
|
+
reason: verificationClass === VERIFICATION_CLASS_STRUCTURAL
|
|
1277
|
+
? STRICT_EXTERNAL_VERIFICATION_REASON
|
|
1278
|
+
: STRICT_DIGEST_ONLY_REASON,
|
|
1279
|
+
cryptographically_verified: false
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
return decorated;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
function verifyNativeSignedReceiptProof(receipt, ceb, verification = null) {
|
|
1057
1286
|
try {
|
|
1058
1287
|
const proof = receipt.native_proof;
|
|
1059
1288
|
if (!proof || typeof proof !== "object" || Array.isArray(proof)) {
|
|
@@ -1115,17 +1344,21 @@ function verifyNativeSignedReceiptProof(receipt, ceb) {
|
|
|
1115
1344
|
if (!verified) {
|
|
1116
1345
|
return { valid: false, reason: "native_signature_invalid" };
|
|
1117
1346
|
}
|
|
1347
|
+
if (!nativeSignedPublicKeyTrusted(verification, expectedPublicKeyRef, identityRef, receipt.agent_identity)) {
|
|
1348
|
+
return { valid: false, reason: NATIVE_PUBLIC_KEY_NOT_TRUSTED_REASON, public_key_ref: expectedPublicKeyRef };
|
|
1349
|
+
}
|
|
1118
1350
|
return {
|
|
1119
1351
|
valid: true,
|
|
1120
1352
|
verification_state: "verified_native_signature",
|
|
1121
|
-
signature_suite: suite.id
|
|
1353
|
+
signature_suite: suite.id,
|
|
1354
|
+
public_key_ref: expectedPublicKeyRef
|
|
1122
1355
|
};
|
|
1123
1356
|
} catch (error) {
|
|
1124
1357
|
return { valid: false, reason: error.message };
|
|
1125
1358
|
}
|
|
1126
1359
|
}
|
|
1127
1360
|
|
|
1128
|
-
function verifyNativeHybridReceiptProof(receipt, ceb) {
|
|
1361
|
+
function verifyNativeHybridReceiptProof(receipt, ceb, verification = null) {
|
|
1129
1362
|
try {
|
|
1130
1363
|
const proof = receipt.native_proof;
|
|
1131
1364
|
if (!proof || typeof proof !== "object" || Array.isArray(proof)) {
|
|
@@ -1210,10 +1443,27 @@ function verifyNativeHybridReceiptProof(receipt, ceb) {
|
|
|
1210
1443
|
if (!crypto.verify(nativeSigningHashForSuite(hybridSuite.pq_suite), signingBytes, pqPublicKey, pqSignatureBytes)) {
|
|
1211
1444
|
return { valid: false, reason: "native_hybrid_pq_signature_invalid" };
|
|
1212
1445
|
}
|
|
1446
|
+
const publicKeyRefs = {
|
|
1447
|
+
classical: expectedClassicalPublicKeyRef,
|
|
1448
|
+
pq: expectedPqPublicKeyRef,
|
|
1449
|
+
hybrid: expectedHybridPublicKeyRef
|
|
1450
|
+
};
|
|
1451
|
+
if (!nativeHybridPublicKeyTrusted(verification, publicKeyRefs, identityRef, receipt.agent_identity)) {
|
|
1452
|
+
return {
|
|
1453
|
+
valid: false,
|
|
1454
|
+
reason: NATIVE_HYBRID_PUBLIC_KEY_NOT_TRUSTED_REASON,
|
|
1455
|
+
public_key_ref: expectedHybridPublicKeyRef,
|
|
1456
|
+
classical_public_key_ref: expectedClassicalPublicKeyRef,
|
|
1457
|
+
pq_public_key_ref: expectedPqPublicKeyRef
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1213
1460
|
return {
|
|
1214
1461
|
valid: true,
|
|
1215
1462
|
verification_state: "verified_native_hybrid_signature",
|
|
1216
|
-
signature_suite: hybridSuite.id
|
|
1463
|
+
signature_suite: hybridSuite.id,
|
|
1464
|
+
public_key_ref: expectedHybridPublicKeyRef,
|
|
1465
|
+
classical_public_key_ref: expectedClassicalPublicKeyRef,
|
|
1466
|
+
pq_public_key_ref: expectedPqPublicKeyRef
|
|
1217
1467
|
};
|
|
1218
1468
|
} catch (error) {
|
|
1219
1469
|
return { valid: false, reason: error.message };
|
|
@@ -1355,8 +1605,19 @@ function emitReceipt(event, options = {}) {
|
|
|
1355
1605
|
};
|
|
1356
1606
|
}
|
|
1357
1607
|
|
|
1358
|
-
function verifyReceiptProofMethod(receipt, ceb, bundle) {
|
|
1608
|
+
function verifyReceiptProofMethod(receipt, ceb, bundle, options = {}) {
|
|
1609
|
+
const verification = options && options.__resolved_verification_options === true
|
|
1610
|
+
? options
|
|
1611
|
+
: resolveProofVerificationOptions(options);
|
|
1359
1612
|
const algorithm = normalizeString(receipt.algorithm);
|
|
1613
|
+
return finalizeProofMethodVerification(
|
|
1614
|
+
verifyReceiptProofMethodInner(receipt, ceb, bundle, algorithm, verification),
|
|
1615
|
+
algorithm,
|
|
1616
|
+
verification
|
|
1617
|
+
);
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
function verifyReceiptProofMethodInner(receipt, ceb, bundle, algorithm, verification) {
|
|
1360
1621
|
if (!algorithm) {
|
|
1361
1622
|
return { valid: false, reason: "verification_method_missing" };
|
|
1362
1623
|
}
|
|
@@ -1364,9 +1625,9 @@ function verifyReceiptProofMethod(receipt, ceb, bundle) {
|
|
|
1364
1625
|
return { valid: false, reason: "verification_method_none" };
|
|
1365
1626
|
}
|
|
1366
1627
|
if (algorithm === XOONYA_NATIVE_HYBRID_METHOD) {
|
|
1367
|
-
const
|
|
1368
|
-
if (!
|
|
1369
|
-
return { valid: false, reason:
|
|
1628
|
+
const hybridVerification = verifyNativeHybridReceiptProof(receipt, ceb, verification);
|
|
1629
|
+
if (!hybridVerification.valid) {
|
|
1630
|
+
return { valid: false, reason: hybridVerification.reason };
|
|
1370
1631
|
}
|
|
1371
1632
|
return {
|
|
1372
1633
|
valid: true,
|
|
@@ -1376,8 +1637,11 @@ function verifyReceiptProofMethod(receipt, ceb, bundle) {
|
|
|
1376
1637
|
verification_posture: "native_hybrid_signature_verified",
|
|
1377
1638
|
description: "Verifies xoonya's native hybrid proof envelope with a classical and PQ signature bound to one deterministic signing material."
|
|
1378
1639
|
},
|
|
1379
|
-
verification_state:
|
|
1380
|
-
signature_suite:
|
|
1640
|
+
verification_state: hybridVerification.verification_state,
|
|
1641
|
+
signature_suite: hybridVerification.signature_suite,
|
|
1642
|
+
public_key_ref: hybridVerification.public_key_ref,
|
|
1643
|
+
classical_public_key_ref: hybridVerification.classical_public_key_ref,
|
|
1644
|
+
pq_public_key_ref: hybridVerification.pq_public_key_ref
|
|
1381
1645
|
};
|
|
1382
1646
|
}
|
|
1383
1647
|
|
|
@@ -1428,29 +1692,30 @@ function verifyReceiptProofMethod(receipt, ceb, bundle) {
|
|
|
1428
1692
|
}
|
|
1429
1693
|
|
|
1430
1694
|
if (algorithm === XOONYA_NATIVE_SIGNED_METHOD) {
|
|
1431
|
-
const
|
|
1432
|
-
if (!
|
|
1433
|
-
return { valid: false, reason:
|
|
1695
|
+
const signedVerification = verifyNativeSignedReceiptProof(receipt, ceb, verification);
|
|
1696
|
+
if (!signedVerification.valid) {
|
|
1697
|
+
return { valid: false, reason: signedVerification.reason };
|
|
1434
1698
|
}
|
|
1435
1699
|
return {
|
|
1436
1700
|
valid: true,
|
|
1437
1701
|
method,
|
|
1438
|
-
verification_state:
|
|
1439
|
-
signature_suite:
|
|
1702
|
+
verification_state: signedVerification.verification_state,
|
|
1703
|
+
signature_suite: signedVerification.signature_suite,
|
|
1704
|
+
public_key_ref: signedVerification.public_key_ref
|
|
1440
1705
|
};
|
|
1441
1706
|
}
|
|
1442
1707
|
|
|
1443
1708
|
if (algorithm === XOONYA_NATIVE_ANCHORED_METHOD) {
|
|
1444
|
-
const
|
|
1445
|
-
if (!
|
|
1446
|
-
return { valid: false, reason:
|
|
1709
|
+
const anchoredVerification = verifyNativeAnchoredReceiptProof(receipt, ceb, bundle);
|
|
1710
|
+
if (!anchoredVerification.valid) {
|
|
1711
|
+
return { valid: false, reason: anchoredVerification.reason };
|
|
1447
1712
|
}
|
|
1448
1713
|
return {
|
|
1449
1714
|
valid: true,
|
|
1450
1715
|
method,
|
|
1451
|
-
verification_state:
|
|
1452
|
-
anchor_count:
|
|
1453
|
-
anchor_root_sha256:
|
|
1716
|
+
verification_state: anchoredVerification.verification_state,
|
|
1717
|
+
anchor_count: anchoredVerification.anchor_count,
|
|
1718
|
+
anchor_root_sha256: anchoredVerification.anchor_root_sha256
|
|
1454
1719
|
};
|
|
1455
1720
|
}
|
|
1456
1721
|
|
|
@@ -1503,7 +1768,22 @@ function buildProofBundle(input) {
|
|
|
1503
1768
|
};
|
|
1504
1769
|
}
|
|
1505
1770
|
|
|
1506
|
-
function verifyProofBundle(bundle) {
|
|
1771
|
+
function verifyProofBundle(bundle, options = {}) {
|
|
1772
|
+
const verification = resolveProofVerificationOptions(options);
|
|
1773
|
+
const result = verifyProofBundleInner(bundle, verification);
|
|
1774
|
+
const algorithm = bundle && bundle.receipt && typeof bundle.receipt === "object" && !Array.isArray(bundle.receipt)
|
|
1775
|
+
? bundle.receipt.algorithm
|
|
1776
|
+
: null;
|
|
1777
|
+
return {
|
|
1778
|
+
...result,
|
|
1779
|
+
cryptographically_verified: result.cryptographically_verified === true,
|
|
1780
|
+
verification_class: result.verification_class !== undefined ? result.verification_class : verificationClassForMethod(algorithm),
|
|
1781
|
+
strict: verification.strict,
|
|
1782
|
+
trust_root_applied: verification.trust_root_applied
|
|
1783
|
+
};
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
function verifyProofBundleInner(bundle, verification) {
|
|
1507
1787
|
try {
|
|
1508
1788
|
if (!bundle || typeof bundle !== "object" || Array.isArray(bundle)) {
|
|
1509
1789
|
return { valid: false, reason: "bundle_not_object" };
|
|
@@ -1549,9 +1829,14 @@ function verifyProofBundle(bundle) {
|
|
|
1549
1829
|
return { valid: false, reason: "signature_missing" };
|
|
1550
1830
|
}
|
|
1551
1831
|
|
|
1552
|
-
const methodVerification = verifyReceiptProofMethod(receipt, ceb, bundle);
|
|
1832
|
+
const methodVerification = verifyReceiptProofMethod(receipt, ceb, bundle, verification);
|
|
1553
1833
|
if (!methodVerification.valid) {
|
|
1554
|
-
return {
|
|
1834
|
+
return {
|
|
1835
|
+
valid: false,
|
|
1836
|
+
reason: methodVerification.reason,
|
|
1837
|
+
verification_class: methodVerification.verification_class,
|
|
1838
|
+
cryptographically_verified: false
|
|
1839
|
+
};
|
|
1555
1840
|
}
|
|
1556
1841
|
|
|
1557
1842
|
return {
|
|
@@ -1565,7 +1850,12 @@ function verifyProofBundle(bundle) {
|
|
|
1565
1850
|
verification_state: methodVerification.verification_state,
|
|
1566
1851
|
...(methodVerification.anchor_count != null ? { anchor_count: methodVerification.anchor_count } : {}),
|
|
1567
1852
|
...(methodVerification.anchor_root_sha256 ? { anchor_root_sha256: methodVerification.anchor_root_sha256 } : {}),
|
|
1568
|
-
...(methodVerification.signature_suite ? { signature_suite: methodVerification.signature_suite } : {})
|
|
1853
|
+
...(methodVerification.signature_suite ? { signature_suite: methodVerification.signature_suite } : {}),
|
|
1854
|
+
...(methodVerification.public_key_ref ? { public_key_ref: methodVerification.public_key_ref } : {}),
|
|
1855
|
+
...(methodVerification.classical_public_key_ref ? { classical_public_key_ref: methodVerification.classical_public_key_ref } : {}),
|
|
1856
|
+
...(methodVerification.pq_public_key_ref ? { pq_public_key_ref: methodVerification.pq_public_key_ref } : {}),
|
|
1857
|
+
verification_class: methodVerification.verification_class,
|
|
1858
|
+
cryptographically_verified: methodVerification.cryptographically_verified === true
|
|
1569
1859
|
};
|
|
1570
1860
|
} catch (error) {
|
|
1571
1861
|
return { valid: false, reason: error.message };
|
|
@@ -3207,6 +3497,9 @@ function buildxoonyaNativeHybridReadinessPack() {
|
|
|
3207
3497
|
atom_rule: "XNYA_atom_remains_commitment_only",
|
|
3208
3498
|
envelope_rule: "hybrid_or_pqc_material_lives_in_proof_envelope_or_profile",
|
|
3209
3499
|
current_deterministic_reject_reason: runtimeSupportedHybridSuites.length > 0 ? null : "native_hybrid_runtime_validator_unavailable",
|
|
3500
|
+
trust_root_reject_reason: NATIVE_HYBRID_PUBLIC_KEY_NOT_TRUSTED_REASON,
|
|
3501
|
+
strict_mode_reject_reasons: PROOF_VERIFICATION_STRICT_REJECTION_CODES.slice(),
|
|
3502
|
+
identity_binding_rule: "public_key_ref_is_self_bound_to_carried_key_material_identity_binding_requires_verifier_supplied_trust_root",
|
|
3210
3503
|
downgrade_rule: "hybrid_receipts_must_not_verify_as_classical_native_signed_receipts",
|
|
3211
3504
|
claim_rule: "claim_only_registered_runtime_supported_vector_covered_hybrid_suites"
|
|
3212
3505
|
},
|
|
@@ -6228,6 +6521,9 @@ module.exports = {
|
|
|
6228
6521
|
hashEvent,
|
|
6229
6522
|
emitReceipt,
|
|
6230
6523
|
listProofVerificationMethods,
|
|
6524
|
+
listProofVerificationClasses,
|
|
6525
|
+
PROOF_VERIFICATION_STRICT_REJECTION_CODES,
|
|
6526
|
+
PROOF_VERIFICATION_TRUST_ROOT_REJECTION_CODES,
|
|
6231
6527
|
getProofVerificationMethod,
|
|
6232
6528
|
listNativeSignatureSuites,
|
|
6233
6529
|
getNativeSignatureSuite,
|
|
Binary file
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const assert = require("assert");
|
|
4
|
+
const {
|
|
5
|
+
summarizeAdapterCertificationContractPack,
|
|
6
|
+
summarizeTrustPortabilityBoundaryContractPack,
|
|
7
|
+
summarizeSignedAuditProvenanceContractPack
|
|
8
|
+
} = require("../index.js");
|
|
9
|
+
|
|
10
|
+
function main() {
|
|
11
|
+
const adapterCertification = summarizeAdapterCertificationContractPack();
|
|
12
|
+
const trustPortabilityBoundary = summarizeTrustPortabilityBoundaryContractPack();
|
|
13
|
+
const signedAuditProvenance = summarizeSignedAuditProvenanceContractPack();
|
|
14
|
+
|
|
15
|
+
assert.strictEqual(adapterCertification.ok, true, "adapter certification summary failed");
|
|
16
|
+
assert.strictEqual(adapterCertification.certification_state_count >= 5, true, "adapter certification state count mismatch");
|
|
17
|
+
assert.strictEqual(adapterCertification.certification_front_count >= 8, true, "adapter certification front count mismatch");
|
|
18
|
+
assert.strictEqual(adapterCertification.required_test_category_count >= 8, true, "adapter certification test category count mismatch");
|
|
19
|
+
|
|
20
|
+
assert.strictEqual(trustPortabilityBoundary.ok, true, "trust portability boundary summary failed");
|
|
21
|
+
assert.strictEqual(trustPortabilityBoundary.portable_axis_count >= 4, true, "trust portability axis count mismatch");
|
|
22
|
+
assert.strictEqual(trustPortabilityBoundary.non_claim_count >= 4, true, "trust portability non-claim count mismatch");
|
|
23
|
+
assert.strictEqual(trustPortabilityBoundary.evidence_requirement_count >= 5, true, "trust portability evidence requirement count mismatch");
|
|
24
|
+
|
|
25
|
+
assert.strictEqual(signedAuditProvenance.ok, true, "signed audit provenance summary failed");
|
|
26
|
+
assert.strictEqual(signedAuditProvenance.evidence_surface_count >= 6, true, "signed audit provenance surface count mismatch");
|
|
27
|
+
assert.strictEqual(signedAuditProvenance.required_field_count >= 7, true, "signed audit provenance required field count mismatch");
|
|
28
|
+
assert.strictEqual(signedAuditProvenance.export_rule_count >= 4, true, "signed audit provenance export rule count mismatch");
|
|
29
|
+
|
|
30
|
+
console.log("xoonya verify:control-plane-contracts passed");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
main();
|
|
34
|
+
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const assert = require("assert");
|
|
4
|
+
const { createServer } = require("../server.js");
|
|
5
|
+
|
|
6
|
+
async function getJson(baseUrl, route) {
|
|
7
|
+
const response = await fetch(`${baseUrl}${route}`);
|
|
8
|
+
return {
|
|
9
|
+
status: response.status,
|
|
10
|
+
json: await response.json()
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function main() {
|
|
15
|
+
const server = createServer();
|
|
16
|
+
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
17
|
+
const address = server.address();
|
|
18
|
+
const baseUrl = `http://127.0.0.1:${address.port}`;
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const adapterCertification = await getJson(baseUrl, "/v1/adapter-certification");
|
|
22
|
+
assert.strictEqual(adapterCertification.status, 200, "adapter certification route failed");
|
|
23
|
+
assert.strictEqual(adapterCertification.json.category, "naxytra-adapter-certification-contract", "adapter certification category mismatch");
|
|
24
|
+
assert.strictEqual(adapterCertification.json.certification_fronts.length >= 8, true, "adapter certification front count mismatch");
|
|
25
|
+
|
|
26
|
+
const adapterCertificationSummary = await getJson(baseUrl, "/v1/adapter-certification/summary");
|
|
27
|
+
assert.strictEqual(adapterCertificationSummary.status, 200, "adapter certification summary route failed");
|
|
28
|
+
assert.strictEqual(adapterCertificationSummary.json.certification_state_count >= 5, true, "adapter certification summary count mismatch");
|
|
29
|
+
|
|
30
|
+
const adapterCertificationDetail = await getJson(baseUrl, "/v1/adapter-certification/protocol_front");
|
|
31
|
+
assert.strictEqual(adapterCertificationDetail.status, 200, "adapter certification detail route failed");
|
|
32
|
+
assert.strictEqual(adapterCertificationDetail.json.front.front_id, "protocol_front", "adapter certification detail mismatch");
|
|
33
|
+
|
|
34
|
+
const trustBoundary = await getJson(baseUrl, "/v1/trust-portability-boundary");
|
|
35
|
+
assert.strictEqual(trustBoundary.status, 200, "trust portability boundary route failed");
|
|
36
|
+
assert.strictEqual(trustBoundary.json.category, "naxytra-trust-portability-boundary-contract", "trust portability boundary category mismatch");
|
|
37
|
+
assert.strictEqual(trustBoundary.json.portable_axes.length >= 4, true, "trust portability boundary axis count mismatch");
|
|
38
|
+
|
|
39
|
+
const trustBoundarySummary = await getJson(baseUrl, "/v1/trust-portability-boundary/summary");
|
|
40
|
+
assert.strictEqual(trustBoundarySummary.status, 200, "trust portability boundary summary route failed");
|
|
41
|
+
assert.strictEqual(trustBoundarySummary.json.portable_axis_count >= 4, true, "trust portability boundary summary count mismatch");
|
|
42
|
+
|
|
43
|
+
const trustBoundaryDetail = await getJson(baseUrl, "/v1/trust-portability-boundary/agent_identity_portability");
|
|
44
|
+
assert.strictEqual(trustBoundaryDetail.status, 200, "trust portability boundary detail route failed");
|
|
45
|
+
assert.strictEqual(trustBoundaryDetail.json.axis.axis_id, "agent_identity_portability", "trust portability boundary detail mismatch");
|
|
46
|
+
|
|
47
|
+
const signedAudit = await getJson(baseUrl, "/v1/signed-audit-provenance");
|
|
48
|
+
assert.strictEqual(signedAudit.status, 200, "signed audit provenance route failed");
|
|
49
|
+
assert.strictEqual(signedAudit.json.category, "naxytra-signed-audit-provenance-contract", "signed audit provenance category mismatch");
|
|
50
|
+
assert.strictEqual(signedAudit.json.evidence_surfaces.length >= 6, true, "signed audit provenance evidence surface count mismatch");
|
|
51
|
+
|
|
52
|
+
const signedAuditSummary = await getJson(baseUrl, "/v1/signed-audit-provenance/summary");
|
|
53
|
+
assert.strictEqual(signedAuditSummary.status, 200, "signed audit provenance summary route failed");
|
|
54
|
+
assert.strictEqual(signedAuditSummary.json.evidence_surface_count >= 6, true, "signed audit provenance summary count mismatch");
|
|
55
|
+
|
|
56
|
+
const signedAuditDetail = await getJson(baseUrl, "/v1/signed-audit-provenance/release_provenance");
|
|
57
|
+
assert.strictEqual(signedAuditDetail.status, 200, "signed audit provenance detail route failed");
|
|
58
|
+
assert.strictEqual(signedAuditDetail.json.surface.surface_id, "release_provenance", "signed audit provenance detail mismatch");
|
|
59
|
+
} finally {
|
|
60
|
+
await new Promise((resolve) => server.close(resolve));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log("xoonya verify:control-plane-routes passed");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
main().catch((error) => {
|
|
67
|
+
console.error(error);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
});
|
|
70
|
+
|