2021-10-17 08:37:28 -07:00
|
|
|
#[cfg(not(target_arch = "bpf"))]
|
|
|
|
use {
|
|
|
|
aes_gcm::{aead::Aead, Aes128Gcm, NewAead},
|
|
|
|
rand::{rngs::OsRng, CryptoRng, Rng, RngCore},
|
|
|
|
};
|
2021-10-15 03:44:56 -07:00
|
|
|
use {
|
2021-10-17 08:37:28 -07:00
|
|
|
arrayref::{array_ref, array_refs},
|
2021-10-21 17:48:25 -07:00
|
|
|
solana_sdk::{
|
|
|
|
instruction::Instruction,
|
|
|
|
message::Message,
|
|
|
|
pubkey::Pubkey,
|
|
|
|
signature::Signature,
|
|
|
|
signer::{Signer, SignerError},
|
|
|
|
},
|
2021-10-17 08:16:18 -07:00
|
|
|
std::convert::TryInto,
|
2021-10-15 03:44:56 -07:00
|
|
|
zeroize::Zeroize,
|
2021-10-14 09:19:22 -07:00
|
|
|
};
|
2021-10-08 06:12:54 -07:00
|
|
|
|
2021-10-17 08:37:28 -07:00
|
|
|
struct Aes;
|
|
|
|
impl Aes {
|
2021-10-11 12:16:29 -07:00
|
|
|
#[cfg(not(target_arch = "bpf"))]
|
|
|
|
#[allow(clippy::new_ret_no_self)]
|
2021-10-17 08:16:18 -07:00
|
|
|
fn keygen<T: RngCore + CryptoRng>(rng: &mut T) -> AesKey {
|
2021-10-17 08:37:28 -07:00
|
|
|
let random_bytes = rng.gen::<[u8; 16]>();
|
2021-10-17 08:16:18 -07:00
|
|
|
AesKey(random_bytes)
|
2021-10-07 15:21:31 -07:00
|
|
|
}
|
|
|
|
|
2021-10-11 12:16:29 -07:00
|
|
|
#[cfg(not(target_arch = "bpf"))]
|
2021-10-17 08:16:18 -07:00
|
|
|
fn encrypt(sk: &AesKey, amount: u64) -> AesCiphertext {
|
|
|
|
let plaintext = amount.to_le_bytes();
|
|
|
|
let nonce = OsRng.gen::<[u8; 12]>();
|
2021-10-08 06:12:54 -07:00
|
|
|
|
2021-10-17 08:16:18 -07:00
|
|
|
// TODO: it seems like encryption cannot fail, but will need to double check
|
|
|
|
let ciphertext = Aes128Gcm::new(&sk.0.into())
|
2021-10-17 08:37:28 -07:00
|
|
|
.encrypt(&nonce.into(), plaintext.as_ref())
|
|
|
|
.unwrap();
|
2021-10-08 06:12:54 -07:00
|
|
|
|
2021-10-17 08:16:18 -07:00
|
|
|
AesCiphertext {
|
|
|
|
nonce,
|
|
|
|
ciphertext: ciphertext.try_into().unwrap(),
|
|
|
|
}
|
2021-10-07 15:21:31 -07:00
|
|
|
}
|
|
|
|
|
2021-10-11 12:16:29 -07:00
|
|
|
#[cfg(not(target_arch = "bpf"))]
|
2021-10-17 08:16:18 -07:00
|
|
|
fn decrypt(sk: &AesKey, ct: &AesCiphertext) -> Option<u64> {
|
2021-10-17 08:37:28 -07:00
|
|
|
let plaintext =
|
|
|
|
Aes128Gcm::new(&sk.0.into()).decrypt(&ct.nonce.into(), ct.ciphertext.as_ref());
|
2021-10-08 06:12:54 -07:00
|
|
|
|
2021-10-17 08:16:18 -07:00
|
|
|
if let Ok(plaintext) = plaintext {
|
|
|
|
let amount_bytes: [u8; 8] = plaintext.try_into().unwrap();
|
|
|
|
Some(u64::from_le_bytes(amount_bytes))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2021-10-07 15:21:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-11 12:16:29 -07:00
|
|
|
#[derive(Debug, Zeroize)]
|
2021-10-17 08:16:18 -07:00
|
|
|
pub struct AesKey([u8; 16]);
|
|
|
|
impl AesKey {
|
2021-10-21 17:48:25 -07:00
|
|
|
pub fn new(signer: &dyn Signer, address: &Pubkey) -> Result<Self, SignerError> {
|
|
|
|
let message = Message::new(
|
|
|
|
&[Instruction::new_with_bytes(*address, b"AesKey", 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(AesKey(signature.as_ref()[..16].try_into().unwrap()))
|
|
|
|
}
|
2021-10-17 08:16:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn random<T: RngCore + CryptoRng>(rng: &mut T) -> Self {
|
2021-10-17 08:37:28 -07:00
|
|
|
Aes::keygen(rng)
|
2021-10-17 08:16:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn encrypt(&self, amount: u64) -> AesCiphertext {
|
2021-10-17 08:37:28 -07:00
|
|
|
Aes::encrypt(self, amount)
|
2021-10-07 15:21:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-08 06:12:54 -07:00
|
|
|
#[derive(Debug)]
|
2021-10-17 08:16:18 -07:00
|
|
|
pub struct AesCiphertext {
|
|
|
|
pub nonce: [u8; 12],
|
|
|
|
pub ciphertext: [u8; 24],
|
2021-10-07 15:21:31 -07:00
|
|
|
}
|
2021-10-17 08:16:18 -07:00
|
|
|
impl AesCiphertext {
|
|
|
|
pub fn decrypt(&self, key: &AesKey) -> Option<u64> {
|
2021-10-17 08:37:28 -07:00
|
|
|
Aes::decrypt(key, self)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_bytes(&self) -> [u8; 36] {
|
|
|
|
let mut buf = [0_u8; 36];
|
|
|
|
buf[..12].copy_from_slice(&self.nonce);
|
|
|
|
buf[12..].copy_from_slice(&self.ciphertext);
|
|
|
|
buf
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_bytes(bytes: &[u8]) -> Option<AesCiphertext> {
|
|
|
|
if bytes.len() != 36 {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let bytes = array_ref![bytes, 0, 36];
|
|
|
|
let (nonce, ciphertext) = array_refs![bytes, 12, 24];
|
|
|
|
Some(AesCiphertext {
|
|
|
|
nonce: *nonce,
|
|
|
|
ciphertext: *ciphertext,
|
|
|
|
})
|
2021-10-14 09:56:37 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-17 08:45:41 -07:00
|
|
|
impl Default for AesCiphertext {
|
|
|
|
fn default() -> Self {
|
|
|
|
AesCiphertext {
|
|
|
|
nonce: [0_u8; 12],
|
|
|
|
ciphertext: [0_u8; 24],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-08 06:12:54 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-10-21 17:48:25 -07:00
|
|
|
use {
|
|
|
|
super::*,
|
|
|
|
solana_sdk::{signature::Keypair, signer::null_signer::NullSigner},
|
|
|
|
};
|
2021-10-08 06:12:54 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_aes_encrypt_decrypt_correctness() {
|
2021-10-17 08:16:18 -07:00
|
|
|
let key = AesKey::random(&mut OsRng);
|
2021-10-08 06:12:54 -07:00
|
|
|
let amount = 55;
|
|
|
|
|
2021-10-17 08:16:18 -07:00
|
|
|
let ct = key.encrypt(amount);
|
|
|
|
let decrypted_amount = ct.decrypt(&key).unwrap();
|
2021-10-08 06:12:54 -07:00
|
|
|
|
|
|
|
assert_eq!(amount, decrypted_amount);
|
|
|
|
}
|
2021-10-21 17:48:25 -07:00
|
|
|
|
|
|
|
#[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());
|
|
|
|
}
|
2021-10-08 06:12:54 -07:00
|
|
|
}
|