Skip to content

Commit

Permalink
Merge pull request #2828 from TheBlueMatt/2024-01-crypto-module
Browse files Browse the repository at this point in the history
Move cryptographic algorithms and utilities to a new `crypto` mod
  • Loading branch information
valentinewallace authored Jan 17, 2024
2 parents fbeb7ac + 4a0abd5 commit a175958
Show file tree
Hide file tree
Showing 22 changed files with 482 additions and 472 deletions.
2 changes: 1 addition & 1 deletion lightning/src/blinded_path/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::ln::onion_utils;
use crate::onion_message::packet::ControlTlvs;
use crate::prelude::*;
use crate::sign::{NodeSigner, Recipient};
use crate::util::chacha20poly1305rfc::ChaChaPolyReadAdapter;
use crate::crypto::streams::ChaChaPolyReadAdapter;
use crate::util::ser::{FixedLengthReader, LengthReadableArgs, Writeable, Writer};

use core::mem;
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/blinded_path/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use super::{BlindedHop, BlindedPath};
use crate::ln::msgs::DecodeError;
use crate::ln::onion_utils;
use crate::onion_message::messenger::Destination;
use crate::util::chacha20poly1305rfc::ChaChaPolyWriteAdapter;
use crate::crypto::streams::ChaChaPolyWriteAdapter;
use crate::util::ser::{Readable, Writeable};

use crate::io;
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/chain/channelmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4808,7 +4808,7 @@ mod tests {
preimages_slice_to_htlcs!($preimages_slice).into_iter().map(|(htlc, _)| (htlc, None)).collect()
}
}
let dummy_sig = crate::util::crypto::sign(&secp_ctx,
let dummy_sig = crate::crypto::utils::sign(&secp_ctx,
&bitcoin::secp256k1::Message::from_slice(&[42; 32]).unwrap(),
&SecretKey::from_slice(&[42; 32]).unwrap());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
// You may not use this file except in accordance with one or both of these
// licenses.

use crate::io;

#[cfg(not(fuzzing))]
mod real_chacha {
use core::cmp;
Expand Down Expand Up @@ -335,27 +333,14 @@ mod fuzzy_chacha {
#[cfg(fuzzing)]
pub use self::fuzzy_chacha::ChaCha20;

pub(crate) struct ChaChaReader<'a, R: io::Read> {
pub chacha: &'a mut ChaCha20,
pub read: R,
}
impl<'a, R: io::Read> io::Read for ChaChaReader<'a, R> {
fn read(&mut self, dest: &mut [u8]) -> Result<usize, io::Error> {
let res = self.read.read(dest)?;
if res > 0 {
self.chacha.process_in_place(&mut dest[0..res]);
}
Ok(res)
}
}

#[cfg(test)]
mod test {
use crate::prelude::*;
use alloc::vec;
use alloc::vec::{Vec};
use core::convert::TryInto;
use core::iter::repeat;

use super::ChaCha20;
use std::convert::TryInto;

#[test]
fn test_chacha20_256_tls_vectors() {
Expand Down
237 changes: 237 additions & 0 deletions lightning/src/crypto/chacha20poly1305rfc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
// ring has a garbage API so its use is avoided, but rust-crypto doesn't have RFC-variant poly1305
// Instead, we steal rust-crypto's implementation and tweak it to match the RFC.
//
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// You may not use this file except in accordance with one or both of these
// licenses.
//
// This is a port of Andrew Moons poly1305-donna
// https://github.com/floodyberry/poly1305-donna

#[cfg(not(fuzzing))]
mod real_chachapoly {
use super::super::chacha20::ChaCha20;
use super::super::poly1305::Poly1305;
use super::super::fixed_time_eq;

#[derive(Clone, Copy)]
pub struct ChaCha20Poly1305RFC {
cipher: ChaCha20,
mac: Poly1305,
finished: bool,
data_len: usize,
aad_len: u64,
}

impl ChaCha20Poly1305RFC {
#[inline]
fn pad_mac_16(mac: &mut Poly1305, len: usize) {
if len % 16 != 0 {
mac.input(&[0; 16][0..16 - (len % 16)]);
}
}
pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC {
assert!(key.len() == 16 || key.len() == 32);
assert!(nonce.len() == 12);

// Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);

let mut cipher = ChaCha20::new(key, &nonce[4..]);
let mut mac_key = [0u8; 64];
let zero_key = [0u8; 64];
cipher.process(&zero_key, &mut mac_key);

let mut mac = Poly1305::new(&mac_key[..32]);
mac.input(aad);
ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len());

ChaCha20Poly1305RFC {
cipher,
mac,
finished: false,
data_len: 0,
aad_len: aad.len() as u64,
}
}

pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
assert!(input.len() == output.len());
assert!(self.finished == false);
self.cipher.process(input, output);
self.data_len += input.len();
self.mac.input(output);
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
self.finished = true;
self.mac.input(&self.aad_len.to_le_bytes());
self.mac.input(&(self.data_len as u64).to_le_bytes());
self.mac.raw_result(out_tag);
}

pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) {
self.encrypt_in_place(input_output);
self.finish_and_get_tag(out_tag);
}

// Encrypt `input_output` in-place. To finish and calculate the tag, use `finish_and_get_tag`
// below.
pub(in super::super) fn encrypt_in_place(&mut self, input_output: &mut [u8]) {
debug_assert!(self.finished == false);
self.cipher.process_in_place(input_output);
self.data_len += input_output.len();
self.mac.input(input_output);
}

// If we were previously encrypting with `encrypt_in_place`, this method can be used to finish
// encrypting and calculate the tag.
pub(in super::super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) {
debug_assert!(self.finished == false);
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
self.finished = true;
self.mac.input(&self.aad_len.to_le_bytes());
self.mac.input(&(self.data_len as u64).to_le_bytes());
self.mac.raw_result(out_tag);
}

/// Decrypt the `input`, checking the given `tag` prior to writing the decrypted contents
/// into `output`. Note that, because `output` is not touched until the `tag` is checked,
/// this decryption is *variable time*.
pub fn variable_time_decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
assert!(input.len() == output.len());
assert!(self.finished == false);

self.finished = true;

self.mac.input(input);

self.data_len += input.len();
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
self.mac.input(&self.aad_len.to_le_bytes());
self.mac.input(&(self.data_len as u64).to_le_bytes());

let mut calc_tag = [0u8; 16];
self.mac.raw_result(&mut calc_tag);
if fixed_time_eq(&calc_tag, tag) {
self.cipher.process(input, output);
Ok(())
} else {
Err(())
}
}

pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
self.decrypt_in_place(input_output);
if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) }
}

/// Decrypt in place, without checking the tag. Use `finish_and_check_tag` to check it
/// later when decryption finishes.
///
/// Should never be `pub` because the public API should always enforce tag checking.
pub(in super::super) fn decrypt_in_place(&mut self, input_output: &mut [u8]) {
debug_assert!(self.finished == false);
self.mac.input(input_output);
self.data_len += input_output.len();
self.cipher.process_in_place(input_output);
}

/// If we were previously decrypting with `just_decrypt_in_place`, this method must be used
/// to check the tag. Returns whether or not the tag is valid.
pub(in super::super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool {
debug_assert!(self.finished == false);
self.finished = true;
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
self.mac.input(&self.aad_len.to_le_bytes());
self.mac.input(&(self.data_len as u64).to_le_bytes());

let mut calc_tag = [0u8; 16];
self.mac.raw_result(&mut calc_tag);
if fixed_time_eq(&calc_tag, tag) {
true
} else {
false
}
}
}
}
#[cfg(not(fuzzing))]
pub use self::real_chachapoly::ChaCha20Poly1305RFC;

#[cfg(fuzzing)]
mod fuzzy_chachapoly {
#[derive(Clone, Copy)]
pub struct ChaCha20Poly1305RFC {
tag: [u8; 16],
finished: bool,
}
impl ChaCha20Poly1305RFC {
pub fn new(key: &[u8], nonce: &[u8], _aad: &[u8]) -> ChaCha20Poly1305RFC {
assert!(key.len() == 16 || key.len() == 32);
assert!(nonce.len() == 12);

// Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);

let mut tag = [0; 16];
tag.copy_from_slice(&key[0..16]);

ChaCha20Poly1305RFC {
tag,
finished: false,
}
}

pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
assert!(input.len() == output.len());
assert!(self.finished == false);

output.copy_from_slice(&input);
out_tag.copy_from_slice(&self.tag);
self.finished = true;
}

pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) {
self.encrypt_in_place(input_output);
self.finish_and_get_tag(out_tag);
}

pub(in super::super) fn encrypt_in_place(&mut self, _input_output: &mut [u8]) {
assert!(self.finished == false);
}

pub(in super::super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) {
assert!(self.finished == false);
out_tag.copy_from_slice(&self.tag);
self.finished = true;
}

pub fn variable_time_decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
assert!(input.len() == output.len());
assert!(self.finished == false);

if tag[..] != self.tag[..] { return Err(()); }
output.copy_from_slice(input);
self.finished = true;
Ok(())
}

pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
self.decrypt_in_place(input_output);
if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) }
}

pub(in super::super) fn decrypt_in_place(&mut self, _input: &mut [u8]) {
assert!(self.finished == false);
}

pub(in super::super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool {
if tag[..] != self.tag[..] { return false; }
self.finished = true;
true
}
}
}
#[cfg(fuzzing)]
pub use self::fuzzy_chachapoly::ChaCha20Poly1305RFC;
8 changes: 8 additions & 0 deletions lightning/src/crypto/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use bitcoin::hashes::cmp::fixed_time_eq;

pub(crate) mod chacha20;
#[cfg(not(fuzzing))]
pub(crate) mod poly1305;
pub(crate) mod chacha20poly1305rfc;
pub(crate) mod streams;
pub(crate) mod utils;
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,10 @@ impl Poly1305 {

#[cfg(test)]
mod test {
use crate::prelude::*;
use core::iter::repeat;
use alloc::vec::Vec;

use crate::util::poly1305::Poly1305;
use super::Poly1305;

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

0 comments on commit a175958

Please sign in to comment.