use super::{KeyGenerator, RelayHandshakeError, RelayHandshakeResult};
use crate::util::ct;
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::*;
use tor_llcrypto::pk::rsa::RsaIdentity;
use digest::Mac;
use rand_core::{CryptoRng, RngCore};
pub(crate) struct NtorClient;
impl super::ClientHandshake for NtorClient {
type KeyType = NtorPublicKey;
type StateType = NtorHandshakeState;
type KeyGen = NtorHkdfKeyGenerator;
fn client1<R: RngCore + CryptoRng>(
rng: &mut R,
key: &Self::KeyType,
) -> Result<(Self::StateType, Vec<u8>)> {
client_handshake_ntor_v1(rng, key)
}
fn client2<T: AsRef<[u8]>>(state: Self::StateType, msg: T) -> Result<Self::KeyGen> {
client_handshake2_ntor_v1(msg, &state)
}
}
pub(crate) struct NtorServer;
impl super::ServerHandshake for NtorServer {
type KeyType = NtorSecretKey;
type KeyGen = NtorHkdfKeyGenerator;
fn server<R: RngCore + CryptoRng, T: AsRef<[u8]>>(
rng: &mut R,
key: &[Self::KeyType],
msg: T,
) -> RelayHandshakeResult<(Self::KeyGen, Vec<u8>)> {
server_handshake_ntor_v1(rng, msg, key)
}
}
#[derive(Clone, Debug)]
pub(crate) struct NtorPublicKey {
pub(crate) id: RsaIdentity,
pub(crate) pk: PublicKey,
}
pub(crate) struct NtorSecretKey {
pk: NtorPublicKey,
sk: StaticSecret,
}
use subtle::{Choice, ConstantTimeEq};
impl NtorSecretKey {
#[allow(unused)]
pub(crate) fn new(sk: StaticSecret, pk: PublicKey, id: RsaIdentity) -> Self {
NtorSecretKey {
pk: NtorPublicKey { id, pk },
sk,
}
}
fn matches_pk(&self, pk: &PublicKey) -> Choice {
self.pk.pk.as_bytes().ct_eq(pk.as_bytes())
}
}
pub(crate) struct NtorHandshakeState {
relay_public: NtorPublicKey,
my_sk: StaticSecret,
my_public: PublicKey,
}
pub(crate) struct NtorHkdfKeyGenerator {
seed: SecretBuf,
}
impl NtorHkdfKeyGenerator {
pub(crate) fn new(seed: SecretBuf) -> Self {
NtorHkdfKeyGenerator { seed }
}
}
impl KeyGenerator for NtorHkdfKeyGenerator {
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};
Ntor1Kdf::new(ntor1_key, ntor1_expand).derive(&self.seed[..], keylen)
}
}
type Authcode = digest::CtOutput<hmac::Hmac<d::Sha256>>;
fn client_handshake_ntor_v1<R>(
rng: &mut R,
relay_public: &NtorPublicKey,
) -> Result<(NtorHandshakeState, Vec<u8>)>
where
R: RngCore + CryptoRng,
{
let my_sk = StaticSecret::new(rng);
let my_public = PublicKey::from(&my_sk);
client_handshake_ntor_v1_no_keygen(my_public, my_sk, relay_public)
}
fn client_handshake_ntor_v1_no_keygen(
my_public: PublicKey,
my_sk: StaticSecret,
relay_public: &NtorPublicKey,
) -> Result<(NtorHandshakeState, Vec<u8>)> {
let mut v: Vec<u8> = Vec::new();
v.write(&relay_public.id)
.and_then(|_| v.write(&relay_public.pk))
.and_then(|_| v.write(&my_public))
.map_err(|e| Error::from_bytes_enc(e, "Can't encode client handshake."))?;
assert_eq!(v.len(), 20 + 32 + 32);
let state = NtorHandshakeState {
relay_public: relay_public.clone(),
my_public,
my_sk,
};
Ok((state, v))
}
fn client_handshake2_ntor_v1<T>(msg: T, state: &NtorHandshakeState) -> Result<NtorHkdfKeyGenerator>
where
T: AsRef<[u8]>,
{
let mut cur = Reader::from_slice(msg.as_ref());
let their_pk: PublicKey = cur
.extract()
.map_err(|e| Error::from_bytes_err(e, "v3 ntor handshake"))?;
let auth: Authcode = cur
.extract()
.map_err(|e| Error::from_bytes_err(e, "v3 ntor handshake"))?;
let xy = state.my_sk.diffie_hellman(&their_pk);
let xb = state.my_sk.diffie_hellman(&state.relay_public.pk);
let (keygen, authcode) =
ntor_derive(&xy, &xb, &state.relay_public, &state.my_public, &their_pk)
.map_err(into_internal!("Error deriving keys"))?;
let okay = authcode.ct_eq(&auth)
& ct::bool_to_choice(xy.was_contributory())
& ct::bool_to_choice(xb.was_contributory());
if okay.into() {
Ok(keygen)
} else {
Err(Error::BadCircHandshakeAuth)
}
}
fn ntor_derive(
xy: &SharedSecret,
xb: &SharedSecret,
server_pk: &NtorPublicKey,
x: &PublicKey,
y: &PublicKey,
) -> EncodeResult<(NtorHkdfKeyGenerator, Authcode)> {
let ntor1_protoid = &b"ntor-curve25519-sha256-1"[..];
let ntor1_mac = &b"ntor-curve25519-sha256-1:mac"[..];
let ntor1_verify = &b"ntor-curve25519-sha256-1:verify"[..];
let server_string = &b"Server"[..];
let mut secret_input = SecretBuf::new();
secret_input.write(xy)?; secret_input.write(xb)?; secret_input.write(&server_pk.id)?; secret_input.write(&server_pk.pk)?; secret_input.write(x)?; secret_input.write(y)?; secret_input.write(ntor1_protoid)?;
use hmac::Hmac;
use tor_llcrypto::d::Sha256;
let verify = {
let mut m =
Hmac::<Sha256>::new_from_slice(ntor1_verify).expect("Hmac allows keys of any size");
m.update(&secret_input[..]);
m.finalize()
};
let mut auth_input = Vec::new();
auth_input.write_and_consume(verify)?; auth_input.write(&server_pk.id)?; auth_input.write(&server_pk.pk)?; auth_input.write(y)?; auth_input.write(x)?; auth_input.write(ntor1_protoid)?; auth_input.write(server_string)?;
let auth_mac = {
let mut m =
Hmac::<Sha256>::new_from_slice(ntor1_mac).expect("Hmac allows keys of any size");
m.update(&auth_input[..]);
m.finalize()
};
let keygen = NtorHkdfKeyGenerator::new(secret_input);
Ok((keygen, auth_mac))
}
fn server_handshake_ntor_v1<R, T>(
rng: &mut R,
msg: T,
keys: &[NtorSecretKey],
) -> RelayHandshakeResult<(NtorHkdfKeyGenerator, Vec<u8>)>
where
R: RngCore + CryptoRng,
T: AsRef<[u8]>,
{
let ephem = EphemeralSecret::new(rng);
let ephem_pub = PublicKey::from(&ephem);
server_handshake_ntor_v1_no_keygen(ephem_pub, ephem, msg, keys)
}
fn server_handshake_ntor_v1_no_keygen<T>(
ephem_pub: PublicKey,
ephem: EphemeralSecret,
msg: T,
keys: &[NtorSecretKey],
) -> RelayHandshakeResult<(NtorHkdfKeyGenerator, Vec<u8>)>
where
T: AsRef<[u8]>,
{
let mut cur = Reader::from_slice(msg.as_ref());
let my_id: RsaIdentity = cur.extract()?;
let my_key: PublicKey = cur.extract()?;
let their_pk: PublicKey = cur.extract()?;
let keypair = ct::lookup(keys, |key| key.matches_pk(&my_key));
let keypair = match keypair {
Some(k) => k,
None => return Err(RelayHandshakeError::MissingKey),
};
if my_id != keypair.pk.id {
return Err(RelayHandshakeError::MissingKey);
}
let xy = ephem.diffie_hellman(&their_pk);
let xb = keypair.sk.diffie_hellman(&their_pk);
let okay =
ct::bool_to_choice(xy.was_contributory()) & ct::bool_to_choice(xb.was_contributory());
let (keygen, authcode) = ntor_derive(&xy, &xb, &keypair.pk, &their_pk, &ephem_pub)
.map_err(into_internal!("Error deriving keys"))?;
let mut reply: Vec<u8> = Vec::new();
reply
.write(&ephem_pub)
.and_then(|_| reply.write_and_consume(authcode))
.map_err(into_internal!(
"Generated relay handshake we couldn't encode"
))?;
if okay.into() {
Ok((keygen, reply))
} else {
Err(RelayHandshakeError::BadClientHandshake)
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::*;
use crate::crypto::testing::FakePRNG;
use tor_basic_utils::test_rng::testing_rng;
#[test]
fn simple() -> Result<()> {
use crate::crypto::handshake::{ClientHandshake, ServerHandshake};
let mut rng = testing_rng();
let relay_secret = StaticSecret::new(&mut rng);
let relay_public = PublicKey::from(&relay_secret);
let relay_identity = RsaIdentity::from_bytes(&[12; 20]).unwrap();
let relay_ntpk = NtorPublicKey {
id: relay_identity,
pk: relay_public,
};
let (state, cmsg) = NtorClient::client1(&mut rng, &relay_ntpk)?;
let relay_ntsk = NtorSecretKey {
pk: relay_ntpk,
sk: relay_secret,
};
let relay_ntsks = [relay_ntsk];
let (skeygen, smsg) = NtorServer::server(&mut rng, &relay_ntsks, &cmsg).unwrap();
let ckeygen = NtorClient::client2(state, smsg)?;
let skeys = skeygen.expand(55)?;
let ckeys = ckeygen.expand(55)?;
assert_eq!(skeys, ckeys);
Ok(())
}
fn make_fake_ephem_key(bytes: &[u8]) -> EphemeralSecret {
assert_eq!(bytes.len(), 32);
let rng = FakePRNG::new(bytes);
EphemeralSecret::new(rng)
}
#[test]
fn testvec() -> Result<()> {
use hex_literal::hex;
let b_sk = hex!("4820544f4c4420594f5520444f474954204b454550532048415050454e494e47");
let b_pk = hex!("ccbc8541904d18af08753eae967874749e6149f873de937f57f8fd903a21c471");
let x_sk = hex!("706f6461792069207075742e2e2e2e2e2e2e2e4a454c4c59206f6e2074686973");
let x_pk = hex!("e65dfdbef8b2635837fe2cebc086a8096eae3213e6830dc407516083d412b078");
let y_sk = hex!("70686520737175697272656c2e2e2e2e2e2e2e2e686173206869732067616d65");
let y_pk = hex!("390480a14362761d6aec1fea840f6e9e928fb2adb7b25c670be1045e35133a37");
let id = hex!("69546f6c64596f7541626f75745374616972732e");
let client_handshake = hex!("69546f6c64596f7541626f75745374616972732eccbc8541904d18af08753eae967874749e6149f873de937f57f8fd903a21c471e65dfdbef8b2635837fe2cebc086a8096eae3213e6830dc407516083d412b078");
let server_handshake = hex!("390480a14362761d6aec1fea840f6e9e928fb2adb7b25c670be1045e35133a371cbdf68b89923e1f85e8e18ee6e805ea333fe4849c790ffd2670bd80fec95cc8");
let keys = hex!("0c62dee7f48893370d0ef896758d35729867beef1a5121df80e00f79ed349af39b51cae125719182f19d932a667dae1afbf2e336e6910e7822223e763afad0a13342157969dc6b79");
let relay_pk = NtorPublicKey {
id: RsaIdentity::from_bytes(&id).unwrap(),
pk: b_pk.into(),
};
let relay_sk = NtorSecretKey {
pk: relay_pk.clone(),
sk: b_sk.into(),
};
let (state, create_msg) =
client_handshake_ntor_v1_no_keygen(x_pk.into(), x_sk.into(), &relay_pk).unwrap();
assert_eq!(&create_msg[..], &client_handshake[..]);
let ephem = make_fake_ephem_key(&y_sk[..]);
let ephem_pub = y_pk.into();
let (s_keygen, created_msg) =
server_handshake_ntor_v1_no_keygen(ephem_pub, ephem, &create_msg[..], &[relay_sk])
.unwrap();
assert_eq!(&created_msg[..], &server_handshake[..]);
let c_keygen = client_handshake2_ntor_v1(created_msg, &state)?;
let c_keys = c_keygen.expand(keys.len())?;
let s_keys = s_keygen.expand(keys.len())?;
assert_eq!(&c_keys[..], &keys[..]);
assert_eq!(&s_keys[..], &keys[..]);
Ok(())
}
#[test]
fn failing_handshakes() {
use crate::crypto::handshake::{ClientHandshake, ServerHandshake};
let mut rng = testing_rng();
let relay_secret = StaticSecret::new(&mut rng);
let relay_public = PublicKey::from(&relay_secret);
let wrong_public = PublicKey::from([16_u8; 32]);
let relay_identity = RsaIdentity::from_bytes(&[12; 20]).unwrap();
let wrong_identity = RsaIdentity::from_bytes(&[13; 20]).unwrap();
let relay_ntpk = NtorPublicKey {
id: relay_identity,
pk: relay_public,
};
let relay_ntsk = NtorSecretKey {
pk: relay_ntpk.clone(),
sk: relay_secret,
};
let relay_ntsks = &[relay_ntsk];
let wrong_ntpk1 = NtorPublicKey {
id: wrong_identity,
pk: relay_public,
};
let wrong_ntpk2 = NtorPublicKey {
id: relay_identity,
pk: wrong_public,
};
let (_, handshake1) = NtorClient::client1(&mut rng, &wrong_ntpk1).unwrap();
let (_, handshake2) = NtorClient::client1(&mut rng, &wrong_ntpk2).unwrap();
let (st3, handshake3) = NtorClient::client1(&mut rng, &relay_ntpk).unwrap();
let ans1 = NtorServer::server(&mut rng, relay_ntsks, &handshake1);
let ans2 = NtorServer::server(&mut rng, relay_ntsks, &handshake2);
assert!(ans1.is_err());
assert!(ans2.is_err());
let (_, mut smsg) = NtorServer::server(&mut rng, relay_ntsks, &handshake3).unwrap();
smsg[60] ^= 7;
let ans3 = NtorClient::client2(st3, smsg);
assert!(ans3.is_err());
}
}