@credo-ts/core
Version:
<p align="center"> <br /> <img alt="Credo Logo" src="https://github.com/openwallet-foundation/credo-ts/blob/c7886cb8377ceb8ee4efe8d264211e561a75072d/images/credo-logo.png" height="250px" /> </p> <h1 align="center"><b>Credo - Core</b></h1
118 lines (114 loc) • 3.73 kB
JavaScript
import { CredoError } from "../error/CredoError.mjs";
import "../error/index.mjs";
import { TypedArrayEncoder } from "./TypedArrayEncoder.mjs";
import { Hasher } from "../crypto/hashes/Hasher.mjs";
//#region src/utils/IntegrityVerifier.ts
/**
* Verifies Subresource Integrity (SRI) metadata according to W3C specification.
*
* This class implements the verification logic for integrity metadata strings
* as defined in the W3C Subresource Integrity specification.
*
* @see https://www.w3.org/TR/SRI/
*/
var IntegrityVerifier = class IntegrityVerifier {
/**
* Verifies the integrity of raw data against provided integrity metadata.
*
* @param data - The data to verify
* @param integrityMetadata - The integrity metadata string (e.g., "sha256-...")
* @throws {CredoError} if verification fails
*/
static verifyIntegrity(data, integrityMetadata) {
const parsedMetadata = IntegrityVerifier.parseIntegrityMetadata(integrityMetadata);
if (parsedMetadata.length === 0) throw new CredoError(`Integrity check failed. Parsed integrity metadata is empty.`);
const strongestMetadata = IntegrityVerifier.getStrongestMetadata(parsedMetadata);
for (const metadata of strongestMetadata) if (IntegrityVerifier.applyAlgorithmToBytes(data, metadata.alg) === metadata.val) return;
throw new CredoError(`Integrity check failed. None of the provided hashes match the computed hash for the response.`);
}
/**
* Parses integrity metadata string into structured format.
*
* @param metadata - The integrity metadata string
* @returns Array of parsed metadata objects
*/
static parseIntegrityMetadata(metadata) {
const result = [];
const validAlgorithms = [
"sha256",
"sha384",
"sha512"
];
const items = metadata.trim().split(/\s+/);
for (const item of items) {
if (!item) continue;
const [algorithmExpression] = item.split("?");
const parts = algorithmExpression.split("-");
if (parts.length < 2) continue;
const algorithm = parts[0].toLowerCase();
const base64Value = parts.slice(1).join("-");
if (validAlgorithms.includes(algorithm)) result.push({
alg: algorithm,
val: base64Value
});
}
return result;
}
/**
* Returns the metadata for the strongest algorithm(s) in the set.
*
* @param metadataSet - Array of parsed metadata
* @returns Array containing only the strongest algorithm's metadata
*/
static getStrongestMetadata(metadataSet) {
if (metadataSet.length === 0) return [];
const algorithmPriority = {
sha256: 0,
sha384: 1,
sha512: 2
};
let strongest = null;
const result = [];
for (const item of metadataSet) {
if (!strongest) {
strongest = item;
result.push(item);
continue;
}
const currentIndex = algorithmPriority[strongest.alg];
const newIndex = algorithmPriority[item.alg];
if (newIndex > currentIndex) {
strongest = item;
result.length = 0;
result.push(item);
} else if (newIndex === currentIndex) result.push(item);
}
return result;
}
/**
* Applies the specified hash algorithm to the given bytes.
*
* @param bytes - The bytes to hash
* @param algorithm - The hash algorithm name
* @returns Base64-encoded hash value
*/
static applyAlgorithmToBytes(bytes, algorithm) {
let hashResult;
switch (algorithm) {
case "sha256":
hashResult = Hasher.hash(bytes, "sha-256");
break;
case "sha384":
hashResult = Hasher.hash(bytes, "sha-384");
break;
case "sha512":
hashResult = Hasher.hash(bytes, "sha-512");
break;
default: throw new CredoError(`Unsupported hash algorithm: ${algorithm}`);
}
return TypedArrayEncoder.toBase64(hashResult);
}
};
//#endregion
export { IntegrityVerifier };
//# sourceMappingURL=IntegrityVerifier.mjs.map