Skip to content

Commit 2843289

Browse files
committed
perf: Remove Vec from chacha::encrypt/decrypt
In order to limit perf/allocation changes from master, remove the last use of Vec from the handshake code. The decrypt/encrypt code now takes an out parameter to place the decrypted bytes. All handshake users know the fixed-size of the output decryption so they can use arrays. The Conduit code will still allocate a Vec based on the input message and pass it through. After this patch, the handshake code is now Vec-less and copies are minimized.
1 parent 5f69b6a commit 2843289

File tree

3 files changed

+29
-35
lines changed

3 files changed

+29
-35
lines changed

lightning/src/ln/peers/chacha.rs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,18 @@ use util::chacha20poly1305rfc::ChaCha20Poly1305RFC;
33

44
pub const TAG_SIZE: usize = 16;
55

6-
pub fn encrypt(key: &[u8], nonce: u64, associated_data: &[u8], plaintext: &[u8]) -> Vec<u8> {
6+
pub fn encrypt(key: &[u8], nonce: u64, associated_data: &[u8], plaintext: &[u8], ciphertext_out: &mut [u8]) {
77
let mut nonce_bytes = [0; 12];
88
nonce_bytes[4..].copy_from_slice(&byte_utils::le64_to_array(nonce));
99

1010
let mut chacha = ChaCha20Poly1305RFC::new(key, &nonce_bytes, associated_data);
11-
let mut ciphertext = vec![0u8; plaintext.len()];
1211
let mut authentication_tag = [0u8; 16];
13-
chacha.encrypt(plaintext, &mut ciphertext, &mut authentication_tag);
12+
chacha.encrypt(plaintext, &mut ciphertext_out[..plaintext.len()], &mut authentication_tag);
1413

15-
let mut tagged_ciphertext = ciphertext;
16-
tagged_ciphertext.extend_from_slice(&authentication_tag);
17-
tagged_ciphertext
14+
ciphertext_out[plaintext.len()..].copy_from_slice(&authentication_tag);
1815
}
1916

20-
pub fn decrypt(key: &[u8], nonce: u64, associated_data: &[u8], tagged_ciphertext: &[u8]) -> Result<Vec<u8>, String> {
17+
pub fn decrypt(key: &[u8], nonce: u64, associated_data: &[u8], tagged_ciphertext: &[u8], plaintext_out: &mut [u8]) -> Result<(), String> {
2118
let mut nonce_bytes = [0; 12];
2219
nonce_bytes[4..].copy_from_slice(&byte_utils::le64_to_array(nonce));
2320

@@ -27,14 +24,14 @@ pub fn decrypt(key: &[u8], nonce: u64, associated_data: &[u8], tagged_ciphertext
2724
}
2825
let end_index = length - 16;
2926
let ciphertext = &tagged_ciphertext[0..end_index];
30-
let authentication_tag = &tagged_ciphertext[end_index..length];
27+
let authentication_tag = &tagged_ciphertext[end_index..];
3128

3229
let mut chacha = ChaCha20Poly1305RFC::new(key, &nonce_bytes, associated_data);
33-
let mut plaintext = vec![0u8; length - 16];
34-
let success = chacha.decrypt(ciphertext, &mut plaintext, authentication_tag);
35-
if success {
36-
Ok(plaintext.to_vec())
37-
} else {
30+
let success = chacha.decrypt(ciphertext, plaintext_out, authentication_tag);
31+
32+
if !success {
3833
Err("invalid hmac".to_string())
34+
} else {
35+
Ok(())
3936
}
4037
}

lightning/src/ln/peers/conduit.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,10 @@ impl Encryptor {
119119

120120
let mut ciphertext = vec![0u8; TAGGED_MESSAGE_LENGTH_HEADER_SIZE + length as usize + chacha::TAG_SIZE];
121121

122-
ciphertext[0..TAGGED_MESSAGE_LENGTH_HEADER_SIZE].copy_from_slice(&chacha::encrypt(&self.sending_key, self.sending_nonce as u64, &[0; 0], &length_bytes));
122+
chacha::encrypt(&self.sending_key, self.sending_nonce as u64, &[0; 0], &length_bytes, &mut ciphertext[..TAGGED_MESSAGE_LENGTH_HEADER_SIZE]);
123123
self.increment_nonce();
124124

125-
ciphertext[TAGGED_MESSAGE_LENGTH_HEADER_SIZE..].copy_from_slice(&chacha::encrypt(&self.sending_key, self.sending_nonce as u64, &[0; 0], buffer));
125+
&chacha::encrypt(&self.sending_key, self.sending_nonce as u64, &[0; 0], buffer, &mut ciphertext[TAGGED_MESSAGE_LENGTH_HEADER_SIZE..]);
126126
self.increment_nonce();
127127

128128
ciphertext
@@ -172,7 +172,7 @@ impl Decryptor {
172172

173173
let encrypted_length = &buffer[0..TAGGED_MESSAGE_LENGTH_HEADER_SIZE];
174174
let mut length_bytes = [0u8; MESSAGE_LENGTH_HEADER_SIZE];
175-
length_bytes.copy_from_slice(&chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_length)?);
175+
chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_length, &mut length_bytes)?;
176176

177177
self.increment_nonce();
178178

@@ -190,8 +190,9 @@ impl Decryptor {
190190
self.pending_message_length = None;
191191

192192
let encrypted_message = &buffer[TAGGED_MESSAGE_LENGTH_HEADER_SIZE..message_end_index];
193+
let mut message = vec![0u8; message_length];
193194

194-
let message = chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_message)?;
195+
chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_message, &mut message)?;
195196

196197
self.increment_nonce();
197198

lightning/src/ln/peers/handshake/states.rs

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -285,12 +285,14 @@ impl IHandshakeState for InitiatorAwaitingActTwoState {
285285
hash,
286286
)?;
287287

288+
let mut act_three = EMPTY_ACT_THREE;
289+
288290
// start serializing act three
289291
// 1. c = encryptWithAD(temp_k2, 1, h, s.pub.serializeCompressed())
290-
let tagged_encrypted_pubkey = chacha::encrypt(&temporary_key, 1, &hash, &initiator_static_public_key.serialize());
292+
chacha::encrypt(&temporary_key, 1, &hash, &initiator_static_public_key.serialize(), &mut act_three[1..50]);
291293

292294
// 2. h = SHA-256(h || c)
293-
let hash = concat_then_sha256!(hash, tagged_encrypted_pubkey);
295+
let hash = concat_then_sha256!(hash, act_three[1..50]);
294296

295297
// 3. se = ECDH(s.priv, re)
296298
let ecdh = ecdh(&initiator_static_private_key, &responder_ephemeral_public_key);
@@ -299,7 +301,7 @@ impl IHandshakeState for InitiatorAwaitingActTwoState {
299301
let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh);
300302

301303
// 5. t = encryptWithAD(temp_k3, 0, h, zero)
302-
let authentication_tag = chacha::encrypt(&temporary_key, 0, &hash, &[0; 0]);
304+
chacha::encrypt(&temporary_key, 0, &hash, &[0; 0], &mut act_three[50..]);
303305

304306
// 6. sk, rk = HKDF(ck, zero)
305307
let (sending_key, receiving_key) = hkdf::derive(&chaining_key, &[0; 0]);
@@ -308,11 +310,6 @@ impl IHandshakeState for InitiatorAwaitingActTwoState {
308310
// - done by Conduit
309311
let conduit = Conduit::new(sending_key, receiving_key, chaining_key);
310312

311-
// Send m = 0 || c || t over the network buffer
312-
let mut act_three = EMPTY_ACT_THREE;
313-
act_three[1..50].copy_from_slice(&tagged_encrypted_pubkey);
314-
act_three[50..].copy_from_slice(&authentication_tag);
315-
316313
Ok((
317314
Some(Act::Three(act_three)),
318315
HandshakeState::Complete(Some((conduit, responder_static_public_key)))
@@ -352,7 +349,7 @@ impl IHandshakeState for ResponderAwaitingActThreeState {
352349
// 2. Parse the read message (m) into v, c, and t
353350
let version = act_three_bytes[0];
354351
let tagged_encrypted_pubkey = &act_three_bytes[1..50];
355-
let chacha_tag = &act_three_bytes[50..66];
352+
let chacha_tag = &act_three_bytes[50..];
356353

357354
// 3. If v is an unrecognized handshake version, then the responder MUST abort the connection attempt.
358355
if version != 0 {
@@ -361,8 +358,9 @@ impl IHandshakeState for ResponderAwaitingActThreeState {
361358
}
362359

363360
// 4. rs = decryptWithAD(temp_k2, 1, h, c)
364-
let remote_pubkey_vec = chacha::decrypt(&temporary_key, 1, &hash, &tagged_encrypted_pubkey)?;
365-
let initiator_pubkey = if let Ok(public_key) = PublicKey::from_slice(remote_pubkey_vec.as_slice()) {
361+
let mut remote_pubkey = [0; 33];
362+
chacha::decrypt(&temporary_key, 1, &hash, &tagged_encrypted_pubkey, &mut remote_pubkey)?;
363+
let initiator_pubkey = if let Ok(public_key) = PublicKey::from_slice(&remote_pubkey) {
366364
public_key
367365
} else {
368366
return Err("invalid remote public key".to_string());
@@ -378,7 +376,7 @@ impl IHandshakeState for ResponderAwaitingActThreeState {
378376
let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh);
379377

380378
// 8. p = decryptWithAD(temp_k3, 0, h, t)
381-
let _tag_check = chacha::decrypt(&temporary_key, 0, &hash, &chacha_tag)?;
379+
chacha::decrypt(&temporary_key, 0, &hash, &chacha_tag, &mut [0; 0])?;
382380

383381
// 9. rk, sk = HKDF(ck, zero)
384382
let (receiving_key, sending_key) = hkdf::derive(&chaining_key, &[0; 0]);
@@ -434,15 +432,13 @@ fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_e
434432

435433
// 5. ACT1: c = encryptWithAD(temp_k1, 0, h, zero)
436434
// 5. ACT2: c = encryptWithAD(temp_k2, 0, h, zero)
437-
let tagged_ciphertext = chacha::encrypt(&temporary_key, 0, &hash, &[0; 0]);
435+
chacha::encrypt(&temporary_key, 0, &hash, &[0; 0], &mut act_out[34..]);
438436

439437
// 6. h = SHA-256(h || c)
440-
let hash = concat_then_sha256!(hash, tagged_ciphertext);
438+
let hash = concat_then_sha256!(hash, &act_out[34..]);
441439

442440
// Send m = 0 || e.pub.serializeCompressed() || c
443-
444441
act_out[1..34].copy_from_slice(&serialized_local_public_key);
445-
act_out[34..50].copy_from_slice(&tagged_ciphertext);
446442

447443
(hash, chaining_key, temporary_key)
448444
}
@@ -455,7 +451,7 @@ fn process_act_message(act_bytes: &[u8], local_private_key: &SecretKey, chaining
455451
// 2.Parse the read message (m) into v, re, and c
456452
let version = act_bytes[0];
457453
let ephemeral_public_key_bytes = &act_bytes[1..34];
458-
let chacha_tag = &act_bytes[34..50];
454+
let chacha_tag = &act_bytes[34..];
459455

460456
let ephemeral_public_key = if let Ok(public_key) = PublicKey::from_slice(&ephemeral_public_key_bytes) {
461457
public_key
@@ -481,7 +477,7 @@ fn process_act_message(act_bytes: &[u8], local_private_key: &SecretKey, chaining
481477
let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh);
482478

483479
// 7. p = decryptWithAD(temp_k1, 0, h, c)
484-
let _tag_check = chacha::decrypt(&temporary_key, 0, &hash, &chacha_tag)?;
480+
chacha::decrypt(&temporary_key, 0, &hash, &chacha_tag, &mut [0; 0])?;
485481

486482
// 8. h = SHA-256(h || c)
487483
let hash = concat_then_sha256!(hash, chacha_tag);

0 commit comments

Comments
 (0)