@hicaru/ntrup.js
Version:
Pure JavaScript implementation of NTRU Prime post-quantum cryptography algorithm
1,012 lines (995 loc) • 31.1 kB
JavaScript
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