diff --git a/sodium-native.d.ts b/sodium-native.d.ts new file mode 100644 index 0000000..8cc209c --- /dev/null +++ b/sodium-native.d.ts @@ -0,0 +1,29 @@ +declare module 'sodium-native' { + // Type definitions for sodium-native 4.x + // Minimal definitions for APIs used by lib-crypto-utils + + /// + + // Constants used by lib-crypto-utils + export const crypto_auth_BYTES: number + export const crypto_box_PUBLICKEYBYTES: number + export const crypto_box_SECRETKEYBYTES: number + export const crypto_scalarmult_BYTES: number + export const crypto_sign_BYTES: number + export const crypto_sign_PUBLICKEYBYTES: number + export const crypto_sign_SECRETKEYBYTES: number + + // Functions used by lib-crypto-utils + export function randombytes_buf(buffer: Buffer): void + export function crypto_generichash(output: Buffer, input: Buffer, key?: Buffer): void + export function crypto_sign_keypair(publicKey: Buffer, secretKey: Buffer): void + export function crypto_sign_ed25519_sk_to_curve25519(curve_sk: Buffer, ed_sk: Buffer): void + export function crypto_sign_ed25519_pk_to_curve25519(curve_pk: Buffer, ed_pk: Buffer): void + export function crypto_auth(output: Buffer, input: Buffer, key: Buffer): void + export function crypto_auth_verify(output: Buffer, input: Buffer, key: Buffer): boolean + export function crypto_sign(signedMessage: Buffer, message: Buffer, secretKey: Buffer): void + export function crypto_sign_open(message: Buffer, signedMessage: Buffer, publicKey: Buffer): boolean + export function crypto_sign_detached(signature: Buffer, message: Buffer, secretKey: Buffer): void + export function crypto_sign_verify_detached(signature: Buffer, message: Buffer, publicKey: Buffer): boolean + export function crypto_scalarmult(sharedSecret: Buffer, secretKey: Buffer, remotePublicKey: Buffer): void +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index b67f000..9012e6d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -323,8 +323,9 @@ export function setCustomStringifier(method: (input: unknown) => string, name: s * Returns a signature obtained by signing the input hash (hex string or buffer) with the sk string * @param input * @param sk + * @param detached - If true (default), returns only the signature (64 bytes). If false, returns signature + message */ -export function sign(input: hexstring | Buffer, sk: secretKey | Buffer): string { +export function sign(input: hexstring | Buffer, sk: secretKey | Buffer, detached = true): string { let inputBuf: Buffer let skBuf: Buffer if (typeof input !== 'string') { @@ -353,9 +354,65 @@ export function sign(input: hexstring | Buffer, sk: secretKey | Buffer): string throw new TypeError('Secret key string must be in hex format') } } - const sig = Buffer.allocUnsafe(inputBuf.length + sodium.crypto_sign_BYTES) + if (detached) { + // Use detached signature + const sig = Buffer.allocUnsafe(sodium.crypto_sign_BYTES) + try { + sodium.crypto_sign_detached(sig, inputBuf, skBuf) + } catch (e) { + throw new Error('Failed to sign input with provided secret key.') + } + return sig.toString('hex') + } else { + // Use non-detached signature (legacy behavior) + const sig = Buffer.allocUnsafe(inputBuf.length + sodium.crypto_sign_BYTES) + try { + sodium.crypto_sign(sig, inputBuf, skBuf) + } catch (e) { + throw new Error('Failed to sign input with provided secret key.') + } + return sig.toString('hex') + } +} + +/** + * Returns a detached signature obtained by signing the input hash (hex string or buffer) with the sk string + * @param input - The message to sign (hex string or buffer) + * @param sk - The secret key + * @returns Only the 64-byte signature as hex string + */ +export function signDetached(input: hexstring | Buffer, sk: secretKey | Buffer): string { + let inputBuf: Buffer + let skBuf: Buffer + if (typeof input !== 'string') { + if (Buffer.isBuffer(input)) { + inputBuf = input + } else { + throw new TypeError('Input must be a hex string or buffer.') + } + } else { + try { + inputBuf = Buffer.from(input, 'hex') + } catch (e) { + throw new TypeError('Input string must be in hex format.') + } + } + if (typeof sk !== 'string') { + if (Buffer.isBuffer(sk)) { + skBuf = sk + } else { + throw new TypeError('Secret key must be a hex string or buffer.') + } + } else { + try { + skBuf = Buffer.from(sk, 'hex') + } catch (e) { + throw new TypeError('Secret key string must be in hex format') + } + } + const sig = Buffer.allocUnsafe(sodium.crypto_sign_BYTES) try { - sodium.crypto_sign(sig, inputBuf, skBuf) + sodium.crypto_sign_detached(sig, inputBuf, skBuf) } catch (e) { throw new Error('Failed to sign input with provided secret key.') } @@ -368,9 +425,33 @@ export function sign(input: hexstring | Buffer, sk: secretKey | Buffer): string * @param obj * @param sk * @param pk + * @param detached - If true (default), uses detached signature (64 bytes only). If false, uses non-detached + * @returns the new signed object with the `sign` field. The original object is mutated as well. + */ +export function signObj(obj: object, sk: secretKey | Buffer, pk: publicKey | Buffer, detached = true): SignedObject { + if (typeof obj !== 'object') { + throw new TypeError('Input must be an object.') + } + // If it's an array, we don't want to try to sign it + if (Array.isArray(obj)) { + throw new TypeError('Input cannot be an array.') + } + const objStr = stringify(obj) + const hashed = hash(objStr, 'buffer') + const sig = sign(hashed, sk, detached) + const signPk = Buffer.isBuffer(pk) ? bufferToHex(pk) : pk + ;(obj as SignedObject).sign = { owner: signPk, sig } + return obj as SignedObject +} + +/** + * Attaches a sign field to the input object using detached signatures + * @param obj - The object to sign + * @param sk - The secret key + * @param pk - The public key * @returns the new signed object with the `sign` field. The original object is mutated as well. */ -export function signObj(obj: object, sk: secretKey | Buffer, pk: publicKey | Buffer): SignedObject { +export function signObjDetached(obj: object, sk: secretKey | Buffer, pk: publicKey | Buffer): SignedObject { if (typeof obj !== 'object') { throw new TypeError('Input must be an object.') } @@ -380,7 +461,7 @@ export function signObj(obj: object, sk: secretKey | Buffer, pk: publicKey | Buf } const objStr = stringify(obj) const hashed = hash(objStr, 'buffer') - const sig = sign(hashed, sk) + const sig = signDetached(hashed, sk) const signPk = Buffer.isBuffer(pk) ? bufferToHex(pk) : pk ;(obj as SignedObject).sign = { owner: signPk, sig } return obj as SignedObject @@ -402,13 +483,59 @@ function verify(msg: string, sig: hexstring | Buffer, pk: publicKey | Buffer): b } const sigBuf = _ensureBuffer(sig) const pkBuf = _ensureBuffer(pk) + + // Auto-detect signature type based on length + // Detached signatures are exactly 64 bytes + // Non-detached signatures are 64 bytes + message length + const msgBuf = Buffer.from(msg, 'hex') + const expectedNonDetachedLength = sodium.crypto_sign_BYTES + msgBuf.length + + if (sigBuf.length === sodium.crypto_sign_BYTES) { + // This is a detached signature + try { + return sodium.crypto_sign_verify_detached(sigBuf as Buffer, msgBuf, pkBuf as Buffer) + } catch (e) { + return false + } + } else if (sigBuf.length === expectedNonDetachedLength) { + // This is a non-detached signature + try { + const opened = Buffer.allocUnsafe(sigBuf.length - sodium.crypto_sign_BYTES) + sodium.crypto_sign_open(opened, sigBuf as Buffer, pkBuf as Buffer) + const verified = opened.toString('hex') + return verified === msg + } catch (e) { + return false + } + } else { + // Invalid signature length + throw new Error('Invalid signature length. Expected either detached (64 bytes) or non-detached signature.') + } +} + +/** + * Verifies a detached signature against a message + * @param msg - The message that was signed (hex string) + * @param sig - The detached signature (hex string or buffer) + * @param pk - The public key to verify with + * @returns true if the signature is valid, false otherwise + */ +export function verifyDetached(msg: string, sig: hexstring | Buffer, pk: publicKey | Buffer): boolean { + if (typeof msg !== 'string') { + throw new TypeError('Message to compare must be a string.') + } + const msgBuf = Buffer.from(msg, 'hex') + const sigBuf = _ensureBuffer(sig) + const pkBuf = _ensureBuffer(pk) + + if (sigBuf.length !== sodium.crypto_sign_BYTES) { + throw new Error('Invalid signature length for detached signature.') + } + try { - const opened = Buffer.allocUnsafe(sigBuf.length - sodium.crypto_sign_BYTES) - sodium.crypto_sign_open(opened, sigBuf as Buffer, pkBuf as Buffer) - const verified = opened.toString('hex') - return verified === msg + return sodium.crypto_sign_verify_detached(sigBuf as Buffer, msgBuf, pkBuf as Buffer) } catch (e) { - throw new Error('Unable to verify provided signature with provided public key.') + return false } } @@ -433,6 +560,28 @@ export function verifyObj(obj: SignedObject): boolean { return verify(objHash, obj.sign.sig, obj.sign.owner) } +/** + * Verifies an object signed with detached signature + * @param obj - The signed object to verify + * @returns true if the signature is valid, false otherwise + */ +export function verifyObjDetached(obj: SignedObject): boolean { + if (typeof obj !== 'object') { + throw new TypeError('Input must be an object.') + } + if (!obj.sign || !obj.sign.owner || !obj.sign.sig) { + throw new Error('Object must contain a sign field with the following data: { owner, sig }') + } + if (typeof obj.sign.owner !== 'string') { + throw new TypeError('Owner must be a public key represented as a hex string.') + } + if (typeof obj.sign.sig !== 'string') { + throw new TypeError('Signature must be a valid signature represented as a hex string.') + } + const objHash = hashObj(obj, true) + return verifyDetached(objHash, obj.sign.sig, obj.sign.owner) +} + /** * This function initialized the cryptographic hashing functions * @param key The HASH_KEY for initializing the cryptographic hashing functions diff --git a/src/sodium-native/index.d.ts b/src/sodium-native/index.d.ts deleted file mode 100644 index 66f8ed6..0000000 --- a/src/sodium-native/index.d.ts +++ /dev/null @@ -1,1300 +0,0 @@ -declare module 'sodium-native' { - // Type definitions for sodium-native 2.3 - // Project: https://github.com/sodium-friends/sodium-native - // Definitions by: Florian Keller - // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped - - /// - - export interface CryptoGenericHashWrap { - /** - * Update the instance with a new piece of data. - * - * * `input` should be a buffer of any size. - */ - update(input: Buffer): void - - /** - * Finalize the instance. - * - * * `output` should be a buffer as above with the same length you gave when creating the instance. - * - * The generated hash is stored in `output`. - */ - final(output: Buffer): void - } - - export interface CryptoStreamChacha20XorWrap { - /** - * Encrypt the next message - */ - update(ciphertext: Buffer, message: Buffer): void - - /** - * Finalize the stream. Zeros out internal state. - */ - final(): void - } - - export interface Xchacha20poly1305State { - _pad: Buffer - nonce: Buffer - k: Buffer - } - - export interface CryptoOneTimeAuthWrap { - /** - * Update the instance with a new piece of data. - * - * * `input` should be a buffer of any size. - */ - update(input: Buffer): void - - /** - * Finalize the instance. - * - * * `output` should be a buffer of length `crypto_onetimeauth_BYTES`. - * - * The generated hash is stored in `output`. - */ - final(output: Buffer): void - } - - export interface CryptoHashSha256Wrap { - /** - * Update the instance with a new piece of data. - * - * * `input` should be a buffer of any size. - */ - update(input: Buffer): void - - /** - * Finalize the instance. - * - * * `output` should be a buffer of length `crypto_hash_sha256_BYTES`. - * - * The generated hash is stored in `output`. - */ - final(output: Buffer): void - } - - export const crypto_aead_xchacha20poly1305_ietf_ABYTES: number - export const crypto_aead_xchacha20poly1305_ietf_KEYBYTES: number - /** Note this is `Number.MAX_SAFE_INTEGER` for now */ - export const crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX: number - export const crypto_aead_xchacha20poly1305_ietf_NPUBBYTES: number - export const crypto_aead_xchacha20poly1305_ietf_NSECBYTES: number - export const crypto_core_ed25519_BYTES: number - export const crypto_core_ed25519_NONREDUCEDSCALARBYTES: number - export const crypto_core_ed25519_SCALARBYTES: number - export const crypto_core_ed25519_UNIFORMBYTES: number - export const crypto_secretstream_xchacha20poly1305_ABYTES: number - export const crypto_secretstream_xchacha20poly1305_HEADERBYTES: number - export const crypto_generichash_BYTES_MAX: number - export const crypto_generichash_BYTES_MIN: number - export const crypto_generichash_BYTES: number - export const crypto_generichash_KEYBYTES_MAX: number - export const crypto_generichash_KEYBYTES_MIN: number - export const crypto_generichash_KEYBYTES: number - export const crypto_pwhash_ALG_ARGON2I13: number - export const crypto_pwhash_ALG_ARGON2ID13: number - export const crypto_pwhash_ALG_DEFAULT: number - export const crypto_pwhash_MEMLIMIT_INTERACTIVE: number - export const crypto_pwhash_MEMLIMIT_MODERATE: number - export const crypto_pwhash_MEMLIMIT_SENSITIVE: number - export const crypto_pwhash_OPSLIMIT_INTERACTIVE: number - export const crypto_pwhash_OPSLIMIT_MODERATE: number - export const crypto_pwhash_OPSLIMIT_SENSITIVE: number - export const crypto_scalarmult_ed25519_BYTES: number - export const crypto_secretstream_xchacha20poly1305_KEYBYTES: number - export const crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX: number - export const crypto_scalarmult_ed25519_SCALARBYTES: number - export const crypto_secretstream_xchacha20poly1305_TAG_FINAL: number - export const crypto_secretstream_xchacha20poly1305_TAG_MESSAGE: number - export const crypto_secretstream_xchacha20poly1305_TAG_PUSH: number - export const crypto_secretstream_xchacha20poly1305_TAG_REKEY: number - export const crypto_sign_PUBLICKEYBYTES: number - export const crypto_sign_SECRETKEYBYTES: number - export const crypto_sign_BYTES: number - export const crypto_sign_SEEDBYTES: number - export const crypto_auth_BYTES: number - export const crypto_auth_KEYBYTES: number - export const crypto_scalarmult_BYTES: number - export const crypto_scalarmult_SCALARBYTES: number - /** NOTE: Unofficial constant */ - export const crypto_secretstream_xchacha20poly1305_TAGBYTES: number - export const crypto_box_SEALBYTES: number - export const crypto_box_PUBLICKEYBYTES: number - export const crypto_box_SECRETKEYBYTES: number - export const crypto_box_MACBYTES: number - export const crypto_box_NONCEBYTES: number - - /** - * Zero out the data in `buffer`. - */ - export function sodium_memzero(buffer: Buffer): void - - /** - * Lock the memory contained in `buffer`. - */ - export function sodium_mlock(buffer: Buffer): void - - /** - * Unlock previously `sodium_mlock`ed memory contained in `buffer`. This will also `sodium_memzero` `buffer`. - */ - export function sodium_munlock(buffer: Buffer): void - - /** - * Allocate a buffer of `size` which is memory protected. See [libsodium docs](https://download.libsodium.org/doc/memory_management#guarded-heap-allocations) for details. Be aware that many Buffer - * methods may break the security guarantees of `sodium.sodium_malloc`'ed memory. To check if a `Buffer` is a "secure" buffer, you can call access the getter `buffer.secure` which will be `true`. - */ - export function sodium_malloc(size: number): Buffer - - /** - * Make `buffer` allocated using `sodium.sodium_malloc` inaccessible, crashing the process if any access is attempted. - * Note that this will have no effect for normal `Buffer`s. - */ - export function sodium_mprotect_noaccess(buffer: Buffer): void - - /** - * Make `buffer` allocated using `sodium.sodium_malloc` read-only, crashing the process if any writing is attempted. - * Note that this will have no effect for normal `Buffer`s. - */ - export function sodium_mprotect_readonly(buffer: Buffer): void - - /** - * Make `buffer` allocated using `sodium.sodium_malloc` read-write, undoing `sodium_mprotect_noaccess` or `sodium_mprotect_readonly`. - * Note that this will have no effect for normal `Buffer`s. - */ - export function sodium_mprotect_readwrite(buffer: Buffer): void - - /** - * Generate a random 32-bit unsigned integer `[0, 0xffffffff]` (both inclusive) - */ - export function randombytes_random(): number - - /** - * Generate a random 32-bit unsigned integer `[0, upper_bound)` (last exclusive). - * `upper_bound` must be `0xffffffff` at most. - */ - export function randombytes_uniform(upper_bound: number): number - - /** - * Fill `buffer` with random data. - */ - export function randombytes_buf(buffer: Buffer): void - - /** - * Fill `buffer` with random data, generated from `seed`. - * `seed` must be a Buffer of at least `sodium.randombytes_SEEDBYTES` length - */ - export function randombytes_buf_deterministic(buffer: Buffer, seed: Buffer): void - - /** - * Compare `b1` with `b2`, in **constant-time** for `b1.length`. - * - * * `b1` must be `Buffer` - * * `b2` must be `Buffer` and must be `b1.length` bytes - * - * Returns `true` when equal, otherwise `false`. - */ - export function sodium_memcmp(b1: Buffer, b2: Buffer): boolean - - /** - * Compare `b1` with `b2`, regarding either as little-endian encoded number. - * - * * `b1` must be `Buffer` - * * `b2` must be `Buffer` and must be `b1.length` bytes - * - * Returns `1`, `0` or `-1` on whether `b1` is greater, equal or less than `b2`. - * This is the same scheme as `Array.prototype.sort` expect. - */ - export function sodium_compare(b1: Buffer, b2: Buffer): 1 | 0 | -1 - - /** - * Adds `b` to `a` (wrapping), regarding either as little-endian encoded number, - * and writing the result into `a`. - * - * * `a` must be `Buffer` - * * `b` must be `Buffer` and must be `a.length` bytes - */ - export function sodium_add(a: Buffer, b: Buffer): void - - /** - * Subtracts `b` from `a` (wrapping), regarding either as little-endian encoded - * number, and writing the result into `a`. - * - * * `a` must be `Buffer` - * * `b` must be `Buffer` and must be `a.length` bytes - */ - export function sodium_sub(a: Buffer, b: Buffer): void - - /** - * Increment `buf` as a little-endian number. This operation is **constant-time** - * for the length of `buf`. - * - * * `buf` must be `Buffer` - */ - export function sodium_increment(buf: Buffer): void - - /** - * Test whether `buf` is all zero for `len` bytes. This operation is - * **constant-time** for `len`. - * - * * `len` must be integer at most the length of `buf` - * - * Returns `true` if all `len` bytes are zero, otherwise `false`. - */ - export function sodium_is_zero(buf: Buffer, len: number): boolean - - /** - * Pad `buf` with random data from index `unpaddedLength` up to closest multiple of - * `blocksize`. - * - * * `buf` must be `Buffer` - * * `unpadded_buflen` must be integer at most `buf.length` - * * `blocksize` must be integer greater than 1 but at most `buf.length` - * - * Returns the length of the padded data (so you may `.slice` the buffer to here). - */ - export function sodium_pad(buf: Buffer, unpaddedLength: number, blocksize: number): number - - /** - * Calculate `unpaddedLength` from a padded `buf` with `blocksize` - * - * * `buf` must be `Buffer` - * * `padded_buflen` must be integer at most `buf.length` - * * `blocksize` must be integer greater than 1 but at most `buf.length` - * - * Returns the length of the unpadded data (so you may `.slice` the buffer to here). - */ - export function sodium_unpad(buf: Buffer, paddedLength: number, blocksize: number): number - - /** - * Create a new keypair based on a seed. - * - * * `publicKey` should be a buffer with length `crypto_sign_PUBLICKEYBYTES`. - * * `secretKey` should be a buffer with length `crypto_sign_SECRETKEYBYTES`. - * * `seed` should be a buffer with length `crypto_sign_SEEDBYTES`. - * - * The generated public and secret key will be stored in passed in buffers. - */ - export function crypto_sign_seed_keypair(publicKey: Buffer, secretKey: Buffer, seed: Buffer): void - - /** - * Create a new keypair. - * - * * `publicKey` should be a buffer with length `crypto_sign_PUBLICKEYBYTES`. - * * `secretKey` should be a buffer with length `crypto_sign_SECRETKEYBYTES`. - * - * The generated public and secret key will be stored in passed in buffers. - */ - export function crypto_sign_keypair(publicKey: Buffer, secretKey: Buffer): void - - /** - * Sign a message. - * - * * `signedMessage` should be a buffer with length `crypto_sign_BYTES + message.length`. - * * `message` should be a buffer of any length. - * * `secretKey` should be a secret key. - * - * The generated signed message will be stored in `signedMessage`. - */ - export function crypto_sign(signedMessage: Buffer, message: Buffer, secretKey: Buffer): void - - /** - * Verify and open a message. - * - * * `message` should be a buffer with length `signedMessage.length - crypto_sign_BYTES`. - * * `signedMessage` at least `crypto_sign_BYTES` length. - * * `publicKey` should be a public key. - * - * Will return `true` if the message could be verified. Otherwise `false`. - * If verified the originally signed message is stored in the `message` buffer. - */ - export function crypto_sign_open(message: Buffer, signedMessage: Buffer, publicKey: Buffer): boolean - - /** - * Same as `crypto_sign` except it only stores the signature. - * - * * `signature` should be a buffer with length `crypto_sign_BYTES`. - * * `message` should be a buffer of any length. - * * `secretKey` should be a secret key. - * - * The generated signature is stored in `signature`. - */ - export function crypto_sign_detached(signature: Buffer, message: Buffer, secretKey: Buffer): void - - /** - * Verify a signature. - * - * * `signature` should be a buffer with length `crypto_sign_BYTES`. - * * `message` should be a buffer of any length. - * * `publicKey` should be a public key. - * - * Will return `true` if the message could be verified. Otherwise `false`. - */ - export function crypto_sign_verify_detached(signature: Buffer, message: Buffer, publicKey: Buffer): boolean - - /** - * Convert an ed25519 public key to curve25519 (which can be used with `box` and `scalarmult`) - * - * * `curve_pk` should be a buffer with length `crypto_box_PUBLICKEYBYTES` - * * `ed_pk` should be a buffer with length `crypto_sign_PUBLICKEYBYTES` - */ - export function crypto_sign_ed25519_pk_to_curve25519(curve_pk: Buffer, ed_pk: Buffer): void - - /** - * Convert an ed25519 secret key to curve25519 (which can be used with `box` and `scalarmult`) - * - * * `curve_sk` should be a buffer with length `crypto_box_SECRETKEYBYTES` - * * `ed_sk` should be a buffer with length `crypto_sign_SECRETKEYBYTES` - */ - export function crypto_sign_ed25519_sk_to_curve25519(curve_sk: Buffer, ed_sk: Buffer): void - - /** - * Extract an ed25519 public key from an ed25519 secret key - * - * * `pk` must be `Buffer` of at least `crypto_sign_PUBLICKEYBYTES` bytes - * * `sk` must be `Buffer` of at least `crypto_sign_SECRETKEYBYTES` bytes - */ - export function crypto_sign_ed25519_sk_to_pk(pk: Buffer, sk: Buffer): void - - /** - * Hash a value with an optional key using the generichash method. - * - * * `output` should be a buffer with length within `crypto_generichash_BYTES_MIN` - `crypto_generichash_BYTES_MAX`. - * * `input` should be a buffer of any length. - * * `key` is an optional buffer of length within `crypto_generichash_KEYBYTES_MIN` - `crypto_generichash_KEYBYTES_MAX`. - * - * The generated hash is stored in `output`. - * - * Also exposes `crypto_generichash_BYTES` and `crypto_generichash_KEYBYTES` that can be used as "default" buffer sizes. - */ - export function crypto_generichash(output: Buffer, input: Buffer, key?: Buffer): void - - /** - * Same as `crypto_generichash` except this hashes an array of buffers instead of a single one. - */ - export function crypto_generichash_batch(output: Buffer, inputArray: Buffer[], key?: Buffer): void - - /** - * Create a generichash instance that can hash a stream of input buffers. - * - * * `key` is an optional buffer as above. - * * `outputLength` the buffer size of your output. - */ - export function crypto_generichash_instance(key?: Buffer, outputLength?: number): CryptoGenericHashWrap - - /** - * Create a new keypair based on a seed. - * - * * `publicKey` should be a buffer with length `crypto_box_PUBLICKEYBYTES`. - * * `secretKey` should be a buffer with length `crypto_box_SECRETKEYBYTES`. - * * `seed` should be a buffer with length `crypto_box_SEEDBYTES`. - * - * The generated public and secret key will be stored in passed in buffers. - */ - export function crypto_box_seed_keypair(publicKey: Buffer, secretKey: Buffer, seed: Buffer): void - - /** - * Create a new keypair. - * - * * `publicKey` should be a buffer with length `crypto_box_PUBLICKEYBYTES`. - * * `secretKey` should be a buffer with length `crypto_box_SECRETKEYBYTES`. - * - * The generated public and secret key will be stored in passed in buffers. - */ - export function crypto_box_keypair(publicKey: Buffer, secretKey: Buffer): void - - /** - * Encrypt a message. - * - * * `ciphertext` should be a buffer with length `message.length`. - * * `mac` should be a buffer with length `crypto_box_MACBYTES`. - * * `message` should be a buffer of any length. - * * `nonce` should be a buffer with length `crypto_box_NONCEBYTES`. - * * `publicKey` should be a public key. - * * `secretKey` should be a secret key. - * - * The encrypted message will be stored in `ciphertext` and the authentification code will be stored in `mac`. - */ - export function crypto_box_detached( - ciphertext: Buffer, - mac: Buffer, - message: Buffer, - nonce: Buffer, - publicKey: Buffer, - secretKey: Buffer - ): void - - /** - * Same as `crypto_box_detached` except it encodes the mac in the message. - * - * * `ciphertext` should be a buffer with length `message.length + crypto_box_MACBYTES`. - * * `message` should be a buffer of any length. - * * `nonce` should be a buffer with length `crypto_box_NONCEBYTES`. - * * `publicKey` should be a public key. - * * `secretKey` should be a secret key. - * - * The encrypted message and authentification code will be stored in `ciphertext`. - */ - export function crypto_box_easy( - ciphertext: Buffer, - message: Buffer, - nonce: Buffer, - publicKey: Buffer, - secretKey: Buffer - ): void - - /** - * Decrypt a message. - * - * * `message` should be a buffer with length `ciphertext.length`. - * * `mac` should be a buffer with length `crypto_box_MACBYTES`. - * * `ciphertext` should be a buffer of any length. - * * `nonce` should be a buffer with length `crypto_box_NONCEBYTES`. - * * `publicKey` should be a public key. - * * `secretKey` should be a secret key. - * - * Returns `true` if the message could be decrypted. Otherwise `false`. - * - * The decrypted message will be stored in `message`. - */ - export function crypto_box_open_detached( - message: Buffer, - ciphertext: Buffer, - mac: Buffer, - nonce: Buffer, - publicKey: Buffer, - secretKey: Buffer - ): boolean - - /** - * Decrypt a message encoded with the easy method. - * - * * `message` should be a buffer with length `ciphertext.length - crypto_box_MACBYTES`. - * * `ciphertext` should be a buffer with length at least `crypto_box_MACBYTES`. - * * `nonce` should be a buffer with length `crypto_box_NONCEBYTES`. - * * `publicKey` should be a public key. - * * `secretKey` should be a secret key. - * - * Returns `true` if the message could be decrypted. Otherwise `false`. - * - * The decrypted message will be stored in `message`. - */ - export function crypto_box_open_easy( - message: Buffer, - ciphertext: Buffer, - nonce: Buffer, - publicKey: Buffer, - secretKey: Buffer - ): boolean - - /** - * Encrypt a message in a sealed box using a throwaway keypair. - * The ciphertext cannot be associated with the sender due to the sender's key - * being a single use keypair that is overwritten during encryption. - * - * * `ciphertext` should be a buffer with length at least `message.length + crypto_box_SEALBYTES`. - * * `message` should be a buffer with any length. - * * `publicKey` should be the receipent's public key. - */ - export function crypto_box_seal(ciphertext: Buffer, message: Buffer, publicKey: Buffer): void - - /** - * Decrypt a message encoded with the sealed box method. - * - * * `message` should be a buffer with length at least `ciphertext.length - crypto_box_SEALBYTES`. - * * `ciphertext` should be a buffer with length at least `crypto_box_SEALBYTES`. - * * `publicKey` should be the receipient's public key. - * * `secretKey` should be the receipient's secret key. - * - * Note: the keypair of the recipient is required here, both public and secret key. - * This is because during encryption the recipient's public key is used to generate - * the nonce. The throwaway public key generated by the sender is stored in the first - * `crypto_box_PUBLICKEYBYTE`'s of the ciphertext. - */ - export function crypto_box_seal_open(message: Buffer, ciphertext: Buffer, publicKey: Buffer, secretKey: Buffer): void - - /** - * Encrypt a message. - * - * * `ciphertext` should be a buffer with length `message.length`. - * * `mac` should be a buffer with length `crypto_secretbox_MACBYTES`. - * * `message` should be a buffer of any length. - * * `nonce` should be a buffer with length `crypto_secretbox_NONCEBYTES`. - * * `secretKey` should be a secret key with legnth `crypto_secretbox_KEYBYTES`. - * - * The encrypted message will be stored in `ciphertext` and the authentification code will be stored in `mac`. - */ - export function crypto_secretbox_detached( - ciphertext: Buffer, - mac: Buffer, - message: Buffer, - nonce: Buffer, - secretKey: Buffer - ): void - - /** - * Same as `crypto_secretbox_detached` except it encodes the mac in the message. - * - * * `ciphertext` should be a buffer with length `message.length + crypto_secretbox_MACBYTES`. - * * `message` should be a buffer of any length. - * * `nonce` should be a buffer with length `crypto_secretbox_NONCEBYTES`. - * * `secretKey` should be a secret key with length `crypto_secretbox_KEYBYTES`. - */ - export function crypto_secretbox_easy(ciphertext: Buffer, message: Buffer, nonce: Buffer, secretKey: Buffer): void - - /** - * Decrypt a message. - * - * * `message` should be a buffer with length `ciphertext.length`. - * * `mac` should be a buffer with length `crypto_secretbox_MACBYTES`. - * * `ciphertext` should be a buffer of any length. - * * `nonce` should be a buffer with length `crypto_secretbox_NONCEBYTES`. - * * `secretKey` should be a secret key. - * - * Returns `true` if the message could be decrypted. Otherwise `false`. - * - * The decrypted message will be stored in `message`. - */ - export function crypto_secretbox_open_detached( - message: Buffer, - ciphertext: Buffer, - mac: Buffer, - nonce: Buffer, - secretKey: Buffer - ): boolean - - /** - * Decrypt a message encoded with the easy method. - * - * * `message` should be a buffer with length `ciphertext.length - crypto_secretbox_MACBYTES`. - * * `ciphertext` should be a buffer with length at least `crypto_secretbox_MACBYTES`. - * * `nonce` should be a buffer with length `crypto_secretbox_NONCEBYTES`. - * * `secretKey` should be a secret key. - * - * Returns `true` if the message could be decrypted. Otherwise `false`. - * - * The decrypted message will be stored in `message`. - */ - export function crypto_secretbox_open_easy( - message: Buffer, - ciphertext: Buffer, - nonce: Buffer, - secretKey: Buffer - ): boolean - - /** - * Generate a new encryption key. - * - * * `key` should be a buffer of length `crypto_aead_xchacha20poly1305_ietf_KEYBYTES`. - * - * The generated key is stored in `key`. - */ - export function crypto_aead_xchacha20poly1305_ietf_keygen(key: Buffer): void - - /** - * Encrypt a message with (`npub`, `key`) and optional additional data `ad`. - * - * * `ciphertext` should be a `Buffer` of size `message.length + crypto_aead_xchacha20poly1305_ietf_ABYTES`. - * * `message` should be a `Buffer`. - * * `ad` is optional and should be `null` or `Buffer`. Included in the computation - * of authentication tag appended to the message. - * * `nullValue` is in the position of the unused `nsec` argument. This should always be `null`. - * * `npub` should be `Buffer` of length `crypto_aead_xchacha20poly1305_ietf_NPUBBYTES`. - * * `key` should be a `Buffer` of length `crypto_aead_xchacha20poly1305_ietf_KEYBYTES`. - * - * Returns how many bytes were written to `ciphertext`. Note that in-place - * encryption is possible. - */ - export function crypto_aead_xchacha20poly1305_ietf_encrypt( - ciphertext: Buffer, - message: Buffer, - ad: Buffer | null, - nullValue: null, - npub: Buffer, - key: Buffer - ): void - - /** - * Decrypt a message with (`npub`, `key`) and optional additional data `ad`. - * - * * `message` should be a `Buffer` of size - * `ciphertext.length - crypto_aead_xchacha20poly1305_ietf_ABYTES`. - * * `nullValue` is in the position of the unused `nsec` argument. This should always be - * `null`. - * * `ciphertext` should be a `Buffer`. - * * `ad` is optional and should be `null` or `Buffer`. Included in the computation - * of authentication tag appended to the message. - * * `npub` should be `Buffer` of length `crypto_aead_xchacha20poly1305_ietf_NPUBBYTES`. - * * `key` should be a `Buffer` of length `crypto_aead_xchacha20poly1305_ietf_KEYBYTES`. - * - * Returns how many bytes were written to `message`. Note that in-place - * encryption is possible. - */ - export function crypto_aead_xchacha20poly1305_ietf_decrypt( - message: Buffer, - nullValue: null, - ciphertext: Buffer, - ad: Buffer | null, - npub: Buffer, - key: Buffer - ): void - - /** - * Encrypt a message with (`npub`, `key`) and optional additional data `ad`. - * - * * `ciphertext` should be a `Buffer` of size `message.length`. - * * `mac` should be `Buffer` of size `crypto_aead_xchacha20poly1305_ietf_ABYTES`. - * * `message` should be a `Buffer`. - * * `ad` is optional and should be `null` or `Buffer`. Included in the computation - * of authentication tag appended to the message. - * * `nullValue` is in the position of the unused `nsec` argument. This should always be `null`. - * * `npub` should be `Buffer` of length `crypto_aead_xchacha20poly1305_ietf_NPUBBYTES`. - * * `key` should be a `Buffer` of length `crypto_aead_xchacha20poly1305_ietf_KEYBYTES`. - * - * Returns how many bytes were written to `mac`. Note that in-place - * encryption is possible. - */ - export function crypto_aead_xchacha20poly1305_ietf_encrypt_detached( - ciphertext: Buffer, - mac: Buffer, - message: Buffer, - ad: Buffer | null, - nullValue: null, - npub: Buffer, - key: Buffer - ): void - - /** - * Decrypt a message with (`npub`, `key`) and optional additional data `ad`. - * - * * `message` should be a `Buffer` of size `ciphertext.length`. - * * `nullValue` is in the position of the unused `nsec` argument. This should always be `null`. - * * `ciphertext` should be a `Buffer`. - * * `mac` should be `Buffer` of size `crypto_aead_xchacha20poly1305_ietf_ABYTES`. - * * `ad` is optional and should be `null` or `Buffer`. Included in the computation - * of authentication tag appended to the message. - * * `npub` should be `Buffer` of length `crypto_aead_xchacha20poly1305_ietf_NPUBBYTES`. - * * `key` should be a `Buffer` of length `crypto_aead_xchacha20poly1305_ietf_KEYBYTES`. - * - * Returns nothing, but will throw on in case the MAC cannot be authenticated. Note - * that in-place encryption is possible. - */ - export function crypto_aead_xchacha20poly1305_ietf_decrypt_detached( - message: Buffer, - nullValue: null, - ciphertext: Buffer, - mac: Buffer, - ad: Buffer | null, - npub: Buffer, - key: Buffer - ): void - - /** - * Generate random data based on a nonce and key into the ciphertext. - * - * * `ciphertext` should be a buffer of any size. - * * `nonce` should be a buffer with length `crypto_stream_NONCEBYTES`. - * * `key` should be a secret key with length `crypto_stream_KEYBYTES`. - * - * The generated data is stored in `ciphertext`. - */ - export function crypto_stream(ciphertext: Buffer, nonce: Buffer, key: Buffer): void - - /** - * Encrypt, but *not* authenticate, a message based on a nonce and key - * - * * `ciphertext` should be a buffer with length `message.length`. - * * `message` should be a buffer of any size. - * * `nonce` should be a buffer with length `crypto_stream_NONCEBYTES`. - * * `key` should be a secret key with length `crypto_stream_KEYBYTES`. - * - * The encrypted data is stored in `ciphertext`. To decrypt, swap `ciphertext` and `message`. - * Also supports in-place encryption where you use the same buffer as `ciphertext` and `message`. - * - * Encryption defaults to XSalsa20, use `crypto_stream_chacha20_xor` if you want - * to encrypt/decrypt with ChaCha20 instead. - * - */ - export function crypto_stream_xor(ciphertext: Buffer, message: Buffer, nonce: Buffer, key: Buffer): void - - /** - * Encrypt, but *not* authenticate, a message based on a nonce and key - * - * * `ciphertext` should be a buffer with length `message.length`. - * * `message` should be a buffer of any size. - * * `nonce` should be a buffer with length `crypto_stream_NONCEBYTES`. - * * `key` should be a secret key with length `crypto_stream_KEYBYTES`. - * - * The encrypted data is stored in `ciphertext`. To decrypt, swap `ciphertext` and `message`. - * Also supports in-place encryption where you use the same buffer as `ciphertext` and `message`. - */ - export function crypto_stream_chacha20_xor(ciphertext: Buffer, message: Buffer, nonce: Buffer, key: Buffer): void - - /** - * A streaming instance to the `crypto_stream_xor` api. Pass a nonce and key in the constructor. - * - * Encryption defaults to XSalsa20, use `crypto_stream_chacha20_xor_instance` if - * you want to encrypt/decrypt with ChaCha20 instead. - */ - export function crypto_stream_xor_instance(nonce: Buffer, key: Buffer): CryptoStreamChacha20XorWrap - - /** - * - * A streaming instance to the `crypto_stream_xor` api. Pass a nonce and key in the constructor. - */ - export function crypto_stream_chacha20_xor_instance(nonce: Buffer, key: Buffer): CryptoStreamChacha20XorWrap - - /** - * Create an authentication token. - * - * * `output` should be a buffer of length `crypto_auth_BYTES`. - * * `input` should be a buffer of any size. - * * `key` should be a buffer of lenght `crypto_auth_KEYBYTES`. - * - * The generated token is stored in `output`. - */ - export function crypto_auth(output: Buffer, input: Buffer, key: Buffer): void - - /** - * Verify a token. - * - * * `output` should be a buffer of length `crypto_auth_BYTES`. - * * `input` should be a buffer of any size. - * * `key` should be a buffer of lenght `crypto_auth_KEYBYTES`. - * - * Returns `true` if the token could be verified. Otherwise `false`. - * - */ - export function crypto_auth_verify(output: Buffer, input: Buffer, key: Buffer): boolean - - /** - * Generate a new encryption key. - * - * * `key` should be a buffer of length `crypto_secretstream_xchacha20poly1305_KEYBYTES`. - * - * The generated key is stored in `key`. - * function crypto_secretstream_xchacha20poly1305_keygen(key) - * - * Create a new stream state. Returns an opaque object used in the next methods. - */ - export function crypto_secretstream_xchacha20poly1305_state_new(): Xchacha20poly1305State - - /** - * Initialise `state` from the writer side with message `header` and - * encryption key `key`. The header must be sent or stored with the stream. - * The key must be exchanged securely with the receiving / reading side. - * - * * `state` should be an opaque state object. - * * `header` should be a buffer of size `crypto_secretstream_xchacha20poly1305_HEADERBYTES`. - * * `key` should be a buffer of length `crypto_secretstream_xchacha20poly1305_KEYBYTES`. - */ - export function crypto_secretstream_xchacha20poly1305_init_push( - state: Xchacha20poly1305State, - header: Buffer, - key: Buffer - ): void - - /** - * Encrypt a message with a certain tag and optional additional data `ad`. - * - * * `state` should be an opaque state object. - * * `ciphertext` should be a buffer of size `message.length + crypto_secretstream_xchacha20poly1305_ABYTES`. - * * `message` should be a buffer. - * * `ad` is optional and should be `null` or `Buffer`. Included in the computation - * of authentication tag appended to the message. - * * `tag` should be `Buffer` of length `crypto_secretstream_xchacha20poly1305_TAGBYTES` - * - * Note that `tag` should be one of the `crypto_secretstream_xchacha20poly1305_TAG_*` constants. - * Returns number of encrypted bytes written to `ciphertext`. - */ - export function crypto_secretstream_xchacha20poly1305_push( - state: Xchacha20poly1305State, - ciphertext: Buffer, - message: Buffer, - ad: Buffer | null, - tag: Buffer - ): void - - /** - * Initialise `state` from the reader side with message `header` and - * encryption key `key`. The header must be retrieved from somewhere. - * The key must be exchanged securely with the sending / writing side. - * - * * `state` should be an opaque state object. - * * `header` should be a buffer of size `crypto_secretstream_xchacha20poly1305_HEADERBYTES`. - * * `key` should be a buffer of length `crypto_secretstream_xchacha20poly1305_KEYBYTES`. - */ - export function crypto_secretstream_xchacha20poly1305_init_pull( - state: Xchacha20poly1305State, - header: Buffer, - key: Buffer - ): void - - /** - * Decrypt a message with optional additional data `ad`, and write message tag to - * `tag`. Make sure to check this! - * - * * `state` should be an opaque state object. - * * `message` should be a buffer of size `ciphertext.length - crypto_secretstream_xchacha20poly1305_ABYTES`. - * * `tag` should be a buffer of `crypto_secretstream_xchacha20poly1305_TAGBYTES`. - * * `ad` is optional and should be `null` or `Buffer`. Included in the computation - * of the authentication tag appended to the message. - * - * Note that `tag` should be one of the `crypto_secretstream_xchacha20poly1305_TAG_*` constants. - * Returns number of decrypted bytes written to `message`. - */ - export function crypto_secretstream_xchacha20poly1305_pull( - state: Xchacha20poly1305State, - message: Buffer, - tag: Buffer, - ciphertext: Buffer, - ad: Buffer | null - ): void - - /** - * Rekey the opaque `state` object. - */ - export function crypto_secretstream_xchacha20poly1305_rekey(state: Xchacha20poly1305State): void - - /** - * Create a authentication token based on a onetime key. - * - * * `output` should be a buffer of length `crypto_onetimauth_BYTES`. - * * `input` should be a buffer of any size. - * * `key` should be a buffer of length `crypto_onetimeauth_KEYBYTES`. - * - * The generated token is stored in `output`. - */ - export function crypto_onetimeauth(output: Buffer, input: Buffer, key: Buffer): void - - /** - * Verify a token. - * - * * `output` should be a buffer of length `crypto_onetimeauth_BYTES`. - * * `input` should be a buffer of any size. - * * `key` should be a buffer of lenght `crypto_onetimeauth_KEYBYTES`. - * - * Returns `true` if the token could be verified. Otherwise `false`. - */ - export function crypto_onetimeauth_verify(output: Buffer, input: Buffer, key: Buffer): boolean - - /** - * Create an instance that create a token from a onetime key and a stream of input data. - * - * * `key` should be a buffer of length `crypto_onetimeauth_KEYBYTES`. - */ - export function crypto_onetimeauth_instance(key: Buffer): CryptoOneTimeAuthWrap - - /** - * Create a password hash. - * - * * `output` should be a buffer with length within `crypto_pwhash_BYTES_MIN` - `crypto_pwhash_BYTES_MAX`. - * * `password` should be a buffer of any size. - * * `salt` should be a buffer with length `crypto_pwhash_SALTBYTES`. - * * `opslimit` should a be number containing your ops limit setting in the range `crypto_pwhash_OPSLIMIT_MIN` - `crypto_pwhash_OPSLIMIT_MAX`. - * * `memlimit` should a be number containing your mem limit setting in the range `crypto_pwhash_MEMLIMIT_MIN` - `crypto_pwhash_OPSLIMIT_MAX`. - * * `algorithm` should be a number specifying the algorithm you want to use. - * - * Available default ops and mem limits are - * - * * `crypto_pwhash_OPSLIMIT_INTERACTIVE` - * * `crypto_pwhash_OPSLIMIT_MODERATE` - * * `crypto_pwhash_OPSLIMIT_SENSITIVE` - * * `crypto_pwhash_MEMLIMIT_INTERACTIVE` - * * `crypto_pwhash_MEMLIMIT_MODERATE` - * * `crypto_pwhash_MEMLIMIT_SENSITIVE` - * - * The available algorithms are - * - * * `crypto_pwhash_ALG_DEFAULT` - * * `crypto_pwhash_ALG_ARGON2ID13` - * * `crypto_pwhash_ALG_ARGON2I13` - * - * The generated hash will be stored in `output` and the entire `output` buffer will be used. - */ - export function crypto_pwhash( - output: Buffer, - password: Buffer, - salt: Buffer, - opslimit: number, - memlimit: number, - algorithm: number - ): void - - /** - * Create a password hash with a random salt. - * - * * `output` should be a buffer with length `crypto_pwhash_STRBYTES`. - * * `password` should be a buffer of any size. - * * `opslimit` should a be number containing your ops limit setting in the range `crypto_pwhash_OPSLIMIT_MIN` - `crypto_pwhash_OPSLIMIT_MAX`. - * * `memlimit` should a be number containing your mem limit setting in the range `crypto_pwhash_MEMLIMIT_MIN` - `crypto_pwhash_OPSLIMIT_MAX`. - * - * The generated hash, settings, salt, version and algorithm will be stored in `output` and the entire `output` buffer will be used. - */ - export function crypto_pwhash_str(output: Buffer, password: Buffer, opslimit: number, memlimit: number): void - - /** - * Verify a password hash generated with the above method. - * - * * `str` should be a buffer with length `crypto_pwhash_STRBYTES`. - * * `password` should be a buffer of any size. - * - * Returns `true` if the hash could be verified with the settings contained in `str`. Otherwise `false`. - */ - export function crypto_pwhash_str_verify(str: Buffer, password: Buffer): boolean - - /** - * Check if a password hash needs rehash, either because the default algorithm - * changed, opslimit or memlimit increased or because the hash is malformed. - * - * * `hash` should be a buffer with length `crypto_pwhash_STRBYTES`. - * * `opslimit` should a be number containing your ops limit setting in the range `crypto_pwhash_OPSLIMIT_MIN` - `crypto_pwhash_OPSLIMIT_MAX`. - * * `memlimit` should a be number containing your mem limit setting in the range `crypto_pwhash_MEMLIMIT_MIN` - `crypto_pwhash_OPSLIMIT_MAX`. - * - * Returns `true` if the hash should be rehashed the settings contained in `str`. - * Otherwise `false` if it is still good. - */ - export function crypto_pwhash_str_needs_rehash(hash: Buffer, opslimit: number, memlimit: number): boolean - - /** - * Just like `crypto_pwhash` but will run password hashing on a seperate worker so it will not block the event loop. `callback(err)` will receive any errors from the hashing but all argument - * errors will `throw`. The resulting hash is written to `output`. This function also supports [`async_hook`s](https://nodejs.org/dist/latest/docs/api/async_hooks.html) as the type - * `sodium-native:crypto_pwhash_async` - */ - export function crypto_pwhash_async( - output: Buffer, - password: Buffer, - salt: Buffer, - opslimit: number, - memlimit: number, - algorithm: number, - callback: (err: Error | null) => void - ): void - - /** - * Just like `crypto_pwhash_str` but will run password hashing on a seperate worker so it will not block the event loop. `callback(err)` will receive any errors from the hashing but all argument - * errors will `throw`. The resulting hash with parameters is written to `output`. This function also supports [`async_hook`s](https://nodejs.org/dist/latest/docs/api/async_hooks.html) as the type - * `sodium-native:crypto_pwhash_str_async` - */ - export function crypto_pwhash_str_async( - output: Buffer, - password: Buffer, - opslimit: number, - memlimit: number, - callback: (err: Error | null) => void - ): void - - /** - * Just like `crypto_pwhash_str_verify` but will run password hashing on a seperate worker so it will not block the event loop. `callback(err, bool)` will receive any errors from the hashing but - * all argument errors will `throw`. If the verification succeeds `bool` is `true`, otherwise `false`. Due to an issue with libsodium `err` is currently never set. This function also supports - * [`async_hook`s](https://nodejs.org/dist/latest/docs/api/async_hooks.html) as the type `sodium-native:crypto_pwhash_str_verify_async` - */ - export function crypto_pwhash_str_verify_async( - str: Buffer, - password: Buffer, - callback: (err: Error | null, bool: boolean) => void - ): void - - /** - * Create a key exchange key pair. - * - * * `publicKey` should be a buffer of length `crypto_kx_PUBLICKEYBYTES`. - * * `secretKey` should be a buffer of length `crypto_kx_SECRETKEYBYTES`. - */ - export function crypto_kx_keypair(publicKey: Buffer, secretKey: Buffer): void - - /** - * Create a key exchange key pair based on a seed. - * - * * `publicKey` should be a buffer of length `crypto_kx_PUBLICKEYBYTES`. - * * `secretKey` should be a buffer of length `crypto_kx_SECRETKEYBYTES`. - * * `seed` should be a buffer of length `crypto_kx_SEEDBYTES` - */ - export function crypto_kx_seed_keypair(publicKey: Buffer, secretKey: Buffer, seed: Buffer): void - - /** - * Generate a session receive and transmission key for a client. - * The public / secret keys should be generated using the key pair method above. - * - * * `rx` should be a buffer of length `crypto_kx_SESSIONKEYBYTES` or `null`. - * * `tx` should be a buffer of length `crypto_kx_SESSIONKEYBYTES` or `null`. - * - * You should use the `rx` to decrypt incoming data and `tx` to encrypt outgoing. - * If you need to make a one-way or half-duplex channel you can give only one of - * `rx` or `tx`. - */ - export function crypto_kx_client_session_keys( - rx: Buffer | null, - tx: Buffer | null, - clientPublicKey: Buffer, - clientSecretKey: Buffer, - serverPublicKey: Buffer - ): void - - /** - * Generate a session receive and transmission key for a server. - * The public / secret keys should be generated using the key pair method above. - * - * * `rx` should be a buffer of length `crypto_kx_SESSIONKEYBYTES` or `null`. - * * `tx` should be a buffer of length `crypto_kx_SESSIONKEYBYTES` or `null`. - * - * You should use the `rx` to decrypt incoming data and `tx` to encrypt outgoing. - * If you need to make a one-way or half-duplex channel you can give only one of - * `rx` or `tx`. - */ - export function crypto_kx_server_session_keys( - rx: Buffer | null, - tx: Buffer | null, - serverPublicKey: Buffer, - serverSecretKey: Buffer, - clientPublicKey: Buffer - ): void - - /** - * Create a scalar multiplication public key based on a secret key - * - * * `publicKey` should be a buffer of length `crypto_scalarmult_BYTES`. - * * `secretKey` should be a buffer of length `crypto_scalarmult_SCALARBYTES`. - * - * The generated public key is stored in `publicKey`. - */ - export function crypto_scalarmult_base(publicKey: Buffer, secretKey: Buffer): void - - /** - * Derive a shared secret from a local secret key and a remote public key. - * - * * `sharedSecret` shoudl be a buffer of length `crypto_scalarmult_BYTES`. - * * `secretKey` should be a buffer of length `crypto_scalarmult_SCALARBYTES`. - * * `remotePublicKey` should be a buffer of length `crypto_scalarmult_BYTES`. - * - * The generated shared secret is stored in `sharedSecret`. - */ - export function crypto_scalarmult(sharedSecret: Buffer, secretKey: Buffer, remotePublicKey: Buffer): void - - /** - * > The crypto_core_ed25519_is_valid_point() function checks that p represents - * > a point on the edwards25519 curve, in canonical form, on the main subgroup, - * > and that the point doesn't have a small order. - * - * * `p` must be `Buffer` of at least `crypto_core_ed25519_BYTES` bytes - * - * Returns `true` or `false` - */ - export function crypto_core_ed25519_is_valid_point(p: Buffer): boolean - - /** - * Maps a `crypto_core_ed25519_UNIFORMBYTES` bytes vector (usually the output of - * a hash function) to a a valid curve point and stores its compressed - * representation in `p`. - * - * The point is guaranteed to be on the main subgroup. - * - * * `p` must be `Buffer` of at least `crypto_core_ed25519_BYTES` bytes - * * `r` must be `Buffer` of at least `crypto_core_ed25519_UNIFORMBYTES` bytes - */ - export function crypto_core_ed25519_from_uniform(p: Buffer, r: Buffer): void - - /** - * Multiply point `p` by scalar `n` and store its compressed representation in `q`. - * - * * `q` must be `Buffer` of at least `crypto_scalarmult_ed25519_BYTES` bytes - * * `n` must be `Buffer` of at least `crypto_scalarmult_ed25519_SCALARBYTES` bytes - * * `p` must be `Buffer` of at least `crypto_scalarmult_ed25519_BYTES` bytes - * - * Note this function will throw if `n` is zero or `p` is an invalid curve point. - */ - export function crypto_scalarmult_ed25519(q: Buffer, n: Buffer, p: Buffer): void - - /** - * Multiply the basepoint by scalar `n` and store its compressed representation in - * `q`. Note that `n` will be clamped. - * - * * `q` must be `Buffer` of at least `crypto_scalarmult_ed25519_BYTES` bytes - * * `n` must be `Buffer` of at least `crypto_scalarmult_ed25519_SCALARBYTES` bytes - * - * Note this function will throw if `n` is zero - */ - export function crypto_scalarmult_ed25519_base(q: Buffer, n: Buffer): void - - /** - * Multiply point `p` by scalar `n` and store its compressed representation in `q`. - * This version does not clamp. - * - * * `q` must be `Buffer` of at least `crypto_scalarmult_ed25519_BYTES` bytes - * * `n` must be `Buffer` of at least `crypto_scalarmult_ed25519_SCALARBYTES` bytes - * * `p` must be `Buffer` of at least `crypto_scalarmult_ed25519_BYTES` bytes - * - * Note this function will throw if `n` is zero or `p` is an invalid curve point. - */ - export function crypto_scalarmult_ed25519_noclamp(q: Buffer, n: Buffer, p: Buffer): void - - /** - * Multiply the basepoint by scalar `n` and store its compressed representation in - * `q`. This version does not clamp. - * - * * `q` must be `Buffer` of at least `crypto_scalarmult_ed25519_BYTES` bytes - * * `n` must be `Buffer` of at least `crypto_scalarmult_ed25519_SCALARBYTES` bytes - * - * Note this function will throw if `n` is zero - */ - export function crypto_scalarmult_ed25519_base_noclamp(q: Buffer, n: Buffer): void - - /** - * Add point `q` to `p`, storing the result to `r`. - * - * * `r` must be `Buffer` of at least `crypto_core_ed25519_BYTES` bytes - * * `p` must be `Buffer` of at least `crypto_core_ed25519_BYTES` bytes - * * `q` must be `Buffer` of at least `crypto_core_ed25519_BYTES` bytes - * - * Will throw if `p`, `q` are not valid curve points - */ - export function crypto_core_ed25519_add(r: Buffer, p: Buffer, q: Buffer): void - - /** - * Subtract point `q` to `p`, storing the result to `r`. - * - * * `r` must be `Buffer` of at least `crypto_core_ed25519_BYTES` bytes - * * `p` must be `Buffer` of at least `crypto_core_ed25519_BYTES` bytes - * * `q` must be `Buffer` of at least `crypto_core_ed25519_BYTES` bytes - * - * Will throw if `p`, `q` are not valid curve points - */ - export function crypto_core_ed25519_sub(r: Buffer, p: Buffer, q: Buffer): void - - /** - * Generate random point, storing it in `r`. - * - * * `r` must be `Buffer` of at least `crypto_core_ed25519_SCALARBYTES` bytes - */ - export function crypto_core_ed25519_scalar_random(r: Buffer): void - - /** - * Reduce `s mod L`, storing it in `r`. - * - * * `r` must be `Buffer` of at least `crypto_core_ed25519_SCALARBYTES` bytes - * * `s` must be `Buffer` of at least `crypto_core_ed25519_NONREDUCEDSCALARBYTES` bytes - */ - export function crypto_core_ed25519_scalar_reduce(r: Buffer, s: Buffer): void - - /** - * Find `recip` such that `s * recip = 1 (mod L)`, storing it in `recip`. - * - * * `recip` must be `Buffer` of at least `crypto_core_ed25519_SCALARBYTES` bytes - * * `s` must be `Buffer` of at least `crypto_core_ed25519_SCALARBYTES` bytes - */ - export function crypto_core_ed25519_scalar_invert(recip: Buffer, s: Buffer): void - - /** - * Find `neg` such that `s + neg = 0 (mod L)`, storing it in `recip`. - * - * * `recip` must be `Buffer` of at least `crypto_core_ed25519_SCALARBYTES` bytes - * * `s` must be `Buffer` of at least `crypto_core_ed25519_SCALARBYTES` bytes - */ - export function crypto_core_ed25519_scalar_negate(neg: Buffer, s: Buffer): void - - /** - * Find `comp` such that `s + comp = 1 (mod L)`, storing it in `recip`. - * - * * `comp` must be `Buffer` of at least `crypto_core_ed25519_SCALARBYTES` bytes - * * `s` must be `Buffer` of at least `crypto_core_ed25519_SCALARBYTES` bytes - */ - export function crypto_core_ed25519_scalar_complement(comp: Buffer, s: Buffer): void - - /** - * Add `x` and `y` such that `x + y = z (mod L)`, storing it in `z`. - * - * * `x` must be `Buffer` of at least `crypto_core_ed25519_SCALARBYTES` bytes - * * `y` must be `Buffer` of at least `crypto_core_ed25519_SCALARBYTES` bytes - * * `z` must be `Buffer` of at least `crypto_core_ed25519_SCALARBYTES` bytes - */ - export function crypto_core_ed25519_scalar_add(z: Buffer, x: Buffer, y: Buffer): void - - /** - * Subtract `x` and `y` such that `x - y = z (mod L)`, storing it in `z`. - * - * * `x` must be `Buffer` of at least `crypto_core_ed25519_SCALARBYTES` bytes - * * `y` must be `Buffer` of at least `crypto_core_ed25519_SCALARBYTES` bytes - * * `z` must be `Buffer` of at least `crypto_core_ed25519_SCALARBYTES` bytes - */ - export function crypto_core_ed25519_scalar_sub(z: Buffer, x: Buffer, y: Buffer): void - - /** - * Hash a value to a short hash based on a key. - * - * * `output` should be a buffer of length `crypto_shorthash_BYTES`. - * * `input` should be a buffer of any size. - * * `key` should be a buffer of length `crypto_shorthash_KEYBYTES`. - * - * The generated short hash is stored in `output`. - */ - export function crypto_shorthash(output: Buffer, input: Buffer, key: Buffer): void - - /** - * Generate a new master key. - * - * * `key` should be a buffer of length `crypto_kdf_KEYBYTES` - */ - export function crypto_kdf_keygen(key: Buffer): void - - /** - * Derive a new key from a master key. - * - * * `subkey` should be a buffer between `crypto_kdf_BYTES_MIN` and `crypto_kdf_BYTES_MAX`. - * * `subkeyId` should be an integer. - * * `context` should be a buffer of length `crypto_kdf_CONTEXTBYTES` - * * `key` should by a buffer of length `crypto_kdf_KEYBYTES` - */ - export function crypto_kdf_derive_from_key(subkey: Buffer, subkeyId: number, context: Buffer, key: Buffer): void - - /** - * Hash a value to a short hash based on a key. - * - * * `output` should be a buffer of length `crypto_hash_sha256_BYTES`. - * * `input` should be a buffer of any size. - * - * The generated short hash is stored in `output`. - */ - export function crypto_hash_sha256(output: Buffer, input: Buffer): void - - /** - * Create an instance that has stream of input data to sha256. - */ - export function crypto_hash_sha256_instance(): CryptoHashSha256Wrap - - /** - * Hash a value to a short hash based on a key. - * - * * `output` should be a buffer of length `crypto_hash_sha512_BYTES`. - * * `input` should be a buffer of any size. - * - * The generated short hash is stored in `output`. - */ - export function crypto_hash_sha512(output: Buffer, input: Buffer): void - - /** - * Create an instance that has stream of input data to sha512. - */ - export function crypto_hash_sha512_instance(): CryptoHashSha512Wrap - - export interface CryptoHashSha512Wrap { - /** - * Update the instance with a new piece of data. - * - * * `input` should be a buffer of any size. - */ - update(input: Buffer): void - - /** - * Finalize the instance. - * - * * `output` should be a buffer of length `crypto_hash_sha512_BYTES`. - * - * The generated hash is stored in `output`. - */ - final(output: Buffer): void - } -} diff --git a/test/unit/detachedSignatures.test.ts b/test/unit/detachedSignatures.test.ts new file mode 100644 index 0000000..8e64d91 --- /dev/null +++ b/test/unit/detachedSignatures.test.ts @@ -0,0 +1,214 @@ +import * as crypto from '../../src/index' + +describe('Detached Signatures', () => { + beforeAll(() => { + crypto.init('69fa4195670576c0160d660c3be36556ff8d504725be8a59b5a96509e0c994bc') + }) + + describe('signDetached and verifyDetached', () => { + it('should create and verify a detached signature', () => { + const keypair = crypto.generateKeypair() + const message = 'Hello, World!' + const messageHash = crypto.hash(message) + + // Create detached signature + const signature = crypto.signDetached(messageHash, keypair.secretKey) + + // Verify signature is 64 bytes (128 hex chars) + expect(signature.length).toBe(128) + + // Verify the signature + const isValid = crypto.verifyDetached(messageHash, signature, keypair.publicKey) + expect(isValid).toBe(true) + + // Verify with wrong message fails + const wrongMessage = crypto.hash('Wrong message') + const isInvalid = crypto.verifyDetached(wrongMessage, signature, keypair.publicKey) + expect(isInvalid).toBe(false) + }) + }) + + describe('signObjDetached and verifyObjDetached', () => { + it('should sign and verify objects with detached signatures', () => { + const keypair = crypto.generateKeypair() + const testObj = { + message: 'Test object', + value: 42, + timestamp: Date.now(), + } + + // Sign with detached signature + const signedObj = crypto.signObjDetached(testObj, keypair.secretKey, keypair.publicKey) + + // Verify signature is 64 bytes + expect(signedObj.sign.sig.length).toBe(128) + + // Verify the signed object + const isValid = crypto.verifyObjDetached(signedObj) + expect(isValid).toBe(true) + + // Tamper with object and verify it fails + signedObj.value = 43 + const isInvalid = crypto.verifyObjDetached(signedObj) + expect(isInvalid).toBe(false) + }) + }) + + describe('Backward compatibility', () => { + it('should verify both old and new signatures with auto-detection', () => { + const keypair = crypto.generateKeypair() + const message = 'Test message' + const messageHash = crypto.hash(message) + + // Create old-style (non-detached) signature + const oldSignature = crypto.sign(messageHash, keypair.secretKey, false) + // Old signature should be 96 bytes for 32-byte hash (64 + 32) + expect(oldSignature.length).toBe(192) + + // Create new-style (detached) signature + const newSignature = crypto.sign(messageHash, keypair.secretKey, true) + // New signature should be 64 bytes + expect(newSignature.length).toBe(128) + + // Both should verify successfully with verifyObj + const testObj1 = { data: 'test' } + const testObj2 = { data: 'test' } + + // Sign with old style + const signedObj1 = crypto.signObj(testObj1, keypair.secretKey, keypair.publicKey, false) + expect(crypto.verifyObj(signedObj1)).toBe(true) + + // Sign with new style + const signedObj2 = crypto.signObj(testObj2, keypair.secretKey, keypair.publicKey, true) + expect(crypto.verifyObj(signedObj2)).toBe(true) + }) + + it('should handle mixed signature types correctly', () => { + const keypair = crypto.generateKeypair() + const testObj = { data: 'test data', id: 123 } + + // Create both types of signatures + const objOld = JSON.parse(JSON.stringify(testObj)) + const objNew = JSON.parse(JSON.stringify(testObj)) + + // Sign with old method + crypto.signObj(objOld, keypair.secretKey, keypair.publicKey, false) + + // Sign with new method + crypto.signObj(objNew, keypair.secretKey, keypair.publicKey, true) + + // Old signature should be longer + expect(objOld.sign.sig.length).toBeGreaterThan(objNew.sign.sig.length) + + // Both should verify with verifyObj (auto-detection) + expect(crypto.verifyObj(objOld)).toBe(true) + expect(crypto.verifyObj(objNew)).toBe(true) + + // New detached verify should work only with detached signature + expect(crypto.verifyObjDetached(objNew)).toBe(true) + expect(() => crypto.verifyObjDetached(objOld)).toThrow() + }) + }) + + describe('Sign function with detached parameter', () => { + it('should produce different signature lengths based on detached parameter', () => { + const keypair = crypto.generateKeypair() + const message = crypto.hash('test') + + // Default (undefined) should now use detached + const defaultSig = crypto.sign(message, keypair.secretKey) + expect(defaultSig.length).toBe(128) // 64 bytes sig only + + // Explicit false should use non-detached + const nonDetachedSig = crypto.sign(message, keypair.secretKey, false) + expect(nonDetachedSig.length).toBe(192) // 96 bytes = 64 sig + 32 msg + + // Explicit true should use detached + const detachedSig = crypto.sign(message, keypair.secretKey, true) + expect(detachedSig.length).toBe(128) // 64 bytes sig only + }) + }) + + describe('Default behavior change', () => { + it('sign() should default to detached signatures', () => { + const keypair = crypto.generateKeypair() + const message = crypto.hash('test message') + + // Call without detached parameter + const signature = crypto.sign(message, keypair.secretKey) + + // Should produce 64-byte signature + expect(signature.length).toBe(128) // 128 hex chars = 64 bytes + + // Should verify with verifyDetached + expect(crypto.verifyDetached(message, signature, keypair.publicKey)).toBe(true) + }) + + it('signObj() should default to detached signatures', () => { + const keypair = crypto.generateKeypair() + const obj = { data: 'test', value: 123 } + + // Call without detached parameter + const signedObj = crypto.signObj(obj, keypair.secretKey, keypair.publicKey) + + // Should produce 64-byte signature + expect(signedObj.sign.sig.length).toBe(128) + + // Should verify with both methods due to auto-detection + expect(crypto.verifyObj(signedObj)).toBe(true) + expect(crypto.verifyObjDetached(signedObj)).toBe(true) + }) + }) + + describe('Manual signature conversion', () => { + it('should convert non-detached signature to detached by removing excess and verify with verifyObj', () => { + const keypair = crypto.generateKeypair() + const testObj = { + message: 'Test conversion', + timestamp: Date.now(), + id: 'test-123', + } + + // Create a copy for manual signature manipulation + const objForManualConversion = JSON.parse(JSON.stringify(testObj)) + + // Sign with old method (non-detached) - this includes message + signature + const signedObjOld = crypto.signObj(objForManualConversion, keypair.secretKey, keypair.publicKey, false) + + // Verify the old signature works + expect(crypto.verifyObj(signedObjOld)).toBe(true) + + // Get the non-detached signature + const nonDetachedSig = signedObjOld.sign.sig + + // Non-detached signature should be longer than 128 hex chars (64 bytes) + expect(nonDetachedSig.length).toBeGreaterThan(128) + + // Manual conversion: Extract just the signature part (first 128 hex chars = 64 bytes) + // In libsodium, non-detached signatures have format: [64-byte signature][original message] + const detachedSigFromOld = nonDetachedSig.substring(0, 128) + + // Verify the detached signature is exactly 64 bytes + expect(detachedSigFromOld.length).toBe(128) + + // Create a new object with the manually converted detached signature + const objWithDetachedSig = { + ...testObj, + sign: { + owner: signedObjOld.sign.owner, + sig: detachedSigFromOld, + }, + } + + // This should verify successfully using verifyObj with auto-detection + expect(crypto.verifyObj(objWithDetachedSig)).toBe(true) + + // It should also verify with the specific detached verification method + expect(crypto.verifyObjDetached(objWithDetachedSig)).toBe(true) + + // For additional verification, let's also test direct signature verification + const objHash = crypto.hashObj(objWithDetachedSig, true) // true to remove sign field for hashing + expect(crypto.verifyDetached(objHash, detachedSigFromOld, keypair.publicKey)).toBe(true) + }) + }) +}) diff --git a/tsconfig.json b/tsconfig.json index 03bae60..23e6ba9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,6 @@ "noImplicitReturns": false, "typeRoots": ["node_modules/@types"] }, - "include": ["src/**/*", "src/tests"], + "include": ["src/**/*", "src/tests", "sodium-native.d.ts"], "exclude": ["./node_modules", "build"] }