Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
davidv1992 committed Sep 18, 2024
1 parent e7069b8 commit 1877d2c
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 9 deletions.
2 changes: 1 addition & 1 deletion rustls/src/client/hs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ fn emit_client_hello_for_retry(
}

if let Some(challenge) = retryreq.and_then(|r| r.puzzle_challenge()) {
let solution = challenge.solve().ok_or_else(|| {
let solution = challenge.solve(&config.provider).ok_or_else(|| {
cx.common.send_fatal_alert(
AlertDescription::IllegalParameter,
PeerMisbehaved::IllegalHelloRetryRequestWithUnsolvablePuzzle,
Expand Down
1 change: 1 addition & 0 deletions rustls/src/crypto/aws_lc_rs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub fn default_provider() -> CryptoProvider {
kx_groups: default_kx_groups(),
signature_verification_algorithms: SUPPORTED_SIG_ALGS,
secure_random: &AwsLcRs,
sha256_hasher: &hash::SHA256,
key_provider: &AwsLcRs,
}
}
Expand Down
3 changes: 2 additions & 1 deletion rustls/src/crypto/hash.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use alloc::boxed::Box;
use core::fmt::Debug;

pub use crate::msgs::enums::HashAlgorithm;

/// Describes a single cryptographic hash function.
///
/// This interface can do both one-shot and incremental hashing, using
/// [`Hash::hash()`] and [`Hash::start()`] respectively.
pub trait Hash: Send + Sync {
pub trait Hash: Send + Sync + Debug {
/// Start an incremental hash computation.
fn start(&self) -> Box<dyn Context>;

Expand Down
5 changes: 5 additions & 0 deletions rustls/src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use once_cell::sync::OnceCell;
use pki_types::PrivateKeyDer;
use zeroize::Zeroize;

use crate::crypto::hash::Hash;
use crate::sign::SigningKey;
pub use crate::webpki::{
verify_tls12_signature, verify_tls13_signature, WebPkiSupportedAlgorithms,
Expand Down Expand Up @@ -209,6 +210,9 @@ pub struct CryptoProvider {
/// Source of cryptographically secure random numbers.
pub secure_random: &'static dyn SecureRandom,

/// Sha256 hasher
pub sha256_hasher: &'static dyn Hash,

/// Provider for loading private [SigningKey]s from [PrivateKeyDer].
pub key_provider: &'static dyn KeyProvider,
}
Expand Down Expand Up @@ -295,6 +299,7 @@ impl CryptoProvider {
signature_verification_algorithms,
secure_random,
key_provider,
..
} = self;
cipher_suites.iter().all(|cs| cs.fips())
&& kx_groups.iter().all(|kx| kx.fips())
Expand Down
1 change: 1 addition & 0 deletions rustls/src/crypto/ring/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::msgs::enums::HashAlgorithm;
pub(crate) static SHA256: Hash = Hash(&digest::SHA256, HashAlgorithm::SHA256);
pub(crate) static SHA384: Hash = Hash(&digest::SHA384, HashAlgorithm::SHA384);

#[derive(Debug)]
pub(crate) struct Hash(&'static digest::Algorithm, HashAlgorithm);

impl crypto::hash::Hash for Hash {
Expand Down
1 change: 1 addition & 0 deletions rustls/src/crypto/ring/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub fn default_provider() -> CryptoProvider {
kx_groups: ALL_KX_GROUPS.to_vec(),
signature_verification_algorithms: SUPPORTED_SIG_ALGS,
secure_random: &Ring,
sha256_hasher: &hash::SHA256,
key_provider: &Ring,
}
}
Expand Down
1 change: 1 addition & 0 deletions rustls/src/msgs/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ enum_builder! {
@U16
pub enum ClientPuzzleType {
COOKIE => 0,
SHA256 => 1,
}
}

Expand Down
80 changes: 76 additions & 4 deletions rustls/src/msgs/handshake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use pki_types::{CertificateDer, DnsName};

#[cfg(feature = "tls12")]
use crate::crypto::ActiveKeyExchange;
use crate::crypto::SecureRandom;
use crate::crypto::{CryptoProvider, SecureRandom};
use crate::enums::{
CertificateCompressionAlgorithm, CipherSuite, EchClientHelloType, HandshakeType,
ProtocolVersion, SignatureScheme,
Expand Down Expand Up @@ -532,6 +532,7 @@ impl TlsListElement for ClientPuzzleType {
pub enum ClientPuzzle {
SupportIndication(Vec<ClientPuzzleType>),
Cookie(PayloadU16),
Sha256(PayloadU16),
Unknown(ClientPuzzleType, PayloadU16),
}

Expand All @@ -546,6 +547,10 @@ impl Codec<'_> for ClientPuzzle {
vec![ClientPuzzleType::COOKIE].encode(bytes);
cookie.encode(bytes);
}
Self::Sha256(solution) => {
vec![ClientPuzzleType::SHA256].encode(bytes);
solution.encode(bytes);
}
Self::Unknown(puzzletype, data) => {
vec![*puzzletype].encode(bytes);
data.encode(bytes);
Expand All @@ -562,6 +567,7 @@ impl Codec<'_> for ClientPuzzle {
([], _) => Err(InvalidMessage::InvalidClientPuzzle),
(_, 0) => Ok(Self::SupportIndication(puzzletype)),
([ClientPuzzleType::COOKIE], _) => Ok(Self::Cookie(PayloadU16(sub.rest().into()))),
([ClientPuzzleType::SHA256], _) => Ok(Self::Sha256(PayloadU16(sub.rest().into()))),
([unknown], _) => Ok(Self::Unknown(*unknown, PayloadU16(sub.rest().into()))),
_ => Err(InvalidMessage::InvalidClientPuzzle),
}
Expand All @@ -570,12 +576,28 @@ impl Codec<'_> for ClientPuzzle {

impl ClientPuzzle {
pub fn support_indicator() -> Self {
Self::SupportIndication(vec![ClientPuzzleType::COOKIE])
Self::SupportIndication(vec![ClientPuzzleType::COOKIE, ClientPuzzleType::SHA256])
}

pub fn check(&self, challenge: &ClientPuzzleChallenge) -> bool {
pub fn check(&self, challenge: &ClientPuzzleChallenge, provider: &CryptoProvider) -> bool {
match (self, challenge) {
(Self::Cookie(actual), ClientPuzzleChallenge::Cookie(expected)) => actual == expected,
(Self::Sha256(solution), ClientPuzzleChallenge::Sha256(difficulty, challenge)) => {
const TAIL: &'static [u8] = b"TLS SHA256CPUPuzzle\0";
let mut buf = vec![0; challenge.0.len() + size_of::<u64>() + TAIL.len()];
buf[0..challenge.0.len()].copy_from_slice(&challenge.0);
buf[challenge.0.len()..][..solution.0.len()].copy_from_slice(&solution.0);
buf[challenge.0.len() + solution.0.len()..].copy_from_slice(TAIL);
let full = difficulty / 8;
let partial = difficulty % 8;
let hash = provider.sha256_hasher.hash(&buf);
let hashbytes: &[u8] = hash.as_ref();
hashbytes
.iter()
.take(full as _)
.all(|v| *v == 0)
&& hashbytes[full as usize].leading_zeros() >= partial as _
}
_ => false,
}
}
Expand All @@ -584,6 +606,7 @@ impl ClientPuzzle {
#[derive(Debug, Clone)]
pub enum ClientPuzzleChallenge {
Cookie(PayloadU16),
Sha256(u8, PayloadU16),
Unknown(ClientPuzzleType, PayloadU16),
}

Expand All @@ -594,6 +617,12 @@ impl Codec<'_> for ClientPuzzleChallenge {
vec![ClientPuzzleType::COOKIE].encode(bytes);
cookie.encode(bytes);
}
Self::Sha256(difficulty, challenge) => {
vec![ClientPuzzleType::SHA256].encode(bytes);
let nested = LengthPrefixedBuffer::new(ListLength::U16, bytes);
difficulty.encode(nested.buf);
challenge.encode(nested.buf);
}
Self::Unknown(puzzletype, data) => {
vec![*puzzletype].encode(bytes);
data.encode(bytes);
Expand All @@ -608,6 +637,12 @@ impl Codec<'_> for ClientPuzzleChallenge {

match &puzzletype[..] {
[ClientPuzzleType::COOKIE] => Ok(Self::Cookie(PayloadU16(sub.rest().into()))),
[ClientPuzzleType::SHA256] => {
let difficulty = u8::read(&mut sub)?;
let challenge = PayloadU16::read(&mut sub)?;
sub.expect_empty("puzzle")?;
Ok(Self::Sha256(difficulty, challenge))
}
[unknown] => Ok(Self::Unknown(*unknown, PayloadU16(sub.rest().into()))),
_ => Err(InvalidMessage::InvalidClientPuzzle),
}
Expand All @@ -621,11 +656,48 @@ impl ClientPuzzleChallenge {
Ok(Self::Cookie(PayloadU16(challenge)))
}

pub fn solve(&self) -> Option<ClientPuzzle> {
pub fn new_sha(difficulty: u8, secure_random: &dyn SecureRandom) -> Result<Self, Error> {
let mut challenge = vec![0; 16];
secure_random.fill(&mut challenge)?;
Ok(Self::Sha256(difficulty, PayloadU16::new(challenge)))
}

pub fn solve(&self, provider: &CryptoProvider) -> Option<ClientPuzzle> {
match self {
Self::Cookie(challenge) => {
Some(ClientPuzzle::Cookie(challenge.clone()))
}
Self::Sha256(difficulty, challenge) => {
if *difficulty > 32 {
// too difficult
return None;
}

const TAIL: &'static [u8] = b"TLS SHA256CPUPuzzle\0";
let mut buf = vec![0; challenge.0.len() + size_of::<u64>() + TAIL.len()];
buf[0..challenge.0.len()].copy_from_slice(&challenge.0);
buf[challenge.0.len() + size_of::<u64>()..].copy_from_slice(TAIL);
let full = difficulty / 8;
let partial = difficulty % 8;
for sol in 0..u64::MAX {
buf[challenge.0.len()..][..size_of::<u64>()]
.copy_from_slice(&sol.to_ne_bytes());

let hash = provider.sha256_hasher.hash(&buf);
let hashbytes: &[u8] = hash.as_ref();
if hashbytes
.iter()
.take(full as _)
.all(|v| *v == 0)
&& hashbytes[full as usize].leading_zeros() >= partial as _
{
return Some(ClientPuzzle::Sha256(PayloadU16::new(
sol.to_ne_bytes().into(),
)));
}
}
None
}
Self::Unknown(_, _) => None,
}
}
Expand Down
6 changes: 3 additions & 3 deletions rustls/src/server/tls13.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ mod client_hello {
let chosen_share_and_kxg = if let (Some(challenge), Some(solution)) =
(&self.challenge, client_hello.puzzle_solution())
{
if solution.check(challenge) {
if solution.check(challenge, &self.config.provider) {
chosen_share_and_kxg
} else {
None
Expand All @@ -225,7 +225,7 @@ mod client_hello {

if !client_hello
.supported_puzzles()
.contains(&crate::msgs::enums::ClientPuzzleType::COOKIE)
.contains(&crate::msgs::enums::ClientPuzzleType::SHA256)
{
return Err(cx.common.send_fatal_alert(
AlertDescription::HandshakeFailure,
Expand All @@ -234,7 +234,7 @@ mod client_hello {
}

let challenge =
ClientPuzzleChallenge::new_cookie(self.config.provider.secure_random)?;
ClientPuzzleChallenge::new_sha(8, self.config.provider.secure_random)?;

emit_hello_retry_request(
&mut self.transcript,
Expand Down

0 comments on commit 1877d2c

Please sign in to comment.