ElGamal: add From impls; deprecate from/to_bytes (#246)

* ElGamal: add From impls; deprecate from/to_bytes

* variable names
This commit is contained in:
abcalphabet 2024-04-04 20:47:07 -03:00 committed by GitHub
parent 87fc227b51
commit 855a0c1a92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 130 additions and 28 deletions

View File

@ -68,7 +68,7 @@ const ELGAMAL_PUBKEY_LEN: usize = RISTRETTO_POINT_LEN;
const ELGAMAL_SECRET_KEY_LEN: usize = SCALAR_LEN;
/// Byte length of an ElGamal keypair
const ELGAMAL_KEYPAIR_LEN: usize = ELGAMAL_PUBKEY_LEN + ELGAMAL_SECRET_KEY_LEN;
pub const ELGAMAL_KEYPAIR_LEN: usize = ELGAMAL_PUBKEY_LEN + ELGAMAL_SECRET_KEY_LEN;
#[derive(Error, Clone, Debug, Eq, PartialEq)]
pub enum ElGamalError {
@ -82,6 +82,10 @@ pub enum ElGamalError {
CiphertextDeserialization,
#[error("failed to deserialize public key")]
PubkeyDeserialization,
#[error("failed to deserialize keypair")]
KeypairDeserialization,
#[error("failed to deserialize secret key")]
SecretKeyDeserialization,
}
/// Algorithm handle for the twisted ElGamal encryption scheme
@ -235,6 +239,8 @@ impl ElGamalKeypair {
&self.secret
}
#[deprecated(note = "please use `into()` instead")]
#[allow(deprecated)]
pub fn to_bytes(&self) -> [u8; ELGAMAL_KEYPAIR_LEN] {
let mut bytes = [0u8; ELGAMAL_KEYPAIR_LEN];
bytes[..ELGAMAL_PUBKEY_LEN].copy_from_slice(&self.public.to_bytes());
@ -242,6 +248,8 @@ impl ElGamalKeypair {
bytes
}
#[deprecated(note = "please use `try_from()` instead")]
#[allow(deprecated)]
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
if bytes.len() != ELGAMAL_KEYPAIR_LEN {
return None;
@ -256,7 +264,7 @@ impl ElGamalKeypair {
/// Reads a JSON-encoded keypair from a `Reader` implementor
pub fn read_json<R: Read>(reader: &mut R) -> Result<Self, Box<dyn error::Error>> {
let bytes: Vec<u8> = serde_json::from_reader(reader)?;
Self::from_bytes(&bytes).ok_or_else(|| {
Self::try_from(bytes.as_slice()).ok().ok_or_else(|| {
std::io::Error::new(std::io::ErrorKind::Other, "Invalid ElGamalKeypair").into()
})
}
@ -268,8 +276,8 @@ impl ElGamalKeypair {
/// Writes to a `Write` implementer with JSON-encoding
pub fn write_json<W: Write>(&self, writer: &mut W) -> Result<String, Box<dyn error::Error>> {
let bytes = self.to_bytes();
let json = serde_json::to_string(&bytes.to_vec())?;
let json =
serde_json::to_string(&Into::<[u8; ELGAMAL_KEYPAIR_LEN]>::into(self).as_slice())?;
writer.write_all(&json.clone().into_bytes())?;
Ok(json)
}
@ -293,6 +301,40 @@ impl EncodableKey for ElGamalKeypair {
}
}
impl TryFrom<&[u8]> for ElGamalKeypair {
type Error = ElGamalError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
if bytes.len() != ELGAMAL_KEYPAIR_LEN {
return Err(ElGamalError::KeypairDeserialization);
}
Ok(Self {
public: ElGamalPubkey::try_from(&bytes[..ELGAMAL_PUBKEY_LEN])?,
secret: ElGamalSecretKey::try_from(&bytes[ELGAMAL_PUBKEY_LEN..])?,
})
}
}
impl From<ElGamalKeypair> for [u8; ELGAMAL_KEYPAIR_LEN] {
fn from(keypair: ElGamalKeypair) -> Self {
let mut bytes = [0u8; ELGAMAL_KEYPAIR_LEN];
bytes[..ELGAMAL_PUBKEY_LEN]
.copy_from_slice(&Into::<[u8; ELGAMAL_PUBKEY_LEN]>::into(keypair.public));
bytes[ELGAMAL_PUBKEY_LEN..].copy_from_slice(keypair.secret.as_bytes());
bytes
}
}
impl From<&ElGamalKeypair> for [u8; ELGAMAL_KEYPAIR_LEN] {
fn from(keypair: &ElGamalKeypair) -> Self {
let mut bytes = [0u8; ELGAMAL_KEYPAIR_LEN];
bytes[..ELGAMAL_PUBKEY_LEN]
.copy_from_slice(&Into::<[u8; ELGAMAL_PUBKEY_LEN]>::into(keypair.public));
bytes[ELGAMAL_PUBKEY_LEN..].copy_from_slice(keypair.secret.as_bytes());
bytes
}
}
impl SeedDerivable for ElGamalKeypair {
fn from_seed(seed: &[u8]) -> Result<Self, Box<dyn error::Error>> {
let secret = ElGamalSecretKey::from_seed(seed)?;
@ -343,10 +385,12 @@ impl ElGamalPubkey {
&self.0
}
#[deprecated(note = "please use `into()` instead")]
pub fn to_bytes(&self) -> [u8; ELGAMAL_PUBKEY_LEN] {
self.0.compress().to_bytes()
}
#[deprecated(note = "please use `try_from()` instead")]
pub fn from_bytes(bytes: &[u8]) -> Option<ElGamalPubkey> {
if bytes.len() != ELGAMAL_PUBKEY_LEN {
return None;
@ -384,13 +428,13 @@ impl ElGamalPubkey {
impl EncodableKey for ElGamalPubkey {
fn read<R: Read>(reader: &mut R) -> Result<Self, Box<dyn error::Error>> {
let bytes: Vec<u8> = serde_json::from_reader(reader)?;
Self::from_bytes(&bytes).ok_or_else(|| {
Self::try_from(bytes.as_slice()).ok().ok_or_else(|| {
std::io::Error::new(std::io::ErrorKind::Other, "Invalid ElGamalPubkey").into()
})
}
fn write<W: Write>(&self, writer: &mut W) -> Result<String, Box<dyn error::Error>> {
let bytes = self.to_bytes();
let bytes = Into::<[u8; ELGAMAL_PUBKEY_LEN]>::into(*self);
let json = serde_json::to_string(&bytes.to_vec())?;
writer.write_all(&json.clone().into_bytes())?;
Ok(json)
@ -399,7 +443,38 @@ impl EncodableKey for ElGamalPubkey {
impl fmt::Display for ElGamalPubkey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", BASE64_STANDARD.encode(self.to_bytes()))
write!(
f,
"{}",
BASE64_STANDARD.encode(Into::<[u8; ELGAMAL_PUBKEY_LEN]>::into(*self))
)
}
}
impl TryFrom<&[u8]> for ElGamalPubkey {
type Error = ElGamalError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
if bytes.len() != ELGAMAL_PUBKEY_LEN {
return Err(ElGamalError::PubkeyDeserialization);
}
Ok(ElGamalPubkey(
CompressedRistretto::from_slice(bytes)
.decompress()
.ok_or(ElGamalError::PubkeyDeserialization)?,
))
}
}
impl From<ElGamalPubkey> for [u8; ELGAMAL_PUBKEY_LEN] {
fn from(pubkey: ElGamalPubkey) -> Self {
pubkey.0.compress().to_bytes()
}
}
impl From<&ElGamalPubkey> for [u8; ELGAMAL_PUBKEY_LEN] {
fn from(pubkey: &ElGamalPubkey) -> Self {
pubkey.0.compress().to_bytes()
}
}
@ -487,10 +562,12 @@ impl ElGamalSecretKey {
self.0.as_bytes()
}
#[deprecated(note = "please use `into()` instead")]
pub fn to_bytes(&self) -> [u8; ELGAMAL_SECRET_KEY_LEN] {
self.0.to_bytes()
}
#[deprecated(note = "please use `try_from()` instead")]
pub fn from_bytes(bytes: &[u8]) -> Option<ElGamalSecretKey> {
match bytes.try_into() {
Ok(bytes) => Scalar::from_canonical_bytes(bytes).map(ElGamalSecretKey),
@ -502,13 +579,13 @@ impl ElGamalSecretKey {
impl EncodableKey for ElGamalSecretKey {
fn read<R: Read>(reader: &mut R) -> Result<Self, Box<dyn error::Error>> {
let bytes: Vec<u8> = serde_json::from_reader(reader)?;
Self::from_bytes(&bytes).ok_or_else(|| {
Self::try_from(bytes.as_slice()).ok().ok_or_else(|| {
std::io::Error::new(std::io::ErrorKind::Other, "Invalid ElGamalSecretKey").into()
})
}
fn write<W: Write>(&self, writer: &mut W) -> Result<String, Box<dyn error::Error>> {
let bytes = self.to_bytes();
let bytes = Into::<[u8; ELGAMAL_SECRET_KEY_LEN]>::into(self);
let json = serde_json::to_string(&bytes.to_vec())?;
writer.write_all(&json.clone().into_bytes())?;
Ok(json)
@ -546,6 +623,31 @@ impl From<Scalar> for ElGamalSecretKey {
}
}
impl TryFrom<&[u8]> for ElGamalSecretKey {
type Error = ElGamalError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
match bytes.try_into() {
Ok(bytes) => Ok(ElGamalSecretKey::from(
Scalar::from_canonical_bytes(bytes)
.ok_or(ElGamalError::SecretKeyDeserialization)?,
)),
_ => Err(ElGamalError::SecretKeyDeserialization),
}
}
}
impl From<ElGamalSecretKey> for [u8; ELGAMAL_SECRET_KEY_LEN] {
fn from(secret_key: ElGamalSecretKey) -> Self {
secret_key.0.to_bytes()
}
}
impl From<&ElGamalSecretKey> for [u8; ELGAMAL_SECRET_KEY_LEN] {
fn from(secret_key: &ElGamalSecretKey) -> Self {
secret_key.0.to_bytes()
}
}
impl Eq for ElGamalSecretKey {}
impl PartialEq for ElGamalSecretKey {
fn eq(&self, other: &Self) -> bool {
@ -954,10 +1056,10 @@ mod tests {
assert!(Path::new(&outfile).exists());
assert_eq!(
keypair_vec,
ElGamalKeypair::read_json_file(&outfile)
.unwrap()
.to_bytes()
.to_vec()
Into::<[u8; ELGAMAL_KEYPAIR_LEN]>::into(
ElGamalKeypair::read_json_file(&outfile).unwrap()
)
.to_vec()
);
#[cfg(unix)]

View File

@ -70,8 +70,8 @@ impl BatchedGroupedCiphertext2HandlesValidityProofData {
opening_lo: &PedersenOpening,
opening_hi: &PedersenOpening,
) -> Result<Self, ProofGenerationError> {
let pod_destination_pubkey = pod::ElGamalPubkey(destination_pubkey.to_bytes());
let pod_auditor_pubkey = pod::ElGamalPubkey(auditor_pubkey.to_bytes());
let pod_destination_pubkey = pod::ElGamalPubkey(destination_pubkey.into());
let pod_auditor_pubkey = pod::ElGamalPubkey(auditor_pubkey.into());
let pod_grouped_ciphertext_lo = (*grouped_ciphertext_lo).into();
let pod_grouped_ciphertext_hi = (*grouped_ciphertext_hi).into();

View File

@ -66,8 +66,8 @@ impl CiphertextCiphertextEqualityProofData {
destination_opening: &PedersenOpening,
amount: u64,
) -> Result<Self, ProofGenerationError> {
let pod_source_pubkey = pod::ElGamalPubkey(source_keypair.pubkey().to_bytes());
let pod_destination_pubkey = pod::ElGamalPubkey(destination_pubkey.to_bytes());
let pod_source_pubkey = pod::ElGamalPubkey(source_keypair.pubkey().into());
let pod_destination_pubkey = pod::ElGamalPubkey(destination_pubkey.into());
let pod_source_ciphertext = pod::ElGamalCiphertext(source_ciphertext.to_bytes());
let pod_destination_ciphertext = pod::ElGamalCiphertext(destination_ciphertext.to_bytes());

View File

@ -62,7 +62,7 @@ impl CiphertextCommitmentEqualityProofData {
amount: u64,
) -> Result<Self, ProofGenerationError> {
let context = CiphertextCommitmentEqualityProofContext {
pubkey: pod::ElGamalPubkey(keypair.pubkey().to_bytes()),
pubkey: pod::ElGamalPubkey(keypair.pubkey().into()),
ciphertext: pod::ElGamalCiphertext(ciphertext.to_bytes()),
commitment: pod::PedersenCommitment(commitment.to_bytes()),
};

View File

@ -63,8 +63,8 @@ impl GroupedCiphertext2HandlesValidityProofData {
amount: u64,
opening: &PedersenOpening,
) -> Result<Self, ProofGenerationError> {
let pod_destination_pubkey = pod::ElGamalPubkey(destination_pubkey.to_bytes());
let pod_auditor_pubkey = pod::ElGamalPubkey(auditor_pubkey.to_bytes());
let pod_destination_pubkey = pod::ElGamalPubkey(destination_pubkey.into());
let pod_auditor_pubkey = pod::ElGamalPubkey(auditor_pubkey.into());
let pod_grouped_ciphertext = (*grouped_ciphertext).into();
let context = GroupedCiphertext2HandlesValidityProofContext {

View File

@ -50,7 +50,7 @@ pub struct PubkeyValidityProofContext {
#[cfg(not(target_os = "solana"))]
impl PubkeyValidityData {
pub fn new(keypair: &ElGamalKeypair) -> Result<Self, ProofGenerationError> {
let pod_pubkey = pod::ElGamalPubkey(keypair.pubkey().to_bytes());
let pod_pubkey = pod::ElGamalPubkey(keypair.pubkey().into());
let context = PubkeyValidityProofContext { pubkey: pod_pubkey };

View File

@ -69,7 +69,7 @@ impl WithdrawData {
// current source balance
let final_ciphertext = current_ciphertext - &ElGamal::encode(amount);
let pod_pubkey = pod::ElGamalPubkey(keypair.pubkey().to_bytes());
let pod_pubkey = pod::ElGamalPubkey(keypair.pubkey().into());
let pod_final_ciphertext: pod::ElGamalCiphertext = final_ciphertext.into();
let context = WithdrawProofContext {

View File

@ -54,7 +54,7 @@ impl ZeroBalanceProofData {
keypair: &ElGamalKeypair,
ciphertext: &ElGamalCiphertext,
) -> Result<Self, ProofGenerationError> {
let pod_pubkey = pod::ElGamalPubkey(keypair.pubkey().to_bytes());
let pod_pubkey = pod::ElGamalPubkey(keypair.pubkey().into());
let pod_ciphertext = pod::ElGamalCiphertext(ciphertext.to_bytes());
let context = ZeroBalanceProofContext {

View File

@ -309,7 +309,7 @@ mod test {
#[test]
fn test_ciphertext_commitment_equality_proof_edge_cases() {
// if ElGamal public key zero (public key is invalid), then the proof should always reject
let public = ElGamalPubkey::from_bytes(&[0u8; 32]).unwrap();
let public = ElGamalPubkey::try_from([0u8; 32].as_slice()).unwrap();
let secret = ElGamalSecretKey::new_rand();
let elgamal_keypair = ElGamalKeypair::new_for_tests(public, secret);

View File

@ -274,7 +274,7 @@ mod test {
#[test]
fn test_grouped_ciphertext_validity_proof_edge_cases() {
// if destination public key zeroed, then the proof should always reject
let destination_pubkey = ElGamalPubkey::from_bytes(&[0u8; 32]).unwrap();
let destination_pubkey = ElGamalPubkey::try_from([0u8; 32].as_slice()).unwrap();
let auditor_keypair = ElGamalKeypair::new_rand();
let auditor_pubkey = auditor_keypair.pubkey();

View File

@ -286,7 +286,7 @@ mod test {
let mut prover_transcript = Transcript::new(b"test");
let mut verifier_transcript = Transcript::new(b"test");
let public = ElGamalPubkey::from_bytes(&[0u8; 32]).unwrap();
let public = ElGamalPubkey::try_from([0u8; 32].as_slice()).unwrap();
let ciphertext = public.encrypt(0_u64);
let proof = ZeroBalanceProof::new(&source_keypair, &ciphertext, &mut prover_transcript);

View File

@ -100,7 +100,7 @@ impl_from_str!(
#[cfg(not(target_os = "solana"))]
impl From<decoded::ElGamalPubkey> for ElGamalPubkey {
fn from(decoded_pubkey: decoded::ElGamalPubkey) -> Self {
Self(decoded_pubkey.to_bytes())
Self(decoded_pubkey.into())
}
}
@ -109,7 +109,7 @@ impl TryFrom<ElGamalPubkey> for decoded::ElGamalPubkey {
type Error = ElGamalError;
fn try_from(pod_pubkey: ElGamalPubkey) -> Result<Self, Self::Error> {
Self::from_bytes(&pod_pubkey.0).ok_or(ElGamalError::PubkeyDeserialization)
Self::try_from(pod_pubkey.0.as_slice())
}
}