ic-websocket-js
Version:
IC WebSocket on the Internet Computer
89 lines • 3.5 kB
JavaScript
import { Cbor, Certificate, compare, lookup_path, lookupResultToBuffer, reconstruct, } from "@dfinity/agent";
import { logger } from "./logger";
const areBuffersEqual = (buf1, buf2) => {
return compare(buf1, buf2) === 0;
};
export const isMessageBodyValid = async (canisterId, path, body, certificate, tree, agent, maxCertificateAgeInMinutes) => {
let cert;
try {
cert = await Certificate.create({
certificate,
canisterId,
rootKey: agent.rootKey,
maxAgeInMinutes: maxCertificateAgeInMinutes,
});
}
catch (error) {
logger.error("[certification] Error creating certificate:", error);
return false;
}
const hashTree = Cbor.decode(tree);
const reconstructed = await reconstruct(hashTree);
const witnessLookupResult = cert.lookup([
"canister",
canisterId.toUint8Array(),
"certified_data"
]);
const witness = lookupResultToBuffer(witnessLookupResult);
if (!witness) {
throw new Error("Could not find certified data for this canister in the certificate.");
}
// First validate that the Tree is as good as the certification.
if (!areBuffersEqual(witness, reconstructed)) {
logger.error("[certification] Witness != Tree passed in ic-certification");
return false;
}
// Next, calculate the SHA of the content.
const sha = await crypto.subtle.digest("SHA-256", body);
let treeShaLookupResult = lookup_path(["websocket", path], hashTree);
let treeSha = lookupResultToBuffer(treeShaLookupResult);
if (!treeSha) {
// Allow fallback to index path.
treeShaLookupResult = lookup_path(["websocket"], hashTree);
treeSha = lookupResultToBuffer(treeShaLookupResult);
}
if (!treeSha) {
// The tree returned in the certification header is wrong. Return false.
// We don't throw here, just invalidate the request.
logger.error(`[certification] Invalid Tree in the header. Does not contain path ${JSON.stringify(path)}`);
return false;
}
return !!treeSha && areBuffersEqual(sha, treeSha);
};
export const safeExecute = async (fn, warnMessage) => {
try {
return await Promise.resolve(fn());
}
catch (error) {
logger.warn(warnMessage, error);
}
};
/**
* Generates a random unsigned 64-bit integer
* @returns {bigint} a random bigint
*/
export const randomBigInt = () => {
// determine whether browser crypto is available
if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) {
const array = new BigUint64Array(1);
window.crypto.getRandomValues(array);
return array[0];
}
// A second check for webcrypto, in case it is loaded under global instead of window
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
const array = new BigUint64Array(1);
crypto.getRandomValues(array);
return array[0];
}
// determine whether node crypto is available
// @ts-ignore
if (typeof crypto !== 'undefined' && crypto.randomBytes) {
// @ts-ignore
const randomBuffer = crypto.randomBytes(8);
const randomHexString = randomBuffer.toString('hex');
return BigInt('0x' + randomHexString);
}
// TODO: test these fallbacks in a node environment
throw new Error('Random UInt64 generation not supported in this environment');
};
//# sourceMappingURL=utils.js.map