Skip to content

Commit

Permalink
feat(rust-nodejs): Create napi struct for xchacha20poly1305 key (#5370)
Browse files Browse the repository at this point in the history
* feat(rust-nodejs): Create napi struct for xchacha20poly1305 key

* feat(ironfish): Add xchacha namespace

* feat(ironfish): Add X
  • Loading branch information
rohanjadvani authored Sep 13, 2024
1 parent 5ea1113 commit 5ef3368
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 2 deletions.
19 changes: 19 additions & 0 deletions ironfish-rust-nodejs/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,3 +365,22 @@ export namespace multisig {
signers(): Array<Buffer>
}
}
export namespace xchacha20poly1305 {
export const XKEY_LENGTH: number
export const XSALT_LENGTH: number
export const XNONCE_LENGTH: number
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
}
}
3 changes: 2 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, deserializePublicPackage, deserializeRound2CombinedPublicPackage, 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, deserializePublicPackage, deserializeRound2CombinedPublicPackage, 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, xchacha20poly1305 } = nativeBinding

module.exports.FishHashContext = FishHashContext
module.exports.deserializePublicPackage = deserializePublicPackage
Expand Down Expand Up @@ -308,3 +308,4 @@ module.exports.CpuCount = CpuCount
module.exports.getCpuCount = getCpuCount
module.exports.generateRandomizedPublicKey = generateRandomizedPublicKey
module.exports.multisig = multisig
module.exports.xchacha20poly1305 = xchacha20poly1305
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{namespace = "xchacha20poly1305"}]
pub const XKEY_LENGTH: u32 = KEY_SIZE as u32;

#[napi{namespace = "xchacha20poly1305"}]
pub const XSALT_LENGTH: u32 = SALT_SIZE as u32;

#[napi{namespace = "xchacha20poly1305"}]
pub const XNONCE_LENGTH: u32 = XNONCE_SIZE as u32;

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

#[napi{namespace = "xchacha20poly1305"}]
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 5ef3368

Please sign in to comment.