ed25519_zebra/
verification_key.rs

1use core::convert::{TryFrom, TryInto};
2use curve25519_dalek::{
3    digest::Update,
4    edwards::{CompressedEdwardsY, EdwardsPoint},
5    scalar::Scalar,
6    traits::IsIdentity,
7};
8use sha2::Sha512;
9use zeroize::DefaultIsZeroes;
10
11pub use ed25519::{
12    signature::{Signer, Verifier},
13    Signature,
14};
15
16#[cfg(feature = "pkcs8")]
17use pkcs8::der::asn1::BitStringRef;
18#[cfg(feature = "pkcs8")]
19use pkcs8::spki::{
20    AlgorithmIdentifierRef, DecodePublicKey, EncodePublicKey, SubjectPublicKeyInfoRef,
21};
22#[cfg(feature = "pkcs8")]
23use pkcs8::{Document, ObjectIdentifier};
24
25use crate::Error;
26
27/// A refinement type for `[u8; 32]` indicating that the bytes represent an
28/// encoding of an Ed25519 verification key.
29///
30/// This is useful for representing an encoded verification key, while the
31/// [`VerificationKey`] type in this library caches other decoded state used in
32/// signature verification.
33///
34/// A `VerificationKeyBytes` can be used to verify a single signature using the
35/// following idiom:
36/// ```
37/// use core::convert::TryFrom;
38/// # use rand::thread_rng;
39/// # use ed25519_zebra::*;
40/// # let msg = b"Zcash";
41/// # let sk = SigningKey::new(thread_rng());
42/// # let sig = sk.sign(msg);
43/// # let vk_bytes = VerificationKeyBytes::from(&sk);
44/// VerificationKey::try_from(vk_bytes)
45///     .and_then(|vk| vk.verify(&sig, msg));
46/// ```
47#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
48#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
49pub struct VerificationKeyBytes(pub(crate) [u8; 32]);
50
51impl core::fmt::Debug for VerificationKeyBytes {
52    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
53        fmt.debug_tuple("VerificationKeyBytes")
54            .field(&hex::encode(self.0))
55            .finish()
56    }
57}
58
59impl AsRef<[u8]> for VerificationKeyBytes {
60    fn as_ref(&self) -> &[u8] {
61        &self.0[..]
62    }
63}
64
65impl TryFrom<&[u8]> for VerificationKeyBytes {
66    type Error = Error;
67    fn try_from(slice: &[u8]) -> Result<VerificationKeyBytes, Self::Error> {
68        if slice.len() == 32 {
69            let mut bytes = [0u8; 32];
70            bytes[..].copy_from_slice(slice);
71            Ok(bytes.into())
72        } else {
73            Err(Error::InvalidSliceLength)
74        }
75    }
76}
77
78impl From<[u8; 32]> for VerificationKeyBytes {
79    fn from(bytes: [u8; 32]) -> VerificationKeyBytes {
80        VerificationKeyBytes(bytes)
81    }
82}
83
84impl From<VerificationKeyBytes> for [u8; 32] {
85    fn from(refined: VerificationKeyBytes) -> [u8; 32] {
86        refined.0
87    }
88}
89
90#[cfg(feature = "pkcs8")]
91impl<'a> TryFrom<SubjectPublicKeyInfoRef<'a>> for VerificationKeyBytes {
92    type Error = Error;
93
94    fn try_from(spki: SubjectPublicKeyInfoRef) -> Result<VerificationKeyBytes, Error> {
95        Ok(VerificationKeyBytes::try_from(spki.subject_public_key.as_bytes().unwrap()).unwrap())
96    }
97}
98
99/// A valid Ed25519 verification key.
100///
101/// This is also called a public key by other implementations.
102///
103/// This type holds decompressed state used in signature verification; if the
104/// verification key may not be used immediately, it is probably better to use
105/// [`VerificationKeyBytes`], which is a refinement type for `[u8; 32]`.
106///
107/// ## Zcash-specific consensus properties
108///
109/// Ed25519 checks are described in [§5.4.5][ps] of the Zcash protocol specification and in
110/// [ZIP 215].  The verification criteria for an (encoded) verification key `A_bytes` are:
111///
112/// * `A_bytes` MUST be an encoding of a point `A` on the twisted Edwards form of
113///   Curve25519, and non-canonical encodings MUST be accepted;
114///
115/// [ps]: https://zips.z.cash/protocol/protocol.pdf#concreteed25519
116#[derive(Copy, Clone, Debug)]
117#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
118#[cfg_attr(feature = "serde", serde(try_from = "VerificationKeyBytes"))]
119#[cfg_attr(feature = "serde", serde(into = "VerificationKeyBytes"))]
120#[allow(non_snake_case)]
121pub struct VerificationKey {
122    pub(crate) A_bytes: VerificationKeyBytes,
123    pub(crate) minus_A: EdwardsPoint,
124}
125
126impl From<VerificationKey> for VerificationKeyBytes {
127    fn from(vk: VerificationKey) -> VerificationKeyBytes {
128        vk.A_bytes
129    }
130}
131
132impl AsRef<[u8]> for VerificationKey {
133    fn as_ref(&self) -> &[u8] {
134        &self.A_bytes.0[..]
135    }
136}
137
138impl Default for VerificationKey {
139    fn default() -> VerificationKey {
140        let identity: EdwardsPoint = Default::default();
141        let identity_bytes = identity.compress().to_bytes();
142
143        VerificationKey {
144            A_bytes: VerificationKeyBytes::from(identity_bytes),
145            minus_A: -identity,
146        }
147    }
148}
149
150impl DefaultIsZeroes for VerificationKey {}
151
152impl From<VerificationKey> for [u8; 32] {
153    fn from(vk: VerificationKey) -> [u8; 32] {
154        vk.A_bytes.0
155    }
156}
157
158impl TryFrom<VerificationKeyBytes> for VerificationKey {
159    type Error = Error;
160    #[allow(non_snake_case)]
161    fn try_from(bytes: VerificationKeyBytes) -> Result<Self, Self::Error> {
162        // * `A_bytes` and `R_bytes` MUST be encodings of points `A` and `R` respectively on the
163        //   twisted Edwards form of Curve25519, and non-canonical encodings MUST be accepted;
164        let A = CompressedEdwardsY(bytes.0)
165            .decompress()
166            .ok_or(Error::MalformedPublicKey)?;
167
168        Ok(VerificationKey {
169            A_bytes: bytes,
170            minus_A: -A,
171        })
172    }
173}
174
175impl TryFrom<&[u8]> for VerificationKey {
176    type Error = Error;
177    fn try_from(slice: &[u8]) -> Result<VerificationKey, Error> {
178        VerificationKeyBytes::try_from(slice).and_then(|vkb| vkb.try_into())
179    }
180}
181
182impl TryFrom<[u8; 32]> for VerificationKey {
183    type Error = Error;
184    fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
185        VerificationKeyBytes::from(bytes).try_into()
186    }
187}
188
189#[cfg(feature = "pkcs8")]
190impl EncodePublicKey for VerificationKey {
191    /// Serialize [`VerificationKey`] to an ASN.1 DER-encoded document.
192    fn to_public_key_der(&self) -> pkcs8::spki::Result<Document> {
193        let alg_info = AlgorithmIdentifierRef {
194            oid: ObjectIdentifier::new_unwrap("1.3.101.112"), // RFC 8410
195            parameters: None,
196        };
197        SubjectPublicKeyInfoRef {
198            algorithm: alg_info,
199            subject_public_key: BitStringRef::from_bytes(&self.A_bytes.0[..])?,
200        }
201        .try_into()
202    }
203}
204
205#[cfg(feature = "pkcs8")]
206impl DecodePublicKey for VerificationKey {
207    /// Deserialize [`VerificationKey`] from ASN.1 DER bytes (32 bytes).
208    fn from_public_key_der(bytes: &[u8]) -> Result<Self, pkcs8::spki::Error> {
209        let spki = SubjectPublicKeyInfoRef::try_from(bytes).unwrap();
210        let pk_bytes = spki.subject_public_key.as_bytes().unwrap();
211        Ok(Self::try_from(pk_bytes).unwrap())
212    }
213}
214
215impl Verifier<Signature> for VerificationKey {
216    /// Verify a [`Signature`] object against a given [`VerificationKey`].
217    fn verify(
218        &self,
219        message: &[u8],
220        signature: &Signature,
221    ) -> Result<(), ed25519::signature::Error> {
222        self.verify(signature, message)
223            .map_err(|_| ed25519::signature::Error::new())
224    }
225}
226
227impl VerificationKey {
228    /// Verify a purported `signature` on the given `msg`.
229    ///
230    /// ## Zcash-specific consensus properties
231    ///
232    /// Ed25519 checks are described in [§5.4.5][ps] of the Zcash protocol specification and in
233    /// [ZIP215].  The verification criteria for an (encoded) signature `(R_bytes, s_bytes)` with
234    /// (encoded) verification key `A_bytes` are:
235    ///
236    /// * `A_bytes` and `R_bytes` MUST be encodings of points `A` and `R` respectively on the
237    ///   twisted Edwards form of Curve25519, and non-canonical encodings MUST be accepted;
238    ///
239    /// * `s_bytes` MUST represent an integer `s` less than `l`, the order of the prime-order
240    ///   subgroup of Curve25519;
241    ///
242    /// * the verification equation `[8][s]B = [8]R + [8][k]A` MUST be satisfied;
243    ///
244    /// * the alternate verification equation `[s]B = R + [k]A`, allowed by RFC 8032, MUST NOT be
245    ///   used.
246    ///
247    /// [ps]: https://zips.z.cash/protocol/protocol.pdf#concreteed25519
248    /// [ZIP215]: https://zips.z.cash/zip-0215
249    pub fn verify(&self, signature: &Signature, msg: &[u8]) -> Result<(), Error> {
250        let k = Scalar::from_hash(
251            Sha512::default()
252                .chain(&signature.r_bytes()[..])
253                .chain(&self.A_bytes.0[..])
254                .chain(msg),
255        );
256        self.verify_prehashed(signature, k)
257    }
258
259    /// Verify a signature with a prehashed `k` value. Note that this is not the
260    /// same as "prehashing" in RFC8032.
261    #[allow(non_snake_case)]
262    pub(crate) fn verify_prehashed(&self, signature: &Signature, k: Scalar) -> Result<(), Error> {
263        // `s_bytes` MUST represent an integer less than the prime `l`.
264        let s = Option::<Scalar>::from(Scalar::from_canonical_bytes(*signature.s_bytes()))
265            .ok_or(Error::InvalidSignature)?;
266        // `R_bytes` MUST be an encoding of a point on the twisted Edwards form of Curve25519.
267        let R = CompressedEdwardsY(*signature.r_bytes())
268            .decompress()
269            .ok_or(Error::InvalidSignature)?;
270        // We checked the encoding of A_bytes when constructing `self`.
271
272        //       [8][s]B = [8]R + [8][k]A
273        // <=>   [8]R = [8][s]B - [8][k]A
274        // <=>   0 = [8](R - ([s]B - [k]A))
275        // <=>   0 = [8](R - R')  where R' = [s]B - [k]A
276        let R_prime = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &self.minus_A, &s);
277
278        if (R - R_prime).mul_by_cofactor().is_identity() {
279            Ok(())
280        } else {
281            Err(Error::InvalidSignature)
282        }
283    }
284}