tor-proto: Replace SecretBytes with SecretBuf.

This does not yet make sure that `SecretBuf` is used where it
_should_ be, but at least it ensures that most uses of `SecretBytes`
will indeed act as intended, and make sure that whatever they
contain is zeroized.

It requires some corresponding changes to method calls for
correctness and type conformance.
This commit is contained in:
Nick Mathewson 2022-08-01 14:53:34 -04:00
parent f1c6447403
commit eedd63d5e9
8 changed files with 65 additions and 63 deletions

View File

@ -48,7 +48,7 @@ pub(crate) trait CryptInit: Sized {
/// Initialize this object from a key generator.
fn construct<K: super::handshake::KeyGenerator>(keygen: K) -> Result<Self> {
let seed = keygen.expand(Self::seed_len())?;
Self::initialize(&seed)
Self::initialize(&seed[..])
}
}
@ -393,9 +393,9 @@ pub(crate) mod tor1 {
mod test {
#![allow(clippy::unwrap_used)]
use super::*;
use crate::SecretBytes;
use rand::RngCore;
use tor_basic_utils::test_rng::testing_rng;
use tor_bytes::SecretBuf;
fn add_layers(
cc_out: &mut OutboundClientCrypt,
@ -411,10 +411,8 @@ mod test {
fn roundtrip() {
// Take canned keys and make sure we can do crypto correctly.
use crate::crypto::handshake::ShakeKeyGenerator as KGen;
fn s(seed: &[u8]) -> SecretBytes {
let mut s: SecretBytes = SecretBytes::new(Vec::new());
s.extend(seed);
s
fn s(seed: &[u8]) -> SecretBuf {
seed.to_vec().into()
}
let seed1 = s(b"hidden we are free");

View File

@ -17,9 +17,10 @@ pub(crate) mod ntor;
#[cfg(feature = "ntor_v3")]
pub(crate) mod ntor_v3;
use crate::{Result, SecretBytes};
use crate::Result;
//use zeroize::Zeroizing;
use rand_core::{CryptoRng, RngCore};
use tor_bytes::SecretBuf;
/// A ClientHandshake is used to generate a client onionskin and
/// handle a relay onionskin.
@ -73,7 +74,7 @@ pub(crate) trait ServerHandshake {
/// It can only be used once.
pub(crate) trait KeyGenerator {
/// Consume the key
fn expand(self, keylen: usize) -> Result<SecretBytes>;
fn expand(self, keylen: usize) -> Result<SecretBuf>;
}
/// Generates keys based on the KDF-TOR function.
@ -81,18 +82,18 @@ pub(crate) trait KeyGenerator {
/// This is deprecated and shouldn't be used for new keys.
pub(crate) struct TapKeyGenerator {
/// Seed for the TAP KDF.
seed: SecretBytes,
seed: SecretBuf,
}
impl TapKeyGenerator {
/// Create a key generator based on a provided seed
pub(crate) fn new(seed: SecretBytes) -> Self {
pub(crate) fn new(seed: SecretBuf) -> Self {
TapKeyGenerator { seed }
}
}
impl KeyGenerator for TapKeyGenerator {
fn expand(self, keylen: usize) -> Result<SecretBytes> {
fn expand(self, keylen: usize) -> Result<SecretBuf> {
use crate::crypto::ll::kdf::{Kdf, LegacyKdf};
LegacyKdf::new(1).derive(&self.seed[..], keylen)
}
@ -101,19 +102,19 @@ impl KeyGenerator for TapKeyGenerator {
/// Generates keys based on SHAKE-256.
pub(crate) struct ShakeKeyGenerator {
/// Seed for the key generator
seed: SecretBytes,
seed: SecretBuf,
}
impl ShakeKeyGenerator {
/// Create a key generator based on a provided seed
#[allow(dead_code)] // We'll construct these for v3 onion services
pub(crate) fn new(seed: SecretBytes) -> Self {
pub(crate) fn new(seed: SecretBuf) -> Self {
ShakeKeyGenerator { seed }
}
}
impl KeyGenerator for ShakeKeyGenerator {
fn expand(self, keylen: usize) -> Result<SecretBytes> {
fn expand(self, keylen: usize) -> Result<SecretBuf> {
use crate::crypto::ll::kdf::{Kdf, ShakeKdf};
ShakeKdf::new().derive(&self.seed[..], keylen)
}

View File

@ -7,6 +7,7 @@ use crate::util::ct::bytes_eq;
use crate::{Error, Result};
use rand::{CryptoRng, RngCore};
use tor_bytes::SecretBuf;
use tor_error::into_internal;
/// Number of bytes used for a "CREATE_FAST" handshake by the initiator.
@ -22,6 +23,9 @@ pub(crate) struct CreateFastClientState([u8; FAST_C_HANDSHAKE_LEN]);
/// See module documentation; you probably don't want to use this.
pub(crate) struct CreateFastClient;
/// How many bytes does this handshake use for its input seed?
const SECRET_INPUT_LEN: usize = 40;
impl super::ClientHandshake for CreateFastClient {
type KeyType = ();
type StateType = CreateFastClientState;
@ -41,9 +45,9 @@ impl super::ClientHandshake for CreateFastClient {
if msg.len() != FAST_S_HANDSHAKE_LEN {
return Err(Error::BadCircHandshakeAuth);
}
let mut inp = Vec::new();
inp.extend(&state.0[..]);
inp.extend(&msg[0..20]);
let mut inp = SecretBuf::with_capacity(SECRET_INPUT_LEN);
inp.extend_from_slice(&state.0[..]);
inp.extend_from_slice(&msg[0..20]);
let kh_expect = LegacyKdf::new(0).derive(&inp[..], 20)?;
@ -51,7 +55,7 @@ impl super::ClientHandshake for CreateFastClient {
return Err(Error::BadCircHandshakeAuth);
}
Ok(super::TapKeyGenerator::new(inp.into()))
Ok(super::TapKeyGenerator::new(inp))
}
}
@ -76,15 +80,15 @@ impl super::ServerHandshake for CreateFastServer {
let mut reply = vec![0_u8; FAST_S_HANDSHAKE_LEN];
rng.fill_bytes(&mut reply[0..20]);
let mut inp = Vec::new();
inp.extend(msg);
inp.extend(&reply[0..20]);
let mut inp = SecretBuf::with_capacity(SECRET_INPUT_LEN);
inp.extend_from_slice(msg);
inp.extend_from_slice(&reply[0..20]);
let kh = LegacyKdf::new(0)
.derive(&inp[..], 20)
.map_err(into_internal!("Can't expand key"))?;
reply[20..].copy_from_slice(&kh);
Ok((super::TapKeyGenerator::new(inp.into()), reply))
Ok((super::TapKeyGenerator::new(inp), reply))
}
}

View File

@ -26,8 +26,8 @@
use crate::crypto::handshake::KeyGenerator;
use crate::crypto::ll::kdf::{Kdf, ShakeKdf};
use crate::{Error, Result, SecretBytes};
use tor_bytes::{Reader, Writer};
use crate::{Error, Result};
use tor_bytes::{Reader, SecretBuf, Writer};
use tor_llcrypto::d::Sha3_256;
use tor_llcrypto::pk::{curve25519, ed25519};
use tor_llcrypto::util::rand_compat::RngCompatExt;
@ -56,19 +56,19 @@ pub type Subcredential = [u8; 32];
/// expansion protocol specified in section "Key expansion" of rend-spec-v3.txt .
pub struct HsNtorHkdfKeyGenerator {
/// Secret data derived from the handshake, used as input to HKDF
seed: SecretBytes,
seed: SecretBuf,
}
impl HsNtorHkdfKeyGenerator {
/// Create a new key generator to expand a given seed
pub fn new(seed: SecretBytes) -> Self {
pub fn new(seed: SecretBuf) -> Self {
HsNtorHkdfKeyGenerator { seed }
}
}
impl KeyGenerator for HsNtorHkdfKeyGenerator {
/// Expand the seed into a keystream of 'keylen' size
fn expand(self, keylen: usize) -> Result<SecretBytes> {
fn expand(self, keylen: usize) -> Result<SecretBuf> {
ShakeKdf::new().derive(&self.seed[..], keylen)
}
}
@ -473,7 +473,7 @@ fn get_rendezvous1_key_material(
let hs_ntor_key_constant = &b"tor-hs-ntor-curve25519-sha3-256-1:hs_key_extract"[..];
// Start with rend_secret_hs_input
let mut secret_input = Zeroizing::new(Vec::new());
let mut secret_input = SecretBuf::new();
secret_input
.write(xy) // EXP(X,y)
.and_then(|_| secret_input.write(xb)) // EXP(X,b)
@ -491,7 +491,7 @@ fn get_rendezvous1_key_material(
let verify = hs_ntor_mac(&secret_input, hs_ntor_verify_constant)?;
// Start building 'auth_input'
let mut auth_input = Zeroizing::new(Vec::new());
let mut auth_input = SecretBuf::new();
auth_input
.write(&verify)
.and_then(|_| auth_input.write(auth_key)) // AUTH_KEY
@ -506,12 +506,12 @@ fn get_rendezvous1_key_material(
let auth_input_mac = hs_ntor_mac(&auth_input, hs_ntor_mac_constant)?;
// Now finish up with the KDF construction
let mut kdf_seed = Zeroizing::new(Vec::new());
let mut kdf_seed = SecretBuf::new();
kdf_seed
.write(&ntor_key_seed)
.and_then(|_| kdf_seed.write(hs_ntor_expand_constant))
.map_err(into_internal!("Can't encode kdf-input for hs-ntor."))?;
let keygen = HsNtorHkdfKeyGenerator::new(Zeroizing::new(kdf_seed.to_vec()));
let keygen = HsNtorHkdfKeyGenerator::new(kdf_seed);
Ok((keygen, auth_input_mac))
}

View File

@ -2,8 +2,8 @@
use super::{KeyGenerator, RelayHandshakeError, RelayHandshakeResult};
use crate::util::ct;
use crate::{Error, Result, SecretBytes};
use tor_bytes::{EncodeResult, Reader, Writer};
use crate::{Error, Result};
use tor_bytes::{EncodeResult, Reader, SecretBuf, Writer};
use tor_error::into_internal;
use tor_llcrypto::d;
use tor_llcrypto::pk::curve25519::*;
@ -11,7 +11,6 @@ use tor_llcrypto::pk::rsa::RsaIdentity;
use digest::Mac;
use rand_core::{CryptoRng, RngCore};
use zeroize::Zeroizing;
/// Client side of the Ntor handshake.
pub(crate) struct NtorClient;
@ -104,18 +103,18 @@ pub(crate) struct NtorHandshakeState {
pub(crate) struct NtorHkdfKeyGenerator {
/// Secret key information derived from the handshake, used as input
/// to HKDF
seed: SecretBytes,
seed: SecretBuf,
}
impl NtorHkdfKeyGenerator {
/// Create a new key generator to expand a given seed
pub(crate) fn new(seed: SecretBytes) -> Self {
pub(crate) fn new(seed: SecretBuf) -> Self {
NtorHkdfKeyGenerator { seed }
}
}
impl KeyGenerator for NtorHkdfKeyGenerator {
fn expand(self, keylen: usize) -> Result<SecretBytes> {
fn expand(self, keylen: usize) -> Result<SecretBuf> {
let ntor1_key = &b"ntor-curve25519-sha256-1:key_extract"[..];
let ntor1_expand = &b"ntor-curve25519-sha256-1:key_expand"[..];
use crate::crypto::ll::kdf::{Kdf, Ntor1Kdf};
@ -211,7 +210,7 @@ fn ntor_derive(
let ntor1_verify = &b"ntor-curve25519-sha256-1:verify"[..];
let server_string = &b"Server"[..];
let mut secret_input = Zeroizing::new(Vec::new());
let mut secret_input = SecretBuf::new();
secret_input.write(xy)?; // EXP(X,y)
secret_input.write(xb)?; // EXP(X,b)
secret_input.write(&server_pk.id)?; // ID
@ -228,7 +227,7 @@ fn ntor_derive(
m.update(&secret_input[..]);
m.finalize()
};
let mut auth_input: SecretBytes = Zeroizing::new(Vec::new());
let mut auth_input: SecretBuf = SecretBuf::new();
auth_input.write_and_consume(verify)?; // verify
auth_input.write(&server_pk.id)?; // ID
auth_input.write(&server_pk.pk)?; // B

View File

@ -14,8 +14,8 @@
use super::{RelayHandshakeError, RelayHandshakeResult};
use crate::util::ct;
use crate::{Error, Result, SecretBytes};
use tor_bytes::{EncodeResult, Reader, Writeable, Writer};
use crate::{Error, Result};
use tor_bytes::{EncodeResult, Reader, SecretBuf, Writeable, Writer};
use tor_error::into_internal;
use tor_llcrypto::d::{Sha3_256, Shake256};
use tor_llcrypto::pk::{curve25519, ed25519::Ed25519Identity};
@ -316,10 +316,10 @@ pub(crate) struct NtorV3KeyGenerator<R> {
}
impl<R: digest::XofReader> KeyGenerator for NtorV3KeyGenerator<R> {
fn expand(mut self, keylen: usize) -> Result<SecretBytes> {
let mut ret = vec![0; keylen];
self.reader.read(&mut ret);
Ok(Zeroizing::new(ret))
fn expand(mut self, keylen: usize) -> Result<SecretBuf> {
let mut ret: SecretBuf = vec![0; keylen].into();
self.reader.read(ret.as_mut());
Ok(ret)
}
}

View File

@ -14,16 +14,16 @@
//! services, and is likely to be used by other places in the future.
//! It is based on SHAKE-256.
use crate::{Error, Result, SecretBytes};
use crate::{Error, Result};
use digest::{ExtendableOutput, Update, XofReader};
use tor_bytes::SecretBuf;
use tor_llcrypto::d::{Sha1, Sha256, Shake256};
use zeroize::Zeroizing;
use zeroize::Zeroize;
/// A trait for a key derivation function.
pub(crate) trait Kdf {
/// Derive `n_bytes` of key data from some secret `seed`.
fn derive(&self, seed: &[u8], n_bytes: usize) -> Result<SecretBytes>;
fn derive(&self, seed: &[u8], n_bytes: usize) -> Result<SecretBuf>;
}
/// A legacy KDF, for use with TAP.
@ -58,22 +58,25 @@ impl LegacyKdf {
}
}
impl Kdf for LegacyKdf {
fn derive(&self, seed: &[u8], n_bytes: usize) -> Result<SecretBytes> {
fn derive(&self, seed: &[u8], n_bytes: usize) -> Result<SecretBuf> {
use digest::Digest;
let mut result = Zeroizing::new(Vec::with_capacity(n_bytes + Sha1::output_size()));
let mut result = SecretBuf::with_capacity(n_bytes + Sha1::output_size());
let mut k = self.idx;
if n_bytes > Sha1::output_size() * (256 - (k as usize)) {
return Err(Error::InvalidKDFOutputLength);
}
let mut digest_output = Default::default();
while result.len() < n_bytes {
let mut d = Sha1::new();
Digest::update(&mut d, seed);
Digest::update(&mut d, &[k]);
result.extend(d.finalize());
d.finalize_into(&mut digest_output);
result.extend_from_slice(&digest_output);
k += 1;
}
digest_output.zeroize();
result.truncate(n_bytes);
Ok(result)
@ -88,11 +91,11 @@ impl<'a, 'b> Ntor1Kdf<'a, 'b> {
}
impl Kdf for Ntor1Kdf<'_, '_> {
fn derive(&self, seed: &[u8], n_bytes: usize) -> Result<SecretBytes> {
fn derive(&self, seed: &[u8], n_bytes: usize) -> Result<SecretBuf> {
let hkdf = hkdf::Hkdf::<Sha256>::new(Some(self.t_key), seed);
let mut result = Zeroizing::new(vec![0; n_bytes]);
hkdf.expand(self.m_expand, &mut result[..])
let mut result: SecretBuf = vec![0; n_bytes].into();
hkdf.expand(self.m_expand, result.as_mut())
.map_err(|_| Error::InvalidKDFOutputLength)?;
Ok(result)
}
@ -105,11 +108,11 @@ impl ShakeKdf {
}
}
impl Kdf for ShakeKdf {
fn derive(&self, seed: &[u8], n_bytes: usize) -> Result<SecretBytes> {
fn derive(&self, seed: &[u8], n_bytes: usize) -> Result<SecretBuf> {
let mut xof = Shake256::default();
xof.update(seed);
let mut result = Zeroizing::new(vec![0; n_bytes]);
xof.finalize_xof().read(&mut result);
let mut result: SecretBuf = vec![0; n_bytes].into();
xof.finalize_xof().read(result.as_mut());
Ok(result)
}
}
@ -144,7 +147,7 @@ mod test {
#[test]
fn testvec_tap_kdf() {
// Taken from test_crypto.c in Tor, generated by a python script.
fn expand(b: &[u8]) -> SecretBytes {
fn expand(b: &[u8]) -> SecretBuf {
LegacyKdf::new(0).derive(b, 100).unwrap()
}
@ -199,7 +202,7 @@ mod test {
#[test]
fn testvec_ntor1_kdf() {
// From Tor's test_crypto.c; generated with ntor_ref.py
fn expand(b: &[u8]) -> SecretBytes {
fn expand(b: &[u8]) -> SecretBuf {
let t_key = b"ntor-curve25519-sha256-1:key_extract";
let m_expand = b"ntor-curve25519-sha256-1:key_expand";
Ntor1Kdf::new(&t_key[..], &m_expand[..])

View File

@ -129,9 +129,6 @@ pub use util::skew::ClockSkew;
pub use channel::params::ChannelsParams;
/// A vector of bytes that gets cleared when it's dropped.
type SecretBytes = zeroize::Zeroizing<Vec<u8>>;
/// A Result type for this crate.
pub type Result<T> = std::result::Result<T, Error>;