Generate AesKey/ElGamalSecretKey from an ed25519 signature instead of secret key

This commit is contained in:
Michael Vines
2021-10-21 17:48:25 -07:00
parent 221f499041
commit c155519ae1
2 changed files with 92 additions and 27 deletions

View File

@ -2,12 +2,16 @@
use { use {
aes_gcm::{aead::Aead, Aes128Gcm, NewAead}, aes_gcm::{aead::Aead, Aes128Gcm, NewAead},
rand::{rngs::OsRng, CryptoRng, Rng, RngCore}, rand::{rngs::OsRng, CryptoRng, Rng, RngCore},
sha3::{Digest, Sha3_256},
}; };
use { use {
arrayref::{array_ref, array_refs}, arrayref::{array_ref, array_refs},
solana_sdk::pubkey::Pubkey, solana_sdk::{
solana_sdk::signature::Keypair as SigningKeypair, instruction::Instruction,
message::Message,
pubkey::Pubkey,
signature::Signature,
signer::{Signer, SignerError},
},
std::convert::TryInto, std::convert::TryInto,
zeroize::Zeroize, zeroize::Zeroize,
}; };
@ -54,16 +58,20 @@ impl Aes {
#[derive(Debug, Zeroize)] #[derive(Debug, Zeroize)]
pub struct AesKey([u8; 16]); pub struct AesKey([u8; 16]);
impl AesKey { impl AesKey {
pub fn new(signing_keypair: &SigningKeypair, address: &Pubkey) -> Self { pub fn new(signer: &dyn Signer, address: &Pubkey) -> Result<Self, SignerError> {
let mut hashable = [0_u8; 64]; let message = Message::new(
hashable[..32].copy_from_slice(&signing_keypair.secret().to_bytes()); &[Instruction::new_with_bytes(*address, b"AesKey", vec![])],
hashable[32..].copy_from_slice(&address.to_bytes()); Some(&signer.try_pubkey()?),
);
let signature = signer.try_sign_message(&message.serialize())?;
let mut hasher = Sha3_256::new(); // Some `Signer` implementations return the default signature, which is not suitable for
hasher.update(hashable); // use as key material
if signature == Signature::default() {
let result: [u8; 16] = hasher.finalize()[..16].try_into().unwrap(); Err(SignerError::Custom("Rejecting default signature".into()))
AesKey(result) } else {
Ok(AesKey(signature.as_ref()[..16].try_into().unwrap()))
}
} }
pub fn random<T: RngCore + CryptoRng>(rng: &mut T) -> Self { pub fn random<T: RngCore + CryptoRng>(rng: &mut T) -> Self {
@ -117,7 +125,10 @@ impl Default for AesCiphertext {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use {
super::*,
solana_sdk::{signature::Keypair, signer::null_signer::NullSigner},
};
#[test] #[test]
fn test_aes_encrypt_decrypt_correctness() { fn test_aes_encrypt_decrypt_correctness() {
@ -129,4 +140,18 @@ mod tests {
assert_eq!(amount, decrypted_amount); assert_eq!(amount, decrypted_amount);
} }
#[test]
fn test_aes_new() {
let keypair1 = Keypair::new();
let keypair2 = Keypair::new();
assert_ne!(
AesKey::new(&keypair1, &Pubkey::default()).unwrap().0,
AesKey::new(&keypair2, &Pubkey::default()).unwrap().0,
);
let null_signer = NullSigner::new(&Pubkey::default());
assert!(AesKey::new(&null_signer, &Pubkey::default()).is_err());
}
} }

View File

@ -12,10 +12,14 @@ use {
scalar::Scalar, scalar::Scalar,
}, },
serde::{Deserialize, Serialize}, serde::{Deserialize, Serialize},
solana_sdk::pubkey::Pubkey, solana_sdk::{
solana_sdk::signature::Keypair as SigningKeypair, instruction::Instruction,
std::collections::HashMap, message::Message,
std::convert::TryInto, pubkey::Pubkey,
signature::Signature,
signer::{Signer, SignerError},
},
std::{collections::HashMap, convert::TryInto},
subtle::{Choice, ConstantTimeEq}, subtle::{Choice, ConstantTimeEq},
zeroize::Zeroize, zeroize::Zeroize,
}; };
@ -136,11 +140,11 @@ impl ElGamalKeypair {
/// address. /// address.
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn new(signing_keypair: &SigningKeypair, address: &Pubkey) -> Self { pub fn new(signer: &dyn Signer, address: &Pubkey) -> Result<Self, SignerError> {
let secret = ElGamalSecretKey::new(signing_keypair, address); let secret = ElGamalSecretKey::new(signer, address)?;
let public = ElGamalPubkey::new(&secret); let public = ElGamalPubkey::new(&secret);
Self { public, secret } Ok(Self { public, secret })
} }
/// Generates the public and secret keys for ElGamal encryption. /// Generates the public and secret keys for ElGamal encryption.
@ -292,11 +296,26 @@ impl fmt::Display for ElGamalPubkey {
#[zeroize(drop)] #[zeroize(drop)]
pub struct ElGamalSecretKey(Scalar); pub struct ElGamalSecretKey(Scalar);
impl ElGamalSecretKey { impl ElGamalSecretKey {
pub fn new(signing_keypair: &SigningKeypair, address: &Pubkey) -> Self { pub fn new(signer: &dyn Signer, address: &Pubkey) -> Result<Self, SignerError> {
let mut hashable = [0_u8; 64]; let message = Message::new(
hashable[..32].copy_from_slice(&signing_keypair.secret().to_bytes()); &[Instruction::new_with_bytes(
hashable[32..].copy_from_slice(&address.to_bytes()); *address,
ElGamalSecretKey(Scalar::hash_from_bytes::<Sha3_512>(&hashable)) b"ElGamalSecretKey",
vec![],
)],
Some(&signer.try_pubkey()?),
);
let signature = signer.try_sign_message(&message.serialize())?;
// Some `Signer` implementations return the default signature, which is not suitable for
// use as key material
if signature == Signature::default() {
Err(SignerError::Custom("Rejecting default signature".into()))
} else {
Ok(ElGamalSecretKey(Scalar::hash_from_bytes::<Sha3_512>(
&signature.as_ref(),
)))
}
} }
pub fn get_scalar(&self) -> Scalar { pub fn get_scalar(&self) -> Scalar {
@ -494,8 +513,11 @@ define_div_variants!(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use {
use crate::encryption::pedersen::Pedersen; super::*,
crate::encryption::pedersen::Pedersen,
solana_sdk::{signature::Keypair, signer::null_signer::NullSigner},
};
#[test] #[test]
fn test_encrypt_decrypt_correctness() { fn test_encrypt_decrypt_correctness() {
@ -723,4 +745,22 @@ mod tests {
ElGamalKeypair::default().write_json_file(&outfile).unwrap(); ElGamalKeypair::default().write_json_file(&outfile).unwrap();
ElGamalKeypair::read_json_file(&outfile).unwrap(); ElGamalKeypair::read_json_file(&outfile).unwrap();
} }
#[test]
fn test_secret_key_new() {
let keypair1 = Keypair::new();
let keypair2 = Keypair::new();
assert_ne!(
ElGamalSecretKey::new(&keypair1, &Pubkey::default())
.unwrap()
.0,
ElGamalSecretKey::new(&keypair2, &Pubkey::default())
.unwrap()
.0,
);
let null_signer = NullSigner::new(&Pubkey::default());
assert!(ElGamalSecretKey::new(&null_signer, &Pubkey::default()).is_err());
}
} }