ElGamal: add From impls; deprecate from/to_bytes (#246)
* ElGamal: add From impls; deprecate from/to_bytes * variable names
This commit is contained in:
parent
87fc227b51
commit
855a0c1a92
|
@ -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,9 +1056,9 @@ mod tests {
|
|||
assert!(Path::new(&outfile).exists());
|
||||
assert_eq!(
|
||||
keypair_vec,
|
||||
ElGamalKeypair::read_json_file(&outfile)
|
||||
.unwrap()
|
||||
.to_bytes()
|
||||
Into::<[u8; ELGAMAL_KEYPAIR_LEN]>::into(
|
||||
ElGamalKeypair::read_json_file(&outfile).unwrap()
|
||||
)
|
||||
.to_vec()
|
||||
);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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()),
|
||||
};
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 };
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue