Skip to content

Commit a175958

Browse files
Merge pull request #2828 from TheBlueMatt/2024-01-crypto-module
Move cryptographic algorithms and utilities to a new `crypto` mod
2 parents fbeb7ac + 4a0abd5 commit a175958

22 files changed

+482
-472
lines changed

lightning/src/blinded_path/message.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::ln::onion_utils;
88
use crate::onion_message::packet::ControlTlvs;
99
use crate::prelude::*;
1010
use crate::sign::{NodeSigner, Recipient};
11-
use crate::util::chacha20poly1305rfc::ChaChaPolyReadAdapter;
11+
use crate::crypto::streams::ChaChaPolyReadAdapter;
1212
use crate::util::ser::{FixedLengthReader, LengthReadableArgs, Writeable, Writer};
1313

1414
use core::mem;

lightning/src/blinded_path/utils.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use super::{BlindedHop, BlindedPath};
1919
use crate::ln::msgs::DecodeError;
2020
use crate::ln::onion_utils;
2121
use crate::onion_message::messenger::Destination;
22-
use crate::util::chacha20poly1305rfc::ChaChaPolyWriteAdapter;
22+
use crate::crypto::streams::ChaChaPolyWriteAdapter;
2323
use crate::util::ser::{Readable, Writeable};
2424

2525
use crate::io;

lightning/src/chain/channelmonitor.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4808,7 +4808,7 @@ mod tests {
48084808
preimages_slice_to_htlcs!($preimages_slice).into_iter().map(|(htlc, _)| (htlc, None)).collect()
48094809
}
48104810
}
4811-
let dummy_sig = crate::util::crypto::sign(&secp_ctx,
4811+
let dummy_sig = crate::crypto::utils::sign(&secp_ctx,
48124812
&bitcoin::secp256k1::Message::from_slice(&[42; 32]).unwrap(),
48134813
&SecretKey::from_slice(&[42; 32]).unwrap());
48144814

lightning/src/util/chacha20.rs renamed to lightning/src/crypto/chacha20.rs

+3-18
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
// You may not use this file except in accordance with one or both of these
1010
// licenses.
1111

12-
use crate::io;
13-
1412
#[cfg(not(fuzzing))]
1513
mod real_chacha {
1614
use core::cmp;
@@ -335,27 +333,14 @@ mod fuzzy_chacha {
335333
#[cfg(fuzzing)]
336334
pub use self::fuzzy_chacha::ChaCha20;
337335

338-
pub(crate) struct ChaChaReader<'a, R: io::Read> {
339-
pub chacha: &'a mut ChaCha20,
340-
pub read: R,
341-
}
342-
impl<'a, R: io::Read> io::Read for ChaChaReader<'a, R> {
343-
fn read(&mut self, dest: &mut [u8]) -> Result<usize, io::Error> {
344-
let res = self.read.read(dest)?;
345-
if res > 0 {
346-
self.chacha.process_in_place(&mut dest[0..res]);
347-
}
348-
Ok(res)
349-
}
350-
}
351-
352336
#[cfg(test)]
353337
mod test {
354-
use crate::prelude::*;
338+
use alloc::vec;
339+
use alloc::vec::{Vec};
340+
use core::convert::TryInto;
355341
use core::iter::repeat;
356342

357343
use super::ChaCha20;
358-
use std::convert::TryInto;
359344

360345
#[test]
361346
fn test_chacha20_256_tls_vectors() {
+237
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
// ring has a garbage API so its use is avoided, but rust-crypto doesn't have RFC-variant poly1305
2+
// Instead, we steal rust-crypto's implementation and tweak it to match the RFC.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
//
10+
// This is a port of Andrew Moons poly1305-donna
11+
// https://github.com/floodyberry/poly1305-donna
12+
13+
#[cfg(not(fuzzing))]
14+
mod real_chachapoly {
15+
use super::super::chacha20::ChaCha20;
16+
use super::super::poly1305::Poly1305;
17+
use super::super::fixed_time_eq;
18+
19+
#[derive(Clone, Copy)]
20+
pub struct ChaCha20Poly1305RFC {
21+
cipher: ChaCha20,
22+
mac: Poly1305,
23+
finished: bool,
24+
data_len: usize,
25+
aad_len: u64,
26+
}
27+
28+
impl ChaCha20Poly1305RFC {
29+
#[inline]
30+
fn pad_mac_16(mac: &mut Poly1305, len: usize) {
31+
if len % 16 != 0 {
32+
mac.input(&[0; 16][0..16 - (len % 16)]);
33+
}
34+
}
35+
pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC {
36+
assert!(key.len() == 16 || key.len() == 32);
37+
assert!(nonce.len() == 12);
38+
39+
// Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
40+
assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
41+
42+
let mut cipher = ChaCha20::new(key, &nonce[4..]);
43+
let mut mac_key = [0u8; 64];
44+
let zero_key = [0u8; 64];
45+
cipher.process(&zero_key, &mut mac_key);
46+
47+
let mut mac = Poly1305::new(&mac_key[..32]);
48+
mac.input(aad);
49+
ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len());
50+
51+
ChaCha20Poly1305RFC {
52+
cipher,
53+
mac,
54+
finished: false,
55+
data_len: 0,
56+
aad_len: aad.len() as u64,
57+
}
58+
}
59+
60+
pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
61+
assert!(input.len() == output.len());
62+
assert!(self.finished == false);
63+
self.cipher.process(input, output);
64+
self.data_len += input.len();
65+
self.mac.input(output);
66+
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
67+
self.finished = true;
68+
self.mac.input(&self.aad_len.to_le_bytes());
69+
self.mac.input(&(self.data_len as u64).to_le_bytes());
70+
self.mac.raw_result(out_tag);
71+
}
72+
73+
pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) {
74+
self.encrypt_in_place(input_output);
75+
self.finish_and_get_tag(out_tag);
76+
}
77+
78+
// Encrypt `input_output` in-place. To finish and calculate the tag, use `finish_and_get_tag`
79+
// below.
80+
pub(in super::super) fn encrypt_in_place(&mut self, input_output: &mut [u8]) {
81+
debug_assert!(self.finished == false);
82+
self.cipher.process_in_place(input_output);
83+
self.data_len += input_output.len();
84+
self.mac.input(input_output);
85+
}
86+
87+
// If we were previously encrypting with `encrypt_in_place`, this method can be used to finish
88+
// encrypting and calculate the tag.
89+
pub(in super::super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) {
90+
debug_assert!(self.finished == false);
91+
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
92+
self.finished = true;
93+
self.mac.input(&self.aad_len.to_le_bytes());
94+
self.mac.input(&(self.data_len as u64).to_le_bytes());
95+
self.mac.raw_result(out_tag);
96+
}
97+
98+
/// Decrypt the `input`, checking the given `tag` prior to writing the decrypted contents
99+
/// into `output`. Note that, because `output` is not touched until the `tag` is checked,
100+
/// this decryption is *variable time*.
101+
pub fn variable_time_decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
102+
assert!(input.len() == output.len());
103+
assert!(self.finished == false);
104+
105+
self.finished = true;
106+
107+
self.mac.input(input);
108+
109+
self.data_len += input.len();
110+
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
111+
self.mac.input(&self.aad_len.to_le_bytes());
112+
self.mac.input(&(self.data_len as u64).to_le_bytes());
113+
114+
let mut calc_tag = [0u8; 16];
115+
self.mac.raw_result(&mut calc_tag);
116+
if fixed_time_eq(&calc_tag, tag) {
117+
self.cipher.process(input, output);
118+
Ok(())
119+
} else {
120+
Err(())
121+
}
122+
}
123+
124+
pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
125+
self.decrypt_in_place(input_output);
126+
if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) }
127+
}
128+
129+
/// Decrypt in place, without checking the tag. Use `finish_and_check_tag` to check it
130+
/// later when decryption finishes.
131+
///
132+
/// Should never be `pub` because the public API should always enforce tag checking.
133+
pub(in super::super) fn decrypt_in_place(&mut self, input_output: &mut [u8]) {
134+
debug_assert!(self.finished == false);
135+
self.mac.input(input_output);
136+
self.data_len += input_output.len();
137+
self.cipher.process_in_place(input_output);
138+
}
139+
140+
/// If we were previously decrypting with `just_decrypt_in_place`, this method must be used
141+
/// to check the tag. Returns whether or not the tag is valid.
142+
pub(in super::super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool {
143+
debug_assert!(self.finished == false);
144+
self.finished = true;
145+
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
146+
self.mac.input(&self.aad_len.to_le_bytes());
147+
self.mac.input(&(self.data_len as u64).to_le_bytes());
148+
149+
let mut calc_tag = [0u8; 16];
150+
self.mac.raw_result(&mut calc_tag);
151+
if fixed_time_eq(&calc_tag, tag) {
152+
true
153+
} else {
154+
false
155+
}
156+
}
157+
}
158+
}
159+
#[cfg(not(fuzzing))]
160+
pub use self::real_chachapoly::ChaCha20Poly1305RFC;
161+
162+
#[cfg(fuzzing)]
163+
mod fuzzy_chachapoly {
164+
#[derive(Clone, Copy)]
165+
pub struct ChaCha20Poly1305RFC {
166+
tag: [u8; 16],
167+
finished: bool,
168+
}
169+
impl ChaCha20Poly1305RFC {
170+
pub fn new(key: &[u8], nonce: &[u8], _aad: &[u8]) -> ChaCha20Poly1305RFC {
171+
assert!(key.len() == 16 || key.len() == 32);
172+
assert!(nonce.len() == 12);
173+
174+
// Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
175+
assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
176+
177+
let mut tag = [0; 16];
178+
tag.copy_from_slice(&key[0..16]);
179+
180+
ChaCha20Poly1305RFC {
181+
tag,
182+
finished: false,
183+
}
184+
}
185+
186+
pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
187+
assert!(input.len() == output.len());
188+
assert!(self.finished == false);
189+
190+
output.copy_from_slice(&input);
191+
out_tag.copy_from_slice(&self.tag);
192+
self.finished = true;
193+
}
194+
195+
pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) {
196+
self.encrypt_in_place(input_output);
197+
self.finish_and_get_tag(out_tag);
198+
}
199+
200+
pub(in super::super) fn encrypt_in_place(&mut self, _input_output: &mut [u8]) {
201+
assert!(self.finished == false);
202+
}
203+
204+
pub(in super::super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) {
205+
assert!(self.finished == false);
206+
out_tag.copy_from_slice(&self.tag);
207+
self.finished = true;
208+
}
209+
210+
pub fn variable_time_decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
211+
assert!(input.len() == output.len());
212+
assert!(self.finished == false);
213+
214+
if tag[..] != self.tag[..] { return Err(()); }
215+
output.copy_from_slice(input);
216+
self.finished = true;
217+
Ok(())
218+
}
219+
220+
pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
221+
self.decrypt_in_place(input_output);
222+
if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) }
223+
}
224+
225+
pub(in super::super) fn decrypt_in_place(&mut self, _input: &mut [u8]) {
226+
assert!(self.finished == false);
227+
}
228+
229+
pub(in super::super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool {
230+
if tag[..] != self.tag[..] { return false; }
231+
self.finished = true;
232+
true
233+
}
234+
}
235+
}
236+
#[cfg(fuzzing)]
237+
pub use self::fuzzy_chachapoly::ChaCha20Poly1305RFC;

lightning/src/crypto/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use bitcoin::hashes::cmp::fixed_time_eq;
2+
3+
pub(crate) mod chacha20;
4+
#[cfg(not(fuzzing))]
5+
pub(crate) mod poly1305;
6+
pub(crate) mod chacha20poly1305rfc;
7+
pub(crate) mod streams;
8+
pub(crate) mod utils;

lightning/src/util/poly1305.rs renamed to lightning/src/crypto/poly1305.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -205,10 +205,10 @@ impl Poly1305 {
205205

206206
#[cfg(test)]
207207
mod test {
208-
use crate::prelude::*;
209208
use core::iter::repeat;
209+
use alloc::vec::Vec;
210210

211-
use crate::util::poly1305::Poly1305;
211+
use super::Poly1305;
212212

213213
fn poly1305(key: &[u8], msg: &[u8], mac: &mut [u8]) {
214214
let mut poly = Poly1305::new(key);

0 commit comments

Comments
 (0)