Skip to content

Commit

Permalink
feat(rust-nodejs): Create napi struct for xchacha20poly1305 key
Browse files Browse the repository at this point in the history
  • Loading branch information
rohanjadvani committed Sep 13, 2024
1 parent 1b640a2 commit 97c2478
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 2 deletions.
17 changes: 17 additions & 0 deletions ironfish-rust-nodejs/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ export const TRANSACTION_EXPIRATION_LENGTH: number
export const TRANSACTION_FEE_LENGTH: number
export const LATEST_TRANSACTION_VERSION: number
export function verifyTransactions(serializedTransactions: Array<Buffer>): boolean
export const XCHACHA20POLY1305_KEY_LENGTH: number
export const SALT_LENGTH: number
export const XNONCE_LENGTH: number
export function encrypt(plaintext: Buffer, passphrase: string): Buffer
export function decrypt(encryptedBlob: Buffer, passphrase: string): Buffer
export const enum LanguageCode {
Expand Down Expand Up @@ -237,6 +240,20 @@ export class UnsignedTransaction {
sign(spenderHexKey: string): Buffer
addSignature(signature: Buffer): Buffer
}
export type NativeXChaCha20Poly1305Key = XChaCha20Poly1305Key
export class XChaCha20Poly1305Key {
constructor(passphrase: string)
static fromParts(passphrase: string, salt: Buffer, nonce: Buffer): XChaCha20Poly1305Key
deriveKey(salt: Buffer, nonce: Buffer): XChaCha20Poly1305Key
deriveNewKey(): XChaCha20Poly1305Key
static deserialize(jsBytes: Buffer): NativeXChaCha20Poly1305Key
destroy(): void
salt(): Buffer
nonce(): Buffer
key(): Buffer
encrypt(plaintext: Buffer): Buffer
decrypt(ciphertext: Buffer): Buffer
}
export class FoundBlockResult {
randomness: string
miningRequestId: number
Expand Down
6 changes: 5 additions & 1 deletion ironfish-rust-nodejs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}

const { FishHashContext, KEY_LENGTH, NONCE_LENGTH, BoxKeyPair, randomBytes, boxMessage, unboxMessage, RollingFilter, initSignalHandler, triggerSegfault, ASSET_ID_LENGTH, ASSET_METADATA_LENGTH, ASSET_NAME_LENGTH, ASSET_LENGTH, Asset, NOTE_ENCRYPTION_KEY_LENGTH, MAC_LENGTH, ENCRYPTED_NOTE_PLAINTEXT_LENGTH, ENCRYPTED_NOTE_LENGTH, NoteEncrypted, PUBLIC_ADDRESS_LENGTH, RANDOMNESS_LENGTH, MEMO_LENGTH, AMOUNT_VALUE_LENGTH, DECRYPTED_NOTE_LENGTH, Note, PROOF_LENGTH, TRANSACTION_SIGNATURE_LENGTH, TRANSACTION_PUBLIC_KEY_RANDOMNESS_LENGTH, TRANSACTION_EXPIRATION_LENGTH, TRANSACTION_FEE_LENGTH, LATEST_TRANSACTION_VERSION, TransactionPosted, Transaction, verifyTransactions, UnsignedTransaction, encrypt, decrypt, LanguageCode, generateKey, spendingKeyToWords, wordsToSpendingKey, generatePublicAddressFromIncomingViewKey, generateKeyFromPrivateKey, initializeSapling, FoundBlockResult, ThreadPoolHandler, isValidPublicAddress, CpuCount, getCpuCount, generateRandomizedPublicKey, multisig } = nativeBinding
const { FishHashContext, KEY_LENGTH, NONCE_LENGTH, BoxKeyPair, randomBytes, boxMessage, unboxMessage, RollingFilter, initSignalHandler, triggerSegfault, ASSET_ID_LENGTH, ASSET_METADATA_LENGTH, ASSET_NAME_LENGTH, ASSET_LENGTH, Asset, NOTE_ENCRYPTION_KEY_LENGTH, MAC_LENGTH, ENCRYPTED_NOTE_PLAINTEXT_LENGTH, ENCRYPTED_NOTE_LENGTH, NoteEncrypted, PUBLIC_ADDRESS_LENGTH, RANDOMNESS_LENGTH, MEMO_LENGTH, AMOUNT_VALUE_LENGTH, DECRYPTED_NOTE_LENGTH, Note, PROOF_LENGTH, TRANSACTION_SIGNATURE_LENGTH, TRANSACTION_PUBLIC_KEY_RANDOMNESS_LENGTH, TRANSACTION_EXPIRATION_LENGTH, TRANSACTION_FEE_LENGTH, LATEST_TRANSACTION_VERSION, TransactionPosted, Transaction, verifyTransactions, UnsignedTransaction, XCHACHA20POLY1305_KEY_LENGTH, SALT_LENGTH, XNONCE_LENGTH, XChaCha20Poly1305Key, encrypt, decrypt, LanguageCode, generateKey, spendingKeyToWords, wordsToSpendingKey, generatePublicAddressFromIncomingViewKey, generateKeyFromPrivateKey, initializeSapling, FoundBlockResult, ThreadPoolHandler, isValidPublicAddress, CpuCount, getCpuCount, generateRandomizedPublicKey, multisig } = nativeBinding

module.exports.FishHashContext = FishHashContext
module.exports.KEY_LENGTH = KEY_LENGTH
Expand Down Expand Up @@ -290,6 +290,10 @@ module.exports.TransactionPosted = TransactionPosted
module.exports.Transaction = Transaction
module.exports.verifyTransactions = verifyTransactions
module.exports.UnsignedTransaction = UnsignedTransaction
module.exports.XCHACHA20POLY1305_KEY_LENGTH = XCHACHA20POLY1305_KEY_LENGTH
module.exports.SALT_LENGTH = SALT_LENGTH
module.exports.XNONCE_LENGTH = XNONCE_LENGTH
module.exports.XChaCha20Poly1305Key = XChaCha20Poly1305Key
module.exports.encrypt = encrypt
module.exports.decrypt = decrypt
module.exports.LanguageCode = LanguageCode
Expand Down
134 changes: 133 additions & 1 deletion ironfish-rust-nodejs/src/xchacha20poly1305.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,144 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use ironfish::xchacha20poly1305::{self, EncryptOutput};
use ironfish::xchacha20poly1305::{
self, EncryptOutput, XChaCha20Poly1305Key, KEY_LENGTH as KEY_SIZE, SALT_LENGTH as SALT_SIZE,
XNONCE_LENGTH as XNONCE_SIZE,
};
use napi::{bindgen_prelude::*, JsBuffer};
use napi_derive::napi;

use crate::to_napi_err;

#[napi]
pub const XCHACHA20POLY1305_KEY_LENGTH: u32 = KEY_SIZE as u32;

#[napi]
pub const SALT_LENGTH: u32 = SALT_SIZE as u32;

#[napi]
pub const XNONCE_LENGTH: u32 = XNONCE_SIZE as u32;

#[napi(js_name = "XChaCha20Poly1305Key")]
pub struct NativeXChaCha20Poly1305Key {
pub(crate) key: XChaCha20Poly1305Key,
}

#[napi]
impl NativeXChaCha20Poly1305Key {
#[napi(constructor)]
pub fn generate(passphrase: String) -> Result<NativeXChaCha20Poly1305Key> {
let key = XChaCha20Poly1305Key::generate(passphrase.as_bytes()).map_err(to_napi_err)?;

Ok(NativeXChaCha20Poly1305Key { key })
}

#[napi]
pub fn from_parts(
passphrase: String,
salt: JsBuffer,
nonce: JsBuffer,
) -> Result<NativeXChaCha20Poly1305Key> {
let salt_buffer = salt.into_value()?;
let salt_vec = salt_buffer.as_ref();
let mut salt_bytes = [0u8; SALT_SIZE];
salt_bytes.clone_from_slice(&salt_vec[0..SALT_SIZE]);

let nonce_buffer = nonce.into_value()?;
let nonce_vec = nonce_buffer.as_ref();
let mut nonce_bytes = [0; XNONCE_SIZE];
nonce_bytes.clone_from_slice(&nonce_vec[0..XNONCE_SIZE]);

let key = XChaCha20Poly1305Key::from_parts(passphrase.as_bytes(), salt_bytes, nonce_bytes)
.map_err(to_napi_err)?;

Ok(NativeXChaCha20Poly1305Key { key })
}

#[napi]
pub fn derive_key(
&self,
salt: JsBuffer,
nonce: JsBuffer,
) -> Result<NativeXChaCha20Poly1305Key> {
let salt_buffer = salt.into_value()?;
let salt_vec = salt_buffer.as_ref();
let mut salt_bytes = [0; SALT_SIZE];
salt_bytes.clone_from_slice(&salt_vec[0..SALT_SIZE]);

let derived_key = self.key.derive_key(salt_bytes).map_err(to_napi_err)?;

let nonce_buffer = nonce.into_value()?;
let nonce_vec = nonce_buffer.as_ref();
let mut nonce_bytes = [0; XNONCE_SIZE];
nonce_bytes.clone_from_slice(&nonce_vec[0..XNONCE_SIZE]);

let key = XChaCha20Poly1305Key {
key: derived_key,
nonce: nonce_bytes,
salt: salt_bytes,
};

Ok(NativeXChaCha20Poly1305Key { key })
}

#[napi]
pub fn derive_new_key(&self) -> Result<NativeXChaCha20Poly1305Key> {
let key = self.key.derive_new_key().map_err(to_napi_err)?;

Ok(NativeXChaCha20Poly1305Key { key })
}

#[napi(factory)]
pub fn deserialize(js_bytes: JsBuffer) -> Result<Self> {
let byte_vec = js_bytes.into_value()?;

let key = XChaCha20Poly1305Key::read(byte_vec.as_ref()).map_err(to_napi_err)?;

Ok(NativeXChaCha20Poly1305Key { key })
}

#[napi]
pub fn destroy(&mut self) -> Result<()> {
self.key.destroy();
Ok(())
}

#[napi]
pub fn salt(&self) -> Buffer {
Buffer::from(self.key.salt.to_vec())
}

#[napi]
pub fn nonce(&self) -> Buffer {
Buffer::from(self.key.nonce.to_vec())
}

#[napi]
pub fn key(&self) -> Buffer {
Buffer::from(self.key.key.to_vec())
}

#[napi]
pub fn encrypt(&self, plaintext: JsBuffer) -> Result<Buffer> {
let plaintext_bytes = plaintext.into_value()?;
let result = self
.key
.encrypt(plaintext_bytes.as_ref())
.map_err(to_napi_err)?;

Ok(Buffer::from(&result[..]))
}

#[napi]
pub fn decrypt(&self, ciphertext: JsBuffer) -> Result<Buffer> {
let byte_vec = ciphertext.into_value()?;
let result = self.key.decrypt(byte_vec.to_vec()).map_err(to_napi_err)?;

Ok(Buffer::from(&result[..]))
}
}

#[napi]
pub fn encrypt(plaintext: JsBuffer, passphrase: String) -> Result<Buffer> {
let plaintext_bytes = plaintext.into_value()?;
Expand Down

0 comments on commit 97c2478

Please sign in to comment.