Skip to main content

aptos_crypto_link/
traits.rs

1// Copyright (c) Aptos
2// SPDX-License-Identifier: Apache-2.0
3
4//! This module provides a generic set of traits for dealing with cryptographic primitives.
5//!
6//! For examples on how to use these traits, see the implementations of the [`crate::ed25519`]
7
8use crate::hash::{CryptoHash, CryptoHasher};
9use anyhow::Result;
10use core::convert::{From, TryFrom};
11use rand::{rngs::StdRng, CryptoRng, RngCore, SeedableRng};
12use serde::{de::DeserializeOwned, Serialize};
13use std::{fmt::Debug, hash::Hash};
14use thiserror::Error;
15
16/// An error type for key and signature validation issues, see [`ValidCryptoMaterial`][ValidCryptoMaterial].
17///
18/// This enum reflects there are two interesting causes of validation
19/// failure for the ingestion of key or signature material: deserialization errors
20/// (often, due to mangled material or curve equation failure for ECC) and
21/// validation errors (material recognizable but unacceptable for use,
22/// e.g. unsafe).
23#[derive(Clone, Debug, PartialEq, Eq, Error)]
24#[error("{:?}", self)]
25pub enum CryptoMaterialError {
26    /// Struct to be signed does not serialize correctly.
27    SerializationError,
28    /// Key or signature material does not deserialize correctly.
29    DeserializationError,
30    /// Key or signature material deserializes, but is otherwise not valid.
31    ValidationError,
32    /// Key, threshold or signature material does not have the expected size.
33    WrongLengthError,
34    /// Part of the signature or key is not canonical resulting to malleability issues.
35    CanonicalRepresentationError,
36    /// A curve point (i.e., a public key) lies on a small group.
37    SmallSubgroupError,
38    /// A curve point (i.e., a public key) does not satisfy the curve equation.
39    PointNotOnCurveError,
40    /// BitVec errors in accountable multi-sig schemes.
41    BitVecError(String),
42}
43
44/// The serialized length of the data that enables macro derived serialization and deserialization.
45pub trait Length {
46    /// The serialized length of the data
47    fn length(&self) -> usize;
48}
49
50/// Key or more generally crypto material with a notion of byte validation.
51///
52/// A type family for material that knows how to serialize and
53/// deserialize, as well as validate byte-encoded material. The
54/// validation must be implemented as a [`TryFrom`][TryFrom] which
55/// classifies its failures against the above
56/// [`CryptoMaterialError`][CryptoMaterialError].
57///
58/// This provides an implementation for a validation that relies on a
59/// round-trip to bytes and corresponding [`TryFrom`][TryFrom].
60pub trait ValidCryptoMaterial:
61    // The for<'a> exactly matches the assumption "deserializable from any lifetime".
62    for<'a> TryFrom<&'a [u8], Error=CryptoMaterialError> + Serialize + DeserializeOwned
63{
64    /// Convert the valid crypto material to bytes.
65    fn to_bytes(&self) -> Vec<u8>;
66}
67
68/// An extension to to/from Strings for [`ValidCryptoMaterial`][ValidCryptoMaterial].
69///
70/// Relies on [`hex`][::hex] for string encoding / decoding.
71/// No required fields, provides a default implementation.
72pub trait ValidCryptoMaterialStringExt: ValidCryptoMaterial {
73    /// When trying to convert from bytes, we simply decode the string into
74    /// bytes before checking if we can convert.
75    fn from_encoded_string(encoded_str: &str) -> std::result::Result<Self, CryptoMaterialError> {
76        // Strip 0x at beginning if there is one
77        let encoded_str = encoded_str.strip_prefix("0x").unwrap_or(encoded_str);
78
79        let bytes_out = ::hex::decode(encoded_str);
80        // We defer to `try_from` to make sure we only produce valid crypto materials.
81        bytes_out
82            // We reinterpret a failure to serialize: key is mangled someway.
83            .or(Err(CryptoMaterialError::DeserializationError))
84            .and_then(|ref bytes| Self::try_from(bytes))
85    }
86
87    /// A function to encode into hex-string after serializing.
88    fn to_encoded_string(&self) -> Result<String> {
89        Ok(format!("0x{}", ::hex::encode(&self.to_bytes())))
90    }
91}
92
93// There's nothing required in this extension, so let's just derive it
94// for anybody that has a ValidCryptoMaterial.
95impl<T: ValidCryptoMaterial> ValidCryptoMaterialStringExt for T {}
96
97/// A type family for key material that should remain secret and has an
98/// associated type of the [`PublicKey`][PublicKey] family.
99pub trait PrivateKey: Sized {
100    /// We require public / private types to be coupled, i.e. their
101    /// associated type is each other.
102    type PublicKeyMaterial: PublicKey<PrivateKeyMaterial = Self>;
103
104    /// Returns the associated public key
105    fn public_key(&self) -> Self::PublicKeyMaterial {
106        self.into()
107    }
108}
109
110/// A type family of valid keys that know how to sign.
111///
112/// This trait has a requirement on a `pub(crate)` marker trait meant to
113/// specifically limit its implementations to the present crate.
114///
115/// A trait for a [`ValidCryptoMaterial`][ValidCryptoMaterial] which knows how to sign a
116/// message, and return an associated `Signature` type.
117pub trait SigningKey:
118    PrivateKey<PublicKeyMaterial = <Self as SigningKey>::VerifyingKeyMaterial>
119    + ValidCryptoMaterial
120    + private::Sealed
121{
122    /// The associated verifying key type for this signing key.
123    type VerifyingKeyMaterial: VerifyingKey<SigningKeyMaterial = Self>;
124    /// The associated signature type for this signing key.
125    type SignatureMaterial: Signature<SigningKeyMaterial = Self>;
126
127    /// Signs an object that has an distinct domain-separation hasher and
128    /// that we know how to serialize. There is no pre-hashing into a
129    /// `HashValue` to be done by the caller.
130    ///
131    /// Note: this assumes serialization is infallible. See crates::bcs::ser
132    /// for a discussion of this assumption.
133    fn sign<T: CryptoHash + Serialize>(
134        &self,
135        message: &T,
136    ) -> Result<Self::SignatureMaterial, CryptoMaterialError>;
137
138    /// Signs a non-hash input message. For testing only.
139    #[cfg(any(test, feature = "fuzzing"))]
140    fn sign_arbitrary_message(&self, message: &[u8]) -> Self::SignatureMaterial;
141
142    /// Returns the associated verifying key
143    fn verifying_key(&self) -> Self::VerifyingKeyMaterial {
144        self.public_key()
145    }
146}
147
148/// Returns the signing message for the given message.
149/// It is used by `SigningKey#sign` function.
150pub fn signing_message<T: CryptoHash + Serialize>(
151    message: &T,
152) -> Result<Vec<u8>, CryptoMaterialError> {
153    let mut bytes = <T::Hasher as CryptoHasher>::seed().to_vec();
154    bcs::serialize_into(&mut bytes, &message)
155        .map_err(|_| CryptoMaterialError::SerializationError)?;
156    Ok(bytes)
157}
158
159/// A type for key material that can be publicly shared, and in asymmetric
160/// fashion, can be obtained from a [`PrivateKey`][PrivateKey]
161/// reference.
162/// This convertibility requirement ensures the existence of a
163/// deterministic, canonical public key construction from a private key.
164pub trait PublicKey: Sized + Clone + Eq + Hash + ValidCryptoMaterial +
165    // This unsightly turbofish type parameter is the precise constraint
166    // needed to require that there exists an
167    //
168    // ```
169    // impl From<&MyPrivateKeyMaterial> for MyPublicKeyMaterial
170    // ```
171    //
172    // declaration, for any `MyPrivateKeyMaterial`, `MyPublicKeyMaterial`
173    // on which we register (respectively) `PublicKey` and `PrivateKey`
174    // implementations.
175    for<'a> From<&'a <Self as PublicKey>::PrivateKeyMaterial> {
176    /// We require public / private types to be coupled, i.e. their
177    /// associated type is each other.
178    type PrivateKeyMaterial: PrivateKey<PublicKeyMaterial = Self>;
179}
180
181/// A type family of public keys that are used for signing.
182///
183/// This trait has a requirement on a `pub(crate)` marker trait meant to
184/// specifically limit its implementations to the present crate.
185///
186/// It is linked to a type of the Signature family, which carries the
187/// verification implementation.
188pub trait VerifyingKey:
189    PublicKey<PrivateKeyMaterial = <Self as VerifyingKey>::SigningKeyMaterial>
190    + ValidCryptoMaterial
191    + private::Sealed
192{
193    /// The associated signing key type for this verifying key.
194    type SigningKeyMaterial: SigningKey<VerifyingKeyMaterial = Self>;
195    /// The associated signature type for this verifying key.
196    type SignatureMaterial: Signature<VerifyingKeyMaterial = Self>;
197
198    /// We provide the striaghtfoward implementation which dispatches to the signature.
199    fn verify_struct_signature<T: CryptoHash + Serialize>(
200        &self,
201        message: &T,
202        signature: &Self::SignatureMaterial,
203    ) -> Result<()> {
204        signature.verify(message, self)
205    }
206
207    /// We provide the implementation which dispatches to the signature.
208    fn batch_verify<T: CryptoHash + Serialize>(
209        message: &T,
210        keys_and_signatures: Vec<(Self, Self::SignatureMaterial)>,
211    ) -> Result<()> {
212        Self::SignatureMaterial::batch_verify(message, keys_and_signatures)
213    }
214}
215
216/// A type family for signature material that knows which public key type
217/// is needed to verify it, and given such a public key, knows how to
218/// verify.
219///
220/// This trait simply requires an association to some type of the
221/// [`PublicKey`][PublicKey] family of which we are the `SignatureMaterial`.
222///
223/// This trait has a requirement on a `pub(crate)` marker trait meant to
224/// specifically limit its implementations to the present crate.
225///
226/// It should be possible to write a generic signature function that
227/// checks signature material passed as `&[u8]` and only returns Ok when
228/// that material de-serializes to a signature of the expected concrete
229/// scheme. This would be done as an extension trait of
230/// [`Signature`][Signature].
231pub trait Signature:
232    for<'a> TryFrom<&'a [u8], Error = CryptoMaterialError>
233    + Sized
234    + Debug
235    + Clone
236    + Eq
237    + Hash
238    + private::Sealed
239{
240    /// The associated verifying key type for this signature.
241    type VerifyingKeyMaterial: VerifyingKey<SignatureMaterial = Self>;
242    /// The associated signing key type for this signature
243    type SigningKeyMaterial: SigningKey<SignatureMaterial = Self>;
244
245    /// Verification for a struct we unabmiguously know how to serialize and
246    /// that we have a domain separation prefix for.
247    fn verify<T: CryptoHash + Serialize>(
248        &self,
249        message: &T,
250        public_key: &Self::VerifyingKeyMaterial,
251    ) -> Result<()>;
252
253    /// Native verification function.
254    fn verify_arbitrary_msg(
255        &self,
256        message: &[u8],
257        public_key: &Self::VerifyingKeyMaterial,
258    ) -> Result<()>;
259
260    /// Convert the signature into a byte representation.
261    fn to_bytes(&self) -> Vec<u8>;
262
263    /// The implementer can override a batch verification implementation
264    /// that by default iterates over each signature. More efficient
265    /// implementations exist and should be implemented for many schemes.
266    fn batch_verify<T: CryptoHash + Serialize>(
267        message: &T,
268        keys_and_signatures: Vec<(Self::VerifyingKeyMaterial, Self)>,
269    ) -> Result<()> {
270        for (key, signature) in keys_and_signatures {
271            signature.verify(message, &key)?
272        }
273        Ok(())
274    }
275}
276
277/// A type family for schemes which know how to generate key material from
278/// a cryptographically-secure [`CryptoRng`][::rand::CryptoRng].
279pub trait Uniform {
280    /// Generate key material from an RNG. This should generally not be used for production
281    /// purposes even with a good source of randomness. When possible use hardware crypto to generate and
282    /// store private keys.
283    fn generate<R>(rng: &mut R) -> Self
284    where
285        R: RngCore + CryptoRng;
286
287    /// Generate a random key using the shared TEST_SEED
288    fn generate_for_testing() -> Self
289    where
290        Self: Sized,
291    {
292        let mut rng: StdRng = SeedableRng::from_seed(crate::test_utils::TEST_SEED);
293        Self::generate(&mut rng)
294    }
295}
296
297/// A type family with a by-convention notion of genesis private key.
298pub trait Genesis: PrivateKey {
299    /// Produces the genesis private key.
300    fn genesis() -> Self;
301}
302
303/// A pub(crate) mod hiding a Sealed trait and its implementations, allowing
304/// us to make sure implementations are constrained to the crypto crate.
305// See https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
306pub(crate) mod private {
307    pub trait Sealed {}
308
309    // Implement for the ed25519, multi-ed25519 signatures
310    impl Sealed for crate::ed25519::Ed25519PrivateKey {}
311    impl Sealed for crate::ed25519::Ed25519PublicKey {}
312    impl Sealed for crate::ed25519::Ed25519Signature {}
313
314    impl Sealed for crate::multi_ed25519::MultiEd25519PrivateKey {}
315    impl Sealed for crate::multi_ed25519::MultiEd25519PublicKey {}
316    impl Sealed for crate::multi_ed25519::MultiEd25519Signature {}
317
318    impl Sealed for crate::bls12381::PrivateKey {}
319    impl Sealed for crate::bls12381::PublicKey {}
320    impl Sealed for crate::bls12381::Signature {}
321    impl Sealed for crate::bls12381::ProofOfPossession {}
322}