// -*- mode: rust; -*- // // This file is part of ed25519-dalek. // Copyright (c) 2017-2019 isis lovecruft // See LICENSE for licensing information. // // Authors: // - isis agora lovecruft //! ed25519 public keys. use core::fmt::Debug; use curve25519_dalek::constants; use curve25519_dalek::digest::generic_array::typenum::U64; use curve25519_dalek::digest::Digest; use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::scalar::Scalar; pub use sha2::Sha512; #[cfg(feature = "serde")] use serde::de::Error as SerdeError; #[cfg(feature = "serde")] use serde::de::Visitor; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "serde")] use serde::{Deserializer, Serializer}; use crate::constants::*; use crate::errors::*; use crate::secret::*; use crate::signature::*; /// An ed25519 public key. #[derive(Copy, Clone, Default, Eq, PartialEq)] pub struct PublicKey(pub(crate) CompressedEdwardsY, pub(crate) EdwardsPoint); impl Debug for PublicKey { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { write!(f, "PublicKey({:?}), {:?})", self.0, self.1) } } impl AsRef<[u8]> for PublicKey { fn as_ref(&self) -> &[u8] { self.as_bytes() } } impl<'a> From<&'a SecretKey> for PublicKey { /// Derive this public key from its corresponding `SecretKey`. fn from(secret_key: &SecretKey) -> PublicKey { let mut h: Sha512 = Sha512::new(); let mut hash: [u8; 64] = [0u8; 64]; let mut digest: [u8; 32] = [0u8; 32]; h.input(secret_key.as_bytes()); hash.copy_from_slice(h.result().as_slice()); digest.copy_from_slice(&hash[..32]); PublicKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut digest) } } impl<'a> From<&'a ExpandedSecretKey> for PublicKey { /// Derive this public key from its corresponding `ExpandedSecretKey`. fn from(expanded_secret_key: &ExpandedSecretKey) -> PublicKey { let mut bits: [u8; 32] = expanded_secret_key.key.to_bytes(); PublicKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut bits) } } impl PublicKey { /// Convert this public key to a byte array. #[inline] pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] { self.0.to_bytes() } /// View this public key as a byte array. #[inline] pub fn as_bytes<'a>(&'a self) -> &'a [u8; PUBLIC_KEY_LENGTH] { &(self.0).0 } /// Construct a `PublicKey` from a slice of bytes. /// /// # Warning /// /// The caller is responsible for ensuring that the bytes passed into this /// method actually represent a `curve25519_dalek::curve::CompressedEdwardsY` /// and that said compressed point is actually a point on the curve. /// /// # Example /// /// ``` /// # extern crate ed25519_dalek; /// # /// use ed25519_dalek::PublicKey; /// use ed25519_dalek::PUBLIC_KEY_LENGTH; /// use ed25519_dalek::SignatureError; /// /// # fn doctest() -> Result { /// let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [ /// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, /// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]; /// /// let public_key = PublicKey::from_bytes(&public_key_bytes)?; /// # /// # Ok(public_key) /// # } /// # /// # fn main() { /// # doctest(); /// # } /// ``` /// /// # Returns /// /// A `Result` whose okay value is an EdDSA `PublicKey` or whose error value /// is an `SignatureError` describing the error that occurred. #[inline] pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != PUBLIC_KEY_LENGTH { return Err(SignatureError(InternalError::BytesLengthError { name: "PublicKey", length: PUBLIC_KEY_LENGTH, })); } let mut bits: [u8; 32] = [0u8; 32]; bits.copy_from_slice(&bytes[..32]); let compressed = CompressedEdwardsY(bits); let point = compressed .decompress() .ok_or(SignatureError(InternalError::PointDecompressionError))?; Ok(PublicKey(compressed, point)) } /// Internal utility function for mangling the bits of a (formerly /// mathematically well-defined) "scalar" and multiplying it to produce a /// public key. fn mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key( bits: &mut [u8; 32], ) -> PublicKey { bits[0] &= 248; bits[31] &= 127; bits[31] |= 64; let point = &Scalar::from_bits(*bits) * &constants::ED25519_BASEPOINT_TABLE; let compressed = point.compress(); PublicKey(compressed, point) } /// Verify a signature on a message with this keypair's public key. /// /// # Return /// /// Returns `Ok(())` if the signature is valid, and `Err` otherwise. #[allow(non_snake_case)] pub fn verify( &self, message: &[u8], signature: &Signature ) -> Result<(), SignatureError> { let mut h: Sha512 = Sha512::new(); let R: EdwardsPoint; let k: Scalar; let minus_A: EdwardsPoint = -self.1; h.input(signature.R.as_bytes()); h.input(self.as_bytes()); h.input(&message); k = Scalar::from_hash(h); R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); if R.compress() == signature.R { Ok(()) } else { Err(SignatureError(InternalError::VerifyError)) } } /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm. /// /// # Inputs /// /// * `prehashed_message` is an instantiated hash digest with 512-bits of /// output which has had the message to be signed previously fed into its /// state. /// * `context` is an optional context string, up to 255 bytes inclusive, /// which may be used to provide additional domain separation. If not /// set, this will default to an empty string. /// * `signature` is a purported Ed25519ph [`Signature`] on the `prehashed_message`. /// /// # Returns /// /// Returns `true` if the `signature` was a valid signature created by this /// `Keypair` on the `prehashed_message`. /// /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1 #[allow(non_snake_case)] pub fn verify_prehashed( &self, prehashed_message: D, context: Option<&[u8]>, signature: &Signature, ) -> Result<(), SignatureError> where D: Digest, { let mut h: Sha512 = Sha512::default(); let R: EdwardsPoint; let k: Scalar; let ctx: &[u8] = context.unwrap_or(b""); debug_assert!(ctx.len() <= 255, "The context must not be longer than 255 octets."); let minus_A: EdwardsPoint = -self.1; h.input(b"SigEd25519 no Ed25519 collisions"); h.input(&[1]); // Ed25519ph h.input(&[ctx.len() as u8]); h.input(ctx); h.input(signature.R.as_bytes()); h.input(self.as_bytes()); h.input(prehashed_message.result().as_slice()); k = Scalar::from_hash(h); R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); if R.compress() == signature.R { Ok(()) } else { Err(SignatureError(InternalError::VerifyError)) } } } #[cfg(feature = "serde")] impl Serialize for PublicKey { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_bytes(self.as_bytes()) } } #[cfg(feature = "serde")] impl<'d> Deserialize<'d> for PublicKey { fn deserialize(deserializer: D) -> Result where D: Deserializer<'d>, { struct PublicKeyVisitor; impl<'d> Visitor<'d> for PublicKeyVisitor { type Value = PublicKey; fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { formatter.write_str( "An ed25519 public key as a 32-byte compressed point, as specified in RFC8032", ) } fn visit_bytes(self, bytes: &[u8]) -> Result where E: SerdeError, { PublicKey::from_bytes(bytes).or(Err(SerdeError::invalid_length(bytes.len(), &self))) } } deserializer.deserialize_bytes(PublicKeyVisitor) } }