hopr_crypto_types/
types.rs

1use curve25519_dalek::{
2    edwards::{CompressedEdwardsY, EdwardsPoint},
3    montgomery::MontgomeryPoint,
4};
5use elliptic_curve::{sec1::EncodedPoint, NonZeroScalar, ProjectivePoint};
6use k256::elliptic_curve::group::prime::PrimeCurveAffine;
7use k256::{
8    ecdsa::{
9        self,
10        signature::{hazmat::PrehashVerifier, Verifier},
11        RecoveryId, Signature as ECDSASignature, SigningKey, VerifyingKey,
12    },
13    elliptic_curve::{
14        self,
15        generic_array::GenericArray,
16        sec1::{FromEncodedPoint, ToEncodedPoint},
17        CurveArithmetic,
18    },
19    AffinePoint, Secp256k1,
20};
21use serde::{Deserialize, Serialize};
22use sha2::Sha512;
23use std::fmt::Debug;
24use std::sync::OnceLock;
25use std::{
26    fmt::{Display, Formatter},
27    ops::Add,
28    str::FromStr,
29};
30use tracing::warn;
31
32use hopr_primitive_types::errors::GeneralError::ParseError;
33use hopr_primitive_types::prelude::*;
34
35use crate::utils::random_group_element;
36use crate::{
37    errors::{
38        CryptoError::{self, CalculationError, InvalidInputValue},
39        Result,
40    },
41    keypairs::{ChainKeypair, Keypair, OffchainKeypair},
42    primitives::{DigestLike, EthDigest},
43};
44
45pub use libp2p_identity::PeerId;
46
47/// Extend support for arbitrary array sizes in serde
48///
49/// Array of arbitrary sizes are not supported in serde due to backwards compatibility.
50/// Read more in: `<https://github.com/serde-rs/serde/issues/1937>`
51mod arrays {
52    use std::{convert::TryInto, marker::PhantomData};
53
54    use serde::{
55        de::{SeqAccess, Visitor},
56        ser::SerializeTuple,
57        Deserialize, Deserializer, Serialize, Serializer,
58    };
59    pub fn serialize<S: Serializer, T: Serialize, const N: usize>(data: &[T; N], ser: S) -> Result<S::Ok, S::Error> {
60        let mut s = ser.serialize_tuple(N)?;
61        for item in data {
62            s.serialize_element(item)?;
63        }
64        s.end()
65    }
66
67    struct ArrayVisitor<T, const N: usize>(PhantomData<T>);
68
69    impl<'de, T, const N: usize> Visitor<'de> for ArrayVisitor<T, N>
70    where
71        T: Deserialize<'de>,
72    {
73        type Value = [T; N];
74
75        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
76            formatter.write_str(&format!("an array of length {}", N))
77        }
78
79        #[inline]
80        fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
81        where
82            A: SeqAccess<'de>,
83        {
84            // can be optimized using MaybeUninit
85            let mut data = Vec::with_capacity(N);
86            for _ in 0..N {
87                match seq.next_element()? {
88                    Some(val) => data.push(val),
89                    None => return Err(serde::de::Error::invalid_length(N, &self)),
90                }
91            }
92            match data.try_into() {
93                Ok(arr) => Ok(arr),
94                Err(_) => unreachable!(),
95            }
96        }
97    }
98    pub fn deserialize<'de, D, T, const N: usize>(deserializer: D) -> Result<[T; N], D::Error>
99    where
100        D: Deserializer<'de>,
101        T: Deserialize<'de>,
102    {
103        deserializer.deserialize_tuple(N, ArrayVisitor::<T, N>(PhantomData))
104    }
105}
106
107/// Represents an elliptic curve point on the secp256k1 curve
108/// It stores the compressed (and optionally also the uncompressed) form.
109///
110/// ```rust
111/// # use hopr_crypto_types::prelude::*;
112/// # use hex_literal::hex;
113/// # use k256::{elliptic_curve::NonZeroScalar, Scalar, Secp256k1};
114///
115/// let a: [u8; 32] = hex!("876027a13900aad908842c3f79307cc8e96de5c3331090e91a24c315f2a8d43a");
116/// let b: [u8; 32] = hex!("561d3fe2990e6a768b90f7d510b69e967e0922a3b61e8141398113aede8e1d3e");
117///
118/// let A = CurvePoint::from_exponent(&a).unwrap();
119/// let B = CurvePoint::from_exponent(&b).unwrap();
120///
121/// // A_plus_B = a * G + b * G
122/// let A_plus_B = CurvePoint::combine(&[&A, &B]);
123///
124/// let scalar_a: Scalar = *NonZeroScalar::<Secp256k1>::try_from(&a[..]).unwrap();
125/// let scalar_b: Scalar = *NonZeroScalar::<Secp256k1>::try_from(&b[..]).unwrap();
126///
127/// // a_plus_b = (a + b) * G
128/// let a_plus_b = CurvePoint::from_exponent(&(scalar_a + scalar_b).to_bytes()).unwrap();
129///
130/// // group homomorphism
131/// // (a + b) * G = a * G + b * G
132/// assert_eq!(A_plus_B, a_plus_b);
133/// ```
134#[derive(Clone, Debug)]
135pub struct CurvePoint {
136    pub(crate) affine: AffinePoint,
137    compressed: EncodedPoint<Secp256k1>,
138    uncompressed: OnceLock<EncodedPoint<Secp256k1>>,
139}
140
141impl CurvePoint {
142    /// Size of the point if serialized via [`CurvePoint::as_compressed`].
143    pub const SIZE_COMPRESSED: usize = 33;
144    /// Size of the point if serialized via [`CurvePoint::as_uncompressed`].
145    pub const SIZE_UNCOMPRESSED: usize = 65;
146
147    /// Converts the uncompressed representation of the curve point to Ethereum address.
148    pub fn to_address(&self) -> Address {
149        let serialized = self.as_uncompressed();
150        let hash = Hash::create(&[&serialized.as_bytes()[1..]]);
151        Address::new(&hash.as_ref()[12..])
152    }
153
154    /// Creates a curve point from a non-zero scalar.
155    /// The given exponent must represent a non-zero scalar and must result into
156    /// a secp256k1 identity point.
157    pub fn from_exponent(exponent: &[u8]) -> Result<Self> {
158        PublicKey::from_privkey(exponent).map(CurvePoint::from)
159    }
160
161    /// Converts the curve point to a representation suitable for calculations.
162    pub fn into_projective_point(self) -> ProjectivePoint<Secp256k1> {
163        self.affine.into()
164    }
165
166    /// Converts the curve point into a compressed form. This is a cheap operation.
167    pub fn as_compressed(&self) -> &EncodedPoint<Secp256k1> {
168        &self.compressed
169    }
170
171    /// Converts the curve point into an uncompressed form. This is many cases an expensive operation.
172    pub fn as_uncompressed(&self) -> &EncodedPoint<Secp256k1> {
173        self.uncompressed.get_or_init(|| self.affine.to_encoded_point(false))
174    }
175
176    /// Sums all given curve points together, creating a new curve point.
177    pub fn combine(summands: &[&CurvePoint]) -> CurvePoint {
178        // Convert all public keys to EC points in the projective coordinates, which are
179        // more efficient for doing the additions. Then finally make in an affine point
180        let affine: AffinePoint = summands
181            .iter()
182            .map(|p| ProjectivePoint::<Secp256k1>::from(p.affine))
183            .fold(<Secp256k1 as CurveArithmetic>::ProjectivePoint::IDENTITY, |acc, x| {
184                acc.add(x)
185            })
186            .to_affine();
187
188        affine.into()
189    }
190}
191
192impl Default for CurvePoint {
193    fn default() -> Self {
194        Self::from(AffinePoint::default())
195    }
196}
197
198impl PartialEq for CurvePoint {
199    fn eq(&self, other: &Self) -> bool {
200        self.affine.eq(&other.affine)
201    }
202}
203
204impl Eq for CurvePoint {}
205
206impl From<PublicKey> for CurvePoint {
207    fn from(pubkey: PublicKey) -> Self {
208        pubkey.0
209    }
210}
211
212impl From<&PublicKey> for CurvePoint {
213    fn from(pubkey: &PublicKey) -> Self {
214        pubkey.0.clone()
215    }
216}
217
218impl From<AffinePoint> for CurvePoint {
219    fn from(affine: AffinePoint) -> Self {
220        Self {
221            affine,
222            compressed: affine.to_encoded_point(true),
223            uncompressed: OnceLock::new(),
224        }
225    }
226}
227
228impl TryFrom<HalfKeyChallenge> for CurvePoint {
229    type Error = GeneralError;
230
231    fn try_from(value: HalfKeyChallenge) -> std::result::Result<Self, Self::Error> {
232        Self::try_from(value.0.as_ref())
233    }
234}
235
236impl FromStr for CurvePoint {
237    type Err = CryptoError;
238
239    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
240        Ok(CurvePoint::try_from(
241            hex::decode(s)
242                .map_err(|_| GeneralError::ParseError("CurvePoint".into()))?
243                .as_slice(),
244        )?)
245    }
246}
247
248impl From<CurvePoint> for AffinePoint {
249    fn from(value: CurvePoint) -> Self {
250        value.affine
251    }
252}
253
254impl AsRef<[u8]> for CurvePoint {
255    fn as_ref(&self) -> &[u8] {
256        self.compressed.as_ref()
257    }
258}
259
260impl TryFrom<&[u8]> for CurvePoint {
261    type Error = GeneralError;
262
263    fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
264        let ep = elliptic_curve::sec1::EncodedPoint::<Secp256k1>::from_bytes(value)
265            .map_err(|_| GeneralError::ParseError("invalid secp256k1 encoded point".into()))?;
266        Ok(Self {
267            affine: Option::from(AffinePoint::from_encoded_point(&ep))
268                .ok_or(GeneralError::ParseError("invalid secp256k1 encoded point".into()))?,
269            // Compressing an uncompressed EC point is inexpensive
270            compressed: if ep.is_compressed() { ep } else { ep.compress() },
271            // If not directly uncompressed, defer the expensive operation for later
272            uncompressed: if !ep.is_compressed() {
273                ep.into()
274            } else {
275                OnceLock::new()
276            },
277        })
278    }
279}
280
281impl BytesRepresentable for CurvePoint {
282    const SIZE: usize = Self::SIZE_COMPRESSED;
283}
284
285/// Natural extension of the Curve Point to the Proof-of-Relay challenge.
286/// Proof-of-Relay challenge is a secp256k1 curve point.
287#[derive(Clone, Eq, PartialEq, Debug)]
288pub struct Challenge(CurvePoint);
289
290impl Challenge {
291    /// Converts the PoR challenge to an Ethereum challenge.
292    /// This is a one-way (lossy) operation, since the corresponding curve point is hashed
293    /// with the hash value then truncated.
294    pub fn to_ethereum_challenge(&self) -> EthereumChallenge {
295        EthereumChallenge::new(self.0.to_address().as_ref())
296    }
297}
298
299impl From<Challenge> for EthereumChallenge {
300    fn from(challenge: Challenge) -> Self {
301        challenge.to_ethereum_challenge()
302    }
303}
304
305impl Challenge {
306    /// Obtains the PoR challenge by adding the two EC points represented by the half-key challenges
307    pub fn from_hint_and_share(own_share: &HalfKeyChallenge, hint: &HalfKeyChallenge) -> Result<Self> {
308        let curve_point: CurvePoint = PublicKey::combine(&[
309            &PublicKey::try_from(own_share.0.as_ref())?,
310            &PublicKey::try_from(hint.0.as_ref())?,
311        ])
312        .into();
313        Ok(curve_point.into())
314    }
315
316    /// Obtains the PoR challenge by converting the given HalfKey into a secp256k1 point and
317    /// adding it with the given HalfKeyChallenge (which already represents a secp256k1 point).
318    pub fn from_own_share_and_half_key(own_share: &HalfKeyChallenge, half_key: &HalfKey) -> Result<Self> {
319        Self::from_hint_and_share(own_share, &half_key.to_challenge())
320    }
321}
322
323impl From<CurvePoint> for Challenge {
324    fn from(curve_point: CurvePoint) -> Self {
325        Self(curve_point)
326    }
327}
328
329impl From<Response> for Challenge {
330    fn from(response: Response) -> Self {
331        response.to_challenge()
332    }
333}
334
335impl AsRef<[u8]> for Challenge {
336    fn as_ref(&self) -> &[u8] {
337        // Serializes as compressed point
338        self.0.as_ref()
339    }
340}
341
342impl TryFrom<&[u8]> for Challenge {
343    type Error = GeneralError;
344
345    fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
346        // Accepts both compressed and uncompressed points
347        Ok(Self(value.try_into()?))
348    }
349}
350
351impl BytesRepresentable for Challenge {
352    const SIZE: usize = CurvePoint::SIZE_COMPRESSED;
353}
354
355/// Represents a half-key used for the Proof-of-Relay.
356///
357/// Half-key is equivalent to a non-zero scalar in the field used by secp256k1, but the type
358/// itself does not validate nor enforce this fact.
359// TODO: change this to HalfKey([u8; Self::SIZE]) in 3.0
360#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
361pub struct HalfKey {
362    hkey: [u8; Self::SIZE],
363}
364
365impl Default for HalfKey {
366    fn default() -> Self {
367        let mut ret = Self {
368            hkey: [0u8; Self::SIZE],
369        };
370        ret.hkey.copy_from_slice(
371            NonZeroScalar::<Secp256k1>::from_uint(1u16.into())
372                .unwrap()
373                .to_bytes()
374                .as_slice(),
375        );
376        ret
377    }
378}
379
380impl HalfKey {
381    /// Generates random half-key, useful for tests.
382    pub fn random() -> Self {
383        Self {
384            hkey: random_group_element().0,
385        }
386    }
387
388    /// Converts the non-zero scalar represented by this half-key into the half-key challenge.
389    /// This operation naturally enforces the underlying scalar to be non-zero.
390    pub fn to_challenge(&self) -> HalfKeyChallenge {
391        CurvePoint::from_exponent(&self.hkey)
392            .map(|cp| HalfKeyChallenge::new(cp.as_compressed().as_bytes()))
393            .expect("invalid public key")
394    }
395}
396
397impl AsRef<[u8]> for HalfKey {
398    fn as_ref(&self) -> &[u8] {
399        &self.hkey
400    }
401}
402
403impl TryFrom<&[u8]> for HalfKey {
404    type Error = GeneralError;
405
406    fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
407        Ok(Self {
408            hkey: value.try_into().map_err(|_| ParseError("HalfKey".into()))?,
409        })
410    }
411}
412
413impl BytesRepresentable for HalfKey {
414    /// Size of the secp256k1 secret scalar representing the half key.
415    const SIZE: usize = 32;
416}
417
418/// Represents a challenge for the half-key in Proof of Relay.
419/// Half-key challenge is equivalent to a secp256k1 curve point.
420/// Therefore, HalfKeyChallenge can be obtained from a HalfKey.
421#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
422pub struct HalfKeyChallenge(#[serde(with = "arrays")] [u8; Self::SIZE]);
423
424impl Display for HalfKeyChallenge {
425    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
426        write!(f, "{}", hex::encode(self.0))
427    }
428}
429
430impl Default for HalfKeyChallenge {
431    fn default() -> Self {
432        // Note that the default HalfKeyChallenge is the identity point on secp256k1, therefore
433        // will fail all public key checks, which is intended.
434        let mut ret = Self([0u8; Self::SIZE]);
435        ret.0[Self::SIZE - 1] = 1;
436        ret
437    }
438}
439
440impl HalfKeyChallenge {
441    pub fn new(half_key_challenge: &[u8]) -> Self {
442        assert_eq!(half_key_challenge.len(), Self::SIZE, "invalid length");
443        let mut ret = Self::default();
444        ret.0.copy_from_slice(half_key_challenge);
445        ret
446    }
447
448    pub fn to_address(&self) -> Address {
449        PublicKey::try_from(self.0.as_ref())
450            .expect("invalid half-key")
451            .to_address()
452    }
453}
454
455impl AsRef<[u8]> for HalfKeyChallenge {
456    fn as_ref(&self) -> &[u8] {
457        &self.0
458    }
459}
460
461impl TryFrom<&[u8]> for HalfKeyChallenge {
462    type Error = GeneralError;
463
464    fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
465        Ok(Self(
466            value.try_into().map_err(|_| ParseError("HalfKeyChallenge".into()))?,
467        ))
468    }
469}
470
471impl BytesRepresentable for HalfKeyChallenge {
472    /// Size of the compressed secp256k1 point representing the Half Key Challenge.
473    const SIZE: usize = PublicKey::SIZE_COMPRESSED;
474}
475
476impl std::hash::Hash for HalfKeyChallenge {
477    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
478        self.0.hash(state);
479    }
480}
481
482impl FromStr for HalfKeyChallenge {
483    type Err = GeneralError;
484
485    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
486        Self::from_hex(s)
487    }
488}
489
490impl From<HalfKey> for HalfKeyChallenge {
491    fn from(half_key: HalfKey) -> Self {
492        half_key.to_challenge()
493    }
494}
495
496/// Represents an Ethereum 256-bit hash value
497/// This implementation instantiates the hash via Keccak256 digest.
498#[derive(Clone, Copy, Eq, PartialEq, Default, Serialize, Deserialize, PartialOrd, Ord, std::hash::Hash)]
499pub struct Hash([u8; Self::SIZE]);
500
501impl Debug for Hash {
502    // Intentionally same as Display
503    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
504        write!(f, "{}", self.to_hex())
505    }
506}
507
508impl Display for Hash {
509    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
510        write!(f, "{}", self.to_hex())
511    }
512}
513
514impl FromStr for Hash {
515    type Err = GeneralError;
516
517    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
518        Self::from_hex(s)
519    }
520}
521
522impl Hash {
523    /// Convenience method that creates a new hash by hashing this.
524    pub fn hash(&self) -> Self {
525        Self::create(&[&self.0])
526    }
527
528    /// Takes all the byte slices and computes hash of their concatenated value.
529    /// Uses the Keccak256 digest.
530    pub fn create(inputs: &[&[u8]]) -> Self {
531        let mut hash = EthDigest::default();
532        inputs.iter().for_each(|v| hash.update(v));
533        let mut ret = Self([0u8; Self::SIZE]);
534        hash.finalize_into(&mut ret.0);
535        ret
536    }
537}
538
539impl AsRef<[u8]> for Hash {
540    fn as_ref(&self) -> &[u8] {
541        &self.0
542    }
543}
544
545impl TryFrom<&[u8]> for Hash {
546    type Error = GeneralError;
547
548    fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
549        Ok(Self(value.try_into().map_err(|_| ParseError("Hash".into()))?))
550    }
551}
552
553impl BytesRepresentable for Hash {
554    /// Size of the digest, which is [`EthDigest::SIZE`].
555    const SIZE: usize = EthDigest::SIZE;
556}
557
558impl From<[u8; Self::SIZE]> for Hash {
559    fn from(hash: [u8; Self::SIZE]) -> Self {
560        Self(hash)
561    }
562}
563
564impl From<Hash> for [u8; Hash::SIZE] {
565    fn from(value: Hash) -> Self {
566        value.0
567    }
568}
569
570impl From<&Hash> for [u8; Hash::SIZE] {
571    fn from(value: &Hash) -> Self {
572        value.0
573    }
574}
575
576impl From<Hash> for primitive_types::H256 {
577    fn from(value: Hash) -> Self {
578        value.0.into()
579    }
580}
581
582impl From<primitive_types::H256> for Hash {
583    fn from(value: primitive_types::H256) -> Self {
584        Self(value.0)
585    }
586}
587
588/// Represents an Ed25519 public key.
589#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Hash)]
590pub struct OffchainPublicKey(CompressedEdwardsY);
591
592impl AsRef<[u8]> for OffchainPublicKey {
593    fn as_ref(&self) -> &[u8] {
594        self.0.as_bytes()
595    }
596}
597
598impl TryFrom<&[u8]> for OffchainPublicKey {
599    type Error = GeneralError;
600
601    fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
602        Ok(Self(
603            CompressedEdwardsY::from_slice(value).map_err(|_| ParseError("OffchainPublicKey".into()))?,
604        ))
605    }
606}
607
608impl BytesRepresentable for OffchainPublicKey {
609    /// Size of the public key (compressed Edwards Y coordinate)
610    const SIZE: usize = 32;
611}
612
613impl TryFrom<[u8; OffchainPublicKey::SIZE]> for OffchainPublicKey {
614    type Error = GeneralError;
615
616    fn try_from(value: [u8; OffchainPublicKey::SIZE]) -> std::result::Result<Self, Self::Error> {
617        let v: &[u8] = &value;
618        v.try_into()
619    }
620}
621
622impl TryFrom<&PeerId> for OffchainPublicKey {
623    type Error = GeneralError;
624
625    fn try_from(value: &PeerId) -> std::result::Result<Self, Self::Error> {
626        let mh = value.as_ref();
627        if mh.code() == 0 {
628            libp2p_identity::PublicKey::try_decode_protobuf(mh.digest())
629                .map_err(|_| GeneralError::ParseError("invalid ed25519 peer id".into()))
630                .and_then(|pk| {
631                    pk.try_into_ed25519()
632                        .map(|p| p.to_bytes())
633                        .map_err(|_| GeneralError::ParseError("invalid ed25519 peer id".into()))
634                })
635                .and_then(|pk| {
636                    CompressedEdwardsY::from_slice(&pk)
637                        .map_err(|_| GeneralError::ParseError("invalid ed25519 peerid".into()))
638                })
639                .map(Self)
640        } else {
641            Err(GeneralError::ParseError("invalid ed25519 peer id".into()))
642        }
643    }
644}
645
646impl TryFrom<PeerId> for OffchainPublicKey {
647    type Error = GeneralError;
648
649    fn try_from(value: PeerId) -> std::result::Result<Self, Self::Error> {
650        Self::try_from(&value)
651    }
652}
653
654impl From<OffchainPublicKey> for PeerId {
655    fn from(value: OffchainPublicKey) -> Self {
656        let k = libp2p_identity::ed25519::PublicKey::try_from_bytes(value.0.as_bytes()).unwrap();
657        PeerId::from_public_key(&k.into())
658    }
659}
660
661impl From<&OffchainPublicKey> for PeerId {
662    fn from(value: &OffchainPublicKey) -> Self {
663        (*value).into()
664    }
665}
666
667impl Display for OffchainPublicKey {
668    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
669        write!(f, "{}", self.to_hex())
670    }
671}
672
673impl OffchainPublicKey {
674    /// Tries to create the public key from a Ed25519 private key.
675    /// The length must be exactly `ed25519_dalek::SECRET_KEY_LENGTH`.
676    pub fn from_privkey(private_key: &[u8]) -> Result<Self> {
677        let mut pk: [u8; ed25519_dalek::SECRET_KEY_LENGTH] = private_key.try_into().map_err(|_| InvalidInputValue)?;
678        let sk = libp2p_identity::ed25519::SecretKey::try_from_bytes(&mut pk).map_err(|_| InvalidInputValue)?;
679        let kp: libp2p_identity::ed25519::Keypair = sk.into();
680        Ok(Self(
681            CompressedEdwardsY::from_slice(&kp.public().to_bytes())
682                .map_err(|_| GeneralError::ParseError("OffchainPublicKey".into()))?,
683        ))
684    }
685
686    /// Outputs the public key as PeerId represented as Base58 string.
687    pub fn to_peerid_str(&self) -> String {
688        PeerId::from(self).to_base58()
689    }
690}
691
692impl From<&OffchainPublicKey> for EdwardsPoint {
693    fn from(value: &OffchainPublicKey) -> Self {
694        value.0.decompress().unwrap()
695    }
696}
697
698impl From<&OffchainPublicKey> for MontgomeryPoint {
699    fn from(value: &OffchainPublicKey) -> Self {
700        value.0.decompress().unwrap().to_montgomery()
701    }
702}
703
704/// Length of a packet tag
705pub const PACKET_TAG_LENGTH: usize = 16;
706
707/// Represents a fixed size packet verification tag
708pub type PacketTag = [u8; PACKET_TAG_LENGTH];
709
710/// Represents a secp256k1 public key.
711///
712/// ```rust
713/// # use hex_literal::hex;
714/// # use hopr_crypto_types::prelude::*;
715/// # use k256::ecdsa::VerifyingKey;
716///
717/// const PRIVATE_KEY: [u8; 32] = hex!("e17fe86ce6e99f4806715b0c9412f8dad89334bf07f72d5834207a9d8f19d7f8");
718///
719/// // compressed public keys start with `0x02` or `0x03``, depending on sign of y-component
720/// const COMPRESSED: [u8; 33] = hex!("021464586aeaea0eb5736884ca1bf42d165fc8e2243b1d917130fb9e321d7a93b8");
721///
722/// // full public key without prefix
723/// const UNCOMPRESSED_PLAIN: [u8; 64] = hex!("1464586aeaea0eb5736884ca1bf42d165fc8e2243b1d917130fb9e321d7a93b8fb0699d4f177f9c84712f6d7c5f6b7f4f6916116047fa25c79ef806fc6c9523e");
724///
725/// // uncompressed public keys use `0x04` prefix
726/// const UNCOMPRESSED: [u8; 65] = hex!("041464586aeaea0eb5736884ca1bf42d165fc8e2243b1d917130fb9e321d7a93b8fb0699d4f177f9c84712f6d7c5f6b7f4f6916116047fa25c79ef806fc6c9523e");
727///
728/// let from_privkey = PublicKey::from_privkey(&PRIVATE_KEY).unwrap();
729/// let from_compressed = PublicKey::try_from(COMPRESSED.as_ref()).unwrap();
730/// let from_uncompressed_plain = PublicKey::try_from(UNCOMPRESSED_PLAIN.as_ref()).unwrap();
731/// let from_uncompressed = PublicKey::try_from(UNCOMPRESSED.as_ref()).unwrap();
732///
733/// assert_eq!(from_privkey, from_uncompressed);
734/// assert_eq!(from_compressed, from_uncompressed_plain);
735/// assert_eq!(from_uncompressed_plain, from_uncompressed);
736///
737/// // also works from a signed Ethereum transaction
738/// const TX_HASH: [u8; 32] = hex!("eff80b9f035b1d369c6a60f362ac7c8b8c3b61b76d151d1be535145ccaa3e83e");
739///
740/// const R: [u8; 32] = hex!("c8048d137fbb10ddffa1e4ba5141c300fcd19e4fb7d0a4354ca62a7694e46f9b");
741/// const S: [u8; 32] = hex!("5bb43e23d8b430f17ba3649e38b5a94d02815f0bcfaaf171800c52d4794c3136");
742/// const V: u8 = 1u8;
743///
744/// let mut r_and_s = Vec::<u8>::with_capacity(64);
745/// r_and_s.extend_from_slice(&R);
746/// r_and_s.extend_from_slice(&S);
747///
748/// let sig = Signature::new(&r_and_s, V);
749///
750/// let from_transaction_signature = PublicKey::from_signature_hash(&TX_HASH, &sig).unwrap();
751///
752/// assert_eq!(from_uncompressed, from_transaction_signature);
753/// ```
754#[derive(Debug, Clone, Eq, PartialEq)]
755pub struct PublicKey(CurvePoint);
756
757impl PublicKey {
758    /// Size of the compressed public key in bytes
759    pub const SIZE_COMPRESSED: usize = 33;
760
761    pub const SIZE_UNCOMPRESSED_PLAIN: usize = 64;
762
763    /// Size of the uncompressed public key in bytes
764    pub const SIZE_UNCOMPRESSED: usize = 65;
765
766    pub fn from_privkey(private_key: &[u8]) -> Result<PublicKey> {
767        // This verifies that it is indeed a non-zero scalar, and thus represents a valid public key
768        let secret_scalar = NonZeroScalar::<Secp256k1>::try_from(private_key)
769            .map_err(|_| GeneralError::ParseError("PublicKey".into()))?;
770
771        let key = elliptic_curve::PublicKey::<Secp256k1>::from_secret_scalar(&secret_scalar);
772        Ok(key.into())
773    }
774
775    fn from_raw_signature<R>(msg: &[u8], r: &[u8], s: &[u8], v: u8, recovery_method: R) -> Result<PublicKey>
776    where
777        R: Fn(&[u8], &ECDSASignature, RecoveryId) -> std::result::Result<VerifyingKey, ecdsa::Error>,
778    {
779        let recid = RecoveryId::try_from(v).map_err(|_| GeneralError::ParseError("Signature".into()))?;
780        let signature =
781            ECDSASignature::from_scalars(GenericArray::clone_from_slice(r), GenericArray::clone_from_slice(s))
782                .map_err(|_| GeneralError::ParseError("Signature".into()))?;
783
784        let recovered_key = *recovery_method(msg, &signature, recid)
785            .map_err(|_| CalculationError)?
786            .as_affine();
787
788        // Verify that it is a valid public key
789        recovered_key.try_into()
790    }
791
792    pub fn from_signature(msg: &[u8], signature: &Signature) -> Result<PublicKey> {
793        let (raw_signature, recovery) = signature.raw_signature();
794        Self::from_raw_signature(
795            msg,
796            &raw_signature[0..Signature::SIZE / 2],
797            &raw_signature[Signature::SIZE / 2..],
798            recovery,
799            VerifyingKey::recover_from_msg,
800        )
801    }
802
803    pub fn from_signature_hash(hash: &[u8], signature: &Signature) -> Result<PublicKey> {
804        let (raw_signature, recovery) = signature.raw_signature();
805        Self::from_raw_signature(
806            hash,
807            &raw_signature[0..Signature::SIZE / 2],
808            &raw_signature[Signature::SIZE / 2..],
809            recovery,
810            VerifyingKey::recover_from_prehash,
811        )
812    }
813
814    /// Sums all given public keys together, creating a new public key.
815    /// Panics if reaches infinity (EC identity point), which is an invalid public key.
816    pub fn combine(summands: &[&PublicKey]) -> PublicKey {
817        let cps = summands.iter().map(|pk| CurvePoint::from(*pk)).collect::<Vec<_>>();
818        let cps_ref = cps.iter().collect::<Vec<_>>();
819
820        // Verify that it is a valid public key
821        CurvePoint::combine(&cps_ref)
822            .try_into()
823            .expect("combination results in the ec identity (which is an invalid pub key)")
824    }
825
826    /// Adds the given public key with `tweak` times secp256k1 generator, producing a new public key.
827    /// Panics if reaches infinity (EC identity point), which is an invalid public key.
828    pub fn tweak_add(key: &PublicKey, tweak: &[u8]) -> PublicKey {
829        let scalar = NonZeroScalar::<Secp256k1>::try_from(tweak).expect("zero tweak provided");
830
831        let new_pk = (key.0.clone().into_projective_point()
832            + <Secp256k1 as CurveArithmetic>::ProjectivePoint::GENERATOR * scalar.as_ref())
833        .to_affine();
834
835        // Verify that it is a valid public key
836        new_pk.try_into().expect("tweak add resulted in an invalid public key")
837    }
838
839    /// Generates a new random public key.
840    /// Because the corresponding private key is discarded, this might be useful only for testing purposes.
841    pub fn random() -> Self {
842        let (_, cp) = random_group_element();
843        cp.try_into()
844            .expect("random_group_element cannot generate identity points")
845    }
846
847    /// Converts the public key to an Ethereum address
848    pub fn to_address(&self) -> Address {
849        let uncompressed = self.to_bytes(false);
850        let serialized = Hash::create(&[&uncompressed[1..]]);
851        Address::new(&serialized.as_ref()[12..])
852    }
853
854    /// Serializes the public key to a binary form.
855    pub fn to_bytes(&self, compressed: bool) -> Box<[u8]> {
856        match compressed {
857            true => self.0.as_compressed().to_bytes(),
858            false => self.0.as_uncompressed().to_bytes(),
859        }
860    }
861
862    /// Serializes the public key to a binary form and converts it to hexadecimal string representation.
863    pub fn to_hex(&self, compressed: bool) -> String {
864        let offset = if compressed { 0 } else { 1 };
865        format!("0x{}", hex::encode(&self.to_bytes(compressed)[offset..]))
866    }
867}
868
869impl Display for PublicKey {
870    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
871        write!(f, "{}", self.to_hex(true))
872    }
873}
874
875impl TryFrom<&[u8]> for PublicKey {
876    type Error = GeneralError;
877
878    fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
879        match value.len() {
880            Self::SIZE_UNCOMPRESSED => {
881                // already has 0x04 prefix
882                let key = elliptic_curve::PublicKey::<Secp256k1>::from_sec1_bytes(value)
883                    .map_err(|_| GeneralError::ParseError("invalid secp256k1 point".into()))?;
884
885                // Already verified, this is a valid public key
886                Ok(key.into())
887            }
888            Self::SIZE_UNCOMPRESSED_PLAIN => {
889                // add 0x04 prefix
890                let key = elliptic_curve::PublicKey::<Secp256k1>::from_sec1_bytes(&[&[4u8], value].concat())
891                    .map_err(|_| GeneralError::ParseError("invalid secp256k1 point".into()))?;
892
893                // Already verified, this is a valid public key
894                Ok(key.into())
895            }
896            Self::SIZE_COMPRESSED => {
897                // has either 0x02 or 0x03 prefix
898                let key = elliptic_curve::PublicKey::<Secp256k1>::from_sec1_bytes(value)
899                    .map_err(|_| GeneralError::ParseError("invalid secp256k1 point".into()))?;
900
901                // Already verified, this is a valid public key
902                Ok(key.into())
903            }
904            _ => Err(GeneralError::ParseError("invalid secp256k1 point".into())),
905        }
906    }
907}
908
909impl TryFrom<AffinePoint> for PublicKey {
910    type Error = CryptoError;
911
912    fn try_from(value: AffinePoint) -> std::result::Result<Self, Self::Error> {
913        if value.is_identity().into() {
914            return Err(CryptoError::InvalidPublicKey);
915        }
916        Ok(Self(value.into()))
917    }
918}
919
920impl TryFrom<CurvePoint> for PublicKey {
921    type Error = CryptoError;
922
923    fn try_from(value: CurvePoint) -> std::result::Result<Self, Self::Error> {
924        if value.affine.is_identity().into() {
925            return Err(CryptoError::InvalidPublicKey);
926        }
927        Ok(Self(value))
928    }
929}
930
931impl From<elliptic_curve::PublicKey<Secp256k1>> for PublicKey {
932    fn from(key: elliptic_curve::PublicKey<Secp256k1>) -> Self {
933        Self((*key.as_affine()).into())
934    }
935}
936
937// TODO: make this `for &k256::ProjectivePoint`
938impl From<&PublicKey> for k256::ProjectivePoint {
939    fn from(value: &PublicKey) -> Self {
940        value.0.clone().into_projective_point()
941    }
942}
943
944/// Represents a compressed serializable extension of the `PublicKey` using the secp256k1 curve.
945#[derive(Debug, PartialEq, Eq, Clone)]
946pub struct CompressedPublicKey(pub PublicKey);
947
948impl TryFrom<&[u8]> for CompressedPublicKey {
949    type Error = GeneralError;
950
951    fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
952        Ok(PublicKey::try_from(value)?.into())
953    }
954}
955
956impl AsRef<[u8]> for CompressedPublicKey {
957    fn as_ref(&self) -> &[u8] {
958        // CurvePoint::as_ref() returns a compressed representation of the curve point
959        self.0 .0.as_ref()
960    }
961}
962
963impl BytesRepresentable for CompressedPublicKey {
964    const SIZE: usize = PublicKey::SIZE_COMPRESSED;
965}
966
967impl From<PublicKey> for CompressedPublicKey {
968    fn from(value: PublicKey) -> Self {
969        Self(value)
970    }
971}
972
973impl From<&CompressedPublicKey> for k256::ProjectivePoint {
974    fn from(value: &CompressedPublicKey) -> Self {
975        (&value.0).into()
976    }
977}
978
979impl CompressedPublicKey {
980    pub fn to_address(&self) -> Address {
981        self.0.to_address()
982    }
983}
984
985/// Contains a response upon ticket acknowledgement
986/// It is equivalent to a non-zero secret scalar on secp256k1 (EC private key).
987#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
988pub struct Response([u8; Self::SIZE]);
989
990impl Default for Response {
991    fn default() -> Self {
992        let mut ret = Self([0u8; Self::SIZE]);
993        ret.0.copy_from_slice(
994            NonZeroScalar::<Secp256k1>::from_uint(1u16.into())
995                .unwrap()
996                .to_bytes()
997                .as_slice(),
998        );
999        ret
1000    }
1001}
1002
1003impl Display for Response {
1004    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1005        f.write_str(self.to_hex().as_str())
1006    }
1007}
1008
1009impl Response {
1010    /// Converts this response to the PoR challenge by turning the non-zero scalar
1011    /// represented by this response into a secp256k1 curve point (public key)
1012    pub fn to_challenge(&self) -> Challenge {
1013        Challenge(CurvePoint::from_exponent(&self.0).expect("response represents an invalid non-zero scalar"))
1014    }
1015
1016    /// Derives the response from two half-keys.
1017    /// This is done by adding together the two non-zero scalars that the given half-keys represent.
1018    /// Returns an error if any of the given scalars is zero.
1019    pub fn from_half_keys(first: &HalfKey, second: &HalfKey) -> Result<Self> {
1020        let res = NonZeroScalar::<Secp256k1>::try_from(first.as_ref())
1021            .and_then(|s1| NonZeroScalar::<Secp256k1>::try_from(second.as_ref()).map(|s2| s1.as_ref() + s2.as_ref()))
1022            .map_err(|_| CalculationError)?; // One of the scalars was 0
1023
1024        Ok(Response::try_from(res.to_bytes().as_slice())?)
1025    }
1026}
1027
1028impl AsRef<[u8]> for Response {
1029    fn as_ref(&self) -> &[u8] {
1030        &self.0
1031    }
1032}
1033
1034impl TryFrom<&[u8]> for Response {
1035    type Error = GeneralError;
1036
1037    fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
1038        Ok(Self(value.try_into().map_err(|_| ParseError("Response".into()))?))
1039    }
1040}
1041
1042impl BytesRepresentable for Response {
1043    /// Fixed size of the PoR challenge response.
1044    const SIZE: usize = 32;
1045}
1046
1047impl From<[u8; Self::SIZE]> for Response {
1048    fn from(value: [u8; Self::SIZE]) -> Self {
1049        Self(value)
1050    }
1051}
1052
1053/// Represents an EdDSA signature using Ed25519 Edwards curve.
1054// TODO: change this to OffchainSignature([u8; Self::SIZE]) in 3.0
1055#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1056pub struct OffchainSignature {
1057    #[serde(with = "arrays")]
1058    signature: [u8; Self::SIZE],
1059}
1060
1061impl OffchainSignature {
1062    /// Sign the given message using the [OffchainKeypair].
1063    pub fn sign_message(msg: &[u8], signing_keypair: &OffchainKeypair) -> Self {
1064        // Expand the SK from the given keypair
1065        let expanded_sk = ed25519_dalek::hazmat::ExpandedSecretKey::from(
1066            &ed25519_dalek::SecretKey::try_from(signing_keypair.secret().as_ref()).expect("invalid private key"),
1067        );
1068
1069        // Get the verifying key from the SAME keypair, avoiding Double Public Key Signing Function Oracle Attack on Ed25519
1070        // See https://github.com/MystenLabs/ed25519-unsafe-libs for details
1071        let verifying = ed25519_dalek::VerifyingKey::from_bytes(signing_keypair.public().0.as_bytes()).unwrap();
1072
1073        ed25519_dalek::hazmat::raw_sign::<Sha512>(&expanded_sk, msg, &verifying).into()
1074    }
1075
1076    /// Verify this signature of the given message and [OffchainPublicKey].
1077    pub fn verify_message(&self, msg: &[u8], public_key: &OffchainPublicKey) -> bool {
1078        let sgn = ed25519_dalek::Signature::from_slice(&self.signature).expect("corrupted OffchainSignature");
1079        let pk = ed25519_dalek::VerifyingKey::from_bytes(public_key.0.as_bytes()).unwrap();
1080        pk.verify_strict(msg, &sgn).is_ok()
1081    }
1082}
1083
1084impl AsRef<[u8]> for OffchainSignature {
1085    fn as_ref(&self) -> &[u8] {
1086        &self.signature
1087    }
1088}
1089
1090impl TryFrom<&[u8]> for OffchainSignature {
1091    type Error = GeneralError;
1092
1093    fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
1094        Ok(ed25519_dalek::Signature::from_slice(value)
1095            .map_err(|_| ParseError("OffchainSignature".into()))?
1096            .into())
1097    }
1098}
1099
1100impl BytesRepresentable for OffchainSignature {
1101    /// Size of the EdDSA signature using Ed25519.
1102    const SIZE: usize = ed25519_dalek::Signature::BYTE_SIZE;
1103}
1104
1105impl From<ed25519_dalek::Signature> for OffchainSignature {
1106    fn from(value: ed25519_dalek::Signature) -> Self {
1107        let mut ret = Self {
1108            signature: [0u8; Self::SIZE],
1109        };
1110        ret.signature.copy_from_slice(value.to_bytes().as_ref());
1111        ret
1112    }
1113}
1114
1115impl TryFrom<([u8; 32], [u8; 32])> for OffchainSignature {
1116    type Error = GeneralError;
1117
1118    fn try_from(value: ([u8; 32], [u8; 32])) -> std::result::Result<Self, Self::Error> {
1119        Ok(ed25519_dalek::Signature::from_components(value.0, value.1).into())
1120    }
1121}
1122
1123/// Represents an ECDSA signature based on the secp256k1 curve with a recoverable public key.
1124/// This signature encodes the 2-bit recovery information into the
1125/// uppermost bits of MSB of the S value, which are never used by this ECDSA
1126/// instantiation over secp256k1.
1127/// The instance holds the byte array consisting of `R` and `S` values with the recovery bit
1128/// already embedded in S.
1129#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
1130pub struct Signature(#[serde(with = "arrays")] [u8; Self::SIZE]);
1131
1132impl Signature {
1133    pub fn new(raw_bytes: &[u8], recovery: u8) -> Signature {
1134        assert_eq!(raw_bytes.len(), Self::SIZE, "invalid length");
1135        assert!(recovery <= 1, "invalid recovery bit");
1136
1137        let mut ret = Self([0u8; Self::SIZE]);
1138        ret.0.copy_from_slice(raw_bytes);
1139        ret.embed_recovery_bit(recovery);
1140        ret
1141    }
1142
1143    fn sign<S>(data: &[u8], private_key: &[u8], signing_method: S) -> Signature
1144    where
1145        S: FnOnce(&SigningKey, &[u8]) -> ecdsa::signature::Result<(ECDSASignature, RecoveryId)>,
1146    {
1147        let key = SigningKey::from_bytes(private_key.into()).expect("invalid signing key");
1148        let (sig, rec) = signing_method(&key, data).expect("signing failed");
1149
1150        Self::new(&sig.to_vec(), rec.to_byte())
1151    }
1152
1153    /// Signs the given message using the chain private key.
1154    pub fn sign_message(message: &[u8], chain_keypair: &ChainKeypair) -> Signature {
1155        Self::sign(
1156            message,
1157            chain_keypair.secret().as_ref(),
1158            |k: &SigningKey, data: &[u8]| k.sign_recoverable(data),
1159        )
1160    }
1161
1162    /// Signs the given hash using the raw private key.
1163    pub fn sign_hash(hash: &[u8], chain_keypair: &ChainKeypair) -> Signature {
1164        Self::sign(hash, chain_keypair.secret().as_ref(), |k: &SigningKey, data: &[u8]| {
1165            k.sign_prehash_recoverable(data)
1166        })
1167    }
1168
1169    fn verify<V>(&self, message: &[u8], public_key: &[u8], verifier: V) -> bool
1170    where
1171        V: FnOnce(&VerifyingKey, &[u8], &ECDSASignature) -> ecdsa::signature::Result<()>,
1172    {
1173        let pub_key = VerifyingKey::from_sec1_bytes(public_key).expect("invalid public key");
1174
1175        if let Ok(signature) = ECDSASignature::try_from(self.raw_signature().0.as_ref()) {
1176            verifier(&pub_key, message, &signature).is_ok()
1177        } else {
1178            warn!("un-parseable signature encountered");
1179            false
1180        }
1181    }
1182
1183    /// Verifies this signature against the given message and a public key object
1184    pub fn verify_message(&self, message: &[u8], public_key: &PublicKey) -> bool {
1185        self.verify(message, &public_key.to_bytes(false), |k, msg, sgn| k.verify(msg, sgn))
1186    }
1187
1188    /// Verifies this signature against the given hash and a public key object
1189    pub fn verify_hash(&self, hash: &[u8], public_key: &PublicKey) -> bool {
1190        self.verify(hash, &public_key.to_bytes(false), |k, msg, sgn| {
1191            k.verify_prehash(msg, sgn)
1192        })
1193    }
1194
1195    /// Returns the raw signature, without the encoded public key recovery bit and
1196    /// the recovery bit as a separate value.
1197    pub fn raw_signature(&self) -> ([u8; Self::SIZE], u8) {
1198        let mut raw_sig = self.0;
1199        let recovery: u8 = (raw_sig[Self::SIZE / 2] & 0x80 != 0).into();
1200        raw_sig[Self::SIZE / 2] &= 0x7f;
1201        (raw_sig, recovery)
1202    }
1203
1204    fn embed_recovery_bit(&mut self, recovery: u8) {
1205        self.0[Self::SIZE / 2] &= 0x7f;
1206        self.0[Self::SIZE / 2] |= recovery << 7;
1207    }
1208}
1209
1210impl AsRef<[u8]> for Signature {
1211    fn as_ref(&self) -> &[u8] {
1212        &self.0
1213    }
1214}
1215
1216impl TryFrom<&[u8]> for Signature {
1217    type Error = GeneralError;
1218
1219    fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
1220        Ok(Self(value.try_into().map_err(|_| ParseError("Signature".into()))?))
1221    }
1222}
1223
1224impl BytesRepresentable for Signature {
1225    const SIZE: usize = 64;
1226}
1227
1228impl PartialEq for Signature {
1229    fn eq(&self, other: &Self) -> bool {
1230        self.0.eq(&other.0)
1231    }
1232}
1233
1234impl Eq for Signature {}
1235
1236#[cfg(test)]
1237mod tests {
1238    use crate::utils::random_group_element;
1239    use crate::{
1240        keypairs::{ChainKeypair, Keypair, OffchainKeypair},
1241        types::{
1242            Challenge, CurvePoint, HalfKey, HalfKeyChallenge, Hash, OffchainPublicKey, OffchainSignature, PublicKey,
1243            Response, Signature,
1244        },
1245    };
1246    use ed25519_dalek::Signer;
1247    use hex_literal::hex;
1248    use hopr_primitive_types::prelude::*;
1249    use k256::{
1250        ecdsa::VerifyingKey,
1251        elliptic_curve::{sec1::ToEncodedPoint, CurveArithmetic},
1252        AffinePoint, {NonZeroScalar, Secp256k1, U256},
1253    };
1254    use libp2p_identity::PeerId;
1255    use std::str::FromStr;
1256
1257    const PUBLIC_KEY: [u8; 33] = hex!("021464586aeaea0eb5736884ca1bf42d165fc8e2243b1d917130fb9e321d7a93b8");
1258    const PUBLIC_KEY_UNCOMPRESSED_PLAIN: [u8; 64] = hex!("1464586aeaea0eb5736884ca1bf42d165fc8e2243b1d917130fb9e321d7a93b8fb0699d4f177f9c84712f6d7c5f6b7f4f6916116047fa25c79ef806fc6c9523e");
1259    const PUBLIC_KEY_UNCOMPRESSED: [u8; 65] = hex!("041464586aeaea0eb5736884ca1bf42d165fc8e2243b1d917130fb9e321d7a93b8fb0699d4f177f9c84712f6d7c5f6b7f4f6916116047fa25c79ef806fc6c9523e");
1260    const PRIVATE_KEY: [u8; 32] = hex!("e17fe86ce6e99f4806715b0c9412f8dad89334bf07f72d5834207a9d8f19d7f8");
1261
1262    #[test]
1263    fn test_signature_signing() -> anyhow::Result<()> {
1264        let msg = b"test12345";
1265        let kp = ChainKeypair::from_secret(&PRIVATE_KEY)?;
1266        let sgn = Signature::sign_message(msg, &kp);
1267
1268        let expected_pk = PublicKey::try_from(PUBLIC_KEY.as_ref())?;
1269        assert!(sgn.verify_message(msg, &expected_pk));
1270
1271        let extracted_pk = PublicKey::from_signature(msg, &sgn)?;
1272        assert_eq!(expected_pk, extracted_pk, "key extracted from signature does not match");
1273
1274        Ok(())
1275    }
1276
1277    #[test]
1278    fn test_offchain_signature_signing() -> anyhow::Result<()> {
1279        let msg = b"test12345";
1280        let keypair = OffchainKeypair::from_secret(&PRIVATE_KEY)?;
1281
1282        let key = ed25519_dalek::SecretKey::try_from(PRIVATE_KEY)?;
1283        let kp = ed25519_dalek::SigningKey::from_bytes(&key);
1284        let pk = ed25519_dalek::VerifyingKey::from(&kp);
1285
1286        let sgn = kp.sign(msg);
1287        assert!(pk.verify_strict(msg, &sgn).is_ok(), "blomp");
1288
1289        let sgn_1 = OffchainSignature::sign_message(msg, &keypair);
1290        let sgn_2 = OffchainSignature::try_from(sgn_1.as_ref())?;
1291
1292        assert!(
1293            sgn_1.verify_message(msg, keypair.public()),
1294            "cannot verify message via sig 1"
1295        );
1296        assert!(
1297            sgn_2.verify_message(msg, keypair.public()),
1298            "cannot verify message via sig 2"
1299        );
1300        assert_eq!(sgn_1, sgn_2, "signatures must be equal");
1301
1302        Ok(())
1303    }
1304
1305    #[test]
1306    fn test_signature_serialize() -> anyhow::Result<()> {
1307        let msg = b"test000000";
1308        let kp = ChainKeypair::from_secret(&PRIVATE_KEY)?;
1309        let sgn = Signature::sign_message(msg, &kp);
1310
1311        let deserialized = Signature::try_from(sgn.as_ref())?;
1312        assert_eq!(sgn, deserialized, "signatures don't match");
1313
1314        Ok(())
1315    }
1316
1317    #[test]
1318    fn test_offchain_signature() -> anyhow::Result<()> {
1319        let msg = b"test12345";
1320        let keypair = OffchainKeypair::from_secret(&PRIVATE_KEY)?;
1321
1322        let key = ed25519_dalek::SecretKey::try_from(PRIVATE_KEY)?;
1323        let kp = ed25519_dalek::SigningKey::from_bytes(&key);
1324        let pk = ed25519_dalek::VerifyingKey::from(&kp);
1325
1326        let sgn = kp.sign(msg);
1327        assert!(pk.verify_strict(msg, &sgn).is_ok(), "blomp");
1328
1329        let sgn_1 = OffchainSignature::sign_message(msg, &keypair);
1330        let sgn_2 = OffchainSignature::try_from(sgn_1.as_ref())?;
1331
1332        assert!(
1333            sgn_1.verify_message(msg, keypair.public()),
1334            "cannot verify message via sig 1"
1335        );
1336        assert!(
1337            sgn_2.verify_message(msg, keypair.public()),
1338            "cannot verify message via sig 2"
1339        );
1340        assert_eq!(sgn_1, sgn_2, "signatures must be equal");
1341        // let keypair = OffchainKeypair::from_secret(&PRIVATE_KEY)?;
1342
1343        // let sig = OffchainSignature::sign_message("my test msg".as_bytes(), &keypair);
1344
1345        // assert!(sig.verify_message("my test msg".as_bytes(), keypair.public()));
1346
1347        Ok(())
1348    }
1349
1350    #[test]
1351    fn test_public_key_to_hex() -> anyhow::Result<()> {
1352        let pk = PublicKey::from_privkey(&hex!(
1353            "492057cf93e99b31d2a85bc5e98a9c3aa0021feec52c227cc8170e8f7d047775"
1354        ))?;
1355
1356        assert_eq!("0x39d1bc2291826eaed86567d225cf243ebc637275e0a5aedb0d6b1dc82136a38e428804340d4c949a029846f682711d046920b4ca8b8ebeb9d1192b5bdaa54dba",
1357            pk.to_hex(false));
1358        assert_eq!(
1359            "0x0239d1bc2291826eaed86567d225cf243ebc637275e0a5aedb0d6b1dc82136a38e",
1360            pk.to_hex(true)
1361        );
1362
1363        Ok(())
1364    }
1365
1366    #[test]
1367    fn test_public_key_recover() -> anyhow::Result<()> {
1368        let address = Address::from_str("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266")?;
1369
1370        let r = hex!("bcae4d37e3a1cd869984d1d68f9242291773cd33d26f1e754ecc1a9bfaee7d17");
1371        let s = hex!("0b755ab5f6375595fc7fc245c45f6598cc873719183733f4c464d63eefd8579b");
1372        let v = 1u8;
1373
1374        let hash = hex!("fac7acad27047640b069e8157b61623e3cb6bb86e6adf97151f93817c291f3cf");
1375
1376        assert_eq!(
1377            address,
1378            PublicKey::from_raw_signature(&hash, &r, &s, v, VerifyingKey::recover_from_prehash)?.to_address()
1379        );
1380
1381        Ok(())
1382    }
1383
1384    #[test]
1385    fn test_public_key_combine_tweak() -> anyhow::Result<()> {
1386        let (scalar1, point1) = random_group_element();
1387        let (scalar2, point2) = random_group_element();
1388
1389        let pk1 = PublicKey::try_from(point1)?;
1390        let pk2 = PublicKey::try_from(point2)?;
1391
1392        let sum = PublicKey::combine(&[&pk1, &pk2]);
1393        let tweak1 = PublicKey::tweak_add(&pk1, &scalar2);
1394        assert_eq!(sum, tweak1);
1395
1396        let tweak2 = PublicKey::tweak_add(&pk2, &scalar1);
1397        assert_eq!(sum, tweak2);
1398
1399        Ok(())
1400    }
1401
1402    #[test]
1403    fn test_sign_and_recover() -> anyhow::Result<()> {
1404        let msg = hex!("eff80b9f035b1d369c6a60f362ac7c8b8c3b61b76d151d1be535145ccaa3e83e");
1405
1406        let kp = ChainKeypair::from_secret(&PRIVATE_KEY)?;
1407
1408        let signature1 = Signature::sign_message(&msg, &kp);
1409        let signature2 = Signature::sign_hash(&msg, &kp);
1410
1411        let pub_key1 = PublicKey::from_privkey(&PRIVATE_KEY)?;
1412        let pub_key2 = PublicKey::from_signature(&msg, &signature1)?;
1413        let pub_key3 = PublicKey::from_signature_hash(&msg, &signature2)?;
1414
1415        assert_eq!(pub_key1, kp.public().0);
1416        assert_eq!(pub_key1, pub_key2, "recovered public key does not match");
1417        assert_eq!(pub_key1, pub_key3, "recovered public key does not match");
1418
1419        assert!(
1420            signature1.verify_message(&msg, &pub_key1),
1421            "signature 1 verification failed with pub key 1"
1422        );
1423        assert!(
1424            signature1.verify_message(&msg, &pub_key2),
1425            "signature 1 verification failed with pub key 2"
1426        );
1427        assert!(
1428            signature1.verify_message(&msg, &pub_key3),
1429            "signature 1 verification failed with pub key 3"
1430        );
1431
1432        assert!(
1433            signature2.verify_hash(&msg, &pub_key1),
1434            "signature 2 verification failed with pub key 1"
1435        );
1436        assert!(
1437            signature2.verify_hash(&msg, &pub_key2),
1438            "signature 2 verification failed with pub key 2"
1439        );
1440        assert!(
1441            signature2.verify_hash(&msg, &pub_key3),
1442            "signature 2 verification failed with pub key 3"
1443        );
1444
1445        Ok(())
1446    }
1447
1448    #[test]
1449    fn test_public_key_serialize() -> anyhow::Result<()> {
1450        let pk1 = PublicKey::try_from(PUBLIC_KEY.as_ref())?;
1451        let pk2 = PublicKey::try_from(pk1.to_bytes(true).as_ref())?;
1452        let pk3 = PublicKey::try_from(pk1.to_bytes(false).as_ref())?;
1453
1454        assert_eq!(pk1, pk2, "pub keys 1 2 don't match");
1455        assert_eq!(pk2, pk3, "pub keys 2 3 don't match");
1456
1457        let pk1 = PublicKey::try_from(PUBLIC_KEY.as_ref())?;
1458        let pk2 = PublicKey::try_from(PUBLIC_KEY_UNCOMPRESSED.as_ref())?;
1459        let pk3 = PublicKey::try_from(PUBLIC_KEY_UNCOMPRESSED_PLAIN.as_ref())?;
1460
1461        assert_eq!(pk1, pk2, "pubkeys don't match");
1462        assert_eq!(pk2, pk3, "pubkeys don't match");
1463
1464        assert_eq!(PublicKey::SIZE_COMPRESSED, pk1.to_bytes(true).len());
1465        assert_eq!(PublicKey::SIZE_UNCOMPRESSED, pk1.to_bytes(false).len());
1466
1467        let shorter = hex!("f85e38b056284626a7aed0acc5d474605a408e6cccf76d7241ec7b4dedb31929b710e034f4f9a7dba97743b01e1cc35a45a60bebb29642cb0ba6a7fe8433316c");
1468        let s1 = PublicKey::try_from(shorter.as_ref())?;
1469        let s2 = PublicKey::try_from(s1.to_bytes(false).as_ref())?;
1470        assert_eq!(s1, s2);
1471
1472        Ok(())
1473    }
1474
1475    #[test]
1476    fn test_public_key_should_not_accept_identity() -> anyhow::Result<()> {
1477        let cp: CurvePoint = AffinePoint::IDENTITY.into();
1478
1479        PublicKey::try_from(cp).expect_err("must fail for identity point");
1480        PublicKey::try_from(AffinePoint::IDENTITY).expect_err("must fail for identity point");
1481
1482        Ok(())
1483    }
1484
1485    #[test]
1486    fn test_public_key_curve_point() -> anyhow::Result<()> {
1487        let cp1: CurvePoint = PublicKey::try_from(PUBLIC_KEY.as_ref())?.into();
1488        let cp2 = CurvePoint::try_from(cp1.as_ref())?;
1489        assert_eq!(cp1, cp2);
1490
1491        Ok(())
1492    }
1493
1494    #[test]
1495    fn test_public_key_from_privkey() -> anyhow::Result<()> {
1496        let pk1 = PublicKey::from_privkey(&PRIVATE_KEY)?;
1497        let pk2 = PublicKey::try_from(PUBLIC_KEY.as_ref())?;
1498
1499        assert_eq!(pk1, pk2, "failed to match deserialized pub key");
1500
1501        Ok(())
1502    }
1503
1504    #[test]
1505    fn test_offchain_public_key() -> anyhow::Result<()> {
1506        let (s, pk1) = OffchainKeypair::random().unzip();
1507
1508        let pk2 = OffchainPublicKey::from_privkey(s.as_ref())?;
1509        assert_eq!(pk1, pk2, "from privkey failed");
1510
1511        let pk3 = OffchainPublicKey::try_from(pk1.as_ref())?;
1512        assert_eq!(pk1, pk3, "from bytes failed");
1513
1514        Ok(())
1515    }
1516
1517    #[test]
1518    fn test_offchain_public_key_peerid() -> anyhow::Result<()> {
1519        let valid_peerid = PeerId::from_str("12D3KooWLYKsvDB4xEELYoHXxeStj2gzaDXjra2uGaFLpKCZkJHs")?;
1520        let valid = OffchainPublicKey::try_from(valid_peerid)?;
1521        assert_eq!(valid_peerid, valid.into(), "must work with ed25519 peer ids");
1522
1523        let invalid_peerid = PeerId::from_str("16Uiu2HAmPHGyJ7y1Rj3kJ64HxJQgM9rASaeT2bWfXF9EiX3Pbp3K")?;
1524        let invalid = OffchainPublicKey::try_from(invalid_peerid);
1525        assert!(invalid.is_err(), "must not work with secp256k1 peer ids");
1526
1527        let invalid_peerid_2 = PeerId::from_str("QmWvEwidPYBbLHfcZN6ATHdm4NPM4KbUx72LZnZRoRNKEN")?;
1528        let invalid_2 = OffchainPublicKey::try_from(invalid_peerid_2);
1529        assert!(invalid_2.is_err(), "must not work with rsa peer ids");
1530
1531        Ok(())
1532    }
1533
1534    #[test]
1535    pub fn test_response() -> anyhow::Result<()> {
1536        let r1 = Response([0u8; Response::SIZE]);
1537        let r2 = Response::try_from(r1.as_ref())?;
1538        assert_eq!(r1, r2, "deserialized response does not match");
1539
1540        Ok(())
1541    }
1542
1543    #[test]
1544    fn test_curve_point() -> anyhow::Result<()> {
1545        let scalar = NonZeroScalar::from_uint(U256::from_u8(100)).expect("should hold a value");
1546        let test_point = (<Secp256k1 as CurveArithmetic>::ProjectivePoint::GENERATOR * scalar.as_ref()).to_affine();
1547
1548        let cp1 = CurvePoint::from_str(hex::encode(test_point.to_encoded_point(false).to_bytes()).as_str())?;
1549
1550        let cp2 = CurvePoint::try_from(cp1.as_ref())?;
1551
1552        assert_eq!(cp1, cp2, "failed to match deserialized curve point");
1553
1554        let pk = PublicKey::from_privkey(&scalar.to_bytes())?;
1555
1556        assert_eq!(
1557            cp1.to_address(),
1558            pk.to_address(),
1559            "failed to match curve point address with pub key address"
1560        );
1561
1562        let ch1 = Challenge(cp1);
1563        let ch2 = Challenge(cp2);
1564
1565        assert_eq!(ch1.to_ethereum_challenge(), ch2.to_ethereum_challenge());
1566        assert_eq!(ch1, ch2, "failed to match ethereum challenges from curve points");
1567
1568        // Must be able to create from compressed and uncompressed data
1569        let scalar2 = NonZeroScalar::from_uint(U256::from_u8(123)).expect("should hold a value");
1570        let test_point2 = (<Secp256k1 as CurveArithmetic>::ProjectivePoint::GENERATOR * scalar2.as_ref()).to_affine();
1571        let uncompressed = test_point2.to_encoded_point(false);
1572        assert!(!uncompressed.is_compressed(), "given point is compressed");
1573
1574        let compressed = uncompressed.compress();
1575        assert!(compressed.is_compressed(), "failed to compress points");
1576
1577        let cp3 = CurvePoint::try_from(uncompressed.as_bytes())?;
1578        let cp4 = CurvePoint::try_from(compressed.as_bytes())?;
1579
1580        assert_eq!(
1581            cp3, cp4,
1582            "failed to match curve point from compressed and uncompressed source"
1583        );
1584
1585        Ok(())
1586    }
1587
1588    #[test]
1589    fn test_half_key() -> anyhow::Result<()> {
1590        let hk1 = HalfKey {
1591            hkey: [0u8; HalfKey::SIZE],
1592        };
1593        let hk2 = HalfKey::try_from(hk1.as_ref())?;
1594
1595        assert_eq!(hk1, hk2, "failed to match deserialized half-key");
1596
1597        Ok(())
1598    }
1599
1600    #[test]
1601    fn test_half_key_challenge() -> anyhow::Result<()> {
1602        let hkc1 = HalfKeyChallenge::try_from(PUBLIC_KEY.as_ref())?;
1603        let hkc2 = HalfKeyChallenge::try_from(hkc1.as_ref())?;
1604        assert_eq!(hkc1, hkc2, "failed to match deserialized half key challenge");
1605
1606        Ok(())
1607    }
1608
1609    #[test]
1610    fn test_hash() -> anyhow::Result<()> {
1611        let hash1 = Hash::create(&[b"msg"]);
1612        assert_eq!(
1613            "0x92aef1b955b9de564fc50e31a55b470b0c8cdb931f186485d620729fb03d6f2c",
1614            hash1.to_hex(),
1615            "hash test vector failed to match"
1616        );
1617
1618        let hash2 = Hash::try_from(hash1.as_ref())?;
1619        assert_eq!(hash1, hash2, "failed to match deserialized hash");
1620
1621        assert_eq!(
1622            hash1.hash(),
1623            Hash::try_from(hex!("1c4d8d521eccee7225073ea180e0fa075a6443afb7ca06076a9566b07d29470f").as_ref())?
1624        );
1625
1626        Ok(())
1627    }
1628}