1use std::{
2 cmp::Ordering,
3 fmt::{Debug, Display, Formatter},
4 hash,
5 hash::Hasher,
6 marker::PhantomData,
7 result,
8 str::FromStr,
9};
10
11use cipher::crypto_common::OutputSizeUser;
12use curve25519_dalek::{
13 edwards::{CompressedEdwardsY, EdwardsPoint},
14 montgomery::MontgomeryPoint,
15};
16use digest::Digest;
17use elliptic_curve::NonZeroScalar;
18use generic_array::GenericArray;
19use hopr_crypto_random::Randomizable;
20use hopr_primitive_types::{errors::GeneralError::ParseError, prelude::*};
21use k256::{
22 AffinePoint, Secp256k1,
23 elliptic_curve::{
24 self,
25 point::NonIdentity,
26 sec1::{FromEncodedPoint, ToEncodedPoint},
27 },
28};
29use libp2p_identity::PeerId;
30
31use crate::{
32 errors::{
33 CryptoError::{self, CalculationError, InvalidInputValue},
34 Result,
35 },
36 utils::random_group_element,
37};
38
39pub(crate) fn affine_point_from_bytes(bytes: &[u8]) -> Result<AffinePoint> {
40 let ep = k256::EncodedPoint::from_bytes(bytes).map_err(|_| InvalidInputValue("affine_point_from_bytes"))?;
41 AffinePoint::from_encoded_point(&ep)
42 .into_option()
43 .ok_or(InvalidInputValue("affine_point_from_bytes"))
44}
45
46pub(crate) fn affine_point_to_address(ap: &AffinePoint) -> Address {
47 let serialized = ap.to_encoded_point(false);
48 let hash = Hash::create(&[&serialized.as_ref()[1..]]);
49 Address::new(&hash.as_ref()[12..])
50}
51
52#[derive(Clone, Copy)]
56pub struct Challenge(NonIdentity<AffinePoint>);
57
58impl Debug for Challenge {
59 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
60 write!(f, "{}", self.0.to_encoded_point(true))
61 }
62}
63
64impl PartialEq for Challenge {
65 fn eq(&self, other: &Self) -> bool {
66 self.0.eq(other.0.as_ref())
67 }
68}
69
70impl Eq for Challenge {}
71
72impl Challenge {
73 pub fn to_ethereum_challenge(&self) -> EthereumChallenge {
78 EthereumChallenge(affine_point_to_address(&self.0))
79 }
80}
81
82impl Challenge {
83 pub fn from_hint_and_share(own_share: &HalfKeyChallenge, hint: &HalfKeyChallenge) -> Result<Self> {
88 #[cfg(not(feature = "rust-ecdsa"))]
89 {
90 let own_share = secp256k1::PublicKey::from_byte_array_compressed(own_share.0)
91 .map_err(|_| ParseError("invalid half-key challenge for own share".into()))?;
92
93 let hint = secp256k1::PublicKey::from_byte_array_compressed(hint.0)
94 .map_err(|_| ParseError("invalid half-key challenge for hint".into()))?;
95
96 let res = own_share.combine(&hint).map_err(|_| CalculationError)?;
97
98 affine_point_from_bytes(&res.serialize_uncompressed())
99 .and_then(|p| NonIdentity::new(p).into_option().ok_or(CryptoError::InvalidPublicKey))
100 .map(Self)
101 }
102
103 #[cfg(feature = "rust-ecdsa")]
104 {
105 let own_share: k256::ProjectivePoint = affine_point_from_bytes(own_share.as_ref())?.into();
106
107 let hint: k256::ProjectivePoint = affine_point_from_bytes(hint.as_ref())?.into();
108
109 NonIdentity::new((own_share + hint).to_affine())
110 .into_option()
111 .ok_or(CalculationError)
112 .map(Self)
113 }
114 }
115
116 pub fn from_own_share_and_half_key(own_share: &HalfKeyChallenge, half_key: &HalfKey) -> Result<Self> {
122 Self::from_hint_and_share(own_share, &half_key.to_challenge()?)
123 }
124}
125
126#[derive(Debug, Copy, Clone, Eq, PartialEq)]
133#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
134pub struct HalfKey(#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] [u8; Self::SIZE]);
135
136impl Default for HalfKey {
137 fn default() -> Self {
138 let mut ret = Self([0u8; Self::SIZE]);
139
140 ret.0.copy_from_slice(
141 NonZeroScalar::<Secp256k1>::from_uint(1u16.into())
142 .unwrap()
143 .to_bytes()
144 .as_slice(),
145 );
146 ret
147 }
148}
149
150impl HalfKey {
151 pub fn to_challenge(&self) -> Result<HalfKeyChallenge> {
157 Ok(PublicKey::from_privkey(&self.0)?.as_ref().try_into()?)
159 }
160}
161
162impl Randomizable for HalfKey {
163 fn random() -> Self {
164 Self(random_group_element().0)
165 }
166}
167
168impl AsRef<[u8]> for HalfKey {
169 fn as_ref(&self) -> &[u8] {
170 &self.0
171 }
172}
173
174impl TryFrom<&[u8]> for HalfKey {
175 type Error = GeneralError;
176
177 fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
178 Ok(Self(value.try_into().map_err(|_| ParseError("HalfKey".into()))?))
179 }
180}
181
182impl BytesRepresentable for HalfKey {
183 const SIZE: usize = 32;
185}
186
187#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
194#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
195pub struct HalfKeyChallenge(#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] [u8; Self::SIZE]);
196
197impl Display for HalfKeyChallenge {
198 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
199 write!(f, "{}", self.to_hex())
200 }
201}
202
203impl Default for HalfKeyChallenge {
204 fn default() -> Self {
205 let mut ret = Self([0u8; Self::SIZE]);
208 ret.0[Self::SIZE - 1] = 1;
209 ret
210 }
211}
212
213impl HalfKeyChallenge {
214 pub fn new(half_key_challenge: &[u8]) -> Self {
215 let mut ret = Self::default();
216 ret.0.copy_from_slice(half_key_challenge);
217 ret
218 }
219}
220
221impl AsRef<[u8]> for HalfKeyChallenge {
222 fn as_ref(&self) -> &[u8] {
223 &self.0
224 }
225}
226
227impl TryFrom<&[u8]> for HalfKeyChallenge {
228 type Error = GeneralError;
229
230 fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
231 Ok(Self(
232 value.try_into().map_err(|_| ParseError("HalfKeyChallenge".into()))?,
233 ))
234 }
235}
236
237impl BytesRepresentable for HalfKeyChallenge {
238 const SIZE: usize = PublicKey::SIZE_COMPRESSED;
240}
241
242impl FromStr for HalfKeyChallenge {
243 type Err = GeneralError;
244
245 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
246 Self::from_hex(s)
247 }
248}
249
250const HASH_BASE_SIZE: usize = 32;
251
252#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
254pub struct HashBase<H>(
255 #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] [u8; HASH_BASE_SIZE],
256 #[cfg_attr(feature = "serde", serde(skip))] PhantomData<H>,
257);
258
259impl<H> Clone for HashBase<H> {
260 fn clone(&self) -> Self {
261 *self
262 }
263}
264
265impl<H> Copy for HashBase<H> {}
266
267impl<H> PartialEq for HashBase<H> {
268 fn eq(&self, other: &Self) -> bool {
269 self.0 == other.0
270 }
271}
272
273impl<H> Eq for HashBase<H> {}
274
275impl<H> Default for HashBase<H> {
276 fn default() -> Self {
277 Self([0u8; HASH_BASE_SIZE], PhantomData)
278 }
279}
280
281impl<H> PartialOrd<Self> for HashBase<H> {
282 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
283 Some(self.cmp(other))
284 }
285}
286
287impl<H> Ord for HashBase<H> {
288 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
289 self.0.cmp(&other.0)
290 }
291}
292
293impl<H> std::hash::Hash for HashBase<H> {
294 fn hash<H2: Hasher>(&self, state: &mut H2) {
295 self.0.hash(state);
296 }
297}
298
299impl<H> Debug for HashBase<H> {
300 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
302 write!(f, "{}", self.to_hex())
303 }
304}
305
306impl<H> Display for HashBase<H> {
307 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
308 write!(f, "{}", self.to_hex())
309 }
310}
311
312impl<H> FromStr for HashBase<H> {
313 type Err = GeneralError;
314
315 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
316 Self::from_hex(s)
317 }
318}
319
320impl<H> HashBase<H>
321where
322 H: OutputSizeUser<OutputSize = typenum::U32> + Digest,
323{
324 pub fn hash(&self) -> Self {
326 Self::create(&[&self.0])
327 }
328
329 pub fn create(inputs: &[&[u8]]) -> Self {
331 let mut hash = H::new();
332 inputs.iter().for_each(|v| hash.update(v));
333 Self(hash.finalize().into(), PhantomData)
334 }
335}
336
337impl<H> AsRef<[u8]> for HashBase<H> {
338 fn as_ref(&self) -> &[u8] {
339 &self.0
340 }
341}
342
343impl<H> TryFrom<&[u8]> for HashBase<H> {
344 type Error = GeneralError;
345
346 fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
347 Ok(Self(
348 value.try_into().map_err(|_| ParseError("Hash".into()))?,
349 PhantomData,
350 ))
351 }
352}
353
354impl<H> BytesRepresentable for HashBase<H> {
355 const SIZE: usize = HASH_BASE_SIZE;
357}
358
359impl<H> From<[u8; HASH_BASE_SIZE]> for HashBase<H> {
360 fn from(hash: [u8; HASH_BASE_SIZE]) -> Self {
361 Self(hash, PhantomData)
362 }
363}
364
365impl<H> From<HashBase<H>> for [u8; HASH_BASE_SIZE] {
366 fn from(value: HashBase<H>) -> Self {
367 value.0
368 }
369}
370
371impl<H> From<&HashBase<H>> for [u8; HASH_BASE_SIZE] {
372 fn from(value: &HashBase<H>) -> Self {
373 value.0
374 }
375}
376
377impl<H> From<HashBase<H>> for primitive_types::H256 {
378 fn from(value: HashBase<H>) -> Self {
379 value.0.into()
380 }
381}
382
383impl<H> From<primitive_types::H256> for HashBase<H> {
384 fn from(value: primitive_types::H256) -> Self {
385 Self(value.0, PhantomData)
386 }
387}
388
389pub type Hash = HashBase<sha3::Keccak256>;
393
394pub type HashFast = HashBase<blake3::Hasher>;
399
400#[derive(Clone, Copy, Eq)]
402#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
403pub struct OffchainPublicKey {
404 compressed: CompressedEdwardsY,
405 pub(crate) edwards: EdwardsPoint,
406}
407
408impl std::fmt::Debug for OffchainPublicKey {
409 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
410 write!(f, "{}", self.to_hex())
412 }
413}
414
415impl std::hash::Hash for OffchainPublicKey {
416 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
417 self.compressed.hash(state);
418 }
419}
420
421impl PartialEq for OffchainPublicKey {
422 fn eq(&self, other: &Self) -> bool {
423 self.compressed == other.compressed
424 }
425}
426
427impl AsRef<[u8]> for OffchainPublicKey {
428 fn as_ref(&self) -> &[u8] {
429 &self.compressed.0
430 }
431}
432
433impl TryFrom<&[u8]> for OffchainPublicKey {
434 type Error = GeneralError;
435
436 fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
437 let compressed = CompressedEdwardsY::from_slice(value).map_err(|_| ParseError("OffchainPublicKey".into()))?;
438 let edwards = compressed
439 .decompress()
440 .ok_or(ParseError("OffchainPublicKey.decompress".into()))?;
441 Ok(Self { compressed, edwards })
442 }
443}
444
445impl BytesRepresentable for OffchainPublicKey {
446 const SIZE: usize = 32;
448}
449
450impl TryFrom<[u8; OffchainPublicKey::SIZE]> for OffchainPublicKey {
451 type Error = GeneralError;
452
453 fn try_from(value: [u8; OffchainPublicKey::SIZE]) -> std::result::Result<Self, Self::Error> {
454 let v: &[u8] = &value;
455 v.try_into()
456 }
457}
458
459impl From<OffchainPublicKey> for PeerId {
460 fn from(value: OffchainPublicKey) -> Self {
461 let k = libp2p_identity::ed25519::PublicKey::try_from_bytes(value.compressed.as_bytes())
462 .expect("offchain public key is always a valid ed25519 public key");
463 PeerId::from_public_key(&k.into())
464 }
465}
466
467impl From<&OffchainPublicKey> for PeerId {
468 fn from(value: &OffchainPublicKey) -> Self {
469 (*value).into()
470 }
471}
472
473impl Display for OffchainPublicKey {
474 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
475 write!(f, "{}", self.to_hex())
476 }
477}
478
479impl OffchainPublicKey {
480 pub fn from_privkey(private_key: &[u8]) -> Result<Self> {
483 let mut pk: [u8; ed25519_dalek::SECRET_KEY_LENGTH] =
484 private_key.try_into().map_err(|_| InvalidInputValue("private_key"))?;
485 let sk = libp2p_identity::ed25519::SecretKey::try_from_bytes(&mut pk)
486 .map_err(|_| InvalidInputValue("private_key"))?;
487 let kp: libp2p_identity::ed25519::Keypair = sk.into();
488 Ok(Self::try_from(kp.public().to_bytes())?)
489 }
490
491 pub fn to_peerid_str(&self) -> String {
493 PeerId::from(self).to_base58()
494 }
495
496 pub fn from_peerid(peerid: &PeerId) -> std::result::Result<Self, GeneralError> {
501 let mh = peerid.as_ref();
502 if mh.code() == 0 {
503 libp2p_identity::PublicKey::try_decode_protobuf(mh.digest())
504 .map_err(|_| ParseError("invalid ed25519 peer id".into()))
505 .and_then(|pk| {
506 pk.try_into_ed25519()
507 .map(|p| p.to_bytes())
508 .map_err(|_| ParseError("invalid ed25519 peer id".into()))
509 })
510 .and_then(Self::try_from)
511 } else {
512 Err(ParseError("invalid ed25519 peer id".into()))
513 }
514 }
515}
516
517impl From<&OffchainPublicKey> for EdwardsPoint {
518 fn from(value: &OffchainPublicKey) -> Self {
519 value.edwards
520 }
521}
522
523impl<'a> From<&'a OffchainPublicKey> for &'a GenericArray<u8, typenum::U32> {
524 fn from(value: &'a OffchainPublicKey) -> &'a GenericArray<u8, typenum::U32> {
525 GenericArray::from_slice(&value.compressed.0)
526 }
527}
528
529impl From<&OffchainPublicKey> for MontgomeryPoint {
530 fn from(value: &OffchainPublicKey) -> Self {
531 value.edwards.to_montgomery()
534 }
535}
536
537pub const PACKET_TAG_LENGTH: usize = 16;
539
540pub type PacketTag = [u8; PACKET_TAG_LENGTH];
542
543#[derive(Copy, Clone)]
550pub struct PublicKey(NonIdentity<AffinePoint>, [u8; Self::SIZE_COMPRESSED]);
551
552impl PublicKey {
553 pub const SIZE_COMPRESSED: usize = 33;
555 pub const SIZE_UNCOMPRESSED: usize = 65;
557 pub const SIZE_UNCOMPRESSED_PLAIN: usize = 64;
558
559 pub fn from_privkey(private_key: &[u8]) -> Result<PublicKey> {
564 #[cfg(feature = "rust-ecdsa")]
565 {
566 let secret_scalar = NonZeroScalar::<Secp256k1>::try_from(private_key)
568 .map_err(|_| GeneralError::ParseError("PublicKey".into()))?;
569
570 Ok(
571 elliptic_curve::PublicKey::<Secp256k1>::from_secret_scalar(&secret_scalar)
572 .to_nonidentity()
573 .into(),
574 )
575 }
576
577 #[cfg(not(feature = "rust-ecdsa"))]
578 {
579 let sk = secp256k1::SecretKey::from_byte_array(
580 private_key
581 .try_into()
582 .map_err(|_| GeneralError::ParseError("private_key.len".into()))?,
583 )
584 .map_err(|_| GeneralError::ParseError("private_key".into()))?;
585
586 let pk = secp256k1::PublicKey::from_secret_key_global(&sk);
587 affine_point_from_bytes(&pk.serialize_uncompressed())
588 .and_then(|p| NonIdentity::new(p).into_option().ok_or(CryptoError::InvalidPublicKey))
589 .map(Self::from)
590 }
591 }
592
593 pub fn to_address(&self) -> Address {
595 affine_point_to_address(self.0.as_ref())
596 }
597
598 pub fn to_uncompressed_bytes(&self) -> Box<[u8]> {
600 self.0.to_encoded_point(false).to_bytes()
601 }
602
603 pub fn to_uncompressed_hex(&self) -> String {
605 format!("0x{}", hex::encode(self.to_uncompressed_bytes()))
606 }
607}
608
609impl PartialEq for PublicKey {
610 fn eq(&self, other: &Self) -> bool {
611 self.1.eq(&other.1)
612 }
613}
614
615impl Eq for PublicKey {}
616
617impl hash::Hash for PublicKey {
618 fn hash<H: Hasher>(&self, state: &mut H) {
619 self.1.hash(state);
620 }
621}
622
623impl Randomizable for PublicKey {
624 fn random() -> Self {
627 let (_, cp) = random_group_element();
628 cp.into()
629 }
630}
631
632impl Debug for PublicKey {
633 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
634 write!(f, "{}", self.to_hex())
635 }
636}
637
638impl Display for PublicKey {
639 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
640 write!(f, "{}", self.to_hex())
641 }
642}
643
644impl TryFrom<&[u8]> for PublicKey {
645 type Error = GeneralError;
646
647 fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
648 match value.len() {
649 Self::SIZE_UNCOMPRESSED => {
650 let key = elliptic_curve::PublicKey::<Secp256k1>::from_sec1_bytes(value)
652 .map_err(|_| GeneralError::ParseError("invalid secp256k1 point".into()))?;
653
654 Ok(key.to_nonidentity().into())
655 }
656 Self::SIZE_UNCOMPRESSED_PLAIN => {
657 let key = elliptic_curve::PublicKey::<Secp256k1>::from_sec1_bytes(&[&[4u8], value].concat())
659 .map_err(|_| GeneralError::ParseError("invalid secp256k1 point".into()))?;
660
661 Ok(key.to_nonidentity().into())
662 }
663 Self::SIZE_COMPRESSED => {
664 let key = elliptic_curve::PublicKey::<Secp256k1>::from_sec1_bytes(value)
666 .map_err(|_| GeneralError::ParseError("invalid secp256k1 point".into()))?;
667
668 Ok(key.to_nonidentity().into())
669 }
670 _ => Err(GeneralError::ParseError("invalid secp256k1 point".into())),
671 }
672 }
673}
674
675impl AsRef<[u8]> for PublicKey {
676 fn as_ref(&self) -> &[u8] {
677 &self.1
678 }
679}
680
681impl BytesRepresentable for PublicKey {
682 const SIZE: usize = PublicKey::SIZE_COMPRESSED;
683}
684
685impl From<NonIdentity<AffinePoint>> for PublicKey {
686 fn from(value: NonIdentity<AffinePoint>) -> Self {
687 let mut compressed = [0u8; PublicKey::SIZE_COMPRESSED];
688 compressed.copy_from_slice(value.to_encoded_point(true).as_bytes());
689 Self(value, compressed)
690 }
691}
692
693impl From<PublicKey> for NonIdentity<AffinePoint> {
694 fn from(value: PublicKey) -> Self {
695 value.0
696 }
697}
698
699impl TryFrom<AffinePoint> for PublicKey {
700 type Error = CryptoError;
701
702 fn try_from(value: AffinePoint) -> std::result::Result<Self, Self::Error> {
703 Ok(NonIdentity::new(value)
704 .into_option()
705 .ok_or(CryptoError::InvalidPublicKey)?
706 .into())
707 }
708}
709
710impl From<&PublicKey> for k256::ProjectivePoint {
712 fn from(value: &PublicKey) -> Self {
713 (*value.0.as_ref()).into()
714 }
715}
716
717impl<'a> From<&'a PublicKey> for &'a GenericArray<u8, typenum::U33> {
718 fn from(value: &'a PublicKey) -> &'a GenericArray<u8, typenum::U33> {
719 GenericArray::from_slice(&value.1)
720 }
721}
722
723#[derive(Clone, Debug, PartialEq, Eq)]
726#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
727pub struct Response(#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] [u8; Self::SIZE]);
728
729impl Default for Response {
730 fn default() -> Self {
731 Self(HalfKey::default().0)
732 }
733}
734
735impl Display for Response {
736 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
737 write!(f, "{}", self.to_hex())
738 }
739}
740
741impl Response {
742 pub fn to_challenge(&self) -> Result<Challenge> {
749 PublicKey::from_privkey(&self.0).map(|pk| Challenge(pk.into()))
751 }
752
753 pub fn from_half_keys(first: &HalfKey, second: &HalfKey) -> Result<Self> {
758 let first = NonZeroScalar::<Secp256k1>::try_from(first.as_ref()).map_err(|_| InvalidInputValue("first"))?;
759 let second = NonZeroScalar::<Secp256k1>::try_from(second.as_ref()).map_err(|_| InvalidInputValue("second"))?;
760
761 let res = first.as_ref() + second.as_ref();
763 if res.is_zero().into() {
764 return Err(InvalidInputValue("invalid half-key"));
765 }
766
767 let mut ret = [0u8; Self::SIZE];
768 ret.copy_from_slice(res.to_bytes().as_slice());
769 Ok(Self(ret))
770 }
771}
772
773impl AsRef<[u8]> for Response {
774 fn as_ref(&self) -> &[u8] {
775 &self.0
776 }
777}
778
779impl TryFrom<&[u8]> for Response {
780 type Error = GeneralError;
781
782 fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
783 Ok(Self(value.try_into().map_err(|_| ParseError("Response".into()))?))
784 }
785}
786
787impl BytesRepresentable for Response {
788 const SIZE: usize = 32;
790}
791
792impl From<[u8; Self::SIZE]> for Response {
793 fn from(value: [u8; Self::SIZE]) -> Self {
794 Self(value)
795 }
796}
797
798pub trait Pseudonym: BytesRepresentable + hash::Hash + Eq + Display + Randomizable {}
804
805#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
807#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
808pub struct SimplePseudonym(#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] pub [u8; Self::SIZE]);
809
810impl Display for SimplePseudonym {
811 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
812 write!(f, "{}", self.to_hex())
813 }
814}
815
816impl Debug for SimplePseudonym {
817 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
818 write!(f, "{}", self.to_hex())
819 }
820}
821
822impl BytesRepresentable for SimplePseudonym {
823 const SIZE: usize = 10;
824}
825
826impl AsRef<[u8]> for SimplePseudonym {
827 fn as_ref(&self) -> &[u8] {
828 &self.0
829 }
830}
831
832impl<'a> TryFrom<&'a [u8]> for SimplePseudonym {
833 type Error = GeneralError;
834
835 fn try_from(value: &'a [u8]) -> result::Result<Self, Self::Error> {
836 value
837 .try_into()
838 .map(Self)
839 .map_err(|_| ParseError("SimplePseudonym".into()))
840 }
841}
842
843impl Randomizable for SimplePseudonym {
844 fn random() -> Self {
846 let mut data = vec![0u8; Self::SIZE];
847 hopr_crypto_random::random_fill(&mut data);
848 Self::try_from(data.as_slice()).unwrap()
849 }
850}
851
852impl Pseudonym for SimplePseudonym {}
853
854#[cfg(test)]
855mod tests {
856 use std::str::FromStr;
857
858 use hex_literal::hex;
859 use hopr_crypto_random::Randomizable;
860 use hopr_primitive_types::prelude::*;
861 use k256::AffinePoint;
862 use libp2p_identity::PeerId;
863
864 use crate::{
865 keypairs::{Keypair, OffchainKeypair},
866 types::{Challenge, HalfKey, HalfKeyChallenge, Hash, OffchainPublicKey, PublicKey, Response},
867 };
868
869 const PUBLIC_KEY: [u8; 33] = hex!("021464586aeaea0eb5736884ca1bf42d165fc8e2243b1d917130fb9e321d7a93b8");
870 const PUBLIC_KEY_UNCOMPRESSED_PLAIN: [u8; 64] = hex!("1464586aeaea0eb5736884ca1bf42d165fc8e2243b1d917130fb9e321d7a93b8fb0699d4f177f9c84712f6d7c5f6b7f4f6916116047fa25c79ef806fc6c9523e");
871 const PUBLIC_KEY_UNCOMPRESSED: [u8; 65] = hex!("041464586aeaea0eb5736884ca1bf42d165fc8e2243b1d917130fb9e321d7a93b8fb0699d4f177f9c84712f6d7c5f6b7f4f6916116047fa25c79ef806fc6c9523e");
872 const PRIVATE_KEY: [u8; 32] = hex!("e17fe86ce6e99f4806715b0c9412f8dad89334bf07f72d5834207a9d8f19d7f8");
873
874 #[test]
875 fn test_public_key_to_hex() -> anyhow::Result<()> {
876 let pk = PublicKey::from_privkey(&hex!(
877 "492057cf93e99b31d2a85bc5e98a9c3aa0021feec52c227cc8170e8f7d047775"
878 ))?;
879
880 assert_eq!("0x0439d1bc2291826eaed86567d225cf243ebc637275e0a5aedb0d6b1dc82136a38e428804340d4c949a029846f682711d046920b4ca8b8ebeb9d1192b5bdaa54dba",
881 pk.to_uncompressed_hex());
882 assert_eq!(
883 "0x0239d1bc2291826eaed86567d225cf243ebc637275e0a5aedb0d6b1dc82136a38e",
884 pk.to_hex()
885 );
886
887 Ok(())
888 }
889
890 #[test]
891 fn test_public_key_serialize() -> anyhow::Result<()> {
892 let pk1 = PublicKey::try_from(PUBLIC_KEY.as_ref())?;
893 let pk2 = PublicKey::try_from(pk1.as_ref())?;
894 let pk3 = PublicKey::try_from(pk1.to_uncompressed_bytes().as_ref())?;
895
896 assert_eq!(pk1, pk2, "pub keys 1 2 don't match");
897 assert_eq!(pk2, pk3, "pub keys 2 3 don't match");
898
899 let pk1 = PublicKey::try_from(PUBLIC_KEY.as_ref())?;
900 let pk2 = PublicKey::try_from(PUBLIC_KEY_UNCOMPRESSED.as_ref())?;
901 let pk3 = PublicKey::try_from(PUBLIC_KEY_UNCOMPRESSED_PLAIN.as_ref())?;
902
903 assert_eq!(pk1, pk2, "pubkeys don't match");
904 assert_eq!(pk2, pk3, "pubkeys don't match");
905
906 assert_eq!(PublicKey::SIZE_COMPRESSED, pk1.as_ref().len());
907 assert_eq!(PublicKey::SIZE_UNCOMPRESSED, pk1.to_uncompressed_bytes().len());
908
909 let shorter = hex!("f85e38b056284626a7aed0acc5d474605a408e6cccf76d7241ec7b4dedb31929b710e034f4f9a7dba97743b01e1cc35a45a60bebb29642cb0ba6a7fe8433316c");
910 let s1 = PublicKey::try_from(shorter.as_ref())?;
911 let s2 = PublicKey::try_from(s1.to_uncompressed_bytes().as_ref())?;
912 assert_eq!(s1, s2);
913
914 Ok(())
915 }
916
917 #[test]
918 fn test_public_key_should_not_accept_identity() -> anyhow::Result<()> {
919 PublicKey::try_from(AffinePoint::IDENTITY).expect_err("must fail for identity point");
920 Ok(())
921 }
922
923 #[test]
924 fn test_public_key_from_privkey() -> anyhow::Result<()> {
925 let pk1 = PublicKey::from_privkey(&PRIVATE_KEY)?;
926 let pk2 = PublicKey::try_from(PUBLIC_KEY.as_ref())?;
927
928 assert_eq!(pk1, pk2, "failed to match deserialized pub key");
929
930 Ok(())
931 }
932
933 #[test]
934 fn test_offchain_public_key() -> anyhow::Result<()> {
935 let (s, pk1) = OffchainKeypair::random().unzip();
936
937 let pk2 = OffchainPublicKey::from_privkey(s.as_ref())?;
938 assert_eq!(pk1, pk2, "from privkey failed");
939
940 let pk3 = OffchainPublicKey::try_from(pk1.as_ref())?;
941 assert_eq!(pk1, pk3, "from bytes failed");
942
943 Ok(())
944 }
945
946 #[test]
947 fn test_offchain_public_key_peerid() -> anyhow::Result<()> {
948 let valid_peerid = PeerId::from_str("12D3KooWLYKsvDB4xEELYoHXxeStj2gzaDXjra2uGaFLpKCZkJHs")?;
949 let valid = OffchainPublicKey::from_peerid(&valid_peerid)?;
950 assert_eq!(valid_peerid, valid.into(), "must work with ed25519 peer ids");
951
952 let invalid_peerid = PeerId::from_str("16Uiu2HAmPHGyJ7y1Rj3kJ64HxJQgM9rASaeT2bWfXF9EiX3Pbp3K")?;
953 let invalid = OffchainPublicKey::from_peerid(&invalid_peerid);
954 assert!(invalid.is_err(), "must not work with secp256k1 peer ids");
955
956 let invalid_peerid_2 = PeerId::from_str("QmWvEwidPYBbLHfcZN6ATHdm4NPM4KbUx72LZnZRoRNKEN")?;
957 let invalid_2 = OffchainPublicKey::from_peerid(&invalid_peerid_2);
958 assert!(invalid_2.is_err(), "must not work with rsa peer ids");
959
960 Ok(())
961 }
962
963 #[test]
964 pub fn test_response() -> anyhow::Result<()> {
965 let r1 = Response([0u8; Response::SIZE]);
966 let r2 = Response::try_from(r1.as_ref())?;
967 assert_eq!(r1, r2, "deserialized response does not match");
968
969 Ok(())
970 }
971
972 #[test]
973 fn test_half_key() -> anyhow::Result<()> {
974 let hk1 = HalfKey([0u8; HalfKey::SIZE]);
975 let hk2 = HalfKey::try_from(hk1.as_ref())?;
976
977 assert_eq!(hk1, hk2, "failed to match deserialized half-key");
978
979 Ok(())
980 }
981
982 #[test]
983 fn test_half_key_challenge() -> anyhow::Result<()> {
984 let hkc1 = HalfKeyChallenge::try_from(PUBLIC_KEY.as_ref())?;
985 let hkc2 = HalfKeyChallenge::try_from(hkc1.as_ref())?;
986 assert_eq!(hkc1, hkc2, "failed to match deserialized half key challenge");
987
988 Ok(())
989 }
990
991 #[test]
992 fn test_challenge_response_flow() -> anyhow::Result<()> {
993 let hk1 = HalfKey::random();
994 let hk2 = HalfKey::random();
995
996 let response = Response::from_half_keys(&hk1, &hk2)?;
997
998 let half_chal1 = hk1.to_challenge()?;
999 let half_chal2 = hk2.to_challenge()?;
1000
1001 let challenge1 = Challenge::from_hint_and_share(&half_chal1, &half_chal2)?;
1002 assert_eq!(challenge1, Challenge::from_hint_and_share(&half_chal2, &half_chal1)?);
1003 assert_eq!(challenge1, Challenge::from_own_share_and_half_key(&half_chal1, &hk2)?);
1004
1005 let challenge2 = response.to_challenge()?;
1006 assert_eq!(challenge1, challenge2);
1007 assert_eq!(challenge1.to_ethereum_challenge(), challenge2.to_ethereum_challenge());
1008 Ok(())
1009 }
1010
1011 #[test]
1012 fn test_hash() -> anyhow::Result<()> {
1013 let hash1 = Hash::create(&[b"msg"]);
1014 assert_eq!(
1015 "0x92aef1b955b9de564fc50e31a55b470b0c8cdb931f186485d620729fb03d6f2c",
1016 hash1.to_hex(),
1017 "hash test vector failed to match"
1018 );
1019
1020 let hash2 = Hash::try_from(hash1.as_ref())?;
1021 assert_eq!(hash1, hash2, "failed to match deserialized hash");
1022
1023 assert_eq!(
1024 hash1.hash(),
1025 Hash::try_from(hex!("1c4d8d521eccee7225073ea180e0fa075a6443afb7ca06076a9566b07d29470f").as_ref())?
1026 );
1027
1028 Ok(())
1029 }
1030}