carlos / contra Public

Clone
git clone https://amadan.net/carlos/contra

Plain git — no account needed to clone.

Download

Download this file

1// Byte plumbing shared by the crypto modules. No key material logic here —
2// just the conversions every other module would otherwise repeat.
3
4export const enc = new TextEncoder();
5export const dec = new TextDecoder();
6
7export function toHex(b) {
8 return Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
9}
10
11export function fromHex(s) {
12 if (s.length % 2) throw new Error("contra: odd-length hex");
13 const out = new Uint8Array(s.length / 2);
14 for (let i = 0; i < out.length; i++) {
15 out[i] = parseInt(s.substr(i * 2, 2), 16);
16 }
17 return out;
18}
19
20export function concat(...parts) {
21 let n = 0;
22 for (const p of parts) n += p.length;
23 const out = new Uint8Array(n);
24 let o = 0;
25 for (const p of parts) {
26 out.set(p, o);
27 o += p.length;
28 }
29 return out;
30}
31
32export function u32be(n) {
33 return new Uint8Array([(n >>> 24) & 255, (n >>> 16) & 255, (n >>> 8) & 255, n & 255]);
34}
35
36export function u32le(n) {
37 return new Uint8Array([n & 255, (n >>> 8) & 255, (n >>> 16) & 255, (n >>> 24) & 255]);
38}
39
40// u64be takes a BigInt, because a version counter that silently loses
41// precision above 2^53 would break the AAD binding in a way nothing would
42// notice until a ciphertext failed to open.
43export function u64be(n) {
44 const v = BigInt(n);
45 const out = new Uint8Array(8);
46 for (let i = 7; i >= 0; i--) out[i] = Number((v >> BigInt(8 * (7 - i))) & 0xffn);
47 return out;
48}
49
50// equal is constant-time over the compared bytes. Length is not secret here
51// — every field this compares is fixed-width — but the contents are.
52export function equal(a, b) {
53 if (a.length !== b.length) return false;
54 let diff = 0;
55 for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
56 return diff === 0;
57}
58
59export function randomBytes(n) {
60 return crypto.getRandomValues(new Uint8Array(n));
61}
62