@mikeargento/bitgraph-verify 1.2.0 → 1.4.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/fuse-verify.d.ts +56 -0
- package/dist/fuse-verify.d.ts.map +1 -0
- package/dist/fuse-verify.js +231 -0
- package/dist/fuse-verify.js.map +1 -0
- package/dist/fuse.d.ts +145 -0
- package/dist/fuse.d.ts.map +1 -0
- package/dist/fuse.js +502 -0
- package/dist/fuse.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/verifier.d.ts.map +1 -1
- package/dist/verifier.js +73 -0
- package/dist/verifier.js.map +1 -1
- package/package.json +1 -1
- package/src/fuse-verify.ts +306 -0
- package/src/fuse.ts +570 -0
- package/src/index.ts +33 -0
- package/src/types.ts +18 -0
- package/src/verifier.ts +73 -0
package/src/verifier.ts
CHANGED
|
@@ -578,6 +578,28 @@ function checkPolicy(proof: BitGraphProof, policy: VerificationPolicy): string |
|
|
|
578
578
|
}
|
|
579
579
|
}
|
|
580
580
|
|
|
581
|
+
// WebAuthn origin allowlist. The origin's agreement with the rpIdHash has
|
|
582
|
+
// already been checked structurally in verifyAgency; this is the policy
|
|
583
|
+
// that names WHICH origins a reader accepts.
|
|
584
|
+
if (policy.allowedOrigins !== undefined && policy.allowedOrigins.length > 0) {
|
|
585
|
+
if (proof.agency === undefined) {
|
|
586
|
+
return "policy requires an allowed origin but proof has no agency";
|
|
587
|
+
}
|
|
588
|
+
const auth = proof.agency.authorization;
|
|
589
|
+
if (!("format" in auth) || auth.format !== "webauthn") {
|
|
590
|
+
return "policy requires an allowed WebAuthn origin but the authorization is not WebAuthn";
|
|
591
|
+
}
|
|
592
|
+
let origin: string | undefined;
|
|
593
|
+
try {
|
|
594
|
+
origin = (JSON.parse(auth.clientDataJSON) as { origin?: string }).origin;
|
|
595
|
+
} catch {
|
|
596
|
+
origin = undefined;
|
|
597
|
+
}
|
|
598
|
+
if (typeof origin !== "string" || !policy.allowedOrigins.includes(origin)) {
|
|
599
|
+
return `WebAuthn origin "${origin ?? "(none)"}" is not in the allowed set`;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
581
603
|
// Slot allocation required (BitGraph causal ordering)
|
|
582
604
|
if (policy.requireSlot === true) {
|
|
583
605
|
if (proof.slotAllocation === undefined) {
|
|
@@ -594,6 +616,44 @@ function checkPolicy(proof: BitGraphProof, policy: VerificationPolicy): string |
|
|
|
594
616
|
// Agency verification (P-256 device signature)
|
|
595
617
|
// ---------------------------------------------------------------------------
|
|
596
618
|
|
|
619
|
+
/**
|
|
620
|
+
* The RP-binding half of WebAuthn assertion verification: the origin in the
|
|
621
|
+
* client data must be a well-formed origin, and the first 32 bytes of the
|
|
622
|
+
* authenticator data (rpIdHash) must be SHA-256 of that origin's host or of
|
|
623
|
+
* a parent domain of it with at least two labels (the RP ID is allowed to be
|
|
624
|
+
* a registrable suffix of the origin's effective domain; without a public
|
|
625
|
+
* suffix list, "at least two labels" is the usual approximation and errs on
|
|
626
|
+
* the permissive side). A single-label host such as localhost is its own RP
|
|
627
|
+
* ID. Returns a reason string or null.
|
|
628
|
+
*/
|
|
629
|
+
function checkWebAuthnOrigin(origin: unknown, authData: Buffer): string | null {
|
|
630
|
+
if (typeof origin !== "string" || origin.length === 0) {
|
|
631
|
+
return "agency: clientDataJSON missing origin field";
|
|
632
|
+
}
|
|
633
|
+
let host: string;
|
|
634
|
+
try {
|
|
635
|
+
host = new URL(origin).hostname.toLowerCase();
|
|
636
|
+
} catch {
|
|
637
|
+
return "agency: clientDataJSON origin is not a valid origin";
|
|
638
|
+
}
|
|
639
|
+
if (!host) return "agency: clientDataJSON origin has no host";
|
|
640
|
+
const rpIdHash = authData.subarray(0, 32).toString("hex");
|
|
641
|
+
const labels = host.split(".");
|
|
642
|
+
const candidates: string[] = [];
|
|
643
|
+
for (let i = 0; i < labels.length; i++) {
|
|
644
|
+
const candidate = labels.slice(i).join(".");
|
|
645
|
+
if (i > 0 && labels.length - i < 2) break; // no bare TLDs
|
|
646
|
+
candidates.push(candidate);
|
|
647
|
+
}
|
|
648
|
+
const matches = candidates.some(
|
|
649
|
+
(c) => createHash("sha256").update(c, "utf8").digest("hex") === rpIdHash,
|
|
650
|
+
);
|
|
651
|
+
if (!matches) {
|
|
652
|
+
return "agency: authenticatorData rpIdHash does not match the origin's domain";
|
|
653
|
+
}
|
|
654
|
+
return null;
|
|
655
|
+
}
|
|
656
|
+
|
|
597
657
|
/**
|
|
598
658
|
* Verify the agency envelope: P-256 signature, structural consistency,
|
|
599
659
|
* and artifact binding.
|
|
@@ -717,6 +777,19 @@ function verifyAgency(proof: BitGraphProof): string | null {
|
|
|
717
777
|
if (!(flags & 0x01)) return "agency: authenticatorData UP flag not set";
|
|
718
778
|
if (!(flags & 0x04)) return "agency: authenticatorData UV flag not set";
|
|
719
779
|
|
|
780
|
+
// Origin and RP binding. The client data names the origin the
|
|
781
|
+
// assertion was made on; the authenticator data opens with the hash
|
|
782
|
+
// of the RP ID it scoped the credential to. WebAuthn requires the RP
|
|
783
|
+
// ID to be the origin's host or a registrable parent of it, so the
|
|
784
|
+
// two must agree, and a verifier that reads only the JSON has taken
|
|
785
|
+
// the client's word for which site asked. This is the RP-binding
|
|
786
|
+
// check the WebAuthn verification procedure lists; it is structural
|
|
787
|
+
// (any origin, as long as the authenticator agrees), and a SPECIFIC
|
|
788
|
+
// origin is a policy (VerificationPolicy.allowedOrigins), because
|
|
789
|
+
// the protocol can be run under any domain.
|
|
790
|
+
const originError = checkWebAuthnOrigin(clientData.origin, authData);
|
|
791
|
+
if (originError !== null) return originError;
|
|
792
|
+
|
|
720
793
|
// Build signed data: authenticatorData || SHA-256(clientDataJSON)
|
|
721
794
|
const clientDataHash = createHash("sha256")
|
|
722
795
|
.update(Buffer.from(webauthn.clientDataJSON, "utf8"))
|