tor-llcrypto: Improve documentation and move keyblinding.

This commit is contained in:
Nick Mathewson 2021-05-21 11:27:41 -04:00
parent 2ea0ed7eb2
commit 57f65b261e
9 changed files with 317 additions and 162 deletions

View File

@ -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};

View File

@ -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};

View File

@ -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)]

View File

@ -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.

View File

@ -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]
);
}
}
}

View File

@ -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]
);
}
}
}

View File

@ -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 {

View File

@ -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!

View File

@ -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;