linera_base/
crypto.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2// Copyright (c) Zefchain Labs, Inc.
3// SPDX-License-Identifier: Apache-2.0
4
5//! Define the cryptographic primitives used by the Linera protocol.
6
7use std::{borrow::Cow, fmt, io, num::ParseIntError, str::FromStr};
8
9use ed25519_dalek::{self as dalek, Signer, Verifier};
10use generic_array::typenum::Unsigned;
11use linera_witty::{
12    GuestPointer, HList, InstanceWithMemory, Layout, Memory, Runtime, RuntimeError, RuntimeMemory,
13    WitLoad, WitStore, WitType,
14};
15use serde::{Deserialize, Serialize};
16use thiserror::Error;
17#[cfg(with_testing)]
18use {
19    proptest::{
20        collection::{vec, VecStrategy},
21        prelude::Arbitrary,
22        strategy::{self, Strategy},
23    },
24    std::ops::RangeInclusive,
25};
26
27use crate::doc_scalar;
28
29/// A signature key-pair.
30pub struct KeyPair(dalek::SigningKey);
31
32/// A signature public key.
33#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash)]
34pub struct PublicKey(pub [u8; dalek::PUBLIC_KEY_LENGTH]);
35
36type HasherOutputSize = <sha3::Sha3_256 as sha3::digest::OutputSizeUser>::OutputSize;
37type HasherOutput = generic_array::GenericArray<u8, HasherOutputSize>;
38
39/// A Sha3-256 value.
40#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash)]
41#[cfg_attr(with_testing, derive(Default))]
42pub struct CryptoHash(HasherOutput);
43
44/// A signature value.
45#[derive(Eq, PartialEq, Copy, Clone)]
46pub struct Signature(pub dalek::Signature);
47
48/// Error type for cryptographic errors.
49#[derive(Error, Debug)]
50#[allow(missing_docs)]
51pub enum CryptoError {
52    #[error("Signature for object {type_name} is not valid: {error}")]
53    InvalidSignature { error: String, type_name: String },
54    #[error("Signature for object {type_name} is missing")]
55    MissingSignature { type_name: String },
56    #[error("String contains non-hexadecimal digits")]
57    NonHexDigits(#[from] hex::FromHexError),
58    #[error(
59        "Byte slice has length {0} but a `CryptoHash` requires exactly {expected} bytes",
60        expected = HasherOutputSize::to_usize(),
61    )]
62    IncorrectHashSize(usize),
63    #[error(
64        "Byte slice has length {0} but a `PublicKey` requires exactly {expected} bytes",
65        expected = dalek::PUBLIC_KEY_LENGTH,
66    )]
67    IncorrectPublicKeySize(usize),
68    #[error("Could not parse integer")]
69    ParseIntError(#[from] ParseIntError),
70}
71
72impl PublicKey {
73    /// A fake public key used for testing.
74    #[cfg(with_testing)]
75    pub fn test_key(name: u8) -> PublicKey {
76        let addr = [name; dalek::PUBLIC_KEY_LENGTH];
77        PublicKey(addr)
78    }
79}
80
81#[cfg(with_getrandom)]
82/// Wrapper around [`rand::CryptoRng`] and [`rand::RngCore`].
83pub trait CryptoRng: rand::CryptoRng + rand::RngCore + Send + Sync {}
84
85#[cfg(with_getrandom)]
86impl<T: rand::CryptoRng + rand::RngCore + Send + Sync> CryptoRng for T {}
87
88#[cfg(with_getrandom)]
89impl From<Option<u64>> for Box<dyn CryptoRng> {
90    fn from(seed: Option<u64>) -> Self {
91        use rand::SeedableRng;
92
93        match seed {
94            Some(seed) => Box::new(rand::rngs::StdRng::seed_from_u64(seed)),
95            None => Box::new(rand::rngs::OsRng),
96        }
97    }
98}
99
100impl KeyPair {
101    #[cfg(all(with_getrandom, with_testing))]
102    /// Generates a new key-pair.
103    pub fn generate() -> Self {
104        let mut rng = rand::rngs::OsRng;
105        Self::generate_from(&mut rng)
106    }
107
108    #[cfg(with_getrandom)]
109    /// Generates a new key-pair from the given RNG. Use with care.
110    pub fn generate_from<R: CryptoRng>(rng: &mut R) -> Self {
111        let keypair = dalek::SigningKey::generate(rng);
112        KeyPair(keypair)
113    }
114
115    /// Obtains the public key of a key-pair.
116    pub fn public(&self) -> PublicKey {
117        PublicKey(self.0.verifying_key().to_bytes())
118    }
119
120    /// Copies the key-pair, **including the secret key**.
121    ///
122    /// The `Clone` and `Copy` traits are deliberately not implemented for `KeyPair` to prevent
123    /// accidental copies of secret keys.
124    pub fn copy(&self) -> KeyPair {
125        KeyPair(self.0.clone())
126    }
127}
128
129impl Serialize for PublicKey {
130    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
131    where
132        S: serde::ser::Serializer,
133    {
134        if serializer.is_human_readable() {
135            serializer.serialize_str(&self.to_string())
136        } else {
137            serializer.serialize_newtype_struct("PublicKey", &self.0)
138        }
139    }
140}
141
142impl<'de> Deserialize<'de> for PublicKey {
143    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
144    where
145        D: serde::de::Deserializer<'de>,
146    {
147        if deserializer.is_human_readable() {
148            let s = String::deserialize(deserializer)?;
149            let value = Self::from_str(&s).map_err(serde::de::Error::custom)?;
150            Ok(value)
151        } else {
152            #[derive(Deserialize)]
153            #[serde(rename = "PublicKey")]
154            struct Foo([u8; dalek::PUBLIC_KEY_LENGTH]);
155
156            let value = Foo::deserialize(deserializer)?;
157            Ok(Self(value.0))
158        }
159    }
160}
161
162impl Serialize for CryptoHash {
163    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
164    where
165        S: serde::ser::Serializer,
166    {
167        if serializer.is_human_readable() {
168            serializer.serialize_str(&self.to_string())
169        } else {
170            serializer.serialize_newtype_struct("CryptoHash", &self.0)
171        }
172    }
173}
174
175impl<'de> Deserialize<'de> for CryptoHash {
176    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
177    where
178        D: serde::de::Deserializer<'de>,
179    {
180        if deserializer.is_human_readable() {
181            let s = String::deserialize(deserializer)?;
182            let value = Self::from_str(&s).map_err(serde::de::Error::custom)?;
183            Ok(value)
184        } else {
185            #[derive(Deserialize)]
186            #[serde(rename = "CryptoHash")]
187            struct Foo(HasherOutput);
188
189            let value = Foo::deserialize(deserializer)?;
190            Ok(Self(value.0))
191        }
192    }
193}
194
195impl Serialize for KeyPair {
196    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
197    where
198        S: serde::ser::Serializer,
199    {
200        // This is only used for JSON configuration.
201        assert!(serializer.is_human_readable());
202        serializer.serialize_str(&hex::encode(self.0.to_bytes()))
203    }
204}
205
206impl<'de> Deserialize<'de> for KeyPair {
207    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
208    where
209        D: serde::de::Deserializer<'de>,
210    {
211        // This is only used for JSON configuration.
212        assert!(deserializer.is_human_readable());
213        let s = String::deserialize(deserializer)?;
214        let value = hex::decode(s).map_err(serde::de::Error::custom)?;
215        let key =
216            dalek::SigningKey::from_bytes(value[..].try_into().map_err(serde::de::Error::custom)?);
217        Ok(KeyPair(key))
218    }
219}
220
221impl Serialize for Signature {
222    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
223    where
224        S: serde::ser::Serializer,
225    {
226        if serializer.is_human_readable() {
227            serializer.serialize_str(&hex::encode(self.0.to_bytes()))
228        } else {
229            serializer.serialize_newtype_struct("Signature", &self.0)
230        }
231    }
232}
233
234impl<'de> Deserialize<'de> for Signature {
235    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
236    where
237        D: serde::de::Deserializer<'de>,
238    {
239        if deserializer.is_human_readable() {
240            let s = String::deserialize(deserializer)?;
241            let value = hex::decode(s).map_err(serde::de::Error::custom)?;
242            let sig =
243                dalek::Signature::try_from(value.as_slice()).map_err(serde::de::Error::custom)?;
244            Ok(Signature(sig))
245        } else {
246            #[derive(Deserialize)]
247            #[serde(rename = "Signature")]
248            struct Foo(dalek::Signature);
249
250            let value = Foo::deserialize(deserializer)?;
251            Ok(Self(value.0))
252        }
253    }
254}
255
256impl FromStr for PublicKey {
257    type Err = CryptoError;
258
259    fn from_str(s: &str) -> Result<Self, Self::Err> {
260        let value = hex::decode(s)?;
261        (value.as_slice()).try_into()
262    }
263}
264
265impl TryFrom<&[u8]> for PublicKey {
266    type Error = CryptoError;
267
268    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
269        if value.len() != dalek::PUBLIC_KEY_LENGTH {
270            return Err(CryptoError::IncorrectPublicKeySize(value.len()));
271        }
272        let mut pubkey = [0u8; dalek::PUBLIC_KEY_LENGTH];
273        pubkey.copy_from_slice(value);
274        Ok(PublicKey(pubkey))
275    }
276}
277
278impl From<[u64; 4]> for PublicKey {
279    fn from(integers: [u64; 4]) -> Self {
280        PublicKey(u64_array_to_le_bytes(integers))
281    }
282}
283
284impl From<PublicKey> for [u64; 4] {
285    fn from(pub_key: PublicKey) -> Self {
286        le_bytes_to_u64_array(&pub_key.0)
287    }
288}
289
290impl FromStr for CryptoHash {
291    type Err = CryptoError;
292
293    fn from_str(s: &str) -> Result<Self, Self::Err> {
294        let value = hex::decode(s)?;
295        (value.as_slice()).try_into()
296    }
297}
298
299impl TryFrom<&[u8]> for CryptoHash {
300    type Error = CryptoError;
301
302    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
303        if value.len() != HasherOutputSize::to_usize() {
304            return Err(CryptoError::IncorrectHashSize(value.len()));
305        }
306        let mut bytes = HasherOutput::default();
307        bytes.copy_from_slice(value);
308        Ok(Self(bytes))
309    }
310}
311
312impl From<[u64; 4]> for CryptoHash {
313    fn from(integers: [u64; 4]) -> Self {
314        CryptoHash(u64_array_to_le_bytes(integers).into())
315    }
316}
317
318impl From<CryptoHash> for [u64; 4] {
319    fn from(crypto_hash: CryptoHash) -> Self {
320        le_bytes_to_u64_array(&crypto_hash.0)
321    }
322}
323
324impl fmt::Display for Signature {
325    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326        let s = hex::encode(self.0.to_bytes());
327        write!(f, "{}", s)
328    }
329}
330
331impl fmt::Display for PublicKey {
332    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333        write!(f, "{}", hex::encode(&self.0[..]))
334    }
335}
336
337impl fmt::Display for CryptoHash {
338    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339        let prec = f.precision().unwrap_or(self.0.len() * 2);
340        hex::encode(&self.0[..((prec + 1) / 2)]).fmt(f)
341    }
342}
343
344impl fmt::Debug for Signature {
345    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346        write!(f, "{}", hex::encode(&self.0.to_bytes()[0..8]))
347    }
348}
349
350impl fmt::Debug for PublicKey {
351    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352        write!(f, "{}", hex::encode(&self.0[..8]))
353    }
354}
355
356impl fmt::Debug for CryptoHash {
357    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358        write!(f, "{}", hex::encode(&self.0[..8]))
359    }
360}
361
362/// Something that we know how to hash.
363pub trait Hashable<Hasher> {
364    /// Send the content of `Self` to the given hasher.
365    fn write(&self, hasher: &mut Hasher);
366}
367
368/// Something that we know how to hash and sign.
369pub trait HasTypeName {
370    /// The name of the type.
371    fn type_name() -> &'static str;
372}
373
374/// Activate the blanket implementation of `Hashable` based on serde and BCS.
375/// * We use `serde_name` to extract a seed from the name of structs and enums.
376/// * We use `BCS` to generate canonical bytes suitable for hashing.
377pub trait BcsHashable: Serialize + serde::de::DeserializeOwned {}
378
379/// Activate the blanket implementation of `Signable` based on serde and BCS.
380/// * We use `serde_name` to extract a seed from the name of structs and enums.
381/// * We use `BCS` to generate canonical bytes suitable for signing.
382pub trait BcsSignable: Serialize + serde::de::DeserializeOwned {}
383
384impl<T: BcsSignable> BcsHashable for T {}
385
386impl<T, Hasher> Hashable<Hasher> for T
387where
388    T: BcsHashable,
389    Hasher: io::Write,
390{
391    fn write(&self, hasher: &mut Hasher) {
392        let name = <Self as HasTypeName>::type_name();
393        // Note: This assumes that names never contain the separator `::`.
394        write!(hasher, "{}::", name).expect("Hasher should not fail");
395        bcs::serialize_into(hasher, &self).expect("Message serialization should not fail");
396    }
397}
398
399impl<Hasher> Hashable<Hasher> for [u8]
400where
401    Hasher: io::Write,
402{
403    fn write(&self, hasher: &mut Hasher) {
404        hasher.write_all(self).expect("Hasher should not fail");
405    }
406}
407
408impl<T> HasTypeName for T
409where
410    T: BcsHashable,
411{
412    fn type_name() -> &'static str {
413        serde_name::trace_name::<Self>().expect("Self must be a struct or an enum")
414    }
415}
416
417impl CryptoHash {
418    /// Computes a hash.
419    pub fn new<T: BcsHashable>(value: &T) -> Self {
420        use sha3::digest::Digest;
421
422        let mut hasher = sha3::Sha3_256::default();
423        value.write(&mut hasher);
424        CryptoHash(hasher.finalize())
425    }
426
427    /// Reads the bytes of the hash value.
428    pub fn as_bytes(&self) -> &HasherOutput {
429        &self.0
430    }
431
432    /// Returns the hash of `TestString(s)`, for testing purposes.
433    #[cfg(with_testing)]
434    pub fn test_hash(s: impl Into<String>) -> Self {
435        CryptoHash::new(&TestString::new(s))
436    }
437}
438
439impl Signature {
440    /// Computes a signature.
441    pub fn new<T>(value: &T, secret: &KeyPair) -> Self
442    where
443        T: BcsSignable,
444    {
445        let mut message = Vec::new();
446        value.write(&mut message);
447        let signature = secret.0.sign(&message);
448        Signature(signature)
449    }
450
451    fn check_internal<T>(&self, value: &T, author: PublicKey) -> Result<(), dalek::SignatureError>
452    where
453        T: BcsSignable,
454    {
455        let mut message = Vec::new();
456        value.write(&mut message);
457        let public_key = dalek::VerifyingKey::from_bytes(&author.0)?;
458        public_key.verify(&message, &self.0)
459    }
460
461    /// Checks a signature.
462    pub fn check<T>(&self, value: &T, author: PublicKey) -> Result<(), CryptoError>
463    where
464        T: BcsSignable + fmt::Debug,
465    {
466        self.check_internal(value, author)
467            .map_err(|error| CryptoError::InvalidSignature {
468                error: error.to_string(),
469                type_name: T::type_name().to_string(),
470            })
471    }
472
473    /// Checks an optional signature.
474    pub fn check_optional_signature<T>(
475        signature: Option<&Self>,
476        value: &T,
477        author: &PublicKey,
478    ) -> Result<(), CryptoError>
479    where
480        T: BcsSignable + fmt::Debug,
481    {
482        match signature {
483            Some(sig) => sig.check(value, *author),
484            None => Err(CryptoError::MissingSignature {
485                type_name: T::type_name().to_string(),
486            }),
487        }
488    }
489
490    fn verify_batch_internal<'a, T, I>(value: &'a T, votes: I) -> Result<(), dalek::SignatureError>
491    where
492        T: BcsSignable,
493        I: IntoIterator<Item = (&'a PublicKey, &'a Signature)>,
494    {
495        let mut msg = Vec::new();
496        value.write(&mut msg);
497        let mut messages = Vec::new();
498        let mut signatures = Vec::new();
499        let mut public_keys = Vec::new();
500        for (addr, sig) in votes.into_iter() {
501            messages.push(msg.as_slice());
502            signatures.push(sig.0);
503            public_keys.push(dalek::VerifyingKey::from_bytes(&addr.0)?);
504        }
505        dalek::verify_batch(&messages[..], &signatures[..], &public_keys[..])
506    }
507
508    /// Verifies a batch of signatures.
509    pub fn verify_batch<'a, T, I>(value: &'a T, votes: I) -> Result<(), CryptoError>
510    where
511        T: BcsSignable,
512        I: IntoIterator<Item = (&'a PublicKey, &'a Signature)>,
513    {
514        Signature::verify_batch_internal(value, votes).map_err(|error| {
515            CryptoError::InvalidSignature {
516                error: format!("batched {}", error),
517                type_name: T::type_name().to_string(),
518            }
519        })
520    }
521}
522
523impl WitType for CryptoHash {
524    const SIZE: u32 = <(u64, u64, u64, u64) as WitType>::SIZE;
525    type Layout = <(u64, u64, u64, u64) as WitType>::Layout;
526    type Dependencies = HList![];
527
528    fn wit_type_name() -> Cow<'static, str> {
529        "crypto-hash".into()
530    }
531
532    fn wit_type_declaration() -> Cow<'static, str> {
533        concat!(
534            "    record crypto-hash {\n",
535            "        part1: u64,\n",
536            "        part2: u64,\n",
537            "        part3: u64,\n",
538            "        part4: u64,\n",
539            "    }\n",
540        )
541        .into()
542    }
543}
544
545impl WitLoad for CryptoHash {
546    fn load<Instance>(
547        memory: &Memory<'_, Instance>,
548        location: GuestPointer,
549    ) -> Result<Self, RuntimeError>
550    where
551        Instance: InstanceWithMemory,
552        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
553    {
554        let (part1, part2, part3, part4) = WitLoad::load(memory, location)?;
555        Ok(CryptoHash::from([part1, part2, part3, part4]))
556    }
557
558    fn lift_from<Instance>(
559        flat_layout: <Self::Layout as Layout>::Flat,
560        memory: &Memory<'_, Instance>,
561    ) -> Result<Self, RuntimeError>
562    where
563        Instance: InstanceWithMemory,
564        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
565    {
566        let (part1, part2, part3, part4) = WitLoad::lift_from(flat_layout, memory)?;
567        Ok(CryptoHash::from([part1, part2, part3, part4]))
568    }
569}
570
571impl WitStore for CryptoHash {
572    fn store<Instance>(
573        &self,
574        memory: &mut Memory<'_, Instance>,
575        location: GuestPointer,
576    ) -> Result<(), RuntimeError>
577    where
578        Instance: InstanceWithMemory,
579        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
580    {
581        let [part1, part2, part3, part4] = (*self).into();
582        (part1, part2, part3, part4).store(memory, location)
583    }
584
585    fn lower<Instance>(
586        &self,
587        memory: &mut Memory<'_, Instance>,
588    ) -> Result<<Self::Layout as Layout>::Flat, RuntimeError>
589    where
590        Instance: InstanceWithMemory,
591        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
592    {
593        let [part1, part2, part3, part4] = (*self).into();
594        (part1, part2, part3, part4).lower(memory)
595    }
596}
597
598impl WitType for PublicKey {
599    const SIZE: u32 = <(u64, u64, u64, u64) as WitType>::SIZE;
600    type Layout = <(u64, u64, u64, u64) as WitType>::Layout;
601    type Dependencies = HList![];
602
603    fn wit_type_name() -> Cow<'static, str> {
604        "public-key".into()
605    }
606
607    fn wit_type_declaration() -> Cow<'static, str> {
608        concat!(
609            "    record public-key {\n",
610            "        part1: u64,\n",
611            "        part2: u64,\n",
612            "        part3: u64,\n",
613            "        part4: u64,\n",
614            "    }\n",
615        )
616        .into()
617    }
618}
619
620impl WitLoad for PublicKey {
621    fn load<Instance>(
622        memory: &Memory<'_, Instance>,
623        location: GuestPointer,
624    ) -> Result<Self, RuntimeError>
625    where
626        Instance: InstanceWithMemory,
627        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
628    {
629        let (part1, part2, part3, part4) = WitLoad::load(memory, location)?;
630        Ok(PublicKey::from([part1, part2, part3, part4]))
631    }
632
633    fn lift_from<Instance>(
634        flat_layout: <Self::Layout as Layout>::Flat,
635        memory: &Memory<'_, Instance>,
636    ) -> Result<Self, RuntimeError>
637    where
638        Instance: InstanceWithMemory,
639        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
640    {
641        let (part1, part2, part3, part4) = WitLoad::lift_from(flat_layout, memory)?;
642        Ok(PublicKey::from([part1, part2, part3, part4]))
643    }
644}
645
646impl WitStore for PublicKey {
647    fn store<Instance>(
648        &self,
649        memory: &mut Memory<'_, Instance>,
650        location: GuestPointer,
651    ) -> Result<(), RuntimeError>
652    where
653        Instance: InstanceWithMemory,
654        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
655    {
656        let [part1, part2, part3, part4] = (*self).into();
657        (part1, part2, part3, part4).store(memory, location)
658    }
659
660    fn lower<Instance>(
661        &self,
662        memory: &mut Memory<'_, Instance>,
663    ) -> Result<<Self::Layout as Layout>::Flat, RuntimeError>
664    where
665        Instance: InstanceWithMemory,
666        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
667    {
668        let [part1, part2, part3, part4] = (*self).into();
669        (part1, part2, part3, part4).lower(memory)
670    }
671}
672
673#[cfg(with_testing)]
674impl Arbitrary for CryptoHash {
675    type Parameters = ();
676    type Strategy = strategy::Map<VecStrategy<RangeInclusive<u8>>, fn(Vec<u8>) -> CryptoHash>;
677
678    fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
679        vec(u8::MIN..=u8::MAX, HasherOutputSize::to_usize()).prop_map(|vector| {
680            CryptoHash(generic_array::GenericArray::clone_from_slice(&vector[..]))
681        })
682    }
683}
684
685impl BcsHashable for PublicKey {}
686
687doc_scalar!(CryptoHash, "A Sha3-256 value");
688doc_scalar!(PublicKey, "A signature public key");
689doc_scalar!(Signature, "A signature value");
690
691/// A BCS-signable struct for testing.
692#[cfg(with_testing)]
693#[derive(Debug, Serialize, Deserialize)]
694pub struct TestString(pub String);
695
696#[cfg(with_testing)]
697impl TestString {
698    /// Creates a new `TestString` with the given string.
699    pub fn new(s: impl Into<String>) -> Self {
700        Self(s.into())
701    }
702}
703
704#[cfg(with_testing)]
705impl BcsSignable for TestString {}
706
707#[cfg(with_getrandom)]
708#[test]
709fn test_signatures() {
710    #[derive(Debug, Serialize, Deserialize)]
711    struct Foo(String);
712
713    impl BcsSignable for Foo {}
714
715    let key1 = KeyPair::generate();
716    let addr1 = key1.public();
717    let key2 = KeyPair::generate();
718    let addr2 = key2.public();
719
720    let ts = TestString("hello".into());
721    let tsx = TestString("hellox".into());
722    let foo = Foo("hello".into());
723
724    let s = Signature::new(&ts, &key1);
725    assert!(s.check(&ts, addr1).is_ok());
726    assert!(s.check(&ts, addr2).is_err());
727    assert!(s.check(&tsx, addr1).is_err());
728    assert!(s.check(&foo, addr1).is_err());
729}
730
731/// Reads the `bytes` as four little-endian unsigned 64-bit integers and returns them.
732fn le_bytes_to_u64_array(bytes: &[u8]) -> [u64; 4] {
733    let mut integers = [0u64; 4];
734
735    integers[0] = u64::from_le_bytes(bytes[0..8].try_into().expect("incorrect indices"));
736    integers[1] = u64::from_le_bytes(bytes[8..16].try_into().expect("incorrect indices"));
737    integers[2] = u64::from_le_bytes(bytes[16..24].try_into().expect("incorrect indices"));
738    integers[3] = u64::from_le_bytes(bytes[24..32].try_into().expect("incorrect indices"));
739
740    integers
741}
742
743/// Returns the bytes that represent the `integers` in little-endian.
744fn u64_array_to_le_bytes(integers: [u64; 4]) -> [u8; 32] {
745    let mut bytes = [0u8; 32];
746
747    bytes[0..8].copy_from_slice(&integers[0].to_le_bytes());
748    bytes[8..16].copy_from_slice(&integers[1].to_le_bytes());
749    bytes[16..24].copy_from_slice(&integers[2].to_le_bytes());
750    bytes[24..32].copy_from_slice(&integers[3].to_le_bytes());
751
752    bytes
753}