Generate AesKey/ElGamalSecretKey from an ed25519 signature instead of secret key
This commit is contained in:
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user