Skip to content

Commit 5ef3368

Browse files
authored
feat(rust-nodejs): Create napi struct for xchacha20poly1305 key (#5370)
* feat(rust-nodejs): Create napi struct for xchacha20poly1305 key * feat(ironfish): Add xchacha namespace * feat(ironfish): Add X
1 parent 5ea1113 commit 5ef3368

File tree

3 files changed

+154
-2
lines changed

3 files changed

+154
-2
lines changed

ironfish-rust-nodejs/index.d.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,3 +365,22 @@ export namespace multisig {
365365
signers(): Array<Buffer>
366366
}
367367
}
368+
export namespace xchacha20poly1305 {
369+
export const XKEY_LENGTH: number
370+
export const XSALT_LENGTH: number
371+
export const XNONCE_LENGTH: number
372+
export type NativeXChaCha20Poly1305Key = XChaCha20Poly1305Key
373+
export class XChaCha20Poly1305Key {
374+
constructor(passphrase: string)
375+
static fromParts(passphrase: string, salt: Buffer, nonce: Buffer): XChaCha20Poly1305Key
376+
deriveKey(salt: Buffer, nonce: Buffer): XChaCha20Poly1305Key
377+
deriveNewKey(): XChaCha20Poly1305Key
378+
static deserialize(jsBytes: Buffer): NativeXChaCha20Poly1305Key
379+
destroy(): void
380+
salt(): Buffer
381+
nonce(): Buffer
382+
key(): Buffer
383+
encrypt(plaintext: Buffer): Buffer
384+
decrypt(ciphertext: Buffer): Buffer
385+
}
386+
}

ironfish-rust-nodejs/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ if (!nativeBinding) {
252252
throw new Error(`Failed to load native binding`)
253253
}
254254

255-
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
255+
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
256256

257257
module.exports.FishHashContext = FishHashContext
258258
module.exports.deserializePublicPackage = deserializePublicPackage
@@ -308,3 +308,4 @@ module.exports.CpuCount = CpuCount
308308
module.exports.getCpuCount = getCpuCount
309309
module.exports.generateRandomizedPublicKey = generateRandomizedPublicKey
310310
module.exports.multisig = multisig
311+
module.exports.xchacha20poly1305 = xchacha20poly1305

ironfish-rust-nodejs/src/xchacha20poly1305.rs

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,144 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
44

5-
use ironfish::xchacha20poly1305::{self, EncryptOutput};
5+
use ironfish::xchacha20poly1305::{
6+
self, EncryptOutput, XChaCha20Poly1305Key, KEY_LENGTH as KEY_SIZE, SALT_LENGTH as SALT_SIZE,
7+
XNONCE_LENGTH as XNONCE_SIZE,
8+
};
69
use napi::{bindgen_prelude::*, JsBuffer};
710
use napi_derive::napi;
811

912
use crate::to_napi_err;
1013

14+
#[napi{namespace = "xchacha20poly1305"}]
15+
pub const XKEY_LENGTH: u32 = KEY_SIZE as u32;
16+
17+
#[napi{namespace = "xchacha20poly1305"}]
18+
pub const XSALT_LENGTH: u32 = SALT_SIZE as u32;
19+
20+
#[napi{namespace = "xchacha20poly1305"}]
21+
pub const XNONCE_LENGTH: u32 = XNONCE_SIZE as u32;
22+
23+
#[napi(js_name = "XChaCha20Poly1305Key", namespace = "xchacha20poly1305")]
24+
pub struct NativeXChaCha20Poly1305Key {
25+
pub(crate) key: XChaCha20Poly1305Key,
26+
}
27+
28+
#[napi{namespace = "xchacha20poly1305"}]
29+
impl NativeXChaCha20Poly1305Key {
30+
#[napi(constructor)]
31+
pub fn generate(passphrase: String) -> Result<NativeXChaCha20Poly1305Key> {
32+
let key = XChaCha20Poly1305Key::generate(passphrase.as_bytes()).map_err(to_napi_err)?;
33+
34+
Ok(NativeXChaCha20Poly1305Key { key })
35+
}
36+
37+
#[napi]
38+
pub fn from_parts(
39+
passphrase: String,
40+
salt: JsBuffer,
41+
nonce: JsBuffer,
42+
) -> Result<NativeXChaCha20Poly1305Key> {
43+
let salt_buffer = salt.into_value()?;
44+
let salt_vec = salt_buffer.as_ref();
45+
let mut salt_bytes = [0u8; SALT_SIZE];
46+
salt_bytes.clone_from_slice(&salt_vec[0..SALT_SIZE]);
47+
48+
let nonce_buffer = nonce.into_value()?;
49+
let nonce_vec = nonce_buffer.as_ref();
50+
let mut nonce_bytes = [0; XNONCE_SIZE];
51+
nonce_bytes.clone_from_slice(&nonce_vec[0..XNONCE_SIZE]);
52+
53+
let key = XChaCha20Poly1305Key::from_parts(passphrase.as_bytes(), salt_bytes, nonce_bytes)
54+
.map_err(to_napi_err)?;
55+
56+
Ok(NativeXChaCha20Poly1305Key { key })
57+
}
58+
59+
#[napi]
60+
pub fn derive_key(
61+
&self,
62+
salt: JsBuffer,
63+
nonce: JsBuffer,
64+
) -> Result<NativeXChaCha20Poly1305Key> {
65+
let salt_buffer = salt.into_value()?;
66+
let salt_vec = salt_buffer.as_ref();
67+
let mut salt_bytes = [0; SALT_SIZE];
68+
salt_bytes.clone_from_slice(&salt_vec[0..SALT_SIZE]);
69+
70+
let derived_key = self.key.derive_key(salt_bytes).map_err(to_napi_err)?;
71+
72+
let nonce_buffer = nonce.into_value()?;
73+
let nonce_vec = nonce_buffer.as_ref();
74+
let mut nonce_bytes = [0; XNONCE_SIZE];
75+
nonce_bytes.clone_from_slice(&nonce_vec[0..XNONCE_SIZE]);
76+
77+
let key = XChaCha20Poly1305Key {
78+
key: derived_key,
79+
nonce: nonce_bytes,
80+
salt: salt_bytes,
81+
};
82+
83+
Ok(NativeXChaCha20Poly1305Key { key })
84+
}
85+
86+
#[napi]
87+
pub fn derive_new_key(&self) -> Result<NativeXChaCha20Poly1305Key> {
88+
let key = self.key.derive_new_key().map_err(to_napi_err)?;
89+
90+
Ok(NativeXChaCha20Poly1305Key { key })
91+
}
92+
93+
#[napi(factory)]
94+
pub fn deserialize(js_bytes: JsBuffer) -> Result<Self> {
95+
let byte_vec = js_bytes.into_value()?;
96+
97+
let key = XChaCha20Poly1305Key::read(byte_vec.as_ref()).map_err(to_napi_err)?;
98+
99+
Ok(NativeXChaCha20Poly1305Key { key })
100+
}
101+
102+
#[napi]
103+
pub fn destroy(&mut self) -> Result<()> {
104+
self.key.destroy();
105+
Ok(())
106+
}
107+
108+
#[napi]
109+
pub fn salt(&self) -> Buffer {
110+
Buffer::from(self.key.salt.to_vec())
111+
}
112+
113+
#[napi]
114+
pub fn nonce(&self) -> Buffer {
115+
Buffer::from(self.key.nonce.to_vec())
116+
}
117+
118+
#[napi]
119+
pub fn key(&self) -> Buffer {
120+
Buffer::from(self.key.key.to_vec())
121+
}
122+
123+
#[napi]
124+
pub fn encrypt(&self, plaintext: JsBuffer) -> Result<Buffer> {
125+
let plaintext_bytes = plaintext.into_value()?;
126+
let result = self
127+
.key
128+
.encrypt(plaintext_bytes.as_ref())
129+
.map_err(to_napi_err)?;
130+
131+
Ok(Buffer::from(&result[..]))
132+
}
133+
134+
#[napi]
135+
pub fn decrypt(&self, ciphertext: JsBuffer) -> Result<Buffer> {
136+
let byte_vec = ciphertext.into_value()?;
137+
let result = self.key.decrypt(byte_vec.to_vec()).map_err(to_napi_err)?;
138+
139+
Ok(Buffer::from(&result[..]))
140+
}
141+
}
142+
11143
#[napi]
12144
pub fn encrypt(plaintext: JsBuffer, passphrase: String) -> Result<Buffer> {
13145
let plaintext_bytes = plaintext.into_value()?;

0 commit comments

Comments
 (0)