@nextera.one/axis-server-sdk 1.5.0 → 1.7.0
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/index.d.mts +1358 -832
- package/dist/index.d.ts +1358 -832
- package/dist/index.js +1597 -198
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1592 -206
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/dist/index.mjs
CHANGED
|
@@ -405,6 +405,626 @@ var SensorDecisions = {
|
|
|
405
405
|
}
|
|
406
406
|
};
|
|
407
407
|
|
|
408
|
+
// src/cce/cce-derivation.service.ts
|
|
409
|
+
import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
|
|
410
|
+
import { hkdf } from "@noble/hashes/hkdf.js";
|
|
411
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
412
|
+
|
|
413
|
+
// src/cce/cce.types.ts
|
|
414
|
+
var CCE_PROTOCOL_VERSION = "cce-v1";
|
|
415
|
+
var CCE_DERIVATION = {
|
|
416
|
+
/** Request execution context */
|
|
417
|
+
REQUEST: "axis:cce:req:v1",
|
|
418
|
+
/** Response execution context */
|
|
419
|
+
RESPONSE: "axis:cce:resp:v1",
|
|
420
|
+
/** Witness binding context */
|
|
421
|
+
WITNESS: "axis:cce:witness:v1"
|
|
422
|
+
};
|
|
423
|
+
var CCE_AES_KEY_BYTES = 32;
|
|
424
|
+
var CCE_IV_BYTES = 12;
|
|
425
|
+
var CCE_TAG_BYTES = 16;
|
|
426
|
+
var CCE_NONCE_BYTES = 32;
|
|
427
|
+
var CCE_ERROR = {
|
|
428
|
+
// Envelope errors
|
|
429
|
+
INVALID_ENVELOPE: "CCE_INVALID_ENVELOPE",
|
|
430
|
+
UNSUPPORTED_VERSION: "CCE_UNSUPPORTED_VERSION",
|
|
431
|
+
MISSING_CAPSULE: "CCE_MISSING_CAPSULE",
|
|
432
|
+
MISSING_ENCRYPTED_KEY: "CCE_MISSING_ENCRYPTED_KEY",
|
|
433
|
+
// Signature errors
|
|
434
|
+
CLIENT_SIG_INVALID: "CCE_CLIENT_SIG_INVALID",
|
|
435
|
+
CLIENT_KEY_NOT_FOUND: "CCE_CLIENT_KEY_NOT_FOUND",
|
|
436
|
+
// Capsule errors
|
|
437
|
+
CAPSULE_SIG_INVALID: "CCE_CAPSULE_SIG_INVALID",
|
|
438
|
+
CAPSULE_EXPIRED: "CCE_CAPSULE_EXPIRED",
|
|
439
|
+
CAPSULE_NOT_YET_VALID: "CCE_CAPSULE_NOT_YET_VALID",
|
|
440
|
+
CAPSULE_REVOKED: "CCE_CAPSULE_REVOKED",
|
|
441
|
+
CAPSULE_CONSUMED: "CCE_CAPSULE_CONSUMED",
|
|
442
|
+
// Binding errors
|
|
443
|
+
AUDIENCE_MISMATCH: "CCE_AUDIENCE_MISMATCH",
|
|
444
|
+
INTENT_MISMATCH: "CCE_INTENT_MISMATCH",
|
|
445
|
+
TPS_WINDOW_EXPIRED: "CCE_TPS_WINDOW_EXPIRED",
|
|
446
|
+
TPS_WINDOW_FUTURE: "CCE_TPS_WINDOW_FUTURE",
|
|
447
|
+
// Replay / nonce errors
|
|
448
|
+
REPLAY_DETECTED: "CCE_REPLAY_DETECTED",
|
|
449
|
+
NONCE_REUSED: "CCE_NONCE_REUSED",
|
|
450
|
+
// Decryption errors
|
|
451
|
+
DECRYPTION_FAILED: "CCE_DECRYPTION_FAILED",
|
|
452
|
+
KEY_UNWRAP_FAILED: "CCE_KEY_UNWRAP_FAILED",
|
|
453
|
+
AEAD_TAG_MISMATCH: "CCE_AEAD_TAG_MISMATCH",
|
|
454
|
+
PAYLOAD_TOO_LARGE: "CCE_PAYLOAD_TOO_LARGE",
|
|
455
|
+
// Schema / validation errors
|
|
456
|
+
PAYLOAD_SCHEMA_INVALID: "CCE_PAYLOAD_SCHEMA_INVALID",
|
|
457
|
+
INTENT_SCHEMA_MISMATCH: "CCE_INTENT_SCHEMA_MISMATCH",
|
|
458
|
+
// Policy errors
|
|
459
|
+
POLICY_DENIED: "CCE_POLICY_DENIED",
|
|
460
|
+
CONSTRAINT_VIOLATED: "CCE_CONSTRAINT_VIOLATED",
|
|
461
|
+
// Handler errors
|
|
462
|
+
HANDLER_NOT_FOUND: "CCE_HANDLER_NOT_FOUND",
|
|
463
|
+
HANDLER_EXECUTION_FAILED: "CCE_HANDLER_EXECUTION_FAILED",
|
|
464
|
+
HANDLER_TIMEOUT: "CCE_HANDLER_TIMEOUT",
|
|
465
|
+
// Response errors
|
|
466
|
+
RESPONSE_ENCRYPTION_FAILED: "CCE_RESPONSE_ENCRYPTION_FAILED"
|
|
467
|
+
};
|
|
468
|
+
var CceError = class extends Error {
|
|
469
|
+
constructor(code, message, metadata) {
|
|
470
|
+
super(`[${code}] ${message}`);
|
|
471
|
+
this.code = code;
|
|
472
|
+
this.metadata = metadata;
|
|
473
|
+
this.name = "CceError";
|
|
474
|
+
}
|
|
475
|
+
/** Whether this error is safe to expose to the client */
|
|
476
|
+
get clientSafe() {
|
|
477
|
+
const internal = [
|
|
478
|
+
CCE_ERROR.DECRYPTION_FAILED,
|
|
479
|
+
CCE_ERROR.KEY_UNWRAP_FAILED,
|
|
480
|
+
CCE_ERROR.AEAD_TAG_MISMATCH,
|
|
481
|
+
CCE_ERROR.HANDLER_EXECUTION_FAILED,
|
|
482
|
+
CCE_ERROR.RESPONSE_ENCRYPTION_FAILED
|
|
483
|
+
];
|
|
484
|
+
return !internal.includes(this.code);
|
|
485
|
+
}
|
|
486
|
+
/** Get client-safe representation */
|
|
487
|
+
toClientError() {
|
|
488
|
+
if (this.clientSafe) {
|
|
489
|
+
return { code: this.code, message: this.message };
|
|
490
|
+
}
|
|
491
|
+
return {
|
|
492
|
+
code: CCE_ERROR.DECRYPTION_FAILED,
|
|
493
|
+
message: "Request processing failed"
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
// src/cce/cce-derivation.service.ts
|
|
499
|
+
function buildSalt(capsuleId, capsuleNonce, requestNonce) {
|
|
500
|
+
const encoder = new TextEncoder();
|
|
501
|
+
const data = encoder.encode(
|
|
502
|
+
capsuleId + "|" + capsuleNonce + "|" + requestNonce
|
|
503
|
+
);
|
|
504
|
+
return sha256(data);
|
|
505
|
+
}
|
|
506
|
+
function buildInfo(contextPrefix, capsule, extraNonce) {
|
|
507
|
+
const encoder = new TextEncoder();
|
|
508
|
+
const parts = [
|
|
509
|
+
contextPrefix,
|
|
510
|
+
capsule.sub,
|
|
511
|
+
capsule.kid,
|
|
512
|
+
capsule.intent,
|
|
513
|
+
capsule.aud,
|
|
514
|
+
String(capsule.tps_from),
|
|
515
|
+
String(capsule.tps_to),
|
|
516
|
+
capsule.policy_hash ?? "",
|
|
517
|
+
capsule.ver
|
|
518
|
+
];
|
|
519
|
+
if (extraNonce) {
|
|
520
|
+
parts.push(extraNonce);
|
|
521
|
+
}
|
|
522
|
+
return encoder.encode(parts.join("|"));
|
|
523
|
+
}
|
|
524
|
+
function deriveRequestExecutionKey(input) {
|
|
525
|
+
const ikm = hexToBytes(input.axisLocalSecret);
|
|
526
|
+
const salt = buildSalt(
|
|
527
|
+
input.capsule.capsule_id,
|
|
528
|
+
input.capsule.capsule_nonce,
|
|
529
|
+
input.requestNonce
|
|
530
|
+
);
|
|
531
|
+
const info = buildInfo(CCE_DERIVATION.REQUEST, input.capsule);
|
|
532
|
+
return hkdf(sha256, ikm, salt, info, CCE_AES_KEY_BYTES);
|
|
533
|
+
}
|
|
534
|
+
function deriveResponseExecutionKey(input) {
|
|
535
|
+
const ikm = hexToBytes(input.axisLocalSecret);
|
|
536
|
+
const encoder = new TextEncoder();
|
|
537
|
+
const saltData = encoder.encode(
|
|
538
|
+
input.capsule.capsule_id + "|" + input.capsule.capsule_nonce + "|" + input.requestNonce + "|" + input.responseNonce
|
|
539
|
+
);
|
|
540
|
+
const salt = sha256(saltData);
|
|
541
|
+
const info = buildInfo(
|
|
542
|
+
CCE_DERIVATION.RESPONSE,
|
|
543
|
+
input.capsule,
|
|
544
|
+
input.responseNonce
|
|
545
|
+
);
|
|
546
|
+
return hkdf(sha256, ikm, salt, info, CCE_AES_KEY_BYTES);
|
|
547
|
+
}
|
|
548
|
+
function deriveWitnessKey(input) {
|
|
549
|
+
const ikm = hexToBytes(input.axisLocalSecret);
|
|
550
|
+
const salt = buildSalt(
|
|
551
|
+
input.capsule.capsule_id,
|
|
552
|
+
input.capsule.capsule_nonce,
|
|
553
|
+
input.requestNonce
|
|
554
|
+
);
|
|
555
|
+
const info = buildInfo(CCE_DERIVATION.WITNESS, input.capsule);
|
|
556
|
+
return hkdf(sha256, ikm, salt, info, CCE_AES_KEY_BYTES);
|
|
557
|
+
}
|
|
558
|
+
function buildExecutionContext(input, requestId) {
|
|
559
|
+
const executionKey = deriveRequestExecutionKey(input);
|
|
560
|
+
const keyHash = bytesToHex(sha256(executionKey));
|
|
561
|
+
executionKey.fill(0);
|
|
562
|
+
return {
|
|
563
|
+
execution_key_hash: keyHash,
|
|
564
|
+
request_id: requestId,
|
|
565
|
+
capsule_id: input.capsule.capsule_id,
|
|
566
|
+
sub: input.capsule.sub,
|
|
567
|
+
kid: input.capsule.kid,
|
|
568
|
+
intent: input.capsule.intent,
|
|
569
|
+
aud: input.capsule.aud,
|
|
570
|
+
tps_from: input.capsule.tps_from,
|
|
571
|
+
tps_to: input.capsule.tps_to,
|
|
572
|
+
policy_hash: input.capsule.policy_hash,
|
|
573
|
+
derived_at: Math.floor(Date.now() / 1e3),
|
|
574
|
+
valid: true
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
function generateCceNonce() {
|
|
578
|
+
const bytes2 = new Uint8Array(CCE_NONCE_BYTES);
|
|
579
|
+
crypto.getRandomValues(bytes2);
|
|
580
|
+
return bytesToHex(bytes2);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// src/cce/cce-response.service.ts
|
|
584
|
+
import { bytesToHex as bytesToHex3 } from "@noble/hashes/utils.js";
|
|
585
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
586
|
+
|
|
587
|
+
// src/cce/cce-crypto.ts
|
|
588
|
+
import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils.js";
|
|
589
|
+
import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
|
|
590
|
+
import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
|
|
591
|
+
function aesGcmEncrypt(key, plaintext, aad) {
|
|
592
|
+
if (key.length !== CCE_AES_KEY_BYTES) {
|
|
593
|
+
throw new Error(`AES key must be ${CCE_AES_KEY_BYTES} bytes`);
|
|
594
|
+
}
|
|
595
|
+
const iv = randomBytes(CCE_IV_BYTES);
|
|
596
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
597
|
+
if (aad) {
|
|
598
|
+
cipher.setAAD(aad);
|
|
599
|
+
}
|
|
600
|
+
const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
601
|
+
const tag = cipher.getAuthTag();
|
|
602
|
+
return {
|
|
603
|
+
iv: new Uint8Array(iv),
|
|
604
|
+
ciphertext: new Uint8Array(encrypted),
|
|
605
|
+
tag: new Uint8Array(tag)
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
function aesGcmDecrypt(key, iv, ciphertext, tag, aad) {
|
|
609
|
+
if (key.length !== CCE_AES_KEY_BYTES) {
|
|
610
|
+
throw new Error(`AES key must be ${CCE_AES_KEY_BYTES} bytes`);
|
|
611
|
+
}
|
|
612
|
+
if (iv.length !== CCE_IV_BYTES) {
|
|
613
|
+
throw new Error(`IV must be ${CCE_IV_BYTES} bytes`);
|
|
614
|
+
}
|
|
615
|
+
if (tag.length !== CCE_TAG_BYTES) {
|
|
616
|
+
throw new Error(`Tag must be ${CCE_TAG_BYTES} bytes`);
|
|
617
|
+
}
|
|
618
|
+
try {
|
|
619
|
+
const decipher = createDecipheriv("aes-256-gcm", key, iv);
|
|
620
|
+
decipher.setAuthTag(tag);
|
|
621
|
+
if (aad) {
|
|
622
|
+
decipher.setAAD(aad);
|
|
623
|
+
}
|
|
624
|
+
const decrypted = Buffer.concat([
|
|
625
|
+
decipher.update(ciphertext),
|
|
626
|
+
decipher.final()
|
|
627
|
+
]);
|
|
628
|
+
return new Uint8Array(decrypted);
|
|
629
|
+
} catch {
|
|
630
|
+
return null;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
function generateAesKey() {
|
|
634
|
+
return new Uint8Array(randomBytes(CCE_AES_KEY_BYTES));
|
|
635
|
+
}
|
|
636
|
+
function generateIv() {
|
|
637
|
+
return new Uint8Array(randomBytes(CCE_IV_BYTES));
|
|
638
|
+
}
|
|
639
|
+
function base64UrlEncode(bytes2) {
|
|
640
|
+
return Buffer.from(bytes2).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
641
|
+
}
|
|
642
|
+
function base64UrlDecode(input) {
|
|
643
|
+
const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
644
|
+
const padding = "=".repeat((4 - base64.length % 4) % 4);
|
|
645
|
+
return new Uint8Array(Buffer.from(base64 + padding, "base64"));
|
|
646
|
+
}
|
|
647
|
+
function hashPayload(payload) {
|
|
648
|
+
return bytesToHex2(sha2562(payload));
|
|
649
|
+
}
|
|
650
|
+
var nodeAesGcmProvider = {
|
|
651
|
+
async decrypt(key, iv, ciphertext, tag, aad) {
|
|
652
|
+
return aesGcmDecrypt(key, iv, ciphertext, tag, aad);
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
// src/cce/cce-response.service.ts
|
|
657
|
+
async function buildCceResponse(options, clientKeyEncryptor, axisSigner) {
|
|
658
|
+
const { request, capsule, status, body, clientPublicKeyHex, witnessRef } = options;
|
|
659
|
+
const responseNonce = bytesToHex3(
|
|
660
|
+
new Uint8Array(randomBytes2(CCE_NONCE_BYTES))
|
|
661
|
+
);
|
|
662
|
+
const responseId = generateResponseId();
|
|
663
|
+
const aesKey = generateAesKey();
|
|
664
|
+
const aad = buildResponseAad(
|
|
665
|
+
request.request_id,
|
|
666
|
+
responseId,
|
|
667
|
+
request.correlation_id,
|
|
668
|
+
capsule.capsule_id,
|
|
669
|
+
responseNonce
|
|
670
|
+
);
|
|
671
|
+
const { iv, ciphertext, tag } = aesGcmEncrypt(aesKey, body, aad);
|
|
672
|
+
const encryptedKey = await clientKeyEncryptor.wrapKey(
|
|
673
|
+
aesKey,
|
|
674
|
+
request.client_kid,
|
|
675
|
+
clientPublicKeyHex
|
|
676
|
+
);
|
|
677
|
+
aesKey.fill(0);
|
|
678
|
+
const encryptedPayload = {
|
|
679
|
+
alg: "AES-256-GCM",
|
|
680
|
+
iv: base64UrlEncode(iv),
|
|
681
|
+
ciphertext: base64UrlEncode(ciphertext),
|
|
682
|
+
tag: base64UrlEncode(tag)
|
|
683
|
+
};
|
|
684
|
+
const algorithms = {
|
|
685
|
+
kem: encryptedKey.alg,
|
|
686
|
+
enc: "AES-256-GCM",
|
|
687
|
+
kdf: "HKDF-SHA256",
|
|
688
|
+
sig: "EdDSA"
|
|
689
|
+
};
|
|
690
|
+
const unsignedResponse = {
|
|
691
|
+
ver: CCE_PROTOCOL_VERSION,
|
|
692
|
+
response_id: responseId,
|
|
693
|
+
request_id: request.request_id,
|
|
694
|
+
correlation_id: request.correlation_id,
|
|
695
|
+
encrypted_key: encryptedKey,
|
|
696
|
+
encrypted_payload: encryptedPayload,
|
|
697
|
+
response_nonce: responseNonce,
|
|
698
|
+
algorithms,
|
|
699
|
+
status,
|
|
700
|
+
...witnessRef ? { witness_ref: witnessRef } : {}
|
|
701
|
+
};
|
|
702
|
+
const signPayload = new TextEncoder().encode(canonicalize(unsignedResponse));
|
|
703
|
+
const axisSig = await axisSigner.sign(signPayload);
|
|
704
|
+
const envelope = {
|
|
705
|
+
...unsignedResponse,
|
|
706
|
+
axis_sig: axisSig
|
|
707
|
+
};
|
|
708
|
+
return {
|
|
709
|
+
envelope,
|
|
710
|
+
responsePayloadHash: hashPayload(body)
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
function buildCceErrorResponse(requestId, correlationId, status, errorCode, message) {
|
|
714
|
+
return {
|
|
715
|
+
ver: CCE_PROTOCOL_VERSION,
|
|
716
|
+
request_id: requestId,
|
|
717
|
+
correlation_id: correlationId,
|
|
718
|
+
status,
|
|
719
|
+
error: { code: errorCode, message }
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
function generateResponseId() {
|
|
723
|
+
const bytes2 = randomBytes2(16);
|
|
724
|
+
return "resp_" + bytesToHex3(new Uint8Array(bytes2)).slice(0, 24);
|
|
725
|
+
}
|
|
726
|
+
function buildResponseAad(requestId, responseId, correlationId, capsuleId, responseNonce) {
|
|
727
|
+
const parts = [
|
|
728
|
+
requestId,
|
|
729
|
+
responseId,
|
|
730
|
+
correlationId,
|
|
731
|
+
capsuleId,
|
|
732
|
+
responseNonce
|
|
733
|
+
];
|
|
734
|
+
return new TextEncoder().encode(parts.join("|"));
|
|
735
|
+
}
|
|
736
|
+
function canonicalize(obj) {
|
|
737
|
+
if (Array.isArray(obj)) {
|
|
738
|
+
return "[" + obj.map(canonicalize).join(",") + "]";
|
|
739
|
+
}
|
|
740
|
+
if (obj !== null && typeof obj === "object") {
|
|
741
|
+
const sorted = Object.keys(obj).sort().map(
|
|
742
|
+
(k) => JSON.stringify(k) + ":" + canonicalize(obj[k])
|
|
743
|
+
);
|
|
744
|
+
return "{" + sorted.join(",") + "}";
|
|
745
|
+
}
|
|
746
|
+
return JSON.stringify(obj);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// src/cce/cce-witness.observer.ts
|
|
750
|
+
import { bytesToHex as bytesToHex4 } from "@noble/hashes/utils.js";
|
|
751
|
+
import { hkdf as hkdf2 } from "@noble/hashes/hkdf.js";
|
|
752
|
+
import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
|
|
753
|
+
var InMemoryCceWitnessStore = class {
|
|
754
|
+
constructor() {
|
|
755
|
+
this.records = [];
|
|
756
|
+
}
|
|
757
|
+
async record(witness) {
|
|
758
|
+
this.records.push(witness);
|
|
759
|
+
}
|
|
760
|
+
getByRequestId(requestId) {
|
|
761
|
+
return this.records.find((w) => w.request_id === requestId);
|
|
762
|
+
}
|
|
763
|
+
getByCapsuleId(capsuleId) {
|
|
764
|
+
return this.records.filter((w) => w.capsule_id === capsuleId);
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
function buildWitnessRecord(envelope, capsule, verification, execution, options) {
|
|
768
|
+
const witnessId = generateWitnessId(envelope.request_id, capsule.capsule_id);
|
|
769
|
+
const executionContextHash = computeExecutionContextHash(
|
|
770
|
+
options.axisLocalSecret,
|
|
771
|
+
capsule,
|
|
772
|
+
envelope.request_nonce
|
|
773
|
+
);
|
|
774
|
+
return {
|
|
775
|
+
witness_id: witnessId,
|
|
776
|
+
request_id: envelope.request_id,
|
|
777
|
+
capsule_id: capsule.capsule_id,
|
|
778
|
+
sub: capsule.sub,
|
|
779
|
+
intent: capsule.intent,
|
|
780
|
+
aud: capsule.aud,
|
|
781
|
+
tps_from: capsule.tps_from,
|
|
782
|
+
tps_to: capsule.tps_to,
|
|
783
|
+
timestamp: Math.floor(Date.now() / 1e3),
|
|
784
|
+
verification: {
|
|
785
|
+
client_sig: verification.clientSigVerified,
|
|
786
|
+
capsule_sig: verification.capsuleSigVerified,
|
|
787
|
+
tps_valid: verification.tpsValid,
|
|
788
|
+
audience_match: verification.audienceMatch,
|
|
789
|
+
intent_match: verification.intentMatch,
|
|
790
|
+
replay_clean: verification.replayClean,
|
|
791
|
+
nonce_unique: verification.nonceUnique,
|
|
792
|
+
decryption_ok: verification.decryptionOk
|
|
793
|
+
},
|
|
794
|
+
execution: {
|
|
795
|
+
status: execution.status,
|
|
796
|
+
handler_duration_ms: execution.handlerDurationMs,
|
|
797
|
+
...execution.effect ? { effect: execution.effect } : {}
|
|
798
|
+
},
|
|
799
|
+
response_encrypted: options.responseEncrypted,
|
|
800
|
+
execution_context_hash: executionContextHash,
|
|
801
|
+
...options.requestPayload ? { request_payload_hash: hashPayload(options.requestPayload) } : {},
|
|
802
|
+
...options.responsePayload ? { response_payload_hash: hashPayload(options.responsePayload) } : {}
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
function extractVerificationState(metadata) {
|
|
806
|
+
return {
|
|
807
|
+
clientSigVerified: metadata.cceClientSigVerified === true,
|
|
808
|
+
capsuleSigVerified: metadata.cceCapsuleVerified === true,
|
|
809
|
+
tpsValid: metadata.cceTpsValid === true,
|
|
810
|
+
audienceMatch: metadata.cceBindingVerified === true,
|
|
811
|
+
intentMatch: metadata.cceBindingVerified === true,
|
|
812
|
+
replayClean: metadata.cceReplayClean === true,
|
|
813
|
+
nonceUnique: metadata.cceReplayClean === true,
|
|
814
|
+
decryptionOk: metadata.cceDecryptionOk === true
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
function generateWitnessId(requestId, capsuleId) {
|
|
818
|
+
const input = `witness:${requestId}:${capsuleId}:${Date.now()}`;
|
|
819
|
+
const hash = sha2563(new TextEncoder().encode(input));
|
|
820
|
+
return "wit_" + bytesToHex4(hash).slice(0, 24);
|
|
821
|
+
}
|
|
822
|
+
function computeExecutionContextHash(axisLocalSecret, capsule, requestNonce) {
|
|
823
|
+
const encoder = new TextEncoder();
|
|
824
|
+
const ikm = hexToBytes2(axisLocalSecret);
|
|
825
|
+
const salt = sha2563(
|
|
826
|
+
encoder.encode(
|
|
827
|
+
capsule.capsule_id + "|" + capsule.capsule_nonce + "|" + requestNonce
|
|
828
|
+
)
|
|
829
|
+
);
|
|
830
|
+
const info = encoder.encode(
|
|
831
|
+
[
|
|
832
|
+
CCE_DERIVATION.WITNESS,
|
|
833
|
+
capsule.sub,
|
|
834
|
+
capsule.kid,
|
|
835
|
+
capsule.intent,
|
|
836
|
+
capsule.aud,
|
|
837
|
+
String(capsule.tps_from),
|
|
838
|
+
String(capsule.tps_to),
|
|
839
|
+
capsule.policy_hash ?? "",
|
|
840
|
+
capsule.ver
|
|
841
|
+
].join("|")
|
|
842
|
+
);
|
|
843
|
+
const witnessKey = hkdf2(sha2563, ikm, salt, info, 32);
|
|
844
|
+
const hash = bytesToHex4(sha2563(witnessKey));
|
|
845
|
+
witnessKey.fill(0);
|
|
846
|
+
return hash;
|
|
847
|
+
}
|
|
848
|
+
function hexToBytes2(hex) {
|
|
849
|
+
const bytes2 = new Uint8Array(hex.length / 2);
|
|
850
|
+
for (let i = 0; i < bytes2.length; i++) {
|
|
851
|
+
bytes2[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
852
|
+
}
|
|
853
|
+
return bytes2;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// src/cce/cce-pipeline.ts
|
|
857
|
+
async function executeCcePipeline(envelope, config) {
|
|
858
|
+
const startTime = Date.now();
|
|
859
|
+
if (envelope.ver !== CCE_PROTOCOL_VERSION) {
|
|
860
|
+
return {
|
|
861
|
+
ok: false,
|
|
862
|
+
error: {
|
|
863
|
+
code: CCE_ERROR.UNSUPPORTED_VERSION,
|
|
864
|
+
message: `Unsupported version: ${envelope.ver}`
|
|
865
|
+
},
|
|
866
|
+
status: "ERROR"
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
const sensorInput = {
|
|
870
|
+
intent: envelope.capsule.intent,
|
|
871
|
+
metadata: {
|
|
872
|
+
cce: true,
|
|
873
|
+
cceEnvelope: envelope,
|
|
874
|
+
contentType: "application/axis-cce"
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
const sortedSensors = [...config.sensors].sort(
|
|
878
|
+
(a, b) => (a.order ?? 999) - (b.order ?? 999)
|
|
879
|
+
);
|
|
880
|
+
for (const sensor of sortedSensors) {
|
|
881
|
+
if (sensor.supports && !sensor.supports(sensorInput)) {
|
|
882
|
+
continue;
|
|
883
|
+
}
|
|
884
|
+
let decision;
|
|
885
|
+
try {
|
|
886
|
+
decision = await sensor.run(sensorInput);
|
|
887
|
+
} catch (err) {
|
|
888
|
+
return {
|
|
889
|
+
ok: false,
|
|
890
|
+
error: {
|
|
891
|
+
code: CCE_ERROR.DECRYPTION_FAILED,
|
|
892
|
+
message: `Sensor ${sensor.name} failed`
|
|
893
|
+
},
|
|
894
|
+
status: "ERROR"
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
const normalized = normalizeSensorDecision(decision);
|
|
898
|
+
if (!normalized.allow) {
|
|
899
|
+
const code = normalized.reasons[0]?.split(":")[0] ?? CCE_ERROR.DECRYPTION_FAILED;
|
|
900
|
+
return {
|
|
901
|
+
ok: false,
|
|
902
|
+
error: { code, message: normalized.reasons.join("; ") },
|
|
903
|
+
status: "DENIED"
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
const capsule = sensorInput.metadata?.cceCapsule;
|
|
908
|
+
const decryptedPayload = sensorInput.metadata?.cceDecryptedPayload;
|
|
909
|
+
const clientKey = sensorInput.metadata?.cceClientKey;
|
|
910
|
+
if (!capsule || !decryptedPayload || !clientKey) {
|
|
911
|
+
return {
|
|
912
|
+
ok: false,
|
|
913
|
+
error: {
|
|
914
|
+
code: CCE_ERROR.DECRYPTION_FAILED,
|
|
915
|
+
message: "Sensor chain did not produce required outputs"
|
|
916
|
+
},
|
|
917
|
+
status: "ERROR"
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
const derivationInput = {
|
|
921
|
+
axisLocalSecret: config.axisLocalSecret,
|
|
922
|
+
capsule,
|
|
923
|
+
requestNonce: envelope.request_nonce
|
|
924
|
+
};
|
|
925
|
+
const executionContext = buildExecutionContext(
|
|
926
|
+
derivationInput,
|
|
927
|
+
envelope.request_id
|
|
928
|
+
);
|
|
929
|
+
const handler = config.handlers.get(capsule.intent);
|
|
930
|
+
if (!handler) {
|
|
931
|
+
return {
|
|
932
|
+
ok: false,
|
|
933
|
+
error: {
|
|
934
|
+
code: CCE_ERROR.HANDLER_NOT_FOUND,
|
|
935
|
+
message: `No handler for intent: ${capsule.intent}`
|
|
936
|
+
},
|
|
937
|
+
status: "ERROR"
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
const handlerContext = {
|
|
941
|
+
capsule,
|
|
942
|
+
executionContext,
|
|
943
|
+
envelope,
|
|
944
|
+
clientPublicKeyHex: clientKey.publicKeyHex,
|
|
945
|
+
intent: capsule.intent,
|
|
946
|
+
sub: capsule.sub
|
|
947
|
+
};
|
|
948
|
+
let result;
|
|
949
|
+
const handlerStart = Date.now();
|
|
950
|
+
try {
|
|
951
|
+
result = await handler(decryptedPayload, handlerContext);
|
|
952
|
+
} catch (err) {
|
|
953
|
+
const handlerDuration2 = Date.now() - handlerStart;
|
|
954
|
+
const verification2 = extractVerificationState(sensorInput.metadata ?? {});
|
|
955
|
+
const witness2 = buildWitnessRecord(
|
|
956
|
+
envelope,
|
|
957
|
+
capsule,
|
|
958
|
+
verification2,
|
|
959
|
+
{ status: "FAILED", handlerDurationMs: handlerDuration2 },
|
|
960
|
+
{
|
|
961
|
+
axisLocalSecret: config.axisLocalSecret,
|
|
962
|
+
requestPayload: decryptedPayload,
|
|
963
|
+
responseEncrypted: false
|
|
964
|
+
}
|
|
965
|
+
);
|
|
966
|
+
await config.witnessStore.record(witness2);
|
|
967
|
+
return {
|
|
968
|
+
ok: false,
|
|
969
|
+
error: {
|
|
970
|
+
code: CCE_ERROR.HANDLER_EXECUTION_FAILED,
|
|
971
|
+
message: "Handler execution failed"
|
|
972
|
+
},
|
|
973
|
+
status: "FAILED"
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
const handlerDuration = Date.now() - handlerStart;
|
|
977
|
+
let responseEnvelope;
|
|
978
|
+
let responsePayloadHash;
|
|
979
|
+
try {
|
|
980
|
+
const responseResult = await buildCceResponse(
|
|
981
|
+
{
|
|
982
|
+
request: envelope,
|
|
983
|
+
capsule,
|
|
984
|
+
status: result.status,
|
|
985
|
+
body: result.body,
|
|
986
|
+
clientPublicKeyHex: clientKey.publicKeyHex
|
|
987
|
+
},
|
|
988
|
+
config.clientKeyEncryptor,
|
|
989
|
+
config.axisSigner
|
|
990
|
+
);
|
|
991
|
+
responseEnvelope = responseResult.envelope;
|
|
992
|
+
responsePayloadHash = responseResult.responsePayloadHash;
|
|
993
|
+
} catch (err) {
|
|
994
|
+
return {
|
|
995
|
+
ok: false,
|
|
996
|
+
error: {
|
|
997
|
+
code: CCE_ERROR.RESPONSE_ENCRYPTION_FAILED,
|
|
998
|
+
message: "Response encryption failed"
|
|
999
|
+
},
|
|
1000
|
+
status: "ERROR"
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
const verification = extractVerificationState(sensorInput.metadata ?? {});
|
|
1004
|
+
const witness = buildWitnessRecord(
|
|
1005
|
+
envelope,
|
|
1006
|
+
capsule,
|
|
1007
|
+
verification,
|
|
1008
|
+
{
|
|
1009
|
+
status: result.status,
|
|
1010
|
+
handlerDurationMs: handlerDuration,
|
|
1011
|
+
effect: result.effect
|
|
1012
|
+
},
|
|
1013
|
+
{
|
|
1014
|
+
axisLocalSecret: config.axisLocalSecret,
|
|
1015
|
+
requestPayload: decryptedPayload,
|
|
1016
|
+
responsePayload: result.body,
|
|
1017
|
+
responseEncrypted: true
|
|
1018
|
+
}
|
|
1019
|
+
);
|
|
1020
|
+
await config.witnessStore.record(witness);
|
|
1021
|
+
return {
|
|
1022
|
+
ok: true,
|
|
1023
|
+
response: responseEnvelope,
|
|
1024
|
+
witnessId: witness.witness_id
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
|
|
408
1028
|
// src/engine/intent.router.ts
|
|
409
1029
|
var IntentRouter = class {
|
|
410
1030
|
constructor(moduleRef) {
|
|
@@ -422,6 +1042,10 @@ var IntentRouter = class {
|
|
|
422
1042
|
this.intentValidators = /* @__PURE__ */ new Map();
|
|
423
1043
|
/** Per-intent operation kind */
|
|
424
1044
|
this.intentKinds = /* @__PURE__ */ new Map();
|
|
1045
|
+
/** CCE handler registry */
|
|
1046
|
+
this.cceHandlers = /* @__PURE__ */ new Map();
|
|
1047
|
+
/** CCE pipeline configuration (set via configureCce) */
|
|
1048
|
+
this.ccePipelineConfig = null;
|
|
425
1049
|
}
|
|
426
1050
|
getSchema(intent) {
|
|
427
1051
|
return this.intentSchemas.get(intent);
|
|
@@ -685,6 +1309,58 @@ var IntentRouter = class {
|
|
|
685
1309
|
}
|
|
686
1310
|
}
|
|
687
1311
|
}
|
|
1312
|
+
// ===========================================================================
|
|
1313
|
+
// CCE — Capsule-Carried Encryption Support
|
|
1314
|
+
// ===========================================================================
|
|
1315
|
+
/**
|
|
1316
|
+
* Configure the CCE pipeline.
|
|
1317
|
+
* Must be called before routeCce() can process encrypted requests.
|
|
1318
|
+
*/
|
|
1319
|
+
configureCce(config) {
|
|
1320
|
+
this.ccePipelineConfig = config;
|
|
1321
|
+
this.logger.log("CCE pipeline configured");
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Register a CCE-encrypted intent handler.
|
|
1325
|
+
* CCE handlers receive decrypted payloads and execution context.
|
|
1326
|
+
*/
|
|
1327
|
+
registerCceHandler(intent, handler) {
|
|
1328
|
+
this.cceHandlers.set(intent, handler);
|
|
1329
|
+
this.logger.debug(`CCE handler registered: ${intent}`);
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Check if a CCE handler exists for the given intent.
|
|
1333
|
+
*/
|
|
1334
|
+
hasCceHandler(intent) {
|
|
1335
|
+
return this.cceHandlers.has(intent);
|
|
1336
|
+
}
|
|
1337
|
+
/**
|
|
1338
|
+
* Route a CCE-encrypted request through the full pipeline.
|
|
1339
|
+
*
|
|
1340
|
+
* Steps:
|
|
1341
|
+
* 1. Sensor chain (envelope validation → capsule verification → replay → decrypt)
|
|
1342
|
+
* 2. Execution context derivation
|
|
1343
|
+
* 3. Handler execution
|
|
1344
|
+
* 4. Response encryption
|
|
1345
|
+
* 5. Witness recording
|
|
1346
|
+
*/
|
|
1347
|
+
async routeCce(envelope) {
|
|
1348
|
+
if (!this.ccePipelineConfig) {
|
|
1349
|
+
return {
|
|
1350
|
+
ok: false,
|
|
1351
|
+
error: {
|
|
1352
|
+
code: "CCE_NOT_CONFIGURED",
|
|
1353
|
+
message: "CCE pipeline not configured. Call configureCce() first."
|
|
1354
|
+
},
|
|
1355
|
+
status: "ERROR"
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
const config = {
|
|
1359
|
+
...this.ccePipelineConfig,
|
|
1360
|
+
handlers: this.cceHandlers
|
|
1361
|
+
};
|
|
1362
|
+
return executeCcePipeline(envelope, config);
|
|
1363
|
+
}
|
|
688
1364
|
storeSchema(meta) {
|
|
689
1365
|
if (meta.dto) {
|
|
690
1366
|
if (meta.tlv && meta.tlv.length > 0) {
|
|
@@ -1039,7 +1715,7 @@ function verifyResponse(ctx, response) {
|
|
|
1039
1715
|
import { encodeVarint, decodeVarint, varintLength } from "@nextera.one/axis-protocol";
|
|
1040
1716
|
|
|
1041
1717
|
// src/core/signature.ts
|
|
1042
|
-
import * as
|
|
1718
|
+
import * as crypto2 from "crypto";
|
|
1043
1719
|
|
|
1044
1720
|
// src/core/axis-bin.ts
|
|
1045
1721
|
import * as z from "zod";
|
|
@@ -1169,19 +1845,19 @@ function signFrame(frame, privateKey) {
|
|
|
1169
1845
|
32
|
|
1170
1846
|
]);
|
|
1171
1847
|
const pkcs8Key = Buffer.concat([pkcs8Prefix, privateKey]);
|
|
1172
|
-
keyObject =
|
|
1848
|
+
keyObject = crypto2.createPrivateKey({
|
|
1173
1849
|
key: pkcs8Key,
|
|
1174
1850
|
format: "der",
|
|
1175
1851
|
type: "pkcs8"
|
|
1176
1852
|
});
|
|
1177
1853
|
} else {
|
|
1178
|
-
keyObject =
|
|
1854
|
+
keyObject = crypto2.createPrivateKey({
|
|
1179
1855
|
key: privateKey,
|
|
1180
1856
|
format: "der",
|
|
1181
1857
|
type: "pkcs8"
|
|
1182
1858
|
});
|
|
1183
1859
|
}
|
|
1184
|
-
const signature =
|
|
1860
|
+
const signature = crypto2.sign(null, payload, keyObject);
|
|
1185
1861
|
if (signature.length !== 64) {
|
|
1186
1862
|
throw new Error("Ed25519 signature must be 64 bytes");
|
|
1187
1863
|
}
|
|
@@ -1213,19 +1889,19 @@ function verifyFrameSignature(frame, publicKey) {
|
|
|
1213
1889
|
0
|
|
1214
1890
|
]);
|
|
1215
1891
|
const spkiKey = Buffer.concat([spkiPrefix, publicKey]);
|
|
1216
|
-
keyObject =
|
|
1892
|
+
keyObject = crypto2.createPublicKey({
|
|
1217
1893
|
key: spkiKey,
|
|
1218
1894
|
format: "der",
|
|
1219
1895
|
type: "spki"
|
|
1220
1896
|
});
|
|
1221
1897
|
} else {
|
|
1222
|
-
keyObject =
|
|
1898
|
+
keyObject = crypto2.createPublicKey({
|
|
1223
1899
|
key: publicKey,
|
|
1224
1900
|
format: "der",
|
|
1225
1901
|
type: "spki"
|
|
1226
1902
|
});
|
|
1227
1903
|
}
|
|
1228
|
-
const valid =
|
|
1904
|
+
const valid = crypto2.verify(
|
|
1229
1905
|
null,
|
|
1230
1906
|
payload,
|
|
1231
1907
|
keyObject,
|
|
@@ -1237,17 +1913,17 @@ function verifyFrameSignature(frame, publicKey) {
|
|
|
1237
1913
|
}
|
|
1238
1914
|
}
|
|
1239
1915
|
function generateEd25519KeyPair() {
|
|
1240
|
-
const { privateKey, publicKey } =
|
|
1916
|
+
const { privateKey, publicKey } = crypto2.generateKeyPairSync("ed25519");
|
|
1241
1917
|
return {
|
|
1242
1918
|
privateKey: privateKey.export({ type: "pkcs8", format: "der" }),
|
|
1243
1919
|
publicKey: publicKey.export({ type: "spki", format: "der" })
|
|
1244
1920
|
};
|
|
1245
1921
|
}
|
|
1246
|
-
function
|
|
1247
|
-
return
|
|
1922
|
+
function sha2564(data) {
|
|
1923
|
+
return crypto2.createHash("sha256").update(data).digest();
|
|
1248
1924
|
}
|
|
1249
1925
|
function computeReceiptHash(receiptBytes, prevHash) {
|
|
1250
|
-
const hasher =
|
|
1926
|
+
const hasher = crypto2.createHash("sha256");
|
|
1251
1927
|
hasher.update(receiptBytes);
|
|
1252
1928
|
if (prevHash && prevHash.length > 0) {
|
|
1253
1929
|
hasher.update(prevHash);
|
|
@@ -1305,7 +1981,7 @@ __export(ats1_exports, {
|
|
|
1305
1981
|
encodeU64BE: () => encodeU64BE,
|
|
1306
1982
|
encodeUVarint: () => encodeUVarint,
|
|
1307
1983
|
logicalBodyToTLVs: () => logicalBodyToTLVs,
|
|
1308
|
-
sha256: () =>
|
|
1984
|
+
sha256: () => sha2565,
|
|
1309
1985
|
tlvsToLogicalBody: () => tlvsToLogicalBody,
|
|
1310
1986
|
tlvsToMap: () => tlvsToMap,
|
|
1311
1987
|
validateTLVsAgainstSchema: () => validateTLVsAgainstSchema
|
|
@@ -1359,7 +2035,7 @@ function decodeU64BE(buf) {
|
|
|
1359
2035
|
if (buf.length !== 8) throw new Error("decodeU64BE: length must be 8");
|
|
1360
2036
|
return buf.readBigUInt64BE(0);
|
|
1361
2037
|
}
|
|
1362
|
-
function
|
|
2038
|
+
function sha2565(data) {
|
|
1363
2039
|
return createHash3("sha256").update(data).digest();
|
|
1364
2040
|
}
|
|
1365
2041
|
function encodeTLV(tag, value) {
|
|
@@ -1676,7 +2352,7 @@ function decodeAxisHeaderFromTLVs(hdrTlvs, limits = DEFAULT_LIMITS) {
|
|
|
1676
2352
|
function encodeAxisRequestBinary(schema, req, limits = DEFAULT_LIMITS) {
|
|
1677
2353
|
const bodyTlvs = logicalBodyToTLVs(schema, req.body, limits);
|
|
1678
2354
|
const bodyBytes = encodeTLVStreamCanonical(bodyTlvs);
|
|
1679
|
-
const bodyHash =
|
|
2355
|
+
const bodyHash = sha2565(bodyBytes);
|
|
1680
2356
|
const hdr = {
|
|
1681
2357
|
...req.hdr,
|
|
1682
2358
|
schemaId: schema.schemaId,
|
|
@@ -1692,7 +2368,7 @@ function decodeAxisRequestBinary(schema, hdrBytes, bodyBytes, limits = DEFAULT_L
|
|
|
1692
2368
|
const hdr = decodeAxisHeaderFromTLVs(hdrTlvs, limits);
|
|
1693
2369
|
if (hdr.schemaId !== schema.schemaId)
|
|
1694
2370
|
throw new Error("decodeAxisRequestBinary: schemaId mismatch");
|
|
1695
|
-
const bh =
|
|
2371
|
+
const bh = sha2565(bodyBytes);
|
|
1696
2372
|
if (!Buffer.from(hdr.bodyHash).equals(bh))
|
|
1697
2373
|
throw new Error("decodeAxisRequestBinary: body_hash mismatch");
|
|
1698
2374
|
const body = tlvsToLogicalBody(schema, bodyTlvs, limits);
|
|
@@ -1765,7 +2441,7 @@ function packPasskeyLoginOptionsReq(params) {
|
|
|
1765
2441
|
}
|
|
1766
2442
|
);
|
|
1767
2443
|
const body = encodeTLVStreamCanonical(bodyTlvs);
|
|
1768
|
-
const bodyHash =
|
|
2444
|
+
const bodyHash = sha2565(body);
|
|
1769
2445
|
const hdr = buildAts1Hdr({
|
|
1770
2446
|
intentId: params.intentId,
|
|
1771
2447
|
schemaId: ATS1_SCHEMA.PASSKEY_LOGIN_OPTIONS_REQ,
|
|
@@ -1834,7 +2510,7 @@ function packPasskeyRegisterOptionsReq(params) {
|
|
|
1834
2510
|
}
|
|
1835
2511
|
);
|
|
1836
2512
|
const body = encodeTLVStreamCanonical(bodyTlvs);
|
|
1837
|
-
const bodyHash =
|
|
2513
|
+
const bodyHash = sha2565(body);
|
|
1838
2514
|
const hdr = buildAts1Hdr({
|
|
1839
2515
|
intentId: params.intentId,
|
|
1840
2516
|
schemaId: ATS1_SCHEMA.PASSKEY_REGISTER_OPTIONS_REQ,
|
|
@@ -1865,7 +2541,7 @@ function packPasskeyLoginVerifyReq(params) {
|
|
|
1865
2541
|
}
|
|
1866
2542
|
});
|
|
1867
2543
|
const body = encodeTLVStreamCanonical(bodyTlvs);
|
|
1868
|
-
const bodyHash =
|
|
2544
|
+
const bodyHash = sha2565(body);
|
|
1869
2545
|
const hdr = buildAts1Hdr({
|
|
1870
2546
|
intentId: params.intentId,
|
|
1871
2547
|
schemaId: ATS1_SCHEMA.PASSKEY_LOGIN_VERIFY_REQ,
|
|
@@ -1949,7 +2625,7 @@ function packPasskeyLoginVerifyRes(params) {
|
|
|
1949
2625
|
}
|
|
1950
2626
|
|
|
1951
2627
|
// src/codec/tlv.encode.ts
|
|
1952
|
-
import { randomBytes as
|
|
2628
|
+
import { randomBytes as randomBytes4 } from "crypto";
|
|
1953
2629
|
function encVarint(x) {
|
|
1954
2630
|
if (x < 0n) throw new Error("VARINT_NEG");
|
|
1955
2631
|
const out = [];
|
|
@@ -1977,7 +2653,7 @@ function bytes(b) {
|
|
|
1977
2653
|
return Buffer.isBuffer(b) ? b : Buffer.from(b);
|
|
1978
2654
|
}
|
|
1979
2655
|
function nonce16() {
|
|
1980
|
-
return
|
|
2656
|
+
return randomBytes4(16);
|
|
1981
2657
|
}
|
|
1982
2658
|
function tlv(type, value) {
|
|
1983
2659
|
if (!Number.isSafeInteger(type) || type < 0) throw new Error("TLV_BAD_TYPE");
|
|
@@ -2705,7 +3381,7 @@ function isTimestampValid(ts, skewSeconds = 120) {
|
|
|
2705
3381
|
|
|
2706
3382
|
// src/upload/axis-files.handlers.ts
|
|
2707
3383
|
import { Inject, Injectable as Injectable3, Logger as Logger2, Optional as Optional2 } from "@nestjs/common";
|
|
2708
|
-
import * as
|
|
3384
|
+
import * as crypto3 from "crypto";
|
|
2709
3385
|
|
|
2710
3386
|
// src/upload/upload.tokens.ts
|
|
2711
3387
|
var AXIS_UPLOAD_SESSION_STORE = "AXIS_UPLOAD_SESSION_STORE";
|
|
@@ -2806,7 +3482,7 @@ var AxisFilesFinalizeHandler = class {
|
|
|
2806
3482
|
if (!await this.files.hasTemp(fileId)) {
|
|
2807
3483
|
throw new Error("CHUNKS_NOT_FOUND");
|
|
2808
3484
|
}
|
|
2809
|
-
const hash =
|
|
3485
|
+
const hash = crypto3.createHash("sha256");
|
|
2810
3486
|
const rs = this.files.createTempReadStream(fileId);
|
|
2811
3487
|
for await (const chunk of rs) {
|
|
2812
3488
|
hash.update(chunk);
|
|
@@ -3226,10 +3902,10 @@ SensorRegistry = __decorateClass([
|
|
|
3226
3902
|
], SensorRegistry);
|
|
3227
3903
|
|
|
3228
3904
|
// src/engine/axis-observation.ts
|
|
3229
|
-
import { randomBytes as
|
|
3905
|
+
import { randomBytes as randomBytes5 } from "crypto";
|
|
3230
3906
|
function createObservation(transport, ip) {
|
|
3231
3907
|
return {
|
|
3232
|
-
id:
|
|
3908
|
+
id: randomBytes5(16).toString("hex"),
|
|
3233
3909
|
startMs: Date.now(),
|
|
3234
3910
|
transport,
|
|
3235
3911
|
ip,
|
|
@@ -3387,100 +4063,869 @@ AxisSensorChainService = __decorateClass([
|
|
|
3387
4063
|
Injectable7()
|
|
3388
4064
|
], AxisSensorChainService);
|
|
3389
4065
|
|
|
3390
|
-
// src/
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
4066
|
+
// src/utils/axis-tlv-codec.ts
|
|
4067
|
+
function encodeAxisTlvDto(dtoClass, data) {
|
|
4068
|
+
const schema = extractDtoSchema(dtoClass);
|
|
4069
|
+
const items = schema.fields.flatMap((field) => {
|
|
4070
|
+
const value = data[field.name];
|
|
4071
|
+
if (value === void 0 || value === null) {
|
|
4072
|
+
if (field.required) {
|
|
4073
|
+
throw new Error(`Missing required TLV response field: ${field.name}`);
|
|
4074
|
+
}
|
|
4075
|
+
return [];
|
|
4076
|
+
}
|
|
4077
|
+
return [{ type: field.tag, value: encodeField(field, value) }];
|
|
4078
|
+
});
|
|
4079
|
+
return buildTLVs(items);
|
|
4080
|
+
}
|
|
4081
|
+
function encodeField(field, value) {
|
|
4082
|
+
switch (field.kind) {
|
|
4083
|
+
case "utf8":
|
|
4084
|
+
return Buffer.from(String(value), "utf8");
|
|
4085
|
+
case "u64":
|
|
4086
|
+
return encodeU64(value);
|
|
4087
|
+
case "bytes":
|
|
4088
|
+
case "bytes16":
|
|
4089
|
+
return toBuffer(value);
|
|
4090
|
+
case "bool":
|
|
4091
|
+
return Buffer.from([value ? 1 : 0]);
|
|
4092
|
+
case "obj":
|
|
4093
|
+
case "arr":
|
|
4094
|
+
return Buffer.from(JSON.stringify(value), "utf8");
|
|
4095
|
+
default:
|
|
4096
|
+
return toBuffer(value);
|
|
4097
|
+
}
|
|
4098
|
+
}
|
|
4099
|
+
function encodeU64(value) {
|
|
4100
|
+
const encoded = Buffer.alloc(8);
|
|
4101
|
+
encoded.writeBigUInt64BE(
|
|
4102
|
+
typeof value === "bigint" ? value : BigInt(value)
|
|
4103
|
+
);
|
|
4104
|
+
return encoded;
|
|
4105
|
+
}
|
|
4106
|
+
function toBuffer(value) {
|
|
4107
|
+
if (Buffer.isBuffer(value)) {
|
|
4108
|
+
return value;
|
|
4109
|
+
}
|
|
4110
|
+
if (value instanceof Uint8Array) {
|
|
4111
|
+
return Buffer.from(value);
|
|
4112
|
+
}
|
|
4113
|
+
if (typeof value === "string") {
|
|
4114
|
+
return Buffer.from(value, "utf8");
|
|
4115
|
+
}
|
|
4116
|
+
throw new Error(`Unsupported TLV bytes value: ${typeof value}`);
|
|
4117
|
+
}
|
|
4118
|
+
|
|
4119
|
+
// src/loom/loom.types.ts
|
|
4120
|
+
function deriveAnchorReflection(softid, context = "openlogs", scope = "loom") {
|
|
4121
|
+
return `ar:${context}:${scope}:${softid}`;
|
|
4122
|
+
}
|
|
4123
|
+
function canonicalizeWrit(writ) {
|
|
4124
|
+
const ordered = {
|
|
4125
|
+
head: { tid: writ.head.tid, seq: writ.head.seq },
|
|
4126
|
+
body: {
|
|
4127
|
+
who: writ.body.who,
|
|
4128
|
+
act: writ.body.act,
|
|
4129
|
+
res: writ.body.res,
|
|
4130
|
+
law: writ.body.law
|
|
4131
|
+
},
|
|
4132
|
+
meta: { iat: writ.meta.iat, exp: writ.meta.exp, prev: writ.meta.prev }
|
|
4133
|
+
};
|
|
4134
|
+
return JSON.stringify(ordered);
|
|
4135
|
+
}
|
|
4136
|
+
function canonicalizeGrant(grant) {
|
|
4137
|
+
const ordered = {
|
|
4138
|
+
grant_id: grant.grant_id,
|
|
4139
|
+
issuer: grant.issuer,
|
|
4140
|
+
subject: grant.subject,
|
|
4141
|
+
grant_type: grant.grant_type,
|
|
4142
|
+
caps: grant.caps,
|
|
4143
|
+
meta: grant.meta
|
|
4144
|
+
};
|
|
4145
|
+
return JSON.stringify(ordered);
|
|
4146
|
+
}
|
|
4147
|
+
|
|
4148
|
+
// src/cce/index.ts
|
|
4149
|
+
var cce_exports = {};
|
|
4150
|
+
__export(cce_exports, {
|
|
4151
|
+
CCE_AES_KEY_BYTES: () => CCE_AES_KEY_BYTES,
|
|
4152
|
+
CCE_DERIVATION: () => CCE_DERIVATION,
|
|
4153
|
+
CCE_ERROR: () => CCE_ERROR,
|
|
4154
|
+
CCE_IV_BYTES: () => CCE_IV_BYTES,
|
|
4155
|
+
CCE_NONCE_BYTES: () => CCE_NONCE_BYTES,
|
|
4156
|
+
CCE_PROTOCOL_VERSION: () => CCE_PROTOCOL_VERSION,
|
|
4157
|
+
CCE_TAG_BYTES: () => CCE_TAG_BYTES,
|
|
4158
|
+
CceAudienceIntentBindingSensor: () => CceAudienceIntentBindingSensor,
|
|
4159
|
+
CceCapsuleVerificationSensor: () => CceCapsuleVerificationSensor,
|
|
4160
|
+
CceClientSignatureSensor: () => CceClientSignatureSensor,
|
|
4161
|
+
CceEnvelopeValidationSensor: () => CceEnvelopeValidationSensor,
|
|
4162
|
+
CceError: () => CceError,
|
|
4163
|
+
CcePayloadDecryptionSensor: () => CcePayloadDecryptionSensor,
|
|
4164
|
+
CceReplayProtectionSensor: () => CceReplayProtectionSensor,
|
|
4165
|
+
CceTpsWindowSensor: () => CceTpsWindowSensor,
|
|
4166
|
+
InMemoryCceReplayStore: () => InMemoryCceReplayStore,
|
|
4167
|
+
InMemoryCceWitnessStore: () => InMemoryCceWitnessStore,
|
|
4168
|
+
aesGcmDecrypt: () => aesGcmDecrypt,
|
|
4169
|
+
aesGcmEncrypt: () => aesGcmEncrypt,
|
|
4170
|
+
base64UrlDecode: () => base64UrlDecode,
|
|
4171
|
+
base64UrlEncode: () => base64UrlEncode,
|
|
4172
|
+
buildCceErrorResponse: () => buildCceErrorResponse,
|
|
4173
|
+
buildCceResponse: () => buildCceResponse,
|
|
4174
|
+
buildExecutionContext: () => buildExecutionContext,
|
|
4175
|
+
buildWitnessRecord: () => buildWitnessRecord,
|
|
4176
|
+
deriveRequestExecutionKey: () => deriveRequestExecutionKey,
|
|
4177
|
+
deriveResponseExecutionKey: () => deriveResponseExecutionKey,
|
|
4178
|
+
deriveWitnessKey: () => deriveWitnessKey,
|
|
4179
|
+
executeCcePipeline: () => executeCcePipeline,
|
|
4180
|
+
extractVerificationState: () => extractVerificationState,
|
|
4181
|
+
generateAesKey: () => generateAesKey,
|
|
4182
|
+
generateCceNonce: () => generateCceNonce,
|
|
4183
|
+
generateIv: () => generateIv,
|
|
4184
|
+
hashPayload: () => hashPayload,
|
|
4185
|
+
nodeAesGcmProvider: () => nodeAesGcmProvider
|
|
4186
|
+
});
|
|
4187
|
+
|
|
4188
|
+
// src/cce/sensors/cce-envelope-validation.sensor.ts
|
|
4189
|
+
var REQUIRED_FIELDS = [
|
|
4190
|
+
"ver",
|
|
4191
|
+
"request_id",
|
|
4192
|
+
"correlation_id",
|
|
4193
|
+
"client_kid",
|
|
4194
|
+
"capsule",
|
|
4195
|
+
"encrypted_key",
|
|
4196
|
+
"encrypted_payload",
|
|
4197
|
+
"request_nonce",
|
|
4198
|
+
"client_sig",
|
|
4199
|
+
"algorithms"
|
|
4200
|
+
];
|
|
4201
|
+
var CceEnvelopeValidationSensor = class {
|
|
4202
|
+
constructor() {
|
|
4203
|
+
this.name = "cce.envelope.validation";
|
|
4204
|
+
this.order = 5;
|
|
4205
|
+
this.phase = "PRE_DECODE";
|
|
4206
|
+
}
|
|
4207
|
+
supports(input) {
|
|
4208
|
+
return input.metadata?.cce === true || input.metadata?.contentType === "application/axis-cce";
|
|
4209
|
+
}
|
|
4210
|
+
async run(input) {
|
|
4211
|
+
const envelope = input.metadata?.cceEnvelope;
|
|
4212
|
+
if (!envelope) {
|
|
4213
|
+
return {
|
|
4214
|
+
allow: false,
|
|
4215
|
+
riskScore: 100,
|
|
4216
|
+
reasons: [CCE_ERROR.INVALID_ENVELOPE],
|
|
4217
|
+
code: CCE_ERROR.INVALID_ENVELOPE
|
|
4218
|
+
};
|
|
4219
|
+
}
|
|
4220
|
+
for (const field of REQUIRED_FIELDS) {
|
|
4221
|
+
if (envelope[field] === void 0 || envelope[field] === null) {
|
|
4222
|
+
return {
|
|
4223
|
+
allow: false,
|
|
4224
|
+
riskScore: 100,
|
|
4225
|
+
reasons: [`${CCE_ERROR.INVALID_ENVELOPE}: missing ${field}`],
|
|
4226
|
+
code: CCE_ERROR.INVALID_ENVELOPE
|
|
4227
|
+
};
|
|
4228
|
+
}
|
|
4229
|
+
}
|
|
4230
|
+
if (envelope.ver !== CCE_PROTOCOL_VERSION) {
|
|
4231
|
+
return {
|
|
4232
|
+
allow: false,
|
|
4233
|
+
riskScore: 100,
|
|
4234
|
+
reasons: [`${CCE_ERROR.UNSUPPORTED_VERSION}: ${envelope.ver}`],
|
|
4235
|
+
code: CCE_ERROR.UNSUPPORTED_VERSION
|
|
4236
|
+
};
|
|
4237
|
+
}
|
|
4238
|
+
if (!/^[0-9a-f]+$/i.test(envelope.request_nonce)) {
|
|
4239
|
+
return {
|
|
4240
|
+
allow: false,
|
|
4241
|
+
riskScore: 100,
|
|
4242
|
+
reasons: [
|
|
4243
|
+
`${CCE_ERROR.INVALID_ENVELOPE}: invalid request_nonce format`
|
|
4244
|
+
],
|
|
4245
|
+
code: CCE_ERROR.INVALID_ENVELOPE
|
|
4246
|
+
};
|
|
4247
|
+
}
|
|
4248
|
+
if (envelope.request_nonce.length !== CCE_NONCE_BYTES * 2) {
|
|
4249
|
+
return {
|
|
4250
|
+
allow: false,
|
|
4251
|
+
riskScore: 100,
|
|
4252
|
+
reasons: [`${CCE_ERROR.INVALID_ENVELOPE}: request_nonce wrong length`],
|
|
4253
|
+
code: CCE_ERROR.INVALID_ENVELOPE
|
|
4254
|
+
};
|
|
4255
|
+
}
|
|
4256
|
+
const capsule = envelope.capsule;
|
|
4257
|
+
if (!capsule.capsule_id || !capsule.ver || !capsule.sub || !capsule.kid || !capsule.intent || !capsule.aud || !capsule.issuer_sig) {
|
|
4258
|
+
return {
|
|
4259
|
+
allow: false,
|
|
4260
|
+
riskScore: 100,
|
|
4261
|
+
reasons: [`${CCE_ERROR.MISSING_CAPSULE}: incomplete capsule claims`],
|
|
4262
|
+
code: CCE_ERROR.MISSING_CAPSULE
|
|
4263
|
+
};
|
|
4264
|
+
}
|
|
4265
|
+
if (!envelope.encrypted_key.ciphertext || !envelope.encrypted_key.alg) {
|
|
4266
|
+
return {
|
|
4267
|
+
allow: false,
|
|
4268
|
+
riskScore: 100,
|
|
4269
|
+
reasons: [
|
|
4270
|
+
`${CCE_ERROR.MISSING_ENCRYPTED_KEY}: incomplete encrypted_key`
|
|
4271
|
+
],
|
|
4272
|
+
code: CCE_ERROR.MISSING_ENCRYPTED_KEY
|
|
4273
|
+
};
|
|
4274
|
+
}
|
|
4275
|
+
input.metadata = input.metadata ?? {};
|
|
4276
|
+
input.metadata.cceEnvelopeValid = true;
|
|
4277
|
+
return {
|
|
4278
|
+
decision: "ALLOW" /* ALLOW */,
|
|
4279
|
+
allow: true,
|
|
4280
|
+
riskScore: 0,
|
|
4281
|
+
reasons: []
|
|
4282
|
+
};
|
|
4283
|
+
}
|
|
4284
|
+
};
|
|
4285
|
+
|
|
4286
|
+
// src/cce/sensors/cce-client-signature.sensor.ts
|
|
4287
|
+
var CceClientSignatureSensor = class {
|
|
4288
|
+
constructor(keyResolver, signatureVerifier) {
|
|
4289
|
+
this.keyResolver = keyResolver;
|
|
4290
|
+
this.signatureVerifier = signatureVerifier;
|
|
4291
|
+
this.name = "cce.client.signature";
|
|
4292
|
+
this.order = 45;
|
|
4293
|
+
this.phase = "POST_DECODE";
|
|
4294
|
+
}
|
|
4295
|
+
supports(input) {
|
|
4296
|
+
return input.metadata?.cceEnvelopeValid === true;
|
|
4297
|
+
}
|
|
4298
|
+
async run(input) {
|
|
4299
|
+
const envelope = input.metadata?.cceEnvelope;
|
|
4300
|
+
if (!envelope) {
|
|
4301
|
+
return {
|
|
4302
|
+
allow: false,
|
|
4303
|
+
riskScore: 100,
|
|
4304
|
+
reasons: [CCE_ERROR.INVALID_ENVELOPE],
|
|
4305
|
+
code: CCE_ERROR.INVALID_ENVELOPE
|
|
4306
|
+
};
|
|
4307
|
+
}
|
|
4308
|
+
const keyRecord = await this.keyResolver.resolve(envelope.client_kid);
|
|
4309
|
+
if (!keyRecord) {
|
|
4310
|
+
return {
|
|
4311
|
+
allow: false,
|
|
4312
|
+
riskScore: 100,
|
|
4313
|
+
reasons: [
|
|
4314
|
+
`${CCE_ERROR.CLIENT_KEY_NOT_FOUND}: kid=${envelope.client_kid}`
|
|
4315
|
+
],
|
|
4316
|
+
code: CCE_ERROR.CLIENT_KEY_NOT_FOUND
|
|
4317
|
+
};
|
|
4318
|
+
}
|
|
4319
|
+
const { client_sig, ...signable } = envelope;
|
|
4320
|
+
const canonical = canonicalize2(signable);
|
|
4321
|
+
const message = new TextEncoder().encode(canonical);
|
|
4322
|
+
const valid = await this.signatureVerifier.verify(
|
|
4323
|
+
message,
|
|
4324
|
+
client_sig.value,
|
|
4325
|
+
keyRecord.publicKeyHex,
|
|
4326
|
+
keyRecord.alg
|
|
4327
|
+
);
|
|
4328
|
+
if (!valid) {
|
|
4329
|
+
return {
|
|
4330
|
+
allow: false,
|
|
4331
|
+
riskScore: 100,
|
|
4332
|
+
reasons: [CCE_ERROR.CLIENT_SIG_INVALID],
|
|
4333
|
+
code: CCE_ERROR.CLIENT_SIG_INVALID
|
|
4334
|
+
};
|
|
4335
|
+
}
|
|
4336
|
+
input.metadata = input.metadata ?? {};
|
|
4337
|
+
input.metadata.cceClientKey = keyRecord;
|
|
4338
|
+
input.metadata.cceClientSigVerified = true;
|
|
4339
|
+
return {
|
|
4340
|
+
decision: "ALLOW" /* ALLOW */,
|
|
4341
|
+
allow: true,
|
|
4342
|
+
riskScore: 0,
|
|
4343
|
+
reasons: [],
|
|
4344
|
+
meta: { kid: envelope.client_kid }
|
|
4345
|
+
};
|
|
4346
|
+
}
|
|
4347
|
+
};
|
|
4348
|
+
function canonicalize2(obj) {
|
|
4349
|
+
if (Array.isArray(obj)) {
|
|
4350
|
+
return "[" + obj.map(canonicalize2).join(",") + "]";
|
|
4351
|
+
}
|
|
4352
|
+
if (obj !== null && typeof obj === "object") {
|
|
4353
|
+
const sorted = Object.keys(obj).sort().map(
|
|
4354
|
+
(k) => JSON.stringify(k) + ":" + canonicalize2(obj[k])
|
|
4355
|
+
);
|
|
4356
|
+
return "{" + sorted.join(",") + "}";
|
|
4357
|
+
}
|
|
4358
|
+
return JSON.stringify(obj);
|
|
4359
|
+
}
|
|
4360
|
+
|
|
4361
|
+
// src/cce/sensors/cce-capsule-verification.sensor.ts
|
|
4362
|
+
import { blake3 } from "@noble/hashes/blake3.js";
|
|
4363
|
+
import { bytesToHex as bytesToHex5 } from "@noble/hashes/utils.js";
|
|
4364
|
+
var CceCapsuleVerificationSensor = class {
|
|
4365
|
+
constructor(issuerKeyResolver, capsuleVerifier) {
|
|
4366
|
+
this.issuerKeyResolver = issuerKeyResolver;
|
|
4367
|
+
this.capsuleVerifier = capsuleVerifier;
|
|
4368
|
+
this.name = "cce.capsule.verification";
|
|
4369
|
+
this.order = 50;
|
|
4370
|
+
this.phase = "POST_DECODE";
|
|
4371
|
+
}
|
|
4372
|
+
supports(input) {
|
|
4373
|
+
return input.metadata?.cceEnvelopeValid === true;
|
|
4374
|
+
}
|
|
4375
|
+
async run(input) {
|
|
4376
|
+
const capsule = input.metadata?.cceEnvelope?.capsule;
|
|
4377
|
+
if (!capsule) {
|
|
4378
|
+
return {
|
|
4379
|
+
allow: false,
|
|
4380
|
+
riskScore: 100,
|
|
4381
|
+
reasons: [CCE_ERROR.MISSING_CAPSULE],
|
|
4382
|
+
code: CCE_ERROR.MISSING_CAPSULE
|
|
4383
|
+
};
|
|
4384
|
+
}
|
|
4385
|
+
if (capsule.ver !== CCE_PROTOCOL_VERSION) {
|
|
4386
|
+
return {
|
|
4387
|
+
allow: false,
|
|
4388
|
+
riskScore: 100,
|
|
4389
|
+
reasons: [
|
|
4390
|
+
`${CCE_ERROR.CAPSULE_SIG_INVALID}: wrong version ${capsule.ver}`
|
|
4391
|
+
],
|
|
4392
|
+
code: CCE_ERROR.CAPSULE_SIG_INVALID
|
|
4393
|
+
};
|
|
4394
|
+
}
|
|
4395
|
+
const { capsule_id, issuer_sig, ...claimsBody } = capsule;
|
|
4396
|
+
const expectedId = computeCceCapsuleId(claimsBody);
|
|
4397
|
+
if (capsule_id !== expectedId) {
|
|
4398
|
+
return {
|
|
4399
|
+
allow: false,
|
|
4400
|
+
riskScore: 100,
|
|
4401
|
+
reasons: [`${CCE_ERROR.CAPSULE_SIG_INVALID}: content hash mismatch`],
|
|
4402
|
+
code: CCE_ERROR.CAPSULE_SIG_INVALID
|
|
4403
|
+
};
|
|
4404
|
+
}
|
|
4405
|
+
const issuerKey = await this.issuerKeyResolver.resolve(
|
|
4406
|
+
capsule.issuer_sig.kid
|
|
4407
|
+
);
|
|
4408
|
+
if (!issuerKey) {
|
|
4409
|
+
return {
|
|
4410
|
+
allow: false,
|
|
4411
|
+
riskScore: 100,
|
|
4412
|
+
reasons: [`${CCE_ERROR.CAPSULE_SIG_INVALID}: issuer key not found`],
|
|
4413
|
+
code: CCE_ERROR.CAPSULE_SIG_INVALID
|
|
4414
|
+
};
|
|
4415
|
+
}
|
|
4416
|
+
const { issuer_sig: sig, ...rest } = capsule;
|
|
4417
|
+
const sigValid = await this.capsuleVerifier.verify(
|
|
4418
|
+
rest,
|
|
4419
|
+
sig,
|
|
4420
|
+
issuerKey.publicKeyHex
|
|
4421
|
+
);
|
|
4422
|
+
if (!sigValid) {
|
|
4423
|
+
return {
|
|
4424
|
+
allow: false,
|
|
4425
|
+
riskScore: 100,
|
|
4426
|
+
reasons: [CCE_ERROR.CAPSULE_SIG_INVALID],
|
|
4427
|
+
code: CCE_ERROR.CAPSULE_SIG_INVALID
|
|
4428
|
+
};
|
|
4429
|
+
}
|
|
4430
|
+
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
4431
|
+
if (capsule.exp < nowSeconds) {
|
|
4432
|
+
return {
|
|
4433
|
+
allow: false,
|
|
4434
|
+
riskScore: 100,
|
|
4435
|
+
reasons: [`${CCE_ERROR.CAPSULE_EXPIRED}: exp=${capsule.exp}`],
|
|
4436
|
+
code: CCE_ERROR.CAPSULE_EXPIRED
|
|
4437
|
+
};
|
|
4438
|
+
}
|
|
4439
|
+
if (capsule.iat > nowSeconds + 5) {
|
|
4440
|
+
return {
|
|
4441
|
+
allow: false,
|
|
4442
|
+
riskScore: 100,
|
|
4443
|
+
reasons: [`${CCE_ERROR.CAPSULE_NOT_YET_VALID}: iat=${capsule.iat}`],
|
|
4444
|
+
code: CCE_ERROR.CAPSULE_NOT_YET_VALID
|
|
4445
|
+
};
|
|
4446
|
+
}
|
|
4447
|
+
input.metadata = input.metadata ?? {};
|
|
4448
|
+
input.metadata.cceCapsuleVerified = true;
|
|
4449
|
+
input.metadata.cceCapsule = capsule;
|
|
4450
|
+
return {
|
|
4451
|
+
decision: "ALLOW" /* ALLOW */,
|
|
4452
|
+
allow: true,
|
|
4453
|
+
riskScore: 0,
|
|
4454
|
+
reasons: [],
|
|
4455
|
+
meta: { capsule_id: capsule.capsule_id }
|
|
4456
|
+
};
|
|
4457
|
+
}
|
|
4458
|
+
};
|
|
4459
|
+
function canonicalize3(obj) {
|
|
4460
|
+
if (Array.isArray(obj)) {
|
|
4461
|
+
return "[" + obj.map(canonicalize3).join(",") + "]";
|
|
4462
|
+
}
|
|
4463
|
+
if (obj !== null && typeof obj === "object") {
|
|
4464
|
+
const sorted = Object.keys(obj).sort().map(
|
|
4465
|
+
(k) => JSON.stringify(k) + ":" + canonicalize3(obj[k])
|
|
4466
|
+
);
|
|
4467
|
+
return "{" + sorted.join(",") + "}";
|
|
4468
|
+
}
|
|
4469
|
+
return JSON.stringify(obj);
|
|
4470
|
+
}
|
|
4471
|
+
function computeCceCapsuleId(claims) {
|
|
4472
|
+
const canonical = canonicalize3(claims);
|
|
4473
|
+
const hash = blake3(new TextEncoder().encode(canonical));
|
|
4474
|
+
return "cce_b3_" + bytesToHex5(hash).slice(0, 32);
|
|
4475
|
+
}
|
|
4476
|
+
|
|
4477
|
+
// src/cce/sensors/cce-tps-window.sensor.ts
|
|
4478
|
+
var DEFAULT_SKEW_MS = 5e3;
|
|
4479
|
+
var CceTpsWindowSensor = class {
|
|
4480
|
+
constructor(skewMs = DEFAULT_SKEW_MS) {
|
|
4481
|
+
this.skewMs = skewMs;
|
|
4482
|
+
this.name = "cce.tps.window";
|
|
4483
|
+
this.order = 92;
|
|
4484
|
+
this.phase = "POST_DECODE";
|
|
4485
|
+
}
|
|
4486
|
+
supports(input) {
|
|
4487
|
+
return input.metadata?.cceCapsuleVerified === true;
|
|
4488
|
+
}
|
|
4489
|
+
async run(input) {
|
|
4490
|
+
const capsule = input.metadata?.cceCapsule;
|
|
4491
|
+
if (!capsule) {
|
|
4492
|
+
return {
|
|
4493
|
+
allow: false,
|
|
4494
|
+
riskScore: 100,
|
|
4495
|
+
reasons: [CCE_ERROR.MISSING_CAPSULE],
|
|
4496
|
+
code: CCE_ERROR.MISSING_CAPSULE
|
|
4497
|
+
};
|
|
4498
|
+
}
|
|
4499
|
+
const nowMs = Date.now();
|
|
4500
|
+
if (nowMs > capsule.tps_to + this.skewMs) {
|
|
4501
|
+
return {
|
|
4502
|
+
allow: false,
|
|
4503
|
+
riskScore: 100,
|
|
4504
|
+
reasons: [
|
|
4505
|
+
`${CCE_ERROR.TPS_WINDOW_EXPIRED}: window ended at ${capsule.tps_to}, now=${nowMs}`
|
|
4506
|
+
],
|
|
4507
|
+
code: CCE_ERROR.TPS_WINDOW_EXPIRED
|
|
4508
|
+
};
|
|
4509
|
+
}
|
|
4510
|
+
if (nowMs < capsule.tps_from - this.skewMs) {
|
|
4511
|
+
return {
|
|
4512
|
+
allow: false,
|
|
4513
|
+
riskScore: 100,
|
|
4514
|
+
reasons: [
|
|
4515
|
+
`${CCE_ERROR.TPS_WINDOW_FUTURE}: window starts at ${capsule.tps_from}, now=${nowMs}`
|
|
4516
|
+
],
|
|
4517
|
+
code: CCE_ERROR.TPS_WINDOW_FUTURE
|
|
4518
|
+
};
|
|
4519
|
+
}
|
|
4520
|
+
input.metadata = input.metadata ?? {};
|
|
4521
|
+
input.metadata.cceTpsValid = true;
|
|
4522
|
+
return {
|
|
4523
|
+
decision: "ALLOW" /* ALLOW */,
|
|
4524
|
+
allow: true,
|
|
4525
|
+
riskScore: 0,
|
|
4526
|
+
reasons: []
|
|
4527
|
+
};
|
|
4528
|
+
}
|
|
4529
|
+
};
|
|
4530
|
+
|
|
4531
|
+
// src/cce/sensors/cce-audience-intent-binding.sensor.ts
|
|
4532
|
+
var CceAudienceIntentBindingSensor = class {
|
|
4533
|
+
constructor(axisAudience) {
|
|
4534
|
+
this.axisAudience = axisAudience;
|
|
4535
|
+
this.name = "cce.audience.intent.binding";
|
|
4536
|
+
this.order = 95;
|
|
4537
|
+
this.phase = "POST_DECODE";
|
|
4538
|
+
}
|
|
4539
|
+
supports(input) {
|
|
4540
|
+
return input.metadata?.cceCapsuleVerified === true;
|
|
4541
|
+
}
|
|
4542
|
+
async run(input) {
|
|
4543
|
+
const capsule = input.metadata?.cceCapsule;
|
|
4544
|
+
const envelope = input.metadata?.cceEnvelope;
|
|
4545
|
+
if (!capsule || !envelope) {
|
|
4546
|
+
return {
|
|
4547
|
+
allow: false,
|
|
4548
|
+
riskScore: 100,
|
|
4549
|
+
reasons: [CCE_ERROR.MISSING_CAPSULE],
|
|
4550
|
+
code: CCE_ERROR.MISSING_CAPSULE
|
|
4551
|
+
};
|
|
4552
|
+
}
|
|
4553
|
+
if (capsule.aud !== this.axisAudience) {
|
|
4554
|
+
return {
|
|
4555
|
+
allow: false,
|
|
4556
|
+
riskScore: 100,
|
|
4557
|
+
reasons: [
|
|
4558
|
+
`${CCE_ERROR.AUDIENCE_MISMATCH}: capsule.aud=${capsule.aud}, expected=${this.axisAudience}`
|
|
4559
|
+
],
|
|
4560
|
+
code: CCE_ERROR.AUDIENCE_MISMATCH
|
|
4561
|
+
};
|
|
4562
|
+
}
|
|
4563
|
+
const requestIntent = input.intent ?? input.metadata?.cceRequestIntent;
|
|
4564
|
+
if (requestIntent && capsule.intent !== requestIntent) {
|
|
4565
|
+
return {
|
|
4566
|
+
allow: false,
|
|
4567
|
+
riskScore: 100,
|
|
4568
|
+
reasons: [
|
|
4569
|
+
`${CCE_ERROR.INTENT_MISMATCH}: capsule.intent=${capsule.intent}, request=${requestIntent}`
|
|
4570
|
+
],
|
|
4571
|
+
code: CCE_ERROR.INTENT_MISMATCH
|
|
4572
|
+
};
|
|
4573
|
+
}
|
|
4574
|
+
if (envelope.client_kid !== capsule.kid) {
|
|
4575
|
+
return {
|
|
4576
|
+
allow: false,
|
|
4577
|
+
riskScore: 100,
|
|
4578
|
+
reasons: [
|
|
4579
|
+
`${CCE_ERROR.INTENT_MISMATCH}: envelope.kid=${envelope.client_kid}, capsule.kid=${capsule.kid}`
|
|
4580
|
+
],
|
|
4581
|
+
code: CCE_ERROR.INTENT_MISMATCH
|
|
4582
|
+
};
|
|
4583
|
+
}
|
|
4584
|
+
input.metadata = input.metadata ?? {};
|
|
4585
|
+
input.metadata.cceBindingVerified = true;
|
|
4586
|
+
return {
|
|
4587
|
+
decision: "ALLOW" /* ALLOW */,
|
|
4588
|
+
allow: true,
|
|
4589
|
+
riskScore: 0,
|
|
4590
|
+
reasons: []
|
|
4591
|
+
};
|
|
4592
|
+
}
|
|
4593
|
+
};
|
|
4594
|
+
|
|
4595
|
+
// src/cce/sensors/cce-replay-protection.sensor.ts
|
|
4596
|
+
var InMemoryCceReplayStore = class {
|
|
4597
|
+
constructor() {
|
|
4598
|
+
this.nonces = /* @__PURE__ */ new Map();
|
|
4599
|
+
this.consumed = /* @__PURE__ */ new Set();
|
|
4600
|
+
this.revoked = /* @__PURE__ */ new Set();
|
|
4601
|
+
}
|
|
4602
|
+
async checkAndMark(key, ttlMs) {
|
|
4603
|
+
this.cleanup();
|
|
4604
|
+
if (this.nonces.has(key)) return false;
|
|
4605
|
+
this.nonces.set(key, Date.now() + ttlMs);
|
|
4606
|
+
return true;
|
|
4607
|
+
}
|
|
4608
|
+
async isCapsuleConsumed(capsuleId) {
|
|
4609
|
+
return this.consumed.has(capsuleId);
|
|
4610
|
+
}
|
|
4611
|
+
async markCapsuleConsumed(capsuleId, _ttlMs) {
|
|
4612
|
+
this.consumed.add(capsuleId);
|
|
4613
|
+
}
|
|
4614
|
+
async isCapsuleRevoked(capsuleId) {
|
|
4615
|
+
return this.revoked.has(capsuleId);
|
|
4616
|
+
}
|
|
4617
|
+
/** Revoke a capsule (for testing/admin) */
|
|
4618
|
+
revoke(capsuleId) {
|
|
4619
|
+
this.revoked.add(capsuleId);
|
|
4620
|
+
}
|
|
4621
|
+
cleanup() {
|
|
4622
|
+
const now = Date.now();
|
|
4623
|
+
for (const [key, expiresAt] of this.nonces) {
|
|
4624
|
+
if (expiresAt < now) this.nonces.delete(key);
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4627
|
+
};
|
|
4628
|
+
var CceReplayProtectionSensor = class {
|
|
4629
|
+
constructor(replayStore, options) {
|
|
4630
|
+
this.replayStore = replayStore;
|
|
4631
|
+
this.name = "cce.replay.protection";
|
|
4632
|
+
this.order = 98;
|
|
4633
|
+
this.phase = "POST_DECODE";
|
|
4634
|
+
this.nonceTtlMs = options?.nonceTtlMs ?? 5 * 60 * 1e3;
|
|
4635
|
+
}
|
|
4636
|
+
supports(input) {
|
|
4637
|
+
return input.metadata?.cceCapsuleVerified === true;
|
|
4638
|
+
}
|
|
4639
|
+
async run(input) {
|
|
4640
|
+
const capsule = input.metadata?.cceCapsule;
|
|
4641
|
+
const envelope = input.metadata?.cceEnvelope;
|
|
4642
|
+
if (!capsule || !envelope) {
|
|
4643
|
+
return {
|
|
4644
|
+
allow: false,
|
|
4645
|
+
riskScore: 100,
|
|
4646
|
+
reasons: [CCE_ERROR.MISSING_CAPSULE],
|
|
4647
|
+
code: CCE_ERROR.MISSING_CAPSULE
|
|
4648
|
+
};
|
|
4649
|
+
}
|
|
4650
|
+
const revoked = await this.replayStore.isCapsuleRevoked(capsule.capsule_id);
|
|
4651
|
+
if (revoked) {
|
|
4652
|
+
return {
|
|
4653
|
+
allow: false,
|
|
4654
|
+
riskScore: 100,
|
|
4655
|
+
reasons: [`${CCE_ERROR.CAPSULE_REVOKED}: ${capsule.capsule_id}`],
|
|
4656
|
+
code: CCE_ERROR.CAPSULE_REVOKED
|
|
4657
|
+
};
|
|
4658
|
+
}
|
|
4659
|
+
if (capsule.mode === "SINGLE_USE") {
|
|
4660
|
+
const consumed = await this.replayStore.isCapsuleConsumed(
|
|
4661
|
+
capsule.capsule_id
|
|
4662
|
+
);
|
|
4663
|
+
if (consumed) {
|
|
4664
|
+
return {
|
|
4665
|
+
allow: false,
|
|
4666
|
+
riskScore: 100,
|
|
4667
|
+
reasons: [`${CCE_ERROR.CAPSULE_CONSUMED}: ${capsule.capsule_id}`],
|
|
4668
|
+
code: CCE_ERROR.CAPSULE_CONSUMED
|
|
4669
|
+
};
|
|
4670
|
+
}
|
|
4671
|
+
}
|
|
4672
|
+
const nonceKey = `cce:nonce:${capsule.sub}:${capsule.aud}:${capsule.intent}:${envelope.request_nonce}`;
|
|
4673
|
+
const nonceValid = await this.replayStore.checkAndMark(
|
|
4674
|
+
nonceKey,
|
|
4675
|
+
this.nonceTtlMs
|
|
4676
|
+
);
|
|
4677
|
+
if (!nonceValid) {
|
|
4678
|
+
return {
|
|
4679
|
+
allow: false,
|
|
4680
|
+
riskScore: 100,
|
|
4681
|
+
reasons: [
|
|
4682
|
+
`${CCE_ERROR.NONCE_REUSED}: ${envelope.request_nonce.slice(0, 16)}...`
|
|
4683
|
+
],
|
|
4684
|
+
code: CCE_ERROR.NONCE_REUSED
|
|
4685
|
+
};
|
|
4686
|
+
}
|
|
4687
|
+
if (capsule.mode === "SINGLE_USE") {
|
|
4688
|
+
const capsuleTtl = (capsule.exp - capsule.iat) * 1e3 + 6e4;
|
|
4689
|
+
await this.replayStore.markCapsuleConsumed(
|
|
4690
|
+
capsule.capsule_id,
|
|
4691
|
+
capsuleTtl
|
|
4692
|
+
);
|
|
4693
|
+
}
|
|
4694
|
+
input.metadata = input.metadata ?? {};
|
|
4695
|
+
input.metadata.cceReplayClean = true;
|
|
4696
|
+
return {
|
|
4697
|
+
decision: "ALLOW" /* ALLOW */,
|
|
4698
|
+
allow: true,
|
|
4699
|
+
riskScore: 0,
|
|
4700
|
+
reasons: []
|
|
4701
|
+
};
|
|
4702
|
+
}
|
|
4703
|
+
};
|
|
4704
|
+
|
|
4705
|
+
// src/cce/sensors/cce-payload-decryption.sensor.ts
|
|
4706
|
+
var CcePayloadDecryptionSensor = class {
|
|
4707
|
+
constructor(keyProvider, aesProvider, maxPayloadBytes = 64 * 1024) {
|
|
4708
|
+
this.keyProvider = keyProvider;
|
|
4709
|
+
this.aesProvider = aesProvider;
|
|
4710
|
+
this.maxPayloadBytes = maxPayloadBytes;
|
|
4711
|
+
this.name = "cce.payload.decryption";
|
|
4712
|
+
this.order = 145;
|
|
4713
|
+
this.phase = "POST_DECODE";
|
|
4714
|
+
}
|
|
4715
|
+
supports(input) {
|
|
4716
|
+
return input.metadata?.cceEnvelopeValid === true && input.metadata?.cceClientSigVerified === true && input.metadata?.cceCapsuleVerified === true && input.metadata?.cceReplayClean === true;
|
|
4717
|
+
}
|
|
4718
|
+
async run(input) {
|
|
4719
|
+
const envelope = input.metadata?.cceEnvelope;
|
|
4720
|
+
if (!envelope) {
|
|
4721
|
+
return {
|
|
4722
|
+
allow: false,
|
|
4723
|
+
riskScore: 100,
|
|
4724
|
+
reasons: [CCE_ERROR.INVALID_ENVELOPE],
|
|
4725
|
+
code: CCE_ERROR.INVALID_ENVELOPE
|
|
4726
|
+
};
|
|
4727
|
+
}
|
|
4728
|
+
let aesKey;
|
|
4729
|
+
try {
|
|
4730
|
+
aesKey = await this.keyProvider.unwrapKey(
|
|
4731
|
+
envelope.encrypted_key.ciphertext,
|
|
4732
|
+
envelope.encrypted_key.alg,
|
|
4733
|
+
envelope.encrypted_key.axis_kid,
|
|
4734
|
+
envelope.encrypted_key.ephemeral_pk
|
|
4735
|
+
);
|
|
4736
|
+
} catch {
|
|
4737
|
+
return {
|
|
4738
|
+
allow: false,
|
|
4739
|
+
riskScore: 100,
|
|
4740
|
+
reasons: [CCE_ERROR.KEY_UNWRAP_FAILED],
|
|
4741
|
+
code: CCE_ERROR.KEY_UNWRAP_FAILED
|
|
4742
|
+
};
|
|
4743
|
+
}
|
|
4744
|
+
if (!aesKey) {
|
|
4745
|
+
return {
|
|
4746
|
+
allow: false,
|
|
4747
|
+
riskScore: 100,
|
|
4748
|
+
reasons: [CCE_ERROR.KEY_UNWRAP_FAILED],
|
|
4749
|
+
code: CCE_ERROR.KEY_UNWRAP_FAILED
|
|
4750
|
+
};
|
|
4751
|
+
}
|
|
4752
|
+
let iv;
|
|
4753
|
+
let ciphertext;
|
|
4754
|
+
let tag;
|
|
4755
|
+
try {
|
|
4756
|
+
iv = base64UrlDecode2(envelope.encrypted_payload.iv);
|
|
4757
|
+
ciphertext = base64UrlDecode2(envelope.encrypted_payload.ciphertext);
|
|
4758
|
+
tag = base64UrlDecode2(envelope.encrypted_payload.tag);
|
|
4759
|
+
} catch {
|
|
4760
|
+
return {
|
|
4761
|
+
allow: false,
|
|
4762
|
+
riskScore: 100,
|
|
4763
|
+
reasons: [`${CCE_ERROR.DECRYPTION_FAILED}: invalid base64url encoding`],
|
|
4764
|
+
code: CCE_ERROR.DECRYPTION_FAILED
|
|
4765
|
+
};
|
|
4766
|
+
}
|
|
4767
|
+
if (ciphertext.length > this.maxPayloadBytes) {
|
|
4768
|
+
return {
|
|
4769
|
+
allow: false,
|
|
4770
|
+
riskScore: 100,
|
|
4771
|
+
reasons: [
|
|
4772
|
+
`${CCE_ERROR.PAYLOAD_TOO_LARGE}: ${ciphertext.length} > ${this.maxPayloadBytes}`
|
|
4773
|
+
],
|
|
4774
|
+
code: CCE_ERROR.PAYLOAD_TOO_LARGE
|
|
4775
|
+
};
|
|
4776
|
+
}
|
|
4777
|
+
const aad = buildAad(envelope);
|
|
4778
|
+
let plaintext;
|
|
4779
|
+
try {
|
|
4780
|
+
plaintext = await this.aesProvider.decrypt(
|
|
4781
|
+
aesKey,
|
|
4782
|
+
iv,
|
|
4783
|
+
ciphertext,
|
|
4784
|
+
tag,
|
|
4785
|
+
aad
|
|
4786
|
+
);
|
|
4787
|
+
} catch {
|
|
4788
|
+
plaintext = null;
|
|
4789
|
+
} finally {
|
|
4790
|
+
aesKey.fill(0);
|
|
4791
|
+
}
|
|
4792
|
+
if (!plaintext) {
|
|
4793
|
+
return {
|
|
4794
|
+
allow: false,
|
|
4795
|
+
riskScore: 100,
|
|
4796
|
+
reasons: [CCE_ERROR.AEAD_TAG_MISMATCH],
|
|
4797
|
+
code: CCE_ERROR.AEAD_TAG_MISMATCH
|
|
4798
|
+
};
|
|
4799
|
+
}
|
|
4800
|
+
input.metadata = input.metadata ?? {};
|
|
4801
|
+
input.metadata.cceDecryptedPayload = plaintext;
|
|
4802
|
+
input.metadata.cceDecryptionOk = true;
|
|
4803
|
+
return {
|
|
4804
|
+
decision: "ALLOW" /* ALLOW */,
|
|
4805
|
+
allow: true,
|
|
4806
|
+
riskScore: 0,
|
|
4807
|
+
reasons: []
|
|
4808
|
+
};
|
|
4809
|
+
}
|
|
4810
|
+
};
|
|
4811
|
+
function buildAad(envelope) {
|
|
4812
|
+
const parts = [
|
|
4813
|
+
envelope.ver,
|
|
4814
|
+
envelope.request_id,
|
|
4815
|
+
envelope.correlation_id,
|
|
4816
|
+
envelope.client_kid,
|
|
4817
|
+
envelope.capsule.capsule_id,
|
|
4818
|
+
envelope.capsule.intent,
|
|
4819
|
+
envelope.capsule.aud,
|
|
4820
|
+
envelope.request_nonce
|
|
4821
|
+
];
|
|
4822
|
+
return new TextEncoder().encode(parts.join("|"));
|
|
4823
|
+
}
|
|
4824
|
+
function base64UrlDecode2(input) {
|
|
4825
|
+
const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
4826
|
+
const padding = "=".repeat((4 - base64.length % 4) % 4);
|
|
4827
|
+
const binary = atob(base64 + padding);
|
|
4828
|
+
const bytes2 = new Uint8Array(binary.length);
|
|
4829
|
+
for (let i = 0; i < binary.length; i++) {
|
|
4830
|
+
bytes2[i] = binary.charCodeAt(i);
|
|
4831
|
+
}
|
|
4832
|
+
return bytes2;
|
|
4833
|
+
}
|
|
4834
|
+
|
|
4835
|
+
// src/core/index.ts
|
|
4836
|
+
var core_exports = {};
|
|
4837
|
+
__export(core_exports, {
|
|
4838
|
+
AXIS_MAGIC: () => AXIS_MAGIC,
|
|
4839
|
+
AXIS_VERSION: () => AXIS_VERSION,
|
|
4840
|
+
AxisError: () => AxisError,
|
|
4841
|
+
AxisFrameZ: () => AxisFrameZ,
|
|
4842
|
+
BodyProfile: () => BodyProfile,
|
|
4843
|
+
ERR_BAD_SIGNATURE: () => ERR_BAD_SIGNATURE,
|
|
4844
|
+
ERR_CONTRACT_VIOLATION: () => ERR_CONTRACT_VIOLATION,
|
|
4845
|
+
ERR_INVALID_PACKET: () => ERR_INVALID_PACKET,
|
|
4846
|
+
ERR_REPLAY_DETECTED: () => ERR_REPLAY_DETECTED,
|
|
4847
|
+
FLAG_BODY_TLV: () => FLAG_BODY_TLV,
|
|
4848
|
+
FLAG_CHAIN_REQ: () => FLAG_CHAIN_REQ,
|
|
4849
|
+
FLAG_HAS_WITNESS: () => FLAG_HAS_WITNESS,
|
|
4850
|
+
MAX_BODY_LEN: () => MAX_BODY_LEN,
|
|
4851
|
+
MAX_FRAME_LEN: () => MAX_FRAME_LEN,
|
|
4852
|
+
MAX_HDR_LEN: () => MAX_HDR_LEN,
|
|
4853
|
+
MAX_SIG_LEN: () => MAX_SIG_LEN,
|
|
4854
|
+
NCERT_ALG: () => NCERT_ALG,
|
|
4855
|
+
NCERT_EXP: () => NCERT_EXP,
|
|
4856
|
+
NCERT_ISSUER_KID: () => NCERT_ISSUER_KID,
|
|
4857
|
+
NCERT_KID: () => NCERT_KID,
|
|
4858
|
+
NCERT_NBF: () => NCERT_NBF,
|
|
4859
|
+
NCERT_NODE_ID: () => NCERT_NODE_ID,
|
|
4860
|
+
NCERT_PAYLOAD: () => NCERT_PAYLOAD,
|
|
4861
|
+
NCERT_PUB: () => NCERT_PUB,
|
|
4862
|
+
NCERT_SCOPE: () => NCERT_SCOPE,
|
|
4863
|
+
NCERT_SIG: () => NCERT_SIG,
|
|
4864
|
+
PROOF_CAPSULE: () => PROOF_CAPSULE,
|
|
4865
|
+
PROOF_JWT: () => PROOF_JWT,
|
|
4866
|
+
PROOF_LOOM: () => PROOF_LOOM,
|
|
4867
|
+
PROOF_MTLS: () => PROOF_MTLS,
|
|
4868
|
+
PROOF_NONE: () => PROOF_NONE,
|
|
4869
|
+
PROOF_WITNESS: () => PROOF_WITNESS,
|
|
4870
|
+
ProofType: () => ProofType,
|
|
4871
|
+
TLV: () => TLV,
|
|
4872
|
+
TLV_ACTOR_ID: () => TLV_ACTOR_ID,
|
|
4873
|
+
TLV_AUD: () => TLV_AUD,
|
|
4874
|
+
TLV_BODY_ARR: () => TLV_BODY_ARR,
|
|
4875
|
+
TLV_BODY_OBJ: () => TLV_BODY_OBJ,
|
|
4876
|
+
TLV_CAPSULE: () => TLV_CAPSULE,
|
|
4877
|
+
TLV_EFFECT: () => TLV_EFFECT,
|
|
4878
|
+
TLV_ERROR_CODE: () => TLV_ERROR_CODE,
|
|
4879
|
+
TLV_ERROR_MSG: () => TLV_ERROR_MSG,
|
|
4880
|
+
TLV_INDEX: () => TLV_INDEX,
|
|
4881
|
+
TLV_INTENT: () => TLV_INTENT,
|
|
4882
|
+
TLV_KID: () => TLV_KID,
|
|
4883
|
+
TLV_LOOM_PRESENCE_ID: () => TLV_LOOM_PRESENCE_ID,
|
|
4884
|
+
TLV_LOOM_THREAD_HASH: () => TLV_LOOM_THREAD_HASH,
|
|
4885
|
+
TLV_LOOM_WRIT: () => TLV_LOOM_WRIT,
|
|
4886
|
+
TLV_NODE: () => TLV_NODE,
|
|
4887
|
+
TLV_NODE_CERT_HASH: () => TLV_NODE_CERT_HASH,
|
|
4888
|
+
TLV_NODE_KID: () => TLV_NODE_KID,
|
|
4889
|
+
TLV_NONCE: () => TLV_NONCE,
|
|
4890
|
+
TLV_OFFSET: () => TLV_OFFSET,
|
|
4891
|
+
TLV_OK: () => TLV_OK,
|
|
4892
|
+
TLV_PID: () => TLV_PID,
|
|
4893
|
+
TLV_PREV_HASH: () => TLV_PREV_HASH,
|
|
4894
|
+
TLV_PROOF_REF: () => TLV_PROOF_REF,
|
|
4895
|
+
TLV_PROOF_TYPE: () => TLV_PROOF_TYPE,
|
|
4896
|
+
TLV_REALM: () => TLV_REALM,
|
|
4897
|
+
TLV_RECEIPT_HASH: () => TLV_RECEIPT_HASH,
|
|
4898
|
+
TLV_RID: () => TLV_RID,
|
|
4899
|
+
TLV_SHA256_CHUNK: () => TLV_SHA256_CHUNK,
|
|
4900
|
+
TLV_TRACE_ID: () => TLV_TRACE_ID,
|
|
4901
|
+
TLV_TS: () => TLV_TS,
|
|
4902
|
+
TLV_UPLOAD_ID: () => TLV_UPLOAD_ID,
|
|
4903
|
+
computeReceiptHash: () => computeReceiptHash,
|
|
4904
|
+
computeSignaturePayload: () => computeSignaturePayload,
|
|
4905
|
+
decodeArray: () => decodeArray,
|
|
4906
|
+
decodeFrame: () => decodeFrame,
|
|
4907
|
+
decodeObject: () => decodeObject,
|
|
4908
|
+
decodeTLVs: () => decodeTLVs,
|
|
4909
|
+
decodeTLVsList: () => decodeTLVsList,
|
|
4910
|
+
decodeVarint: () => decodeVarint,
|
|
4911
|
+
encodeFrame: () => encodeFrame,
|
|
4912
|
+
encodeTLVs: () => encodeTLVs,
|
|
4913
|
+
encodeVarint: () => encodeVarint,
|
|
4914
|
+
generateEd25519KeyPair: () => generateEd25519KeyPair,
|
|
4915
|
+
getSignTarget: () => getSignTarget,
|
|
4916
|
+
sha256: () => sha2564,
|
|
4917
|
+
signFrame: () => signFrame,
|
|
4918
|
+
varintLength: () => varintLength,
|
|
4919
|
+
verifyFrameSignature: () => verifyFrameSignature
|
|
4920
|
+
});
|
|
4921
|
+
|
|
4922
|
+
// src/crypto/index.ts
|
|
4923
|
+
var crypto_exports = {};
|
|
4924
|
+
__export(crypto_exports, {
|
|
4925
|
+
ProofVerificationService: () => ProofVerificationService,
|
|
4926
|
+
b64urlDecode: () => b64urlDecode,
|
|
4927
|
+
b64urlDecodeString: () => b64urlDecodeString,
|
|
4928
|
+
b64urlEncode: () => b64urlEncode,
|
|
3484
4929
|
b64urlEncodeString: () => b64urlEncodeString,
|
|
3485
4930
|
canonicalJson: () => canonicalJson,
|
|
3486
4931
|
canonicalJsonExcluding: () => canonicalJsonExcluding
|
|
@@ -3488,7 +4933,7 @@ __export(crypto_exports, {
|
|
|
3488
4933
|
|
|
3489
4934
|
// src/crypto/proof-verification.service.ts
|
|
3490
4935
|
import { Injectable as Injectable8, Logger as Logger7 } from "@nestjs/common";
|
|
3491
|
-
import * as
|
|
4936
|
+
import * as crypto4 from "crypto";
|
|
3492
4937
|
import * as nacl from "tweetnacl";
|
|
3493
4938
|
var ProofVerificationService = class {
|
|
3494
4939
|
constructor() {
|
|
@@ -3700,7 +5145,7 @@ var ProofVerificationService = class {
|
|
|
3700
5145
|
certPem.replace(/-----BEGIN CERTIFICATE-----/, "").replace(/-----END CERTIFICATE-----/, "").replace(/\s/g, ""),
|
|
3701
5146
|
"base64"
|
|
3702
5147
|
);
|
|
3703
|
-
return
|
|
5148
|
+
return crypto4.createHash("sha256").update(der).digest("hex");
|
|
3704
5149
|
}
|
|
3705
5150
|
};
|
|
3706
5151
|
ProofVerificationService = __decorateClass([
|
|
@@ -3747,12 +5192,22 @@ __export(engine_exports, {
|
|
|
3747
5192
|
PRE_DECODE_BOUNDARY: () => PRE_DECODE_BOUNDARY,
|
|
3748
5193
|
SensorDiscoveryService: () => SensorDiscoveryService,
|
|
3749
5194
|
SensorRegistry: () => SensorRegistry,
|
|
5195
|
+
buildQueueMessage: () => buildQueueMessage,
|
|
5196
|
+
buildUnsignedWitness: () => buildUnsignedWitness,
|
|
5197
|
+
canonicalizeObservation: () => canonicalizeObservation,
|
|
3750
5198
|
createObservation: () => createObservation,
|
|
5199
|
+
decodeQueueMessage: () => decodeQueueMessage,
|
|
5200
|
+
encodeQueueMessage: () => encodeQueueMessage,
|
|
3751
5201
|
endStage: () => endStage,
|
|
3752
5202
|
finalizeObservation: () => finalizeObservation,
|
|
5203
|
+
hashObservation: () => hashObservation,
|
|
3753
5204
|
observation: () => observation_exports,
|
|
5205
|
+
parseAutoClaimEntries: () => parseAutoClaimEntries,
|
|
5206
|
+
parseStreamEntries: () => parseStreamEntries,
|
|
3754
5207
|
recordSensor: () => recordSensor,
|
|
3755
|
-
|
|
5208
|
+
stableJsonStringify: () => stableJsonStringify,
|
|
5209
|
+
startStage: () => startStage,
|
|
5210
|
+
verifyResponse: () => verifyResponse
|
|
3756
5211
|
});
|
|
3757
5212
|
|
|
3758
5213
|
// src/engine/observation/index.ts
|
|
@@ -3782,35 +5237,6 @@ __export(loom_exports, {
|
|
|
3782
5237
|
deriveAnchorReflection: () => deriveAnchorReflection
|
|
3783
5238
|
});
|
|
3784
5239
|
|
|
3785
|
-
// src/loom/loom.types.ts
|
|
3786
|
-
function deriveAnchorReflection(softid, context = "openlogs", scope = "loom") {
|
|
3787
|
-
return `ar:${context}:${scope}:${softid}`;
|
|
3788
|
-
}
|
|
3789
|
-
function canonicalizeWrit(writ) {
|
|
3790
|
-
const ordered = {
|
|
3791
|
-
head: { tid: writ.head.tid, seq: writ.head.seq },
|
|
3792
|
-
body: {
|
|
3793
|
-
who: writ.body.who,
|
|
3794
|
-
act: writ.body.act,
|
|
3795
|
-
res: writ.body.res,
|
|
3796
|
-
law: writ.body.law
|
|
3797
|
-
},
|
|
3798
|
-
meta: { iat: writ.meta.iat, exp: writ.meta.exp, prev: writ.meta.prev }
|
|
3799
|
-
};
|
|
3800
|
-
return JSON.stringify(ordered);
|
|
3801
|
-
}
|
|
3802
|
-
function canonicalizeGrant(grant) {
|
|
3803
|
-
const ordered = {
|
|
3804
|
-
grant_id: grant.grant_id,
|
|
3805
|
-
issuer: grant.issuer,
|
|
3806
|
-
subject: grant.subject,
|
|
3807
|
-
grant_type: grant.grant_type,
|
|
3808
|
-
caps: grant.caps,
|
|
3809
|
-
meta: grant.meta
|
|
3810
|
-
};
|
|
3811
|
-
return JSON.stringify(ordered);
|
|
3812
|
-
}
|
|
3813
|
-
|
|
3814
5240
|
// src/schemas/index.ts
|
|
3815
5241
|
var schemas_exports = {};
|
|
3816
5242
|
__export(schemas_exports, {
|
|
@@ -4590,7 +6016,7 @@ ChunkHashSensor = __decorateClass([
|
|
|
4590
6016
|
|
|
4591
6017
|
// src/sensors/entropy.sensor.ts
|
|
4592
6018
|
import { Injectable as Injectable14, Logger as Logger10 } from "@nestjs/common";
|
|
4593
|
-
import * as
|
|
6019
|
+
import * as crypto5 from "crypto";
|
|
4594
6020
|
var EntropySensor = class {
|
|
4595
6021
|
constructor() {
|
|
4596
6022
|
this.logger = new Logger10(EntropySensor.name);
|
|
@@ -4758,7 +6184,7 @@ var EntropySensor = class {
|
|
|
4758
6184
|
* @returns {Uint8Array} Cryptographically secure random bytes
|
|
4759
6185
|
*/
|
|
4760
6186
|
static generateSecureRandom(length) {
|
|
4761
|
-
return new Uint8Array(
|
|
6187
|
+
return new Uint8Array(crypto5.randomBytes(length));
|
|
4762
6188
|
}
|
|
4763
6189
|
};
|
|
4764
6190
|
EntropySensor = __decorateClass([
|
|
@@ -5821,59 +7247,6 @@ var utils_exports = {};
|
|
|
5821
7247
|
__export(utils_exports, {
|
|
5822
7248
|
encodeAxisTlvDto: () => encodeAxisTlvDto
|
|
5823
7249
|
});
|
|
5824
|
-
|
|
5825
|
-
// src/utils/axis-tlv-codec.ts
|
|
5826
|
-
function encodeAxisTlvDto(dtoClass, data) {
|
|
5827
|
-
const schema = extractDtoSchema(dtoClass);
|
|
5828
|
-
const items = schema.fields.flatMap((field) => {
|
|
5829
|
-
const value = data[field.name];
|
|
5830
|
-
if (value === void 0 || value === null) {
|
|
5831
|
-
if (field.required) {
|
|
5832
|
-
throw new Error(`Missing required TLV response field: ${field.name}`);
|
|
5833
|
-
}
|
|
5834
|
-
return [];
|
|
5835
|
-
}
|
|
5836
|
-
return [{ type: field.tag, value: encodeField(field, value) }];
|
|
5837
|
-
});
|
|
5838
|
-
return buildTLVs(items);
|
|
5839
|
-
}
|
|
5840
|
-
function encodeField(field, value) {
|
|
5841
|
-
switch (field.kind) {
|
|
5842
|
-
case "utf8":
|
|
5843
|
-
return Buffer.from(String(value), "utf8");
|
|
5844
|
-
case "u64":
|
|
5845
|
-
return encodeU64(value);
|
|
5846
|
-
case "bytes":
|
|
5847
|
-
case "bytes16":
|
|
5848
|
-
return toBuffer(value);
|
|
5849
|
-
case "bool":
|
|
5850
|
-
return Buffer.from([value ? 1 : 0]);
|
|
5851
|
-
case "obj":
|
|
5852
|
-
case "arr":
|
|
5853
|
-
return Buffer.from(JSON.stringify(value), "utf8");
|
|
5854
|
-
default:
|
|
5855
|
-
return toBuffer(value);
|
|
5856
|
-
}
|
|
5857
|
-
}
|
|
5858
|
-
function encodeU64(value) {
|
|
5859
|
-
const encoded = Buffer.alloc(8);
|
|
5860
|
-
encoded.writeBigUInt64BE(
|
|
5861
|
-
typeof value === "bigint" ? value : BigInt(value)
|
|
5862
|
-
);
|
|
5863
|
-
return encoded;
|
|
5864
|
-
}
|
|
5865
|
-
function toBuffer(value) {
|
|
5866
|
-
if (Buffer.isBuffer(value)) {
|
|
5867
|
-
return value;
|
|
5868
|
-
}
|
|
5869
|
-
if (value instanceof Uint8Array) {
|
|
5870
|
-
return Buffer.from(value);
|
|
5871
|
-
}
|
|
5872
|
-
if (typeof value === "string") {
|
|
5873
|
-
return Buffer.from(value, "utf8");
|
|
5874
|
-
}
|
|
5875
|
-
throw new Error(`Unsupported TLV bytes value: ${typeof value}`);
|
|
5876
|
-
}
|
|
5877
7250
|
export {
|
|
5878
7251
|
ATS1_HDR,
|
|
5879
7252
|
ATS1_SCHEMA,
|
|
@@ -5901,6 +7274,9 @@ export {
|
|
|
5901
7274
|
BAND,
|
|
5902
7275
|
BodyProfile,
|
|
5903
7276
|
CAPABILITIES,
|
|
7277
|
+
CCE_ERROR,
|
|
7278
|
+
CCE_PROTOCOL_VERSION,
|
|
7279
|
+
CceError,
|
|
5904
7280
|
ContractViolationError,
|
|
5905
7281
|
DEFAULT_CONTRACTS,
|
|
5906
7282
|
DEFAULT_TIMEOUT,
|
|
@@ -5960,6 +7336,7 @@ export {
|
|
|
5960
7336
|
RESPONSE_TAG_ID,
|
|
5961
7337
|
RESPONSE_TAG_UPDATED_AT,
|
|
5962
7338
|
RESPONSE_TAG_UPDATED_BY,
|
|
7339
|
+
verifyResponse as ResponseObserver,
|
|
5963
7340
|
RiskDecision,
|
|
5964
7341
|
SENSOR_METADATA_KEY,
|
|
5965
7342
|
Schema2002_PasskeyLoginOptionsRes,
|
|
@@ -5993,6 +7370,7 @@ export {
|
|
|
5993
7370
|
TLV_OFFSET,
|
|
5994
7371
|
TLV_OK,
|
|
5995
7372
|
TLV_PID,
|
|
7373
|
+
TLV_LOOM_PRESENCE_ID as TLV_PRESENCE_ID,
|
|
5996
7374
|
TLV_PREV_HASH,
|
|
5997
7375
|
TLV_PROOF_REF,
|
|
5998
7376
|
TLV_PROOF_TYPE,
|
|
@@ -6000,10 +7378,12 @@ export {
|
|
|
6000
7378
|
TLV_RECEIPT_HASH,
|
|
6001
7379
|
TLV_RID,
|
|
6002
7380
|
TLV_SHA256_CHUNK,
|
|
7381
|
+
TLV_LOOM_THREAD_HASH as TLV_THREAD_HASH,
|
|
6003
7382
|
TLV_TRACE_ID,
|
|
6004
7383
|
TLV_TS,
|
|
6005
7384
|
TLV_UPLOAD_ID,
|
|
6006
7385
|
TLV_VALIDATORS_KEY,
|
|
7386
|
+
TLV_LOOM_WRIT as TLV_WRIT,
|
|
6007
7387
|
TlvEnum,
|
|
6008
7388
|
TlvField,
|
|
6009
7389
|
TlvMinLen,
|
|
@@ -6026,7 +7406,10 @@ export {
|
|
|
6026
7406
|
canAccessResource,
|
|
6027
7407
|
canonicalJson,
|
|
6028
7408
|
canonicalJsonExcluding,
|
|
7409
|
+
canonicalizeGrant,
|
|
6029
7410
|
canonicalizeObservation,
|
|
7411
|
+
canonicalizeWrit,
|
|
7412
|
+
cce_exports as cce,
|
|
6030
7413
|
classifyIntent,
|
|
6031
7414
|
computeReceiptHash,
|
|
6032
7415
|
computeSignaturePayload,
|
|
@@ -6042,14 +7425,17 @@ export {
|
|
|
6042
7425
|
decodeTLVsList,
|
|
6043
7426
|
decodeVarint,
|
|
6044
7427
|
decorators_exports as decorators,
|
|
7428
|
+
deriveAnchorReflection,
|
|
6045
7429
|
encVarint,
|
|
6046
7430
|
encodeAxis1Frame,
|
|
7431
|
+
encodeAxisTlvDto,
|
|
6047
7432
|
encodeFrame,
|
|
6048
7433
|
encodeQueueMessage,
|
|
6049
7434
|
encodeTLVs,
|
|
6050
7435
|
encodeVarint,
|
|
6051
7436
|
endStage,
|
|
6052
7437
|
engine_exports as engine,
|
|
7438
|
+
executeCcePipeline,
|
|
6053
7439
|
extractDtoSchema,
|
|
6054
7440
|
finalizeObservation,
|
|
6055
7441
|
generateEd25519KeyPair,
|
|
@@ -6076,7 +7462,7 @@ export {
|
|
|
6076
7462
|
security_exports as security,
|
|
6077
7463
|
sensitivityName,
|
|
6078
7464
|
sensors_exports as sensors,
|
|
6079
|
-
sha256,
|
|
7465
|
+
sha2564 as sha256,
|
|
6080
7466
|
signFrame,
|
|
6081
7467
|
stableJsonStringify,
|
|
6082
7468
|
startStage,
|