mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-02-24 04:00:59 +01:00
463 lines
14 KiB
TypeScript
463 lines
14 KiB
TypeScript
/*
|
|
This file is part of the Notesnook project (https://notesnook.com/)
|
|
|
|
Copyright (C) 2023 Streetwriters (Private) Limited
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import "./@types/sodium-native/index.d.ts";
|
|
import {
|
|
crypto_pwhash as sodium_native_crypto_pwhash,
|
|
crypto_generichash as sodium_native_crypto_generichash,
|
|
sodium_memzero,
|
|
crypto_aead_xchacha20poly1305_ietf_ABYTES,
|
|
randombytes_buf as sodium_native_randombytes_buf,
|
|
crypto_aead_xchacha20poly1305_ietf_encrypt as sodium_native_crypto_aead_xchacha20poly1305_ietf_encrypt,
|
|
crypto_secretstream_xchacha20poly1305_init_push as sodium_native_crypto_secretstream_xchacha20poly1305_init_push,
|
|
crypto_secretstream_xchacha20poly1305_push as sodium_native_crypto_secretstream_xchacha20poly1305_push,
|
|
crypto_aead_xchacha20poly1305_ietf_decrypt as sodium_native_crypto_aead_xchacha20poly1305_ietf_decrypt,
|
|
crypto_secretstream_xchacha20poly1305_init_pull as sodium_native_crypto_secretstream_xchacha20poly1305_init_pull,
|
|
crypto_secretstream_xchacha20poly1305_pull as sodium_native_crypto_secretstream_xchacha20poly1305_pull,
|
|
crypto_secretstream_xchacha20poly1305_HEADERBYTES,
|
|
crypto_secretstream_xchacha20poly1305_ABYTES,
|
|
crypto_secretstream_xchacha20poly1305_STATEBYTES,
|
|
crypto_secretstream_xchacha20poly1305_TAGBYTES
|
|
} from "sodium-native";
|
|
import { Buffer } from "node:buffer";
|
|
|
|
export type Uint8ArrayOutputFormat = "uint8array";
|
|
export type StringOutputFormat = "text" | "hex" | "base64";
|
|
export enum base64_variants {
|
|
ORIGINAL = 1,
|
|
ORIGINAL_NO_PADDING = 3,
|
|
URLSAFE = 5,
|
|
URLSAFE_NO_PADDING = 7
|
|
}
|
|
export type StateAddress = { name: string };
|
|
export interface MessageTag {
|
|
message: Uint8Array;
|
|
tag: number;
|
|
}
|
|
export interface StringMessageTag {
|
|
message: string;
|
|
tag: number;
|
|
}
|
|
|
|
export async function initialize() {}
|
|
|
|
export function crypto_pwhash(
|
|
keyLength: number,
|
|
password: string | Uint8Array,
|
|
salt: Uint8Array,
|
|
opsLimit: number,
|
|
memLimit: number,
|
|
algorithm: number,
|
|
outputFormat?: Uint8ArrayOutputFormat | null
|
|
): Uint8Array;
|
|
export function crypto_pwhash(
|
|
keyLength: number,
|
|
password: string | Uint8Array,
|
|
salt: Uint8Array,
|
|
opsLimit: number,
|
|
memLimit: number,
|
|
algorithm: number,
|
|
outputFormat: StringOutputFormat
|
|
): string;
|
|
export function crypto_pwhash(
|
|
keyLength: number,
|
|
password: string | Uint8Array,
|
|
salt: Uint8Array,
|
|
opsLimit: number,
|
|
memLimit: number,
|
|
algorithm: number,
|
|
outputFormat?: StringOutputFormat | Uint8ArrayOutputFormat | null
|
|
): string | Uint8Array {
|
|
return wrap(
|
|
keyLength,
|
|
(output) =>
|
|
sodium_native_crypto_pwhash(
|
|
output,
|
|
toBuffer(password),
|
|
toBuffer(salt),
|
|
opsLimit,
|
|
memLimit,
|
|
algorithm
|
|
),
|
|
outputFormat
|
|
);
|
|
}
|
|
|
|
export function crypto_generichash(
|
|
hash_length: number,
|
|
message: string | Uint8Array,
|
|
key?: string | Uint8Array | null,
|
|
outputFormat?: Uint8ArrayOutputFormat | null
|
|
): Uint8Array;
|
|
export function crypto_generichash(
|
|
hash_length: number,
|
|
message: string | Uint8Array,
|
|
key: string | Uint8Array | null,
|
|
outputFormat: StringOutputFormat
|
|
): string;
|
|
export function crypto_generichash(
|
|
hash_length: number,
|
|
message: string | Uint8Array,
|
|
key?: string | Uint8Array | null,
|
|
outputFormat?: StringOutputFormat | Uint8ArrayOutputFormat | null
|
|
): string | Uint8Array {
|
|
return wrap(
|
|
hash_length,
|
|
(output) => {
|
|
if (key)
|
|
sodium_native_crypto_generichash(
|
|
output,
|
|
toBuffer(message),
|
|
toBuffer(key)
|
|
);
|
|
else sodium_native_crypto_generichash(output, toBuffer(message));
|
|
},
|
|
outputFormat
|
|
);
|
|
}
|
|
|
|
export function crypto_aead_xchacha20poly1305_ietf_encrypt(
|
|
message: string | Uint8Array,
|
|
additional_data: string | Uint8Array | null,
|
|
secret_nonce: string | Uint8Array | null,
|
|
public_nonce: Uint8Array,
|
|
key: Uint8Array,
|
|
outputFormat?: Uint8ArrayOutputFormat | null
|
|
): Uint8Array;
|
|
export function crypto_aead_xchacha20poly1305_ietf_encrypt(
|
|
message: string | Uint8Array,
|
|
additional_data: string | Uint8Array | null,
|
|
secret_nonce: string | Uint8Array | null,
|
|
public_nonce: Uint8Array,
|
|
key: Uint8Array,
|
|
outputFormat: StringOutputFormat
|
|
): string;
|
|
export function crypto_aead_xchacha20poly1305_ietf_encrypt(
|
|
message: string | Uint8Array,
|
|
additional_data: string | Uint8Array | null,
|
|
secret_nonce: string | Uint8Array | null,
|
|
public_nonce: Uint8Array,
|
|
key: Uint8Array,
|
|
outputFormat?: StringOutputFormat | Uint8ArrayOutputFormat | null
|
|
): string | Uint8Array {
|
|
const m = toBuffer(message);
|
|
return wrap(
|
|
m.byteLength + crypto_aead_xchacha20poly1305_ietf_ABYTES,
|
|
(output) =>
|
|
sodium_native_crypto_aead_xchacha20poly1305_ietf_encrypt(
|
|
output,
|
|
m,
|
|
toBuffer(additional_data) || null,
|
|
null,
|
|
toBuffer(public_nonce),
|
|
toBuffer(key)
|
|
),
|
|
outputFormat
|
|
);
|
|
}
|
|
|
|
export function crypto_secretstream_xchacha20poly1305_init_push(
|
|
key: Uint8Array,
|
|
outputFormat?: Uint8ArrayOutputFormat | null
|
|
): { state: StateAddress; header: Uint8Array };
|
|
export function crypto_secretstream_xchacha20poly1305_init_push(
|
|
key: Uint8Array,
|
|
outputFormat: StringOutputFormat
|
|
): { state: StateAddress; header: string };
|
|
export function crypto_secretstream_xchacha20poly1305_init_push(
|
|
key: Uint8Array,
|
|
outputFormat?: StringOutputFormat | Uint8ArrayOutputFormat | null
|
|
): { state: StateAddress; header: string | Uint8Array } {
|
|
const state = Buffer.alloc(crypto_secretstream_xchacha20poly1305_STATEBYTES);
|
|
|
|
const header = wrap(
|
|
crypto_secretstream_xchacha20poly1305_HEADERBYTES,
|
|
(header) =>
|
|
sodium_native_crypto_secretstream_xchacha20poly1305_init_push(
|
|
state,
|
|
header,
|
|
toBuffer(key)
|
|
),
|
|
outputFormat
|
|
);
|
|
return { state: state as unknown as StateAddress, header };
|
|
}
|
|
|
|
export function crypto_secretstream_xchacha20poly1305_push(
|
|
state_address: StateAddress,
|
|
message_chunk: string | Uint8Array,
|
|
ad: string | Uint8Array | null,
|
|
tag: number,
|
|
outputFormat?: Uint8ArrayOutputFormat | null
|
|
): Uint8Array;
|
|
export function crypto_secretstream_xchacha20poly1305_push(
|
|
state_address: StateAddress,
|
|
message_chunk: string | Uint8Array,
|
|
ad: string | Uint8Array | null,
|
|
tag: number,
|
|
outputFormat: StringOutputFormat
|
|
): string;
|
|
export function crypto_secretstream_xchacha20poly1305_push(
|
|
state_address: StateAddress,
|
|
message_chunk: string | Uint8Array,
|
|
ad: string | Uint8Array | null,
|
|
tag: number,
|
|
outputFormat?: StringOutputFormat | Uint8ArrayOutputFormat | null
|
|
): string | Uint8Array {
|
|
const message = toBuffer(message_chunk);
|
|
return wrap(
|
|
message.byteLength + crypto_secretstream_xchacha20poly1305_ABYTES,
|
|
(cipher) =>
|
|
sodium_native_crypto_secretstream_xchacha20poly1305_push(
|
|
state_address as unknown as Buffer,
|
|
cipher,
|
|
message,
|
|
toBuffer(ad) || null,
|
|
tag
|
|
),
|
|
outputFormat
|
|
);
|
|
}
|
|
|
|
export function crypto_aead_xchacha20poly1305_ietf_decrypt(
|
|
secret_nonce: string | Uint8Array | null,
|
|
ciphertext: string | Uint8Array,
|
|
additional_data: string | Uint8Array | null,
|
|
public_nonce: Uint8Array,
|
|
key: Uint8Array,
|
|
outputFormat?: Uint8ArrayOutputFormat | null
|
|
): Uint8Array;
|
|
export function crypto_aead_xchacha20poly1305_ietf_decrypt(
|
|
secret_nonce: string | Uint8Array | null,
|
|
ciphertext: string | Uint8Array,
|
|
additional_data: string | Uint8Array | null,
|
|
public_nonce: Uint8Array,
|
|
key: Uint8Array,
|
|
outputFormat: StringOutputFormat
|
|
): string;
|
|
export function crypto_aead_xchacha20poly1305_ietf_decrypt(
|
|
_secret_nonce: string | Uint8Array | null,
|
|
ciphertext: string | Uint8Array,
|
|
additional_data: string | Uint8Array | null,
|
|
public_nonce: Uint8Array,
|
|
key: Uint8Array,
|
|
outputFormat?: StringOutputFormat | Uint8ArrayOutputFormat | null
|
|
): string | Uint8Array {
|
|
const cipher = toBuffer(ciphertext);
|
|
return wrap(
|
|
cipher.byteLength - crypto_aead_xchacha20poly1305_ietf_ABYTES,
|
|
(message) =>
|
|
sodium_native_crypto_aead_xchacha20poly1305_ietf_decrypt(
|
|
message,
|
|
null,
|
|
cipher,
|
|
toBuffer(additional_data) || null,
|
|
toBuffer(public_nonce),
|
|
toBuffer(key)
|
|
),
|
|
outputFormat
|
|
);
|
|
}
|
|
|
|
export function crypto_secretstream_xchacha20poly1305_init_pull(
|
|
header: Uint8Array,
|
|
key: Uint8Array
|
|
): StateAddress {
|
|
const state = Buffer.alloc(crypto_secretstream_xchacha20poly1305_STATEBYTES);
|
|
sodium_native_crypto_secretstream_xchacha20poly1305_init_pull(
|
|
state,
|
|
toBuffer(header),
|
|
toBuffer(key)
|
|
);
|
|
return state as unknown as StateAddress;
|
|
}
|
|
|
|
export function crypto_secretstream_xchacha20poly1305_pull(
|
|
state_address: StateAddress,
|
|
cipher: string | Uint8Array,
|
|
ad?: string | Uint8Array | null,
|
|
outputFormat?: Uint8ArrayOutputFormat | null
|
|
): MessageTag;
|
|
export function crypto_secretstream_xchacha20poly1305_pull(
|
|
state_address: StateAddress,
|
|
cipher: string | Uint8Array,
|
|
ad: string | Uint8Array | null,
|
|
outputFormat: StringOutputFormat
|
|
): StringMessageTag;
|
|
export function crypto_secretstream_xchacha20poly1305_pull(
|
|
state_address: StateAddress,
|
|
ciphertext: string | Uint8Array,
|
|
ad?: string | Uint8Array | null,
|
|
outputFormat?: StringOutputFormat | Uint8ArrayOutputFormat | null
|
|
): MessageTag | StringMessageTag {
|
|
const tag = Buffer.alloc(crypto_secretstream_xchacha20poly1305_TAGBYTES);
|
|
const cipher = toBuffer(ciphertext);
|
|
const message = wrap(
|
|
cipher.byteLength - crypto_secretstream_xchacha20poly1305_ABYTES,
|
|
(message) =>
|
|
sodium_native_crypto_secretstream_xchacha20poly1305_pull(
|
|
state_address as unknown as Buffer,
|
|
message,
|
|
tag,
|
|
cipher,
|
|
toBuffer(ad) || null
|
|
),
|
|
outputFormat
|
|
);
|
|
return { message, tag: tag.readUInt8() } as MessageTag | StringMessageTag;
|
|
}
|
|
|
|
export function randombytes_buf(
|
|
length: number,
|
|
outputFormat?: Uint8ArrayOutputFormat | null
|
|
): Uint8Array;
|
|
export function randombytes_buf(
|
|
length: number,
|
|
outputFormat: StringOutputFormat
|
|
): string;
|
|
export function randombytes_buf(
|
|
length: number,
|
|
outputFormat?: StringOutputFormat | Uint8ArrayOutputFormat | null
|
|
): string | Uint8Array {
|
|
return wrap(
|
|
length,
|
|
(output) => sodium_native_randombytes_buf(output),
|
|
outputFormat
|
|
);
|
|
}
|
|
|
|
export function from_base64(
|
|
input: string,
|
|
variant?: base64_variants
|
|
): Uint8Array {
|
|
return new Uint8Array(
|
|
Buffer.from(
|
|
variant === base64_variants.URLSAFE_NO_PADDING ||
|
|
variant === base64_variants.ORIGINAL_NO_PADDING
|
|
? appendPadding(input)
|
|
: input,
|
|
variant === base64_variants.URLSAFE ||
|
|
variant === base64_variants.URLSAFE_NO_PADDING
|
|
? "base64url"
|
|
: "base64"
|
|
)
|
|
);
|
|
}
|
|
|
|
export function to_base64(
|
|
input: string | Uint8Array,
|
|
variant: base64_variants = base64_variants.URLSAFE_NO_PADDING
|
|
): string {
|
|
const base64 = Buffer.from(toBuffer(input)).toString(
|
|
variant === base64_variants.URLSAFE ||
|
|
variant === base64_variants.URLSAFE_NO_PADDING
|
|
? "base64url"
|
|
: "base64"
|
|
);
|
|
return variant === base64_variants.URLSAFE_NO_PADDING ||
|
|
variant === base64_variants.ORIGINAL_NO_PADDING
|
|
? trimPadding(base64)
|
|
: variant === base64_variants.URLSAFE
|
|
? appendPadding(base64)
|
|
: base64;
|
|
}
|
|
|
|
export function from_hex(input: string): Uint8Array {
|
|
return new Uint8Array(Buffer.from(input, "hex"));
|
|
}
|
|
|
|
export function to_string(input: Uint8Array): string {
|
|
return Buffer.from(input).toString("utf-8");
|
|
}
|
|
|
|
export {
|
|
crypto_pwhash_ALG_ARGON2ID13,
|
|
crypto_pwhash_SALTBYTES,
|
|
crypto_pwhash_ALG_ARGON2I13,
|
|
crypto_pwhash_ALG_DEFAULT,
|
|
crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
|
crypto_pwhash_OPSLIMIT_MODERATE,
|
|
crypto_pwhash_OPSLIMIT_SENSITIVE,
|
|
crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
|
crypto_pwhash_MEMLIMIT_MODERATE,
|
|
crypto_pwhash_MEMLIMIT_SENSITIVE,
|
|
crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
|
|
crypto_aead_xchacha20poly1305_ietf_NPUBBYTES,
|
|
crypto_secretstream_xchacha20poly1305_TAG_FINAL,
|
|
crypto_secretstream_xchacha20poly1305_TAG_MESSAGE
|
|
} from "sodium-native";
|
|
|
|
type ToBufferInput = string | Uint8Array | null | undefined;
|
|
type ToBufferResult<TInput extends ToBufferInput> = TInput extends
|
|
| undefined
|
|
| null
|
|
? Buffer | undefined
|
|
: Buffer;
|
|
function toBuffer<TInput extends ToBufferInput>(
|
|
input: TInput
|
|
): ToBufferResult<TInput> {
|
|
if (input instanceof Uint8Array) {
|
|
return Buffer.from(input.buffer, 0, input.byteLength);
|
|
} else if (typeof input === "undefined" || input === null) {
|
|
return undefined as ToBufferResult<TInput>;
|
|
} else {
|
|
return Buffer.from(input, "utf8");
|
|
}
|
|
}
|
|
|
|
function wrap(
|
|
length: number,
|
|
action: (buffer: Buffer) => void,
|
|
outputFormat?: StringOutputFormat | Uint8ArrayOutputFormat | null
|
|
): string | Uint8Array {
|
|
const output = Buffer.alloc(length);
|
|
|
|
action(output);
|
|
|
|
if (!outputFormat || outputFormat === "uint8array") {
|
|
return new Uint8Array(output);
|
|
}
|
|
|
|
const string = output.toString(
|
|
outputFormat === "text"
|
|
? "utf8"
|
|
: outputFormat === "base64"
|
|
? "base64url"
|
|
: "hex"
|
|
);
|
|
|
|
sodium_memzero(output);
|
|
return string;
|
|
}
|
|
|
|
function appendPadding(str: string): string {
|
|
str = str || "";
|
|
if (str.length % 4) {
|
|
str += "=".repeat(4 - (str.length % 4));
|
|
}
|
|
return str;
|
|
}
|
|
|
|
function trimPadding(str: string): string {
|
|
while (str.length && str[str.length - 1] === "=") {
|
|
str = str.slice(0, -1);
|
|
}
|
|
return str;
|
|
}
|