etebase
Version:
Etebase TypeScript API for the web and node
115 lines • 3.9 kB
JavaScript
import * as msgpack from "@msgpack/msgpack";
import _sodium from "libsodium-wrappers";
const sodium = _sodium;
export const symmetricKeyLength = 32; // sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES;
export const symmetricTagLength = 16; // sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES;
export const symmetricNonceSize = 24; // sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES;
export function randomBytes(length) {
return sodium.randombytes_buf(length);
}
export function randomBytesDeterministic(length, seed) {
return sodium.randombytes_buf_deterministic(length, seed);
}
export function toBase64(input) {
return sodium.to_base64(input);
}
export function fromBase64(input) {
return sodium.from_base64(input);
}
export function toString(input) {
return sodium.to_string(input);
}
export function fromString(input) {
return sodium.from_string(input);
}
export function memcmp(b1, b2) {
return sodium.memcmp(b1, b2);
}
// Fisher–Yates shuffle - an unbiased shuffler
// The returend indices of where item is now.
// So if the first item moved to position 3: ret[0] = 3
export function shuffle(a) {
const len = a.length;
const shuffledIndices = new Array(len);
// Fill up with the indices
for (let i = 0; i < len; i++) {
shuffledIndices[i] = i;
}
for (let i = 0; i < len; i++) {
const j = i + sodium.randombytes_uniform(len - i);
const tmp = a[i];
a[i] = a[j];
a[j] = tmp;
// Also swap the index array
const tmp2 = shuffledIndices[i];
shuffledIndices[i] = shuffledIndices[j];
shuffledIndices[j] = tmp2;
}
const ret = new Array(len);
for (let i = 0; i < len; i++) {
ret[shuffledIndices[i]] = i;
}
return ret;
}
export function getPadding(length) {
// Use the padme padding scheme for efficiently
// https://www.petsymposium.org/2019/files/papers/issue4/popets-2019-0056.pdf
// We want a minimum pad size of 4k
if (length < (1 << 14)) {
const size = (1 << 10) - 1;
// We add 1 so we always have some padding
return (length | size) + 1;
}
const e = Math.floor(Math.log2(length));
const s = Math.floor(Math.log2(e)) + 1;
const lastBits = e - s;
const bitMask = Math.pow(2, lastBits) - 1;
return (length + bitMask) & ~bitMask;
}
// FIXME: we should properly pad the meta and probably change these functions
// This function is the same as bufferPad, but doesn't enforce a large minimum padding size
export function bufferPadSmall(buf) {
return sodium.pad(buf, buf.length + 1);
}
export function bufferPad(buf) {
return sodium.pad(buf, getPadding(buf.length));
}
export function bufferUnpad(buf) {
if (buf.length === 0) {
return buf;
}
// We pass the buffer's length as the block size because due to padme there's always some variable-sized padding.
return sodium.unpad(buf, buf.length);
}
export function bufferPadFixed(buf, blocksize) {
return sodium.pad(buf, blocksize);
}
export function bufferUnpadFixed(buf, blocksize) {
return sodium.unpad(buf, blocksize);
}
export function msgpackEncode(value) {
const options = { ignoreUndefined: true };
return msgpack.encode(value, options);
}
export function msgpackDecode(buffer) {
return msgpack.decode(buffer);
}
export function numToUint8Array(num) {
// We are using little-endian because on most platforms it'll mean zero-conversion
return new Uint8Array([
num & 255,
(num >> 8) & 255,
(num >> 16) & 255,
(num >> 24) & 255,
]);
}
export function numFromUint8Array(buf) {
if (buf.length !== 4) {
throw new Error("numFromUint8Array: buffer should be of length 4.");
}
return (buf[0] +
(buf[1] << 8) +
(buf[2] << 16) +
(((buf[3] << 23) >>> 0) * 2));
}
//# sourceMappingURL=Helpers.js.map