UNPKG

@hicaru/ntrup.js

Version:

Pure JavaScript implementation of NTRU Prime post-quantum cryptography algorithm

1,012 lines (995 loc) 31.1 kB
import { ChaChaRng } from '@hicaru/chacharand.js'; var ErrorType; (function (ErrorType) { ErrorType["OverFlow"] = "OverFlow"; ErrorType["Mod2ShouldZero"] = "Mod2ShouldZero"; ErrorType["Mod4ShouldOne"] = "Mod4ShouldOne"; ErrorType["OutOfRange"] = "OutOfRange"; ErrorType["SumShouldEqW"] = "SumShouldEqW"; ErrorType["PolyError"] = "PolyError"; ErrorType["CompressError"] = "CompressError"; ErrorType["KemError"] = "KemError"; ErrorType["InvalidRqChunkSize"] = "InvalidRqChunkSize"; ErrorType["SyncThreadJoinError"] = "SyncThreadJoinError"; ErrorType["SyncLockError"] = "SyncLockError"; ErrorType["NoSolutionRecip3"] = "NoSolutionRecip3"; ErrorType["R3NoSolutionRecip"] = "R3NoSolutionRecip"; ErrorType["SliceLengthNotR3Size"] = "SliceLengthNotR3Size"; ErrorType["SeedSliceError"] = "SeedSliceError"; ErrorType["SizeSliceError"] = "SizeSliceError"; ErrorType["ByteslengthError"] = "ByteslengthError"; ErrorType["InvalidR3GInvrBytes"] = "InvalidR3GInvrBytes"; ErrorType["InvalidR3FBytes"] = "InvalidR3FBytes"; ErrorType["FailGenerateValidKeyPair"] = "FailGenerateValidKeyPair"; })(ErrorType || (ErrorType = {})); const V = 0x80000000; function i16NonzeroMask(x) { const u = x & 0xffff; let v = u; v = v ? ~(v - 1) : 0; v >>>= 31; return v ? -1 : 0; } function i16NegativeMask(x) { const u = x & 0xffff; const u15 = u >>> 15; return u15 == 0 ? u15 : -u15; } function u32DivmodU14(x, m) { let v = V; let qpart; v = Math.floor(v / m); let q = 0; qpart = Math.floor((x * v) / 0x80000000); let newX = x - qpart * m; q += qpart; qpart = Math.floor((newX * v) / 0x80000000); let finalX = newX - qpart * m; q += qpart; let subX = finalX - m; q += 1; const mask = subX >>> 31 !== 0 ? 0xffffffff : 0; const addedX = subX + (mask & m); const finalQ = q + mask; return [finalQ >>> 0, addedX >>> 0]; } function i32DivmodU14(x, m) { const px = V; const [mut_uq, ur] = u32DivmodU14((px + x) >>> 0, m); let mut_ur = ur; const [uq2, ur2] = u32DivmodU14(px, m); mut_ur = mut_ur - ur2; let uq = mut_uq - uq2; const mask = mut_ur >>> 15 !== 0 ? 0xffffffff : 0; mut_ur = (mut_ur + (mask & m)) >>> 0; uq = (uq + mask) >>> 0; return [uq, mut_ur]; } function i32ModU14(x, m) { return i32DivmodU14(x, m)[1]; } function u32ModU14(x, m) { return u32DivmodU14(x, m)[1]; } function weightWMask(r, W) { const weight = r.reduce((sum, x) => sum + (x & 1), 0); return i16NonzeroMask(weight - W); } const params653 = { P: 653, Q: 4621, W: 288, Q12: 2310, // (4621 - 1) / 2 R3_BYTES: 164, // (653 + 3) / 4 RQ_BYTES: 1306, // 653 * 2 PUBLICKEYS_BYTES: 1306, // 653 * 2 SECRETKEYS_BYTES: 328, // 164 * 2 DIFFICULT: 4, }; const params761 = { P: 761, W: 286, Q: 4591, Q12: 2295, // (4591 - 1) / 2 R3_BYTES: 191, // (761 + 3) / 4 RQ_BYTES: 1522, // 761 * 2 PUBLICKEYS_BYTES: 1522, // 761 * 2 SECRETKEYS_BYTES: 382, // 191 * 2 DIFFICULT: 6, }; const params857 = { P: 857, W: 322, Q: 5167, Q12: 2583, // (5167 - 1) / 2 R3_BYTES: 215, // (857 + 3) / 4 RQ_BYTES: 1714, // 857 * 2 PUBLICKEYS_BYTES: 1714, // 857 * 2 SECRETKEYS_BYTES: 430, // 215 * 2 DIFFICULT: 8, }; const params953 = { P: 953, Q: 6343, W: 396, Q12: 3171, // (6343 - 1) / 2 R3_BYTES: 239, // (953 + 3) / 4 RQ_BYTES: 1906, // 953 * 2 PUBLICKEYS_BYTES: 1906, // 953 * 2 SECRETKEYS_BYTES: 478, // 239 * 2 DIFFICULT: 10, }; const params1013 = { P: 1013, Q: 7177, W: 448, Q12: 3588, // (7177 - 1) / 2 R3_BYTES: 254, // (1013 + 3) / 4 RQ_BYTES: 2026, // 1013 * 2 PUBLICKEYS_BYTES: 2026, // 1013 * 2 SECRETKEYS_BYTES: 508, // 254 * 2 DIFFICULT: 12, }; const params1277 = { P: 1277, Q: 7879, W: 492, Q12: 3939, // (7879 - 1) / 2 R3_BYTES: 320, // (1277 + 3) / 4 RQ_BYTES: 2554, // 1277 * 2 PUBLICKEYS_BYTES: 2554, // 1277 * 2 SECRETKEYS_BYTES: 640, // 320 * 2 DIFFICULT: 14, }; const params = params1277; function urandom32(rng) { const c0 = rng.nextU8(); const c1 = rng.nextU8(); const c2 = rng.nextU8(); const c3 = rng.nextU8(); return c0 + 256 * c1 + 65536 * c2 + 16777216 * c3; } function randomSign(getRandomValue) { return getRandomValue() < 0.5 ? 1 : -1; } function randomRange3(rng) { const r = urandom32(rng); return (((r & 0x3fffffff) * 3) >>> 30) - 1; } function randomSmall(rng, params) { const r = new Int8Array(params.P); for (let i = 0; i < params.P; i++) { r[i] = randomRange3(rng); } return r; } function shortRandom(rng, params) { const list = new Uint32Array(params.P); for (let i = 0; i < params.P; i++) { const value = urandom32(rng); list[i] = i < params.W ? value & -2 : (value & -4) | 1; } for (let i = 0; i < params.W; i++) { if (list[i] % 2 !== 0) { throw new Error(ErrorType.Mod2ShouldZero); } } for (let i = params.W; i < params.P; i++) { if (list[i] % 4 !== 1) { throw new Error(ErrorType.Mod4ShouldOne); } } list.sort(); const newList = new Int32Array(params.P); let sum = 0; for (let i = 0; i < params.P; i++) { const newValue = (list[i] % 4) - 1; if (newValue > 1) { throw new Error(ErrorType.OutOfRange); } newList[i] = newValue; sum += Math.abs(newValue); } if (sum !== params.W) { throw new Error(ErrorType.SumShouldEqW); } const i16List = new Int16Array(params.P); for (let i = 0; i < params.P; i++) { i16List[i] = newList[i]; } return i16List; } function shuffleArray(arr, seed, params) { if (arr.length !== params.P) { throw new Error(ErrorType.SliceLengthNotR3Size); } const n = params.P; const rng = ChaChaRng.fromU64Seed(seed, 20); for (let i = 0; i < n; i++) { const j = rng.genRangeU32(0, n); [arr[i], arr[j]] = [arr[j], arr[i]]; } } function unshuffleArray(arr, seed, params) { if (arr.length !== params.P) { throw new Error(ErrorType.SliceLengthNotR3Size); } const n = params.P; const rng = ChaChaRng.fromU64Seed(seed, 20); const indexList = []; for (let i = 0; i < n; i++) { const j = rng.genRangeU32(0, n); indexList.push(j); } for (let i = n - 1; i >= 0; i--) { const j = indexList[i]; [arr[i], arr[j]] = [arr[j], arr[i]]; } } const BITS_SIZE = 6; const SYS_SIZE = 8; function usizeArrayToBytes(list) { const buffer = new ArrayBuffer(list.length * SYS_SIZE); const view = new DataView(buffer); for (let i = 0; i < list.length; i++) { const num = list[i]; if (!Number.isSafeInteger(num) || num < 0) { throw ErrorType.OutOfRange; } view.setBigUint64(i * SYS_SIZE, BigInt(num), true); } return new Uint8Array(buffer); } function bytesToUsizeArray(bytes) { if (bytes.length % SYS_SIZE !== 0) { throw ErrorType.ByteslengthError; } const result = new Array(bytes.length / SYS_SIZE); const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); for (let i = 0; i < result.length; i++) { const val = view.getBigUint64(i * SYS_SIZE, true); if (val > BigInt(Number.MAX_SAFE_INTEGER)) { throw ErrorType.OutOfRange; } result[i] = Number(val); } return result; } function packBytes(dataBytes, size, seed) { const sizeBytes = usizeArrayToBytes(size); const sizeLenBytes = usizeArrayToBytes([sizeBytes.length]); const seedBytes = new Uint8Array(8); new DataView(seedBytes.buffer).setBigUint64(0, seed, true); const totalLength = dataBytes.length + sizeBytes.length + sizeLenBytes.length + seedBytes.length; const result = new Uint8Array(totalLength); let offset = 0; result.set(dataBytes, offset); offset += dataBytes.length; result.set(sizeBytes, offset); offset += sizeBytes.length; result.set(sizeLenBytes, offset); offset += sizeLenBytes.length; result.set(seedBytes, offset); return result; } function unpackBytes(bytes) { const bytesLen = bytes.length; const X2_SYS_SIZE = SYS_SIZE * 2; if (bytesLen < X2_SYS_SIZE) { throw ErrorType.ByteslengthError; } let seed; try { const seedBytes = bytes.subarray(bytesLen - 8); seed = new DataView(seedBytes.buffer, seedBytes.byteOffset).getBigUint64(0, true); } catch (e) { throw ErrorType.SeedSliceError; } let sizeLen; try { const sizeLenBytes = bytes.subarray(bytesLen - X2_SYS_SIZE, bytesLen - SYS_SIZE); const sizeLenArray = bytesToUsizeArray(sizeLenBytes); if (sizeLenArray.length !== 1) { throw ErrorType.SizeSliceError; } sizeLen = sizeLenArray[0]; } catch (e) { throw ErrorType.SizeSliceError; } const dataSectionLength = bytesLen - sizeLen - X2_SYS_SIZE; if (bytesLen < sizeLen + X2_SYS_SIZE || dataSectionLength < 0) { throw ErrorType.ByteslengthError; } let size; try { const sizeBytes = bytes.subarray(bytesLen - sizeLen - X2_SYS_SIZE, bytesLen - X2_SYS_SIZE); if (sizeBytes.length !== sizeLen) { throw ErrorType.SizeSliceError; } size = bytesToUsizeArray(sizeBytes); } catch (e) { throw ErrorType.SizeSliceError; } const dataBytes = bytes.subarray(0, dataSectionLength); return { dataBytes, size, seed }; } function convertToTernary(num) { if (!Number.isInteger(num) || num < 0 || num > 255) { throw ErrorType.OutOfRange; } const result = new Int8Array(BITS_SIZE).fill(0); let n = num; for (let i = BITS_SIZE - 1; i >= 0; i--) { const digit = n % 3; result[i] = digit === 2 ? -1 : digit; n = Math.floor(n / 3); } return result; } function convertToDecimal(ternary) { if (ternary.length !== BITS_SIZE) { throw ErrorType.SliceLengthNotR3Size; } let result = 0; for (let i = 0; i < BITS_SIZE; i++) { const digit = ternary[i]; if (digit !== 0 && digit !== 1 && digit !== -1) { throw ErrorType.CompressError; } const x = digit === -1 ? 2 : digit; result = result * 3 + x; } return result & 0xFF; } function r3EncodeChunks(r3) { const chunkCount = Math.ceil(r3.length / BITS_SIZE); const output = new Uint8Array(chunkCount); for (let i = 0; i < chunkCount; i++) { const start = i * BITS_SIZE; const end = Math.min(start + BITS_SIZE, r3.length); const chunk = r3.subarray(start, end); const bits = new Int8Array(BITS_SIZE).fill(0); bits.set(chunk); output[i] = convertToDecimal(bits); } return output; } function r3DecodeChunks(bytes) { const output = new Int8Array(bytes.length * BITS_SIZE); for (let i = 0; i < bytes.length; i++) { const byte = bytes[i]; const bits = convertToTernary(byte); output.set(bits, i * BITS_SIZE); } return output; } function r3MergeWChunks(chunks, size, seed, params) { if (chunks.length !== size.length) { throw ErrorType.SliceLengthNotR3Size; } const output = []; for (let i = 0; i < chunks.length; i++) { const chunkSeed = seed + BigInt(i); const point = size[i]; const chunk = chunks[i]; if (chunk.length !== params.P) { throw ErrorType.SliceLengthNotR3Size; } if (point < 0 || point > params.P) { throw ErrorType.OutOfRange; } const part = Array.from(chunk); unshuffleArray(part, chunkSeed, params); output.push(...part.slice(0, point)); } return Int8Array.from(output); } function r3SplitWChunks(input, rng, params) { const LIMIT = params.W - params.DIFFICULT; const P = params.P; const W = params.W; const seedHigh = BigInt(rng()); const seedLow = BigInt(rng()); const originSeed = (seedHigh << 32n) | seedLow; let currentSeed = originSeed; const chunks = []; const size = []; let part = new Int8Array(P); let sum = 0; let inputPtr = 0; let partPtr = 0; while (inputPtr < input.length) { while (sum < LIMIT && inputPtr < input.length) { const value = input[inputPtr]; if (value !== -1 && value !== 0 && value !== 1) { throw ErrorType.CompressError; } const absValue = Math.abs(value); if (partPtr >= P) { throw ErrorType.OverFlow; } sum += absValue; part[partPtr] = value; inputPtr++; partPtr++; } size.push(partPtr); while (sum < W) { if (partPtr >= P) { throw ErrorType.OverFlow; } const value = randomSign(rng); part[partPtr] = value; sum += 1; partPtr++; } if (sum !== W) { throw ErrorType.SumShouldEqW; } for (let k = partPtr; k < P; k++) { part[k] = 0; } const partArray = Array.from(part); shuffleArray(partArray, currentSeed, params); chunks.push(Int8Array.from(partArray)); part = new Int8Array(P); currentSeed += 1n; partPtr = 0; sum = 0; } return { chunks, size, seed: originSeed }; } function r3Encode(f, params) { const s = new Uint8Array(params.R3_BYTES); for (let i = 0; i < Math.floor(f.length / 4); i++) { s[i] = 0; for (let j = 0; j < 4; j++) { if (i * 4 + j < f.length) { s[i] |= ((f[i * 4 + j] + 1) & 0x3) << (j * 2); } } } if (params.P % 4 !== 0) { s[Math.floor(params.P / 4)] = (f[params.P - 1] + 1) & 0xFF; } return s; } function r3Decode(s, params) { const f = new Int8Array(params.P); let x; let i = 0; const swap = (x) => (x & 3) - 1; while (i < Math.floor(params.P / 4)) { x = s[i]; f[i * 4] = swap(x); x >>= 2; f[i * 4 + 1] = swap(x); x >>= 2; f[i * 4 + 2] = swap(x); x >>= 2; f[i * 4 + 3] = swap(x); i++; } if (params.P % 4 !== 0) { x = s[i]; f[i * 4] = swap(x); } return f; } function rqEncodeToBytes(input, { P, RQ_BYTES }) { const bytes = new Uint8Array(RQ_BYTES); for (let i = 0; i < P; i++) { const value = input[i]; const byteIndex = i * 2; bytes[byteIndex] = (value >> 8) & 0xFF; bytes[byteIndex + 1] = value & 0xFF; } return bytes; } function bytesRqDecode(input, { P }) { const coeffs = new Int16Array(P); for (let i = 0; i < P; i++) { const byteIndex = i * 2; coeffs[i] = ((input[byteIndex] << 8) | input[byteIndex + 1]) << 0; } return coeffs; } function freeze(a) { const a_32 = a; const b = a_32 - (3 * Math.floor((10923 * a_32) / 32768)); const c = b - (3 * Math.floor((89478485 * b + 134217728) / 268435456)); return c; } function round(a) { for (let i = 0; i < a.length; i++) { a[i] -= freeze(a[i]); } } function freezeFq(x, q12, q) { const r = i32ModU14(x + q12, q); const result = r - q12; return result === 0 ? 0 : result; } function recip(a1, q12, q) { let i = 1; let ai = a1; while (i < q - 2) { ai = freezeFq((a1 * ai), q12, q); i += 1; } return ai; } class Rq { coeffs; constructor(params) { this.coeffs = new Int16Array(params.P).fill(0); } static from(coeffs, params) { const rq = new Rq(params); if (coeffs instanceof Int16Array) { rq.coeffs = coeffs; } else { rq.coeffs = new Int16Array(coeffs); } return rq; } eqOne() { for (let i = 1; i < this.coeffs.length; i++) { if (this.coeffs[i] !== 0) { return false; } } return this.coeffs[0] === 1; } eqZero() { for (const c of this.coeffs) { if (c !== 0) { return false; } } return true; } multR3(gq, params) { const out = new Int16Array(params.P); const f = this.coeffs; const g = gq.coeffs; const fg = new Int16Array(params.P + params.P - 1); const quotient = (r, f, g) => { const value = r + f * g; return freezeFq(value, params.Q12, params.Q); }; for (let i = 0; i < params.P; i++) { let result = 0; for (let j = 0; j <= i; j++) { result = quotient(result, f[j], g[i - j]); } fg[i] = result; } for (let i = params.P; i < params.P + params.P - 1; i++) { let result = 0; for (let j = i - params.P + 1; j < params.P; j++) { result = quotient(result, f[j], g[i - j]); } fg[i] = result; } for (let i = params.P + params.P - 2; i >= params.P; i--) { fg[i - params.P] = freezeFq(fg[i - params.P] + fg[i], params.Q12, params.Q); fg[i - params.P + 1] = freezeFq(fg[i - params.P + 1] + fg[i], params.Q12, params.Q); } out.set(fg.subarray(0, params.P)); return Rq.from(out, params); } recip(ratio, params) { const input = this.coeffs; const out = new Int16Array(params.P); const f = new Int16Array(params.P + 1); const g = new Int16Array(params.P + 1); const v = new Int16Array(params.P + 1); const r = new Int16Array(params.P + 1); let delta = 1; let swap; let t; let f0; let g0; const quotient = (out, f0, g0, fv) => { for (let i = 0; i < params.P + 1; i++) { const x = f0 * out[i] - g0 * fv[i]; out[i] = freezeFq(x, params.Q12, params.Q); } }; r[0] = recip(ratio, params.Q12, params.Q); f[0] = 1; f[params.P - 1] = -1; f[params.P] = -1; for (let i = 0; i < params.P; i++) { g[params.P - 1 - i] = input[i]; } g[params.P] = 0; delta = 1; for (let _ = 0; _ < 2 * params.P - 1; _++) { for (let i = params.P; i >= 1; i--) { v[i] = v[i - 1]; } v[0] = 0; swap = i16NegativeMask(-delta) & i16NonzeroMask(g[0]); delta ^= swap & (delta ^ -delta); delta += 1; for (let i = 0; i < params.P + 1; i++) { t = swap & (f[i] ^ g[i]); f[i] ^= t; g[i] ^= t; t = swap & (v[i] ^ r[i]); v[i] ^= t; r[i] ^= t; } f0 = f[0]; g0 = g[0]; quotient(g, f0, g0, f); quotient(r, f0, g0, v); for (let i = 0; i < params.P; i++) { g[i] = g[i + 1]; } g[params.P] = 0; } const scale = recip(f[0], params.Q12, params.Q); for (let i = 0; i < params.P; i++) { const x = scale * v[params.P - 1 - i]; out[i] = freezeFq(x, params.Q12, params.Q); } if (i16NonzeroMask(delta) === 0) { return Rq.from(out, params); } else { throw ErrorType.NoSolutionRecip3; } } multInt(num, params) { const out = new Int16Array(params.P); for (let i = 0; i < params.P; i++) { const x = num * this.coeffs[i]; out[i] = freezeFq(x, params.Q12, params.Q); } return Rq.from(out, params); } r3FromRq(params) { const out = new Int8Array(params.P); for (let i = 0; i < params.P; i++) { out[i] = freeze(this.coeffs[i]); } return R3.from(out, params); } toBytes(params) { return rqEncodeToBytes(this.coeffs, params); } } class R3 { coeffs; constructor(params) { this.coeffs = new Int8Array(params.P).fill(0); } static from(coeffs, params) { const r3 = new R3(params); if (coeffs instanceof Int8Array) { r3.coeffs = coeffs; } else { r3.coeffs = new Int8Array(coeffs); } return r3; } eqZero() { for (const c of this.coeffs) { if (c !== 0) { return false; } } return true; } mult(g3, params) { const f = this.coeffs; const g = g3.coeffs; const out = new Int8Array(params.P); const fg = new Int8Array(params.P + params.P - 1); const quotient = (r, f, g) => { const x = r + f * g; return freeze(x); }; for (let i = 0; i < params.P; i++) { let r = 0; for (let j = 0; j <= i; j++) { r = quotient(r, f[j], g[i - j]); } fg[i] = r; } for (let i = params.P; i < params.P + params.P - 1; i++) { let r = 0; for (let j = i - params.P + 1; j < params.P; j++) { r = quotient(r, f[j], g[i - j]); } fg[i] = r; } for (let i = params.P + params.P - 2; i >= params.P; i--) { const x0 = fg[i - params.P] + fg[i]; const x1 = fg[i - params.P + 1] + fg[i]; fg[i - params.P] = freeze(x0); fg[i - params.P + 1] = freeze(x1); } for (let i = 0; i < params.P; i++) { out[i] = fg[i]; } return R3.from(out, params); } eqOne() { for (let i = 1; i < this.coeffs.length; i++) { if (this.coeffs[i] !== 0) { return false; } } return this.coeffs[0] === 1; } recip(params) { const input = this.coeffs; const out = new Int8Array(params.P); const f = new Int8Array(params.P + 1); const g = new Int8Array(params.P + 1); const v = new Int8Array(params.P + 1); const r = new Int8Array(params.P + 1); let delta = 1; let sign; let swap; let t; const quotient = (g, sign, f) => { const x = g + sign * f; return freeze(x); }; r[0] = 1; f[0] = 1; f[params.P - 1] = -1; f[params.P] = -1; for (let i = 0; i < params.P; i++) { g[params.P - 1 - i] = input[i]; } g[params.P] = 0; for (let _ = 0; _ < 2 * params.P - 1; _++) { for (let i = params.P; i >= 1; i--) { v[i] = v[i - 1]; } v[0] = 0; sign = -g[0] * f[0]; swap = (i16NegativeMask(-delta) & i16NonzeroMask(g[0])); delta ^= swap & (delta ^ -delta); delta += 1; for (let i = 0; i < params.P + 1; i++) { t = swap & (f[i] ^ g[i]); f[i] ^= t; g[i] ^= t; t = swap & (v[i] ^ r[i]); v[i] ^= t; r[i] ^= t; } for (let i = 0; i < params.P + 1; i++) { g[i] = quotient(g[i], sign, f[i]); } for (let i = 0; i < params.P + 1; i++) { r[i] = quotient(r[i], sign, v[i]); } for (let i = 0; i < params.P; i++) { g[i] = g[i + 1]; } g[params.P] = 0; } sign = f[0]; for (let i = 0; i < params.P; i++) { out[i] = sign * v[params.P - 1 - i]; } if (i16NonzeroMask(delta) === 0) { return R3.from(out, params); } else { throw ErrorType.R3NoSolutionRecip; } } rqFromR3(params) { const out = new Int16Array(params.P); for (let i = 0; i < params.P; i++) { out[i] = freezeFq(this.coeffs[i], params.Q12, params.Q); } return Rq.from(out, params); } toBytes(params) { return r3Encode(this.coeffs, params); } } class PrivKey { f; ginv; constructor(f, ginv) { this.f = f; this.ginv = ginv; } static compute(f, g, params) { try { const ginv = g.recip(params); const f_r3 = f.r3FromRq(params); return new PrivKey(f_r3, ginv); } catch (e) { if (e === ErrorType.R3NoSolutionRecip) { throw ErrorType.R3NoSolutionRecip; } else { throw ErrorType.KemError; } } } toBytes(params) { const sk = new Uint8Array(params.SECRETKEYS_BYTES); const ginvBytes = this.ginv.toBytes(params); const fBytes = this.f.toBytes(params); if (ginvBytes.length !== params.R3_BYTES || fBytes.length !== params.R3_BYTES) { throw ErrorType.PolyError; } sk.set(ginvBytes, 0); sk.set(fBytes, params.R3_BYTES); return sk; } static import(skBytes, params) { if (skBytes.length !== params.SECRETKEYS_BYTES) { throw ErrorType.ByteslengthError; } let ginvBytes; let fBytes; try { ginvBytes = skBytes.subarray(0, params.R3_BYTES); fBytes = skBytes.subarray(params.R3_BYTES); } catch (e) { throw ErrorType.KemError; } if (ginvBytes.length !== params.R3_BYTES) throw ErrorType.InvalidR3GInvrBytes; if (fBytes.length !== params.R3_BYTES) throw ErrorType.InvalidR3FBytes; const ginvCoeffs = r3Decode(ginvBytes, params); const fCoeffs = r3Decode(fBytes, params); const ginv = R3.from(ginvCoeffs, params); const f = R3.from(fCoeffs, params); return new PrivKey(f, ginv); } } class PubKey extends Rq { constructor(params, coeffs) { super(params); if (coeffs) { if (coeffs.length !== params.P) { throw ErrorType.PolyError; } this.coeffs = coeffs; } } static compute(f, g, params) { try { const finv = f.recip(3, params); const h = finv.multR3(g, params); return new PubKey(params, h.coeffs); } catch (e) { if (e === ErrorType.NoSolutionRecip3) { throw ErrorType.NoSolutionRecip3; } else { throw ErrorType.KemError; } } } static fromSk(privKey, params) { try { const f_r3 = privKey.f; const ginv = privKey.ginv; const f = Rq.from(Int16Array.from(f_r3.coeffs), params); const g = ginv.recip(params); const finv = f.recip(3, params); const h = finv.multR3(g, params); return new PubKey(params, h.coeffs); } catch (e) { if (e === ErrorType.R3NoSolutionRecip || e === ErrorType.NoSolutionRecip3) { throw e; } else { throw ErrorType.KemError; } } } static import(bytes, params) { if (bytes.length !== params.PUBLICKEYS_BYTES) { throw ErrorType.ByteslengthError; } try { const decodedCoeffs = bytesRqDecode(bytes, params); return new PubKey(params, decodedCoeffs); } catch (e) { throw ErrorType.KemError; } } } function rqDecrypt(c, privKey, params) { const f = privKey.f; const ginv = privKey.ginv; const r_coeffs = new Int8Array(params.P); const cf = c.multR3(f, params); const cf3 = cf.multInt(3, params); const e = cf3.r3FromRq(params); const ev = e.mult(ginv, params); const mask = weightWMask(ev.coeffs, params.W); for (let i = 0; i < params.P; i++) { const coeff = ev.coeffs[i]; if (i < params.W) { r_coeffs[i] = (((coeff ^ 1) & ~mask) ^ 1); } else { r_coeffs[i] = (coeff & ~mask); } } return R3.from(r_coeffs, params); } function r3Encrypt(r, pubKey, params) { const hr = pubKey.multR3(r, params); const coeffsAsNumbers = Array.from(hr.coeffs); round(coeffsAsNumbers); hr.coeffs = Int16Array.from(coeffsAsNumbers); return hr; } function staticBytesEncrypt(bytes, pubKey, params) { if (bytes.length !== params.R3_BYTES) { throw ErrorType.ByteslengthError; } const r_coeffs = r3Decode(bytes, params); const r = R3.from(r_coeffs, params); const encryptedRq = r3Encrypt(r, pubKey, params); return encryptedRq.toBytes(params); } function staticBytesDecrypt(cipherBytes, privKey, params) { if (cipherBytes.length !== params.RQ_BYTES) { throw ErrorType.ByteslengthError; } const c_coeffs = bytesRqDecode(cipherBytes, params); const c = Rq.from(c_coeffs, params); const decryptedR3 = rqDecrypt(c, privKey, params); return decryptedR3.toBytes(params); } function generateKeyPair(rng, params, maxAttempts = 100) { let sk = null; let pk = null; let attempts = 0; while (attempts < maxAttempts) { attempts++; try { const f_coeffs = shortRandom(rng, params); const f = Rq.from(f_coeffs, params); const g_coeffs = randomSmall(rng, params); const g = R3.from(g_coeffs, params); const potential_sk = PrivKey.compute(f, g, params); const potential_pk = PubKey.compute(f, g, params); sk = potential_sk; pk = potential_pk; break; } catch (e) { if (e !== ErrorType.R3NoSolutionRecip && e !== ErrorType.NoSolutionRecip3) { throw e; } } } if (!sk || !pk) { throw ErrorType.FailGenerateValidKeyPair; } return { sk, pk }; } export { BITS_SIZE, ErrorType, PrivKey, PubKey, R3, Rq, SYS_SIZE, bytesRqDecode, bytesToUsizeArray, convertToDecimal, convertToTernary, freeze, freezeFq, generateKeyPair, i16NegativeMask, i16NonzeroMask, i32DivmodU14, i32ModU14, packBytes, params, params1013, params1277, params653, params761, params857, params953, r3Decode, r3DecodeChunks, r3Encode, r3EncodeChunks, r3Encrypt, r3MergeWChunks, r3SplitWChunks, randomRange3, randomSign, randomSmall, recip, round, rqDecrypt, rqEncodeToBytes, shortRandom, shuffleArray, staticBytesDecrypt, staticBytesEncrypt, u32DivmodU14, u32ModU14, unpackBytes, unshuffleArray, urandom32, usizeArrayToBytes, weightWMask }; //# sourceMappingURL=index.esm.js.map