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}