From e6528bd68305c04c90c088a086773cc22a1cb71e Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 30 Dec 2018 03:53:11 +0000 Subject: [PATCH] Create new module for public key code. --- src/ed25519.rs | 242 +------------------------------------------ src/lib.rs | 1 + src/public.rs | 272 +++++++++++++++++++++++++++++++++++++++++++++++++ src/secret.rs | 3 +- 4 files changed, 275 insertions(+), 243 deletions(-) create mode 100644 src/public.rs diff --git a/src/ed25519.rs b/src/ed25519.rs index e992c17..a6d5f31 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -10,7 +10,6 @@ //! A Rust implementation of ed25519 key generation, signing, and verification. use core::default::Default; -use core::fmt::Debug; use rand::CryptoRng; use rand::Rng; @@ -30,226 +29,15 @@ pub use curve25519_dalek::digest::Digest; use curve25519_dalek::digest::generic_array::typenum::U64; use curve25519_dalek::constants; -use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::scalar::Scalar; pub use crate::constants::*; pub use crate::errors::*; +pub use crate::public::*; pub use crate::secret::*; pub 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 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)) - } -} - -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 { - /// 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)) - } - } -} - /// Verify a batch of `signatures` on `messages` with their respective `public_keys`. /// /// # Inputs @@ -363,34 +151,6 @@ pub fn verify_batch( } } -#[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) - } -} - /// An ed25519 keypair. #[derive(Debug, Default)] // we derive Default in order to use the clear() method in Drop pub struct Keypair { diff --git a/src/lib.rs b/src/lib.rs index db1b291..de05f8b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -258,6 +258,7 @@ extern crate serde; mod constants; mod ed25519; mod errors; +mod public; mod secret; mod signature; diff --git a/src/public.rs b/src/public.rs new file mode 100644 index 0000000..60e504a --- /dev/null +++ b/src/public.rs @@ -0,0 +1,272 @@ +// -*- mode: rust; -*- +// +// This file is part of ed25519-dalek. +// Copyright (c) 2017-2018 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::Digest; +use curve25519_dalek::digest::generic_array::typenum::U64; +use curve25519_dalek::edwards::CompressedEdwardsY; +use curve25519_dalek::edwards::EdwardsPoint; +use curve25519_dalek::scalar::Scalar; + +pub use sha2::Sha512; + +#[cfg(feature = "serde")] +use serde::{Serialize, Deserialize}; +#[cfg(feature = "serde")] +use serde::{Serializer, Deserializer}; +#[cfg(feature = "serde")] +use serde::de::Error as SerdeError; +#[cfg(feature = "serde")] +use serde::de::Visitor; + +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) + } +} diff --git a/src/secret.rs b/src/secret.rs index e953baa..d5c3e67 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -35,10 +35,9 @@ use serde::de::Visitor; use crate::constants::*; use crate::errors::*; +use crate::public::*; use crate::signature::*; -use crate::PublicKey; - /// An EdDSA secret key. #[derive(Default)] // we derive Default in order to use the clear() method in Drop pub struct SecretKey(pub (crate) [u8; SECRET_KEY_LENGTH]);