hopr_crypto_types/
types.rs

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