Skip to content

Commit

Permalink
ff1: use utils for number to bytes conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
paulmillr committed Sep 17, 2023
1 parent 1925b9c commit 30fc73f
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 22 deletions.
15 changes: 15 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,21 @@ export function hexToBytes(hex: string): Uint8Array {
return array;
}

export function hexToNumber(hex: string): bigint {
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
// Big Endian
return BigInt(hex === '' ? '0' : `0x${hex}`);
}

// BE: Big Endian, LE: Little Endian
export function bytesToNumberBE(bytes: Uint8Array): bigint {
return hexToNumber(bytesToHex(bytes));
}

export function numberToBytesBE(n: number | bigint, len: number): Uint8Array {
return hexToBytes(n.toString(16).padStart(len * 2, '0'));
}

// There is no setImmediate in browser and setTimeout is slow.
// call of async fn will return Promise, which will be fullfiled only on
// next scheduler queue processing step and this is exactly what we need.
Expand Down
27 changes: 5 additions & 22 deletions src/webcrypto/ff1.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,20 @@
import type { AsyncCipher } from '../utils.js';
import { bytesToNumberBE, numberToBytesBE } from '../utils.js';
import { cryptoSubtleUtils } from './utils.js';

// Format-preserving encryption algorithm (FPE-FF1) specified in NIST Special Publication 800-38G.
// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38G.pdf

const BLOCK_LEN = 16;

// Utils
function toBytesBE(num: bigint, length?: number): Uint8Array {
let hex = num.toString(16);
hex = hex.length & 1 ? `0${hex}` : hex;
if (length) hex = hex.padStart(length * 2, '00');
const len = hex.length / 2;
const u8 = new Uint8Array(len);
for (let j = 0, i = 0; i < hex.length && i < len * 2; i += 2, j++)
u8[j] = parseInt(hex[i] + hex[i + 1], 16);
return u8;
}

function fromBytesBE(bytes: Uint8Array): bigint {
let value = 0n;
for (let i = bytes.length - 1, j = 0; i >= 0; i--, j++)
value += (BigInt(bytes[i]) & 255n) << (8n * BigInt(j));
return value;
}

// Calculates a modulo b
function mod(a: number, b: number): number;
function mod(a: bigint, b: bigint): bigint;
function mod(a: any, b: any): number | bigint {
const result = a % b;
return result >= 0 ? result : b + result;
}

function NUMradix(radix: number, data: number[]): bigint {
let res = 0n;
for (let i of data) res = res * BigInt(radix) + BigInt(i);
Expand Down Expand Up @@ -66,7 +49,7 @@ async function getRound(radix: number, key: Uint8Array, tweak: Uint8Array, x: nu
const round = async (A: number[], B: number[], i: number, decrypt = false) => {
// Q = ... || [i]1 || [NUMradix(B)]b.
PQ[PQ.length - b - 1] = i;
if (b) PQ.set(toBytesBE(NUMradix(radix, B), b), PQ.length - b);
if (b) PQ.set(numberToBytesBE(NUMradix(radix, B), b), PQ.length - b);
// PRF
let r = new Uint8Array(16);
for (let j = 0; j < PQ.length / BLOCK_LEN; j++) {
Expand All @@ -77,11 +60,11 @@ async function getRound(radix: number, key: Uint8Array, tweak: Uint8Array, x: nu
// R || CIPHK(R ⊕[1]16) || CIPHK(R ⊕[2]16) ...CIPHK(R ⊕[⎡d / 16⎤ – 1]16).
let s = Array.from(r);
for (let j = 1; s.length < d; j++) {
const block = toBytesBE(BigInt(j), 16);
const block = numberToBytesBE(BigInt(j), 16);
for (let k = 0; k < BLOCK_LEN; k++) block[k] ^= r[k];
s.push(...Array.from(await cryptoSubtleUtils.aesEncryptBlock(block, key)));
}
let y = fromBytesBE(Uint8Array.from(s.slice(0, d)));
let y = bytesToNumberBE(Uint8Array.from(s.slice(0, d)));
s.fill(0);
if (decrypt) y = -y;
const m = i % 2 === 0 ? u : v;
Expand Down

0 comments on commit 30fc73f

Please sign in to comment.