@noble/post-quantum
Version:
Auditable & minimal JS implementation of post-quantum public-key cryptography: FIPS 203, 204, 205
146 lines • 6.07 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.EMPTY = exports.utf8ToBytes = exports.concatBytes = exports.randomBytes = exports.ensureBytes = void 0;
exports.equalBytes = equalBytes;
exports.splitCoder = splitCoder;
exports.vecCoder = vecCoder;
exports.cleanBytes = cleanBytes;
exports.getMask = getMask;
exports.getMessage = getMessage;
exports.getMessagePrehash = getMessagePrehash;
/**
* Utilities for hex, bytearray and number handling.
* @module
*/
/*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) */
const _assert_1 = require("@noble/hashes/_assert");
const sha256_1 = require("@noble/hashes/sha256");
const sha3_1 = require("@noble/hashes/sha3");
const sha512_1 = require("@noble/hashes/sha512");
const utils_1 = require("@noble/hashes/utils");
Object.defineProperty(exports, "concatBytes", { enumerable: true, get: function () { return utils_1.concatBytes; } });
Object.defineProperty(exports, "utf8ToBytes", { enumerable: true, get: function () { return utils_1.utf8ToBytes; } });
exports.ensureBytes = _assert_1.abytes;
exports.randomBytes = utils_1.randomBytes;
// Compares 2 u8a-s in kinda constant time
function equalBytes(a, b) {
if (a.length !== b.length)
return false;
let diff = 0;
for (let i = 0; i < a.length; i++)
diff |= a[i] ^ b[i];
return diff === 0;
}
function splitCoder(...lengths) {
const getLength = (c) => (typeof c === 'number' ? c : c.bytesLen);
const bytesLen = lengths.reduce((sum, a) => sum + getLength(a), 0);
return {
bytesLen,
encode: (bufs) => {
const res = new Uint8Array(bytesLen);
for (let i = 0, pos = 0; i < lengths.length; i++) {
const c = lengths[i];
const l = getLength(c);
const b = typeof c === 'number' ? bufs[i] : c.encode(bufs[i]);
(0, exports.ensureBytes)(b, l);
res.set(b, pos);
if (typeof c !== 'number')
b.fill(0); // clean
pos += l;
}
return res;
},
decode: (buf) => {
(0, exports.ensureBytes)(buf, bytesLen);
const res = [];
for (const c of lengths) {
const l = getLength(c);
const b = buf.subarray(0, l);
res.push(typeof c === 'number' ? b : c.decode(b));
buf = buf.subarray(l);
}
return res;
},
};
}
// nano-packed.array (fixed size)
function vecCoder(c, vecLen) {
const bytesLen = vecLen * c.bytesLen;
return {
bytesLen,
encode: (u) => {
if (u.length !== vecLen)
throw new Error(`vecCoder.encode: wrong length=${u.length}. Expected: ${vecLen}`);
const res = new Uint8Array(bytesLen);
for (let i = 0, pos = 0; i < u.length; i++) {
const b = c.encode(u[i]);
res.set(b, pos);
b.fill(0); // clean
pos += b.length;
}
return res;
},
decode: (a) => {
(0, exports.ensureBytes)(a, bytesLen);
const r = [];
for (let i = 0; i < a.length; i += c.bytesLen)
r.push(c.decode(a.subarray(i, i + c.bytesLen)));
return r;
},
};
}
// cleanBytes(new Uint8Array(), [new Uint16Array(), new Uint32Array()])
function cleanBytes(...list) {
for (const t of list) {
if (Array.isArray(t))
for (const b of t)
b.fill(0);
else
t.fill(0);
}
}
function getMask(bits) {
return (1 << bits) - 1; // 4 -> 0b1111
}
exports.EMPTY = new Uint8Array(0);
function getMessage(msg, ctx = exports.EMPTY) {
(0, exports.ensureBytes)(msg);
(0, exports.ensureBytes)(ctx);
if (ctx.length > 255)
throw new Error('context should be less than 255 bytes');
return (0, utils_1.concatBytes)(new Uint8Array([0, ctx.length]), ctx, msg);
}
// OIDS from https://csrc.nist.gov/projects/computer-security-objects-register/algorithm-registration
// TODO: maybe add 'OID' property to hashes themselves to improve tree-shaking?
const HASHES = {
'SHA2-256': { oid: (0, utils_1.hexToBytes)('0609608648016503040201'), hash: sha256_1.sha256 },
'SHA2-384': { oid: (0, utils_1.hexToBytes)('0609608648016503040202'), hash: sha512_1.sha384 },
'SHA2-512': { oid: (0, utils_1.hexToBytes)('0609608648016503040203'), hash: sha512_1.sha512 },
'SHA2-224': { oid: (0, utils_1.hexToBytes)('0609608648016503040204'), hash: sha256_1.sha224 },
'SHA2-512/224': { oid: (0, utils_1.hexToBytes)('0609608648016503040205'), hash: sha512_1.sha512_224 },
'SHA2-512/256': { oid: (0, utils_1.hexToBytes)('0609608648016503040206'), hash: sha512_1.sha512_256 },
'SHA3-224': { oid: (0, utils_1.hexToBytes)('0609608648016503040207'), hash: sha3_1.sha3_224 },
'SHA3-256': { oid: (0, utils_1.hexToBytes)('0609608648016503040208'), hash: sha3_1.sha3_256 },
'SHA3-384': { oid: (0, utils_1.hexToBytes)('0609608648016503040209'), hash: sha3_1.sha3_384 },
'SHA3-512': { oid: (0, utils_1.hexToBytes)('060960864801650304020A'), hash: sha3_1.sha3_512 },
'SHAKE-128': {
oid: (0, utils_1.hexToBytes)('060960864801650304020B'),
hash: (msg) => (0, sha3_1.shake128)(msg, { dkLen: 32 }),
},
'SHAKE-256': {
oid: (0, utils_1.hexToBytes)('060960864801650304020C'),
hash: (msg) => (0, sha3_1.shake256)(msg, { dkLen: 64 }),
},
};
function getMessagePrehash(hashName, msg, ctx = exports.EMPTY) {
(0, exports.ensureBytes)(msg);
(0, exports.ensureBytes)(ctx);
if (ctx.length > 255)
throw new Error('context should be less than 255 bytes');
if (!HASHES[hashName])
throw new Error('unknown hash: ' + hashName);
const { oid, hash } = HASHES[hashName];
const hashed = hash(msg);
return (0, utils_1.concatBytes)(new Uint8Array([1, ctx.length]), ctx, oid, hashed);
}
//# sourceMappingURL=utils.js.map