@tbd54566975/dap
Version:
TypeScript SDK for DAPs
115 lines (112 loc) • 5.17 kB
JavaScript
// src/crypto.ts
import "@web5/dids";
import { isDidVerificationMethod } from "@web5/dids/utils";
import { Convert } from "@web5/common";
import { canonicalize, isPublicJwk, LocalKeyManager, Sha256 } from "@web5/crypto";
// src/did-resolver.ts
import { DidDht, DidJwk, DidWeb, UniversalResolver } from "@web5/dids";
var DidResolver = new UniversalResolver({
didResolvers: [DidDht, DidJwk, DidWeb]
});
// src/crypto.ts
var keyManager = new LocalKeyManager();
var Crypto = class {
/**
* Computes a digest of the payload by:
*
* 1. JSON Serializing the payload as per [RFC 8785: JSON Canonicalization Scheme](https://datatracker.ietf.org/doc/html/rfc8785).
* 2. Computing the SHA-256 hash of the canonicalized payload.
* 3. Base64URL encoding the hash without padding as per [RFC 7515: JSON Web Signature (JWS)](https://datatracker.ietf.org/doc/html/rfc7515#appendix-C).
*
* @returns The SHA-256 hash of the canonicalized payload, represented as a Base64URL encoded string.
*/
static async digest(payload) {
const canonicalized = canonicalize(payload);
const canonicalizedBytes = Convert.string(canonicalized).toUint8Array();
const digest = await Sha256.digest({ data: canonicalizedBytes });
return digest;
}
/**
* Signs the provided payload and produces a Compact JSON Web Signature (JWS).
*
* @param params - The parameters required for signing.
* @returns A promise that resolves to the generated compact JWS.
* @throws Will throw an error if the specified algorithm is not supported.
*/
static async sign({ did, payload, detached }) {
const signer = await did.getSigner();
const jwsHeader = { alg: signer.algorithm, kid: signer.keyId };
const jwsHeaderBase64Url = Convert.object(jwsHeader).toBase64Url();
const payloadBase64Url = Convert.uint8Array(payload).toBase64Url();
const toSign = jwsHeaderBase64Url + "." + payloadBase64Url;
const toSignBytes = Convert.string(toSign).toUint8Array();
const signatureBytes = await signer.sign({ data: toSignBytes });
const signatureBase64Url = Convert.uint8Array(signatureBytes).toBase64Url();
if (detached) {
return jwsHeaderBase64Url + ".." + signatureBase64Url;
} else {
return jwsHeaderBase64Url + "." + payloadBase64Url + "." + signatureBase64Url;
}
}
/**
* Verifies the integrity of a message or resource's signature.
*
* @param params - The parameters required for verification.
* @returns A Promise that resolves to the DID of the signer if verification is successful.
* @throws Various errors related to invalid input or failed verification.
*/
static async verify({ jws, detachedPayload }) {
if (typeof jws !== "string") {
throw new InvalidJws("Signature verification failed: Expected Compact JWS in string format");
}
let { 0: jwsHeaderBase64Url, 1: payloadBase64Url, 2: signatureBase64Url, length } = jws.split(".");
if (length !== 3) {
throw new InvalidJws("Signature verification failed: Expected Compact JWS with 3 parts");
}
if (detachedPayload) {
if (payloadBase64Url.length !== 0) {
throw new InvalidJws("Signature verification failed: Expected detached JWS with empty payload");
}
payloadBase64Url = Convert.uint8Array(detachedPayload).toBase64Url();
}
let jwsHeader;
try {
jwsHeader = Convert.base64Url(jwsHeaderBase64Url).toObject();
} catch {
throw new InvalidJws("Signature verification failed: Invalid JWS header");
}
if (!jwsHeader.alg || typeof jwsHeader.alg !== "string") {
throw new InvalidJws('Signature verification failed: Missing or invalid algorithm ("alg") in JWS header');
}
if (!jwsHeader.kid || typeof jwsHeader.kid !== "string") {
throw new InvalidJws('Signature verification failed: Missing or invalid key ID ("kid") in JWS header');
}
const dereferencingResult = await DidResolver.dereference(jwsHeader.kid);
if (!isDidVerificationMethod(dereferencingResult.contentStream)) {
throw new InvalidJws('Signature verification failed: Expected key id ("kid") in JWS header to dereference to a DID Document Verification Method');
}
const publicKeyJwk = dereferencingResult.contentStream.publicKeyJwk;
if (!isPublicJwk(publicKeyJwk)) {
throw new Error("Signature verification failed: Expected kid in JWS header to dereference to a DID Document Verification Method with publicKeyJwk");
}
const signedData = jwsHeaderBase64Url + "." + payloadBase64Url;
const signedDataBytes = Convert.string(signedData).toUint8Array();
const signatureBytes = Convert.base64Url(signatureBase64Url).toUint8Array();
const isValidSignature = await keyManager.verify({ key: publicKeyJwk, data: signedDataBytes, signature: signatureBytes });
if (!isValidSignature) {
throw new InvalidJws("Signature verification failed: Integrity mismatch");
}
const [did] = jwsHeader.kid.split("#");
return did;
}
};
var InvalidJws = class extends Error {
constructor(message) {
super(message ?? "Invalid JWS");
this.name = "InvalidJws";
}
};
export {
Crypto,
InvalidJws
};