tor-llcrypto: Improve documentation and move keyblinding.
This commit is contained in:
parent
2ea0ed7eb2
commit
57f65b261e
|
@ -3,12 +3,10 @@
|
|||
//! Fortunately, Tor has managed not to proliferate ciphers. It only
|
||||
//! uses AES, and (so far) only uses AES in counter mode.
|
||||
|
||||
/// Re-exports implementations of counter-mode AES
|
||||
/// Re-exports implementations of counter-mode AES.
|
||||
///
|
||||
/// These ciphers implement the
|
||||
/// [StreamCipher](https://docs.rs/cipher/0.2.1/cipher/stream/trait.StreamCipher.html)
|
||||
/// trait, so use the
|
||||
/// [cipher](https://docs.rs/cipher/0.2.1/cipher/) crate to access them.
|
||||
/// These ciphers implement the `cipher::StreamCipher` trait, so use
|
||||
/// the [`cipher`](https://docs.rs/cipher) crate to access them.
|
||||
pub mod aes {
|
||||
// These implement StreamCipher.
|
||||
pub use ::aes::{Aes128Ctr, Aes256Ctr};
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
//! Digests and XOFs used to implement the Tor protocol.
|
||||
//!
|
||||
//! In various places, for legacy reasons, Tor uses SHA1, SHA2,
|
||||
//! SHA3, and SHAKE. We re-export them all here, implementing
|
||||
//! the Digest trait.
|
||||
//! In various places, for legacy reasons, Tor uses SHA1, SHA2, SHA3,
|
||||
//! and SHAKE. We re-export them all here, in forms implementing the
|
||||
//! the [`digest::Digest`] traits.
|
||||
//!
|
||||
//! Other code should access these digests via the traits in the
|
||||
//! [digest](https://docs.rs/digest/0.8.1/digest/) crate.
|
||||
//! [`digest`] crate.
|
||||
|
||||
pub use sha1::Sha1;
|
||||
pub use sha2::{Sha256, Sha512};
|
||||
|
|
|
@ -1,15 +1,58 @@
|
|||
//! Low-level crypto implementations for Tor.
|
||||
//! # `tor-llcrypto`: Low-level cryptographic implementations for Tor.
|
||||
//!
|
||||
//! This crate doesn't have much of interest: for the most part it
|
||||
//! just wraps other crates that implement lower-level cryptographic
|
||||
//! functionality. In some cases the functionality is just
|
||||
//! re-exported; in others, it is wrapped to present a conseistent
|
||||
//! interface.
|
||||
//! ## Overview
|
||||
//!
|
||||
//! Encryption is implemented in `cipher`, digests are in `d`, and
|
||||
//! public key cryptography (including signatures, encryption, and key
|
||||
//! agreement) are in `pk`.
|
||||
//! The `tor-llcrypto` crate wraps lower-level cryptographic
|
||||
//! primitives that Tor needs, and provides a few smaller pieces of
|
||||
//! cryptographic functionality that are commonly required to
|
||||
//! implement Tor correctly.
|
||||
//!
|
||||
//! This crate is part of
|
||||
//! [Arti](https://gitlab.torproject.org/tpo/core/arti/), a project to
|
||||
//! implement Tor in Rust. Many other crates in Arti depend on it.
|
||||
//!
|
||||
//! You probably wouldn't want to use this crate for implementing
|
||||
//! non-Tor-based protocols; instead you should probably use the other
|
||||
//! crates that it depends on if you have a low-level protocol to
|
||||
//! implement, or a higher-level cryptographic system if you want to
|
||||
//! add security to something else. It is easy to accidentally put
|
||||
//! these functions together in ways that are unsafe.
|
||||
//!
|
||||
//! ### Why a separate crate?
|
||||
//!
|
||||
//! Why do we collect and re-export our cryptography here in
|
||||
//! `tor-llcrypto`, instead of having the different crates in Arti use
|
||||
//! underlying cryptographic crates directly?
|
||||
//!
|
||||
//! By wrapping our cryptography in this crate, we.
|
||||
//!
|
||||
//! ### Adding to `tor-llcrypto`
|
||||
//!
|
||||
//! Any low-level cryptographic algorithm that is used by at least two
|
||||
//! other crates in Arti is a candidate for inclusion in
|
||||
//! `tor-llcrypto`, especially if that algorithm's purpose is not
|
||||
//! specific to any single piece of the Tor algorithm.
|
||||
//!
|
||||
//! Cryptographic _traits_ (like those from RustCrypto) don't have to
|
||||
//! go in `tor-llcrypto`, since they are interfaces rather than
|
||||
//! implementations.
|
||||
//!
|
||||
//! ## Contents
|
||||
//!
|
||||
//! Encryption is implemented in [`cipher`]: Currently only AES is
|
||||
//! exposed or needed.
|
||||
//!
|
||||
//! Cryptographic digests are in [`d`]: The Tor protocol uses several
|
||||
//! digests in different places, and these are all collected here.
|
||||
//!
|
||||
//! Public key cryptography (including signatures, encryption, and key
|
||||
//! agreement) are in [`pk`]. Older parts of the Tor protocol require
|
||||
//! RSA; newer parts are based on Curve25519 and Ed25519. There is
|
||||
//! also functionality here for _key manipulation_ for the keys used
|
||||
//! in these symmetric algorithms.
|
||||
//!
|
||||
//! The [`util`] module has some miscellaneous compatibility utilities
|
||||
//! for manipulating cryptography-related objects and code.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Public-key cryptography for Tor.
|
||||
//!
|
||||
//! In old places, Tor uses RSA; newer Tor public-key cryptography is
|
||||
//! basd on curve25519 and ed25519.
|
||||
//! based on curve25519 and ed25519.
|
||||
|
||||
pub mod ed25519;
|
||||
pub mod keymanip;
|
||||
|
@ -9,17 +9,23 @@ pub mod rsa;
|
|||
|
||||
/// Re-exporting Curve25519 implementations.
|
||||
///
|
||||
/// Eventually there should probably be a key-agreement trait or two
|
||||
/// that this implements, but for now I'm just re-using the API from
|
||||
/// x25519-dalek.
|
||||
/// *TODO*: Eventually we should probably recommend using is code via some
|
||||
/// key-agreement trait, but for now we are just re-using the APIs from
|
||||
/// [`x25519_dalek`].
|
||||
pub mod curve25519 {
|
||||
pub use x25519_dalek::{EphemeralSecret, PublicKey, SharedSecret, StaticSecret};
|
||||
}
|
||||
|
||||
/// Type for a validatable signature.
|
||||
/// A type for a validatable signature.
|
||||
///
|
||||
/// It necessarily includes the signature, the public key, and (a hash
|
||||
/// of?) the document being checked.
|
||||
///
|
||||
/// Having this trait enables us to write code for checking a large number
|
||||
/// of validatable signatures in a way that permits batch signatures for
|
||||
/// Ed25519.
|
||||
///
|
||||
/// To be used with [`validate_all_sigs`].
|
||||
pub trait ValidatableSignature {
|
||||
/// Check whether this signature is a correct signature for the document.
|
||||
fn is_valid(&self) -> bool;
|
||||
|
@ -32,8 +38,14 @@ pub trait ValidatableSignature {
|
|||
|
||||
/// Check whether all of the signatures in this Vec are valid.
|
||||
///
|
||||
/// (Having a separate implementation here enables us to use
|
||||
/// batch-verification when available.)
|
||||
/// Return `true` if every signature is valid; return `false` if even
|
||||
/// one is invalid.
|
||||
///
|
||||
/// This function should typically give the same result as just
|
||||
/// calling `v.iter().all(ValidatableSignature::is_valid))`, while taking
|
||||
/// advantage of batch verification to whatever extent possible.
|
||||
///
|
||||
/// (See [`ed25519::validate_batch`] for caveats.)
|
||||
pub fn validate_all_sigs(v: &[Box<dyn ValidatableSignature>]) -> bool {
|
||||
// First we break out the ed25519 signatures (if any) so we can do
|
||||
// a batch-verification on them.
|
||||
|
|
|
@ -1,20 +1,29 @@
|
|||
//! Re-exporting Ed25519 implementations.
|
||||
//! Re-exporting Ed25519 implementations, and related utilities.
|
||||
//!
|
||||
//! Eventually this should probably be replaced with a wrapper that
|
||||
//! uses the ed25519 trait and the Signature trait.
|
||||
//! Here we re-export types from [`ed25519_dalek`] that implement the
|
||||
//! Ed25519 signature algorithm. (TODO: Eventually, this module
|
||||
//! should probably be replaced with a wrapper that uses the ed25519
|
||||
//! trait and the Signature trait.)
|
||||
//!
|
||||
//! We additionally provide an `Ed25519Identity` type to represent the
|
||||
//! unvalidated Ed25519 "identity keys" that we use throughout the Tor
|
||||
//! protocol to uniquely identify a relay.
|
||||
|
||||
use arrayref::array_ref;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
use subtle::*;
|
||||
use thiserror::Error;
|
||||
use subtle::*; // for ct_eq
|
||||
|
||||
pub use ed25519_dalek::{ExpandedSecretKey, Keypair, PublicKey, SecretKey, Signature};
|
||||
|
||||
use curve25519_dalek::edwards::CompressedEdwardsY;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
|
||||
/// A relay's identity, as an unchecked, unvalidated Ed25519 key.
|
||||
///
|
||||
/// This type is distinct from an Ed25519 [`PublicKey`] for several reasons:
|
||||
/// * We're storing it in a compact format, whereas the public key
|
||||
/// implementation might want an expanded form for more efficient key
|
||||
/// validation.
|
||||
/// * This type hasn't checked whether the bytes herre actually _are_ a
|
||||
/// valid Ed25519 public key.
|
||||
#[derive(Clone, Copy, Hash)]
|
||||
#[allow(clippy::derive_hash_xor_eq)]
|
||||
pub struct Ed25519Identity {
|
||||
|
@ -222,11 +231,24 @@ impl super::ValidatableSignature for ValidatableEd25519Signature {
|
|||
}
|
||||
|
||||
/// Perform a batch verification operation on the provided signatures
|
||||
///
|
||||
/// Return `true` if _every_ signature is valid; otherwise return `false`.
|
||||
///
|
||||
/// Note that the mathematics for batch validation are slightly
|
||||
/// different than those for normal one-signature validation. Because
|
||||
/// of this, it is possible for an ostensible signature that passes
|
||||
/// one validation algorithm might fail the other. (Well-formed
|
||||
/// signatures generated by a correct Ed25519 implementation will
|
||||
/// always pass both kinds of validation, and an attacker should not
|
||||
/// be able to forge a signature that passes either kind.)
|
||||
pub fn validate_batch(sigs: &[&ValidatableEd25519Signature]) -> bool {
|
||||
use crate::pk::ValidatableSignature;
|
||||
if sigs.is_empty() {
|
||||
// ed25519_dalek has nonzero cost for a batch-verification of
|
||||
// zero sigs.
|
||||
true
|
||||
} else if sigs.len() == 1 {
|
||||
// Validating one signature in the traditional way is faster.
|
||||
sigs[0].is_valid()
|
||||
} else {
|
||||
let mut ed_msgs = Vec::new();
|
||||
|
@ -241,107 +263,3 @@ pub fn validate_batch(sigs: &[&ValidatableEd25519Signature]) -> bool {
|
|||
ed25519_dalek::verify_batch(&ed_msgs[..], &ed_sigs[..], &ed_pks[..]).is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
/// An error during our blinding operation
|
||||
#[derive(Error, Debug, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub enum BlindingError {
|
||||
/// A bad public key was provided for blinding
|
||||
#[error("Bad pubkey provided")]
|
||||
BadPubkey,
|
||||
/// Dalek failed the scalar multiplication
|
||||
#[error("Key blinding Failed")]
|
||||
BlindingFailed,
|
||||
}
|
||||
|
||||
// Convert this dalek error to a Blinding Error
|
||||
impl From<ed25519_dalek::SignatureError> for BlindingError {
|
||||
fn from(_: ed25519_dalek::SignatureError) -> BlindingError {
|
||||
BlindingError::BlindingFailed
|
||||
}
|
||||
}
|
||||
|
||||
/// Blind the ed25519 public key 'pk' using the blinding parameter 'param' and
|
||||
/// return the blinded public key.
|
||||
pub fn blind_pubkey(pk: &PublicKey, mut param: [u8; 32]) -> Result<PublicKey, BlindingError> {
|
||||
// Clamp the blinding parameter
|
||||
param[0] &= 248;
|
||||
param[31] &= 63;
|
||||
param[31] |= 64;
|
||||
|
||||
// Transform it into a scalar so that we can do scalar mult
|
||||
let blinding_factor = Scalar::from_bytes_mod_order(param);
|
||||
|
||||
// Convert the public key to a point on the curve
|
||||
let pubkey_point = CompressedEdwardsY(pk.to_bytes())
|
||||
.decompress()
|
||||
.ok_or(BlindingError::BadPubkey)?;
|
||||
|
||||
// Do the scalar multiplication and get a point back
|
||||
let blinded_pubkey_point = (blinding_factor * pubkey_point).compress();
|
||||
// Turn the point back into bytes and return it
|
||||
Ok(PublicKey::from_bytes(&blinded_pubkey_point.0)?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[test]
|
||||
fn blinding() {
|
||||
// Test the ed25519 blinding function.
|
||||
//
|
||||
// These test vectors are from our ed25519 implementation and related
|
||||
// functions. These were automatically generated by the
|
||||
// ed25519_exts_ref.py script in little-t-tor and they are also used by
|
||||
// little-t-tor and onionbalance:
|
||||
let pubkeys = vec![
|
||||
b"c2247870536a192d142d056abefca68d6193158e7c1a59c1654c954eccaff894",
|
||||
b"1519a3b15816a1aafab0b213892026ebf5c0dc232c58b21088d88cb90e9b940d",
|
||||
b"081faa81992e360ea22c06af1aba096e7a73f1c665bc8b3e4e531c46455fd1dd",
|
||||
b"73cfa1189a723aad7966137cbffa35140bb40d7e16eae4c40b79b5f0360dd65a",
|
||||
b"66c1a77104d86461b6f98f73acf3cd229c80624495d2d74d6fda1e940080a96b",
|
||||
b"d21c294db0e64cb2d8976625786ede1d9754186ae8197a64d72f68c792eecc19",
|
||||
b"c4d58b4cf85a348ff3d410dd936fa460c4f18da962c01b1963792b9dcc8a6ea6",
|
||||
b"95126f14d86494020665face03f2d42ee2b312a85bc729903eb17522954a1c4a",
|
||||
b"95126f14d86494020665face03f2d42ee2b312a85bc729903eb17522954a1c4a",
|
||||
b"95126f14d86494020665face03f2d42ee2b312a85bc729903eb17522954a1c4a",
|
||||
];
|
||||
let params = vec![
|
||||
"54a513898b471d1d448a2f3c55c1de2c0ef718c447b04497eeb999ed32027823",
|
||||
"831e9b5325b5d31b7ae6197e9c7a7baf2ec361e08248bce055908971047a2347",
|
||||
"ac78a1d46faf3bfbbdc5af5f053dc6dc9023ed78236bec1760dadfd0b2603760",
|
||||
"f9c84dc0ac31571507993df94da1b3d28684a12ad14e67d0a068aba5c53019fc",
|
||||
"b1fe79d1dec9bc108df69f6612c72812755751f21ecc5af99663b30be8b9081f",
|
||||
"81f1512b63ab5fb5c1711a4ec83d379c420574aedffa8c3368e1c3989a3a0084",
|
||||
"97f45142597c473a4b0e9a12d64561133ad9e1155fe5a9807fe6af8a93557818",
|
||||
"3f44f6a5a92cde816635dfc12ade70539871078d2ff097278be2a555c9859cd0",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"1111111111111111111111111111111111111111111111111111111111111111",
|
||||
];
|
||||
let blinded_pubkeys = vec![
|
||||
"1fc1fa4465bd9d4956fdbdc9d3acb3c7019bb8d5606b951c2e1dfe0b42eaeb41",
|
||||
"1cbbd4a88ce8f165447f159d9f628ada18674158c4f7c5ead44ce8eb0fa6eb7e",
|
||||
"c5419ad133ffde7e0ac882055d942f582054132b092de377d587435722deb028",
|
||||
"3e08d0dc291066272e313014bfac4d39ad84aa93c038478a58011f431648105f",
|
||||
"59381f06acb6bf1389ba305f70874eed3e0f2ab57cdb7bc69ed59a9b8899ff4d",
|
||||
"2b946a484344eb1c17c89dd8b04196a84f3b7222c876a07a4cece85f676f87d9",
|
||||
"c6b585129b135f8769df2eba987e76e089e80ba3a2a6729134d3b28008ac098e",
|
||||
"0eefdc795b59cabbc194c6174e34ba9451e8355108520554ec285acabebb34ac",
|
||||
"312404d06a0a9de489904b18d5233e83a50b225977fa8734f2c897a73c067952",
|
||||
"952a908a4a9e0e5176a2549f8f328955aca6817a9fdc59e3acec5dec50838108",
|
||||
];
|
||||
|
||||
for i in 0..pubkeys.len() {
|
||||
let pk = PublicKey::from_bytes(&hex::decode(pubkeys[i]).unwrap()).unwrap();
|
||||
|
||||
let blinded_pk = blind_pubkey(&pk, hex::decode(params[i]).unwrap().try_into().unwrap());
|
||||
|
||||
assert_eq!(
|
||||
hex::encode(blinded_pk.unwrap().to_bytes()),
|
||||
blinded_pubkeys[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,40 @@
|
|||
//! Key manipulation functions for use with public keys.
|
||||
//!
|
||||
//! Tor does some interesting and not-really-standard things with its
|
||||
//! Tor does some interesting and not-standard things with its
|
||||
//! curve25519 and ed25519 keys, for several reasons.
|
||||
//!
|
||||
//! In order to prove ownership of a curve25519 private key, Tor
|
||||
//! converts it into an ed25519 key, and then uses that ed25519 key to
|
||||
//! sign its identity key.
|
||||
//! sign its identity key. We implement this conversion with
|
||||
//! [`convert_curve25519_to_ed25519_public`] and
|
||||
//! [`convert_curve25519_to_ed25519_private`].
|
||||
//!
|
||||
//! TODO: This is also where we would put the key-derivation code that
|
||||
//! Tor uses in the hsv3 onion services protocol.
|
||||
//! In Tor's v3 onion service design, Tor uses a _key blinding_
|
||||
//! algorithm to derive a publicly known Ed25519 key from a different
|
||||
//! Ed25519 key used as the .onion address. This algorithm allows
|
||||
//! directories to validate the signatures on onion service
|
||||
//! descriptors, without knowing which services they represent. We
|
||||
//! implement this blinding operation via [`blind_pubkey`].
|
||||
//!
|
||||
//! ## TODO
|
||||
//!
|
||||
//! Recommend more standardized ways to do these things.
|
||||
|
||||
use crate::pk;
|
||||
use digest::Digest;
|
||||
use thiserror::Error;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
pub use ed25519_dalek::{ExpandedSecretKey, Keypair, PublicKey, SecretKey, Signature};
|
||||
|
||||
use curve25519_dalek::edwards::CompressedEdwardsY;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
|
||||
/// Convert a curve25519 public key (with sign bit) to an ed25519
|
||||
/// public key, for use in ntor key cross-certification.
|
||||
///
|
||||
/// Note that this formula is not terribly standardized; don't use
|
||||
/// Note that this formula is not standardized; don't use
|
||||
/// it for anything besides cross-certification.
|
||||
///
|
||||
pub fn convert_curve25519_to_ed25519_public(
|
||||
pubkey: &pk::curve25519::PublicKey,
|
||||
signbit: u8,
|
||||
|
@ -40,8 +55,11 @@ pub fn convert_curve25519_to_ed25519_public(
|
|||
/// Convert a curve25519 private key to an ed25519 public key (and
|
||||
/// give a sign bit) to use with it, for use in ntor key cross-certification.
|
||||
///
|
||||
/// Note that this formula is not terribly standardized; don't use
|
||||
/// Note that this formula is not standardized; don't use
|
||||
/// it for anything besides cross-certification.
|
||||
///
|
||||
/// *NEVER* use these keys to sign inputs that may be generated by an
|
||||
/// attacker.
|
||||
pub fn convert_curve25519_to_ed25519_private(
|
||||
privkey: &pk::curve25519::StaticSecret,
|
||||
) -> Option<(pk::ed25519::ExpandedSecretKey, u8)> {
|
||||
|
@ -71,8 +89,67 @@ pub fn convert_curve25519_to_ed25519_private(
|
|||
Some((result, signbit))
|
||||
}
|
||||
|
||||
/// An error occurred during a key-blinding operation.
|
||||
#[derive(Error, Debug, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub enum BlindingError {
|
||||
/// A bad public key was provided for blinding
|
||||
#[error("Bad pubkey provided")]
|
||||
BadPubkey,
|
||||
/// Dalek failed the scalar multiplication
|
||||
#[error("Key blinding Failed")]
|
||||
BlindingFailed,
|
||||
}
|
||||
|
||||
// Convert this dalek error to a BlindingError
|
||||
impl From<ed25519_dalek::SignatureError> for BlindingError {
|
||||
fn from(_: ed25519_dalek::SignatureError) -> BlindingError {
|
||||
BlindingError::BlindingFailed
|
||||
}
|
||||
}
|
||||
|
||||
/// Blind the ed25519 public key `pk` using the blinding parameter
|
||||
/// `param`, and return the blinded public key.
|
||||
///
|
||||
/// This algorithm is described in `rend-spec-v3.txt`, section A.2.
|
||||
/// In the terminology of that section, the value `pk` corresponds to
|
||||
/// `A`, and the value `param` corresponds to `h`.
|
||||
///
|
||||
/// Note that the approach used to clamp `param` to a scalar means
|
||||
/// that different possible values for `param` may yield the same
|
||||
/// output for a given `pk`. This and other limitations make this
|
||||
/// function unsuitable for use outside the context of
|
||||
/// `rend-spec-v3.txt` without careful analysis.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function can fail if the input is not actually a valid
|
||||
/// Ed25519 public key.
|
||||
pub fn blind_pubkey(pk: &PublicKey, mut param: [u8; 32]) -> Result<PublicKey, BlindingError> {
|
||||
// Clamp the blinding parameter
|
||||
param[0] &= 248;
|
||||
param[31] &= 63;
|
||||
param[31] |= 64;
|
||||
|
||||
// Transform it into a scalar so that we can do scalar mult
|
||||
let blinding_factor = Scalar::from_bytes_mod_order(param);
|
||||
|
||||
// Convert the public key to a point on the curve
|
||||
let pubkey_point = CompressedEdwardsY(pk.to_bytes())
|
||||
.decompress()
|
||||
.ok_or(BlindingError::BadPubkey)?;
|
||||
|
||||
// Do the scalar multiplication and get a point back
|
||||
let blinded_pubkey_point = (blinding_factor * pubkey_point).compress();
|
||||
// Turn the point back into bytes and return it
|
||||
Ok(PublicKey::from_bytes(&blinded_pubkey_point.0)?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[test]
|
||||
fn curve_to_ed_compatible() {
|
||||
use super::*;
|
||||
|
@ -97,4 +174,61 @@ mod tests {
|
|||
|
||||
assert_eq!(ed_pk1, ed_pk2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blinding() {
|
||||
// Test the ed25519 blinding function.
|
||||
//
|
||||
// These test vectors are from our ed25519 implementation and related
|
||||
// functions. These were automatically generated by the
|
||||
// ed25519_exts_ref.py script in little-t-tor and they are also used by
|
||||
// little-t-tor and onionbalance:
|
||||
let pubkeys = vec![
|
||||
b"c2247870536a192d142d056abefca68d6193158e7c1a59c1654c954eccaff894",
|
||||
b"1519a3b15816a1aafab0b213892026ebf5c0dc232c58b21088d88cb90e9b940d",
|
||||
b"081faa81992e360ea22c06af1aba096e7a73f1c665bc8b3e4e531c46455fd1dd",
|
||||
b"73cfa1189a723aad7966137cbffa35140bb40d7e16eae4c40b79b5f0360dd65a",
|
||||
b"66c1a77104d86461b6f98f73acf3cd229c80624495d2d74d6fda1e940080a96b",
|
||||
b"d21c294db0e64cb2d8976625786ede1d9754186ae8197a64d72f68c792eecc19",
|
||||
b"c4d58b4cf85a348ff3d410dd936fa460c4f18da962c01b1963792b9dcc8a6ea6",
|
||||
b"95126f14d86494020665face03f2d42ee2b312a85bc729903eb17522954a1c4a",
|
||||
b"95126f14d86494020665face03f2d42ee2b312a85bc729903eb17522954a1c4a",
|
||||
b"95126f14d86494020665face03f2d42ee2b312a85bc729903eb17522954a1c4a",
|
||||
];
|
||||
let params = vec![
|
||||
"54a513898b471d1d448a2f3c55c1de2c0ef718c447b04497eeb999ed32027823",
|
||||
"831e9b5325b5d31b7ae6197e9c7a7baf2ec361e08248bce055908971047a2347",
|
||||
"ac78a1d46faf3bfbbdc5af5f053dc6dc9023ed78236bec1760dadfd0b2603760",
|
||||
"f9c84dc0ac31571507993df94da1b3d28684a12ad14e67d0a068aba5c53019fc",
|
||||
"b1fe79d1dec9bc108df69f6612c72812755751f21ecc5af99663b30be8b9081f",
|
||||
"81f1512b63ab5fb5c1711a4ec83d379c420574aedffa8c3368e1c3989a3a0084",
|
||||
"97f45142597c473a4b0e9a12d64561133ad9e1155fe5a9807fe6af8a93557818",
|
||||
"3f44f6a5a92cde816635dfc12ade70539871078d2ff097278be2a555c9859cd0",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"1111111111111111111111111111111111111111111111111111111111111111",
|
||||
];
|
||||
let blinded_pubkeys = vec![
|
||||
"1fc1fa4465bd9d4956fdbdc9d3acb3c7019bb8d5606b951c2e1dfe0b42eaeb41",
|
||||
"1cbbd4a88ce8f165447f159d9f628ada18674158c4f7c5ead44ce8eb0fa6eb7e",
|
||||
"c5419ad133ffde7e0ac882055d942f582054132b092de377d587435722deb028",
|
||||
"3e08d0dc291066272e313014bfac4d39ad84aa93c038478a58011f431648105f",
|
||||
"59381f06acb6bf1389ba305f70874eed3e0f2ab57cdb7bc69ed59a9b8899ff4d",
|
||||
"2b946a484344eb1c17c89dd8b04196a84f3b7222c876a07a4cece85f676f87d9",
|
||||
"c6b585129b135f8769df2eba987e76e089e80ba3a2a6729134d3b28008ac098e",
|
||||
"0eefdc795b59cabbc194c6174e34ba9451e8355108520554ec285acabebb34ac",
|
||||
"312404d06a0a9de489904b18d5233e83a50b225977fa8734f2c897a73c067952",
|
||||
"952a908a4a9e0e5176a2549f8f328955aca6817a9fdc59e3acec5dec50838108",
|
||||
];
|
||||
|
||||
for i in 0..pubkeys.len() {
|
||||
let pk = PublicKey::from_bytes(&hex::decode(pubkeys[i]).unwrap()).unwrap();
|
||||
|
||||
let blinded_pk = blind_pubkey(&pk, hex::decode(params[i]).unwrap().try_into().unwrap());
|
||||
|
||||
assert_eq!(
|
||||
hex::encode(blinded_pk.unwrap().to_bytes()),
|
||||
blinded_pubkeys[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,15 +4,18 @@
|
|||
//! verification used in the Tor directory protocol and
|
||||
//! similar places.
|
||||
//!
|
||||
//! Currently, that means supporting validating PKCSv1
|
||||
//! signatures, and encoding and decoding keys from DER.
|
||||
//!
|
||||
//! Currently missing is signing and RSA-OEAP.
|
||||
//! Currently, that means validating PKCSv1 signatures, and encoding
|
||||
//! and decoding RSA public keys from DER.
|
||||
//!
|
||||
//! # Limitations:
|
||||
//!
|
||||
//! Currently missing are support for signing and RSA-OEAP. In Tor,
|
||||
//! RSA signing is only needed for relays and authorities, and
|
||||
//! RSA-OAEP padding is only needed for the (obsolete) TAP protocol.
|
||||
//!
|
||||
//!
|
||||
//! XXXX This module should expose RustCrypto trait-based wrappers,
|
||||
//! but the rsa crate didn't support them as of initial writing.
|
||||
//! but the [`rsa`] crate didn't support them as of initial writing.
|
||||
use arrayref::array_ref;
|
||||
use std::fmt;
|
||||
use subtle::*;
|
||||
|
@ -20,11 +23,15 @@ use zeroize::Zeroize;
|
|||
|
||||
/// How many bytes are in an "RSA ID"? (This is a legacy tor
|
||||
/// concept, and refers to identifying a relay by a SHA1 digest
|
||||
/// of its public key.)
|
||||
/// of its RSA public identity key.)
|
||||
pub const RSA_ID_LEN: usize = 20;
|
||||
|
||||
/// An identifier for a Tor relay, based on its legacy RSA
|
||||
/// identity key. These are used all over the Tor protocol.
|
||||
/// An identifier for a Tor relay, based on its legacy RSA identity
|
||||
/// key. These are used all over the Tor protocol.
|
||||
///
|
||||
/// Note that for modern purposes, you should almost always identify a
|
||||
/// relay by its [`crate::pk::ed25519::Ed25519Identity`] instead of
|
||||
/// by this kind of identity key.
|
||||
#[derive(Clone, Copy, Hash, Zeroize, Ord, PartialOrd)]
|
||||
#[allow(clippy::derive_hash_xor_eq)]
|
||||
pub struct RsaIdentity {
|
||||
|
@ -110,7 +117,7 @@ impl<'de> serde::Deserialize<'de> for RsaIdentity {
|
|||
}
|
||||
|
||||
impl RsaIdentity {
|
||||
/// Expose and RsaIdentity as a slice of bytes.
|
||||
/// Expose an RsaIdentity as a slice of bytes.
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.id[..]
|
||||
}
|
||||
|
@ -152,7 +159,12 @@ impl From<[u8; 20]> for RsaIdentity {
|
|||
/// methods and traits on the type.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PublicKey(rsa::RSAPublicKey);
|
||||
|
||||
/// An RSA private key.
|
||||
///
|
||||
/// This is not so useful at present, since Arti currently only has
|
||||
/// client support, and Tor clients never actually need RSA private
|
||||
/// keys.
|
||||
pub struct PrivateKey(rsa::RSAPrivateKey);
|
||||
|
||||
impl PrivateKey {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
//! Utilities for cryptographic purposes
|
||||
//!
|
||||
//! For now, this just has a workaround for some other libraries'
|
||||
//! lack of full x509 support
|
||||
//! For now, this module is just contains a couple of workarounds for the
|
||||
//! limitations of other libraries.
|
||||
|
||||
use simple_asn1::{oid, ASN1Block, BigUint, OID};
|
||||
pub mod rand_compat;
|
||||
|
||||
/// Given an X.509 certificate, return its SubjectPublicKey if that key
|
||||
/// Given an X.509 certificate in DER, return its SubjectPublicKey if that key
|
||||
/// is an RSA key.
|
||||
///
|
||||
/// WARNING: Does not validate the X.509 certificate at all!
|
||||
|
|
|
@ -1,22 +1,50 @@
|
|||
//! Compatibility utilities for working with libraries that consume
|
||||
//! older versions of rand_core.
|
||||
//!
|
||||
//! The dalek-crypto libraries are currently stuck on rand_core 0.5.1,
|
||||
//! but everywhere else we want to use the latest rand_core (0.6.2 as
|
||||
//! of this writing).
|
||||
//! The dalek-crypto libraries are currently stuck on [`rand_core`]
|
||||
//! 0.5.1, but everywhere else in Arti we want to use the latest
|
||||
//! [`rand_core`] (0.6.2 as of this writing). The extension trait in this
|
||||
//! module lets us do so.
|
||||
//!
|
||||
//! # Example:
|
||||
//!
|
||||
//! As of May 2021, if you're using the current version of
|
||||
//! [`x25519-dalek`], and the latest [`rand_core`], then you can't use
|
||||
//! this code, because of the compatibility issue mentioned above.
|
||||
//!
|
||||
//! ```compile_fail
|
||||
//! use rand_core::OsRng;
|
||||
//! use x25519_dalek::EphemeralSecret;
|
||||
//!
|
||||
//! let my_secret = EphemeralSecret::new(OsRng);
|
||||
//! ```
|
||||
//!
|
||||
//! But instead, you can wrap the random number generator using the
|
||||
//! [`RngCompatExt`] extension trait.
|
||||
//!
|
||||
//! ```
|
||||
//! use tor_llcrypto::util::rand_compat::RngCompatExt;
|
||||
//! use rand_core::OsRng;
|
||||
//! use x25519_dalek::EphemeralSecret;
|
||||
//!
|
||||
//! let my_secret = EphemeralSecret::new(OsRng.rng_compat());
|
||||
//! ```
|
||||
//!
|
||||
//! The wrapped RNG can be used with the old version of the RngCode
|
||||
//! trait, as well as the new one.
|
||||
|
||||
use old_rand_core::{CryptoRng as OldCryptoRng, Error as OldError, RngCore as OldRngCore};
|
||||
use rand_core::{CryptoRng, Error, RngCore};
|
||||
|
||||
use std::convert::TryInto;
|
||||
|
||||
/// Extension trait for current versions of RngCore; adds a
|
||||
/// Extension trait for the _current_ versions of [`RngCore`]; adds a
|
||||
/// compatibility-wrappper function.
|
||||
pub trait RngCompatExt: RngCore {
|
||||
/// Wrapper type returned by this trait.
|
||||
type Wrapper: RngCore + OldRngCore;
|
||||
/// Return a version of this Rng that can be used with older versions
|
||||
/// of the rand_core and rand libraries.
|
||||
/// of the rand_core and rand libraries, as well as the current version.
|
||||
fn rng_compat(self) -> Self::Wrapper;
|
||||
}
|
||||
|
||||
|
@ -28,6 +56,16 @@ impl<T: RngCore + Sized> RngCompatExt for T {
|
|||
}
|
||||
|
||||
/// A new-style Rng, wrapped for backward compatibility.
|
||||
///
|
||||
/// This object implements both the current (0.6.2) version of [`RngCore`],
|
||||
/// as well as the version from 0.5.1 that the dalek-crypto functions expect.
|
||||
///
|
||||
/// To get an RngWrapper, use the [`RngCompatExt`] extension trait:
|
||||
/// ```
|
||||
/// use tor_llcrypto::util::rand_compat::RngCompatExt;
|
||||
///
|
||||
/// let mut wrapped_rng = rand::thread_rng().rng_compat();
|
||||
/// ```
|
||||
pub struct RngWrapper<T>(T);
|
||||
|
||||
impl<T: RngCore> From<T> for RngWrapper<T> {
|
||||
|
@ -69,7 +107,7 @@ impl<T: RngCore> RngCore for RngWrapper<T> {
|
|||
impl<T: CryptoRng> OldCryptoRng for RngWrapper<T> {}
|
||||
impl<T: CryptoRng> CryptoRng for RngWrapper<T> {}
|
||||
|
||||
/// Convert a new-ish Rng error into the type that rng_core 0.5.1
|
||||
/// Convert a new-ish Rng error into the error type that rng_core 0.5.1
|
||||
/// would deliver.
|
||||
fn err_to_old(e: Error) -> OldError {
|
||||
use std::num::NonZeroU32;
|
||||
|
|
Loading…
Reference in New Issue