llcrypto: Implement ed25519_to_curve25519_private conversion.
In `ArtiNativeKeyStore`, private keys are stored in OpenSSH format. However, `ssh-key` (the crate we use for parsing OpenSSH keys) doesn't support x25519 keys. As a workaround, this type of key will stored as ed25519 and converted to x25519 upon retrieval. This commit implements the `convert_ed25519_to_curve25519_private` conversion function (needed by `ArtiNativeKeyStore` to support x25519 keys). Part of #900
This commit is contained in:
parent
58a4cc3000
commit
47606ad881
|
@ -18,7 +18,7 @@ full = ["safelog/full"]
|
|||
with-openssl = ["openssl", "typenum", "cipher", "__is_nonadditive"]
|
||||
with-sha1-asm = ["sha1/asm", "__is_nonadditive"]
|
||||
|
||||
experimental = ["relay", "hsv3-client", "hsv3-service"]
|
||||
experimental = ["relay", "hsv3-client", "hsv3-service", "keymgr"]
|
||||
|
||||
# Enable support for cryptography needed to be a Tor relay.
|
||||
relay = ["__is_experimental"]
|
||||
|
@ -26,6 +26,8 @@ relay = ["__is_experimental"]
|
|||
hsv3-client = ["__is_experimental"]
|
||||
# Enable support for cryptography needed to be an onion service v3 service.
|
||||
hsv3-service = ["__is_experimental"]
|
||||
# Enable support for cryptography needed for key management.
|
||||
keymgr = ["__is_experimental"]
|
||||
|
||||
__is_nonadditive = []
|
||||
__is_experimental = []
|
||||
|
|
|
@ -66,6 +66,8 @@ onion service client.
|
|||
`hsv3-service` -- enable cryptography that's only needed when running as a v3
|
||||
onion service.
|
||||
|
||||
`keymgr` -- enable cryptography that's only needed for key management
|
||||
|
||||
### Acceleration features
|
||||
|
||||
These features should never be enabled by default from libraries, since they
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
ADDED: ct_lookup
|
||||
ADDED: `keymgr feature`, `convert_ed25519_to_curve25519_private`
|
||||
|
|
|
@ -108,6 +108,29 @@ pub fn convert_curve25519_to_ed25519_private(
|
|||
Some((pk::ed25519::ExpandedKeypair { public, secret }, signbit))
|
||||
}
|
||||
|
||||
/// Convert an ed25519 private key to a curve25519 private key.
|
||||
///
|
||||
/// Note: Using the same keypair for multiple purposes (such as key-exchange and signing) is
|
||||
/// considered bad practice. Don't use this function unless you know what you're doing.
|
||||
#[cfg(any(test, feature = "keymgr"))]
|
||||
pub fn convert_ed25519_to_curve25519_private(
|
||||
keypair: &pk::ed25519::Keypair,
|
||||
) -> pk::curve25519::StaticSecret {
|
||||
use crate::d::Sha512;
|
||||
use zeroize::Zeroize as _;
|
||||
|
||||
// Generate the key according to section-5.1.5 of rfc8032
|
||||
let h = Sha512::digest(keypair.secret.to_bytes());
|
||||
|
||||
let mut bytes = [0_u8; 32];
|
||||
bytes.clone_from_slice(&h[0..32]);
|
||||
|
||||
// StaticSecret::from handles the clamping
|
||||
let secret = pk::curve25519::StaticSecret::from(bytes);
|
||||
bytes.zeroize();
|
||||
secret
|
||||
}
|
||||
|
||||
/// An error occurred during a key-blinding operation.
|
||||
#[derive(Error, Debug, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
|
@ -292,6 +315,40 @@ mod tests {
|
|||
assert_eq!(ed_pk1, ed_pk2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ed_to_curve_compatible() {
|
||||
use crate::pk::{curve25519, ed25519};
|
||||
use crate::util::rand_compat::RngCompatExt;
|
||||
use signature::Verifier;
|
||||
use tor_basic_utils::test_rng::testing_rng;
|
||||
|
||||
let mut rng = testing_rng().rng_compat();
|
||||
let ed_kp = ed25519::Keypair::generate(&mut rng);
|
||||
let ed_sk1 = ExpandedSecretKey::from(&ed_kp.secret);
|
||||
let ed_pk1 = ed25519::PublicKey::from(&ed_sk1);
|
||||
|
||||
let curve_sk = convert_ed25519_to_curve25519_private(&ed_kp);
|
||||
let curve_pk = curve25519::PublicKey::from(&curve_sk);
|
||||
|
||||
let (ed_kp2, signbit) = convert_curve25519_to_ed25519_private(&curve_sk).unwrap();
|
||||
let ed_pk2 = convert_curve25519_to_ed25519_public(&curve_pk, signbit).unwrap();
|
||||
let ed_sk2 = ed_kp2.secret;
|
||||
|
||||
assert_eq!(ed_pk1, ed_pk2);
|
||||
// Make sure the 2 secret keys are the same.
|
||||
// Note: we only look at the first 32 bytes of the (expanded) key because the last 32 bytes
|
||||
// represent the "domain-separation nonce".
|
||||
assert_eq!(ed_sk1.to_bytes()[..32], ed_sk2.to_bytes()[..32]);
|
||||
|
||||
let msg = b"tis the gift to be simple";
|
||||
|
||||
for sk in &[ed_sk1, ed_sk2] {
|
||||
let sig = sk.sign(&msg[..], &ed_pk1);
|
||||
assert!(ed_pk1.verify(&msg[..], &sig).is_ok());
|
||||
assert!(ed_pk2.verify(&msg[..], &sig).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(feature = "hsv3-client", feature = "hsv3-service"))]
|
||||
fn blinding() {
|
||||
|
|
Loading…
Reference in New Issue