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
136#[allow(deprecated)] impl Default for HalfKey {
138 fn default() -> Self {
139 let mut ret = Self([0u8; Self::SIZE]);
140
141 ret.0.copy_from_slice(
142 NonZeroScalar::<Secp256k1>::from_uint(1u16.into())
143 .unwrap()
144 .to_bytes()
145 .as_slice(),
146 );
147 ret
148 }
149}
150
151impl HalfKey {
152 pub fn to_challenge(&self) -> Result<HalfKeyChallenge> {
158 let pk = PublicKey::from_privkey(&self.0)?;
160 let compressed: &[u8] = pk.as_ref();
161 Ok(compressed.try_into()?)
162 }
163}
164
165impl Randomizable for HalfKey {
166 fn random() -> Self {
167 Self(random_group_element().0)
168 }
169}
170
171impl AsRef<[u8]> for HalfKey {
172 fn as_ref(&self) -> &[u8] {
173 &self.0
174 }
175}
176
177impl TryFrom<&[u8]> for HalfKey {
178 type Error = GeneralError;
179
180 fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
181 Ok(Self(value.try_into().map_err(|_| ParseError("HalfKey".into()))?))
182 }
183}
184
185impl BytesRepresentable for HalfKey {
186 const SIZE: usize = 32;
188}
189
190#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
197#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
198pub struct HalfKeyChallenge(#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] [u8; Self::SIZE]);
199
200impl Display for HalfKeyChallenge {
201 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
202 write!(f, "{}", self.to_hex())
203 }
204}
205
206impl Default for HalfKeyChallenge {
207 fn default() -> Self {
208 let mut ret = Self([0u8; Self::SIZE]);
211 ret.0[Self::SIZE - 1] = 1;
212 ret
213 }
214}
215
216impl HalfKeyChallenge {
217 pub fn new(half_key_challenge: &[u8]) -> Self {
218 let mut ret = Self::default();
219 ret.0.copy_from_slice(half_key_challenge);
220 ret
221 }
222}
223
224impl AsRef<[u8]> for HalfKeyChallenge {
225 fn as_ref(&self) -> &[u8] {
226 &self.0
227 }
228}
229
230impl TryFrom<&[u8]> for HalfKeyChallenge {
231 type Error = GeneralError;
232
233 fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
234 Ok(Self(
235 value.try_into().map_err(|_| ParseError("HalfKeyChallenge".into()))?,
236 ))
237 }
238}
239
240impl BytesRepresentable for HalfKeyChallenge {
241 const SIZE: usize = PublicKey::SIZE_COMPRESSED;
243}
244
245impl FromStr for HalfKeyChallenge {
246 type Err = GeneralError;
247
248 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
249 Self::from_hex(s)
250 }
251}
252
253const HASH_BASE_SIZE: usize = 32;
254
255#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
257pub struct HashBase<H>(
258 #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] [u8; HASH_BASE_SIZE],
259 #[cfg_attr(feature = "serde", serde(skip))] PhantomData<H>,
260);
261
262impl<H> Clone for HashBase<H> {
263 fn clone(&self) -> Self {
264 *self
265 }
266}
267
268impl<H> Copy for HashBase<H> {}
269
270impl<H> PartialEq for HashBase<H> {
271 fn eq(&self, other: &Self) -> bool {
272 self.0 == other.0
273 }
274}
275
276impl<H> Eq for HashBase<H> {}
277
278impl<H> Default for HashBase<H> {
279 fn default() -> Self {
280 Self([0u8; HASH_BASE_SIZE], PhantomData)
281 }
282}
283
284impl<H> PartialOrd<Self> for HashBase<H> {
285 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
286 Some(self.cmp(other))
287 }
288}
289
290impl<H> Ord for HashBase<H> {
291 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
292 self.0.cmp(&other.0)
293 }
294}
295
296impl<H> std::hash::Hash for HashBase<H> {
297 fn hash<H2: Hasher>(&self, state: &mut H2) {
298 self.0.hash(state);
299 }
300}
301
302impl<H> Debug for HashBase<H> {
303 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
305 write!(f, "{}", self.to_hex())
306 }
307}
308
309impl<H> Display for HashBase<H> {
310 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
311 write!(f, "{}", self.to_hex())
312 }
313}
314
315impl<H> FromStr for HashBase<H> {
316 type Err = GeneralError;
317
318 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
319 Self::from_hex(s)
320 }
321}
322
323impl<H> HashBase<H>
324where
325 H: OutputSizeUser<OutputSize = typenum::U32> + Digest,
326{
327 pub fn hash(&self) -> Self {
329 Self::create(&[&self.0])
330 }
331
332 pub fn create(inputs: &[&[u8]]) -> Self {
334 let mut hash = H::new();
335 inputs.iter().for_each(|v| hash.update(v));
336 Self(hash.finalize().into(), PhantomData)
337 }
338}
339
340impl<H> AsRef<[u8]> for HashBase<H> {
341 fn as_ref(&self) -> &[u8] {
342 &self.0
343 }
344}
345
346impl<H> TryFrom<&[u8]> for HashBase<H> {
347 type Error = GeneralError;
348
349 fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
350 Ok(Self(
351 value.try_into().map_err(|_| ParseError("Hash".into()))?,
352 PhantomData,
353 ))
354 }
355}
356
357impl<H> BytesRepresentable for HashBase<H> {
358 const SIZE: usize = HASH_BASE_SIZE;
360}
361
362impl<H> From<[u8; HASH_BASE_SIZE]> for HashBase<H> {
363 fn from(hash: [u8; HASH_BASE_SIZE]) -> Self {
364 Self(hash, PhantomData)
365 }
366}
367
368impl<H> From<HashBase<H>> for [u8; HASH_BASE_SIZE] {
369 fn from(value: HashBase<H>) -> Self {
370 value.0
371 }
372}
373
374impl<H> From<&HashBase<H>> for [u8; HASH_BASE_SIZE] {
375 fn from(value: &HashBase<H>) -> Self {
376 value.0
377 }
378}
379
380impl<H> From<HashBase<H>> for primitive_types::H256 {
381 fn from(value: HashBase<H>) -> Self {
382 value.0.into()
383 }
384}
385
386impl<H> From<primitive_types::H256> for HashBase<H> {
387 fn from(value: primitive_types::H256) -> Self {
388 Self(value.0, PhantomData)
389 }
390}
391
392pub type Hash = HashBase<sha3::Keccak256>;
396
397pub type HashFast = HashBase<blake3::Hasher>;
402
403#[derive(Clone, Copy, Eq)]
405#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
406pub struct OffchainPublicKey {
407 compressed: CompressedEdwardsY,
408 pub(crate) edwards: EdwardsPoint,
409}
410
411impl std::fmt::Debug for OffchainPublicKey {
412 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
413 write!(f, "{}", self.to_hex())
415 }
416}
417
418impl std::hash::Hash for OffchainPublicKey {
419 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
420 self.compressed.hash(state);
421 }
422}
423
424impl PartialEq for OffchainPublicKey {
425 fn eq(&self, other: &Self) -> bool {
426 self.compressed == other.compressed
427 }
428}
429
430impl AsRef<[u8]> for OffchainPublicKey {
431 fn as_ref(&self) -> &[u8] {
432 &self.compressed.0
433 }
434}
435
436impl TryFrom<&[u8]> for OffchainPublicKey {
437 type Error = GeneralError;
438
439 fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
440 let compressed = CompressedEdwardsY::from_slice(value).map_err(|_| ParseError("OffchainPublicKey".into()))?;
441 let edwards = compressed
442 .decompress()
443 .ok_or(ParseError("OffchainPublicKey.decompress".into()))?;
444 Ok(Self { compressed, edwards })
445 }
446}
447
448impl BytesRepresentable for OffchainPublicKey {
449 const SIZE: usize = 32;
451}
452
453impl TryFrom<[u8; OffchainPublicKey::SIZE]> for OffchainPublicKey {
454 type Error = GeneralError;
455
456 fn try_from(value: [u8; OffchainPublicKey::SIZE]) -> std::result::Result<Self, Self::Error> {
457 let v: &[u8] = &value;
458 v.try_into()
459 }
460}
461
462impl From<OffchainPublicKey> for [u8; OffchainPublicKey::SIZE] {
463 fn from(value: OffchainPublicKey) -> Self {
464 value.compressed.0
465 }
466}
467
468impl From<OffchainPublicKey> for PeerId {
469 fn from(value: OffchainPublicKey) -> Self {
470 let k = libp2p_identity::ed25519::PublicKey::try_from_bytes(value.compressed.as_bytes())
471 .expect("offchain public key is always a valid ed25519 public key");
472 PeerId::from_public_key(&k.into())
473 }
474}
475
476impl From<&OffchainPublicKey> for PeerId {
477 fn from(value: &OffchainPublicKey) -> Self {
478 (*value).into()
479 }
480}
481
482impl Display for OffchainPublicKey {
483 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
484 write!(f, "{}", self.to_hex())
485 }
486}
487
488impl FromStr for OffchainPublicKey {
489 type Err = GeneralError;
490
491 fn from_str(s: &str) -> result::Result<Self, Self::Err> {
492 Self::from_hex(s)
493 }
494}
495
496impl OffchainPublicKey {
497 pub fn from_privkey(private_key: &[u8]) -> Result<Self> {
500 let mut pk: [u8; ed25519_dalek::SECRET_KEY_LENGTH] =
501 private_key.try_into().map_err(|_| InvalidInputValue("private_key"))?;
502 let sk = libp2p_identity::ed25519::SecretKey::try_from_bytes(&mut pk)
503 .map_err(|_| InvalidInputValue("private_key"))?;
504 let kp: libp2p_identity::ed25519::Keypair = sk.into();
505 Ok(Self::try_from(kp.public().to_bytes())?)
506 }
507
508 pub fn to_peerid_str(&self) -> String {
510 PeerId::from(self).to_base58()
511 }
512
513 pub fn from_peerid(peerid: &PeerId) -> std::result::Result<Self, GeneralError> {
518 let mh = peerid.as_ref();
519 if mh.code() == 0 {
520 libp2p_identity::PublicKey::try_decode_protobuf(mh.digest())
521 .map_err(|_| ParseError("invalid ed25519 peer id".into()))
522 .and_then(|pk| {
523 pk.try_into_ed25519()
524 .map(|p| p.to_bytes())
525 .map_err(|_| ParseError("invalid ed25519 peer id".into()))
526 })
527 .and_then(Self::try_from)
528 } else {
529 Err(ParseError("invalid ed25519 peer id".into()))
530 }
531 }
532}
533
534impl From<&OffchainPublicKey> for EdwardsPoint {
535 fn from(value: &OffchainPublicKey) -> Self {
536 value.edwards
537 }
538}
539
540impl<'a> From<&'a OffchainPublicKey> for &'a GenericArray<u8, typenum::U32> {
541 fn from(value: &'a OffchainPublicKey) -> &'a GenericArray<u8, typenum::U32> {
542 GenericArray::from_slice(&value.compressed.0)
543 }
544}
545
546impl From<&OffchainPublicKey> for MontgomeryPoint {
547 fn from(value: &OffchainPublicKey) -> Self {
548 value.edwards.to_montgomery()
551 }
552}
553
554pub const PACKET_TAG_LENGTH: usize = 16;
556
557pub type PacketTag = [u8; PACKET_TAG_LENGTH];
559
560#[derive(Copy, Clone)]
567pub struct PublicKey(NonIdentity<AffinePoint>, [u8; Self::SIZE_COMPRESSED], Address);
568
569impl PublicKey {
570 pub const SIZE_COMPRESSED: usize = 33;
572 pub const SIZE_UNCOMPRESSED: usize = 65;
574 pub const SIZE_UNCOMPRESSED_PLAIN: usize = 64;
575
576 pub fn from_privkey(private_key: &[u8]) -> Result<PublicKey> {
581 #[cfg(feature = "rust-ecdsa")]
582 {
583 let secret_scalar = NonZeroScalar::<Secp256k1>::try_from(private_key)
585 .map_err(|_| GeneralError::ParseError("PublicKey".into()))?;
586
587 Ok(
588 elliptic_curve::PublicKey::<Secp256k1>::from_secret_scalar(&secret_scalar)
589 .to_nonidentity()
590 .into(),
591 )
592 }
593
594 #[cfg(not(feature = "rust-ecdsa"))]
595 {
596 let sk = secp256k1::SecretKey::from_byte_array(
597 private_key
598 .try_into()
599 .map_err(|_| GeneralError::ParseError("private_key.len".into()))?,
600 )
601 .map_err(|_| GeneralError::ParseError("private_key".into()))?;
602
603 let pk = secp256k1::PublicKey::from_secret_key_global(&sk);
604 affine_point_from_bytes(&pk.serialize_uncompressed())
605 .and_then(|p| NonIdentity::new(p).into_option().ok_or(CryptoError::InvalidPublicKey))
606 .map(Self::from)
607 }
608 }
609
610 pub fn to_address(&self) -> Address {
612 self.2
613 }
614
615 pub fn to_uncompressed_bytes(&self) -> Box<[u8]> {
617 self.0.to_encoded_point(false).to_bytes()
618 }
619
620 pub fn to_uncompressed_hex(&self) -> String {
622 format!("0x{}", hex::encode(self.to_uncompressed_bytes()))
623 }
624}
625
626impl PartialEq for PublicKey {
627 fn eq(&self, other: &Self) -> bool {
628 self.1.eq(&other.1)
629 }
630}
631
632impl Eq for PublicKey {}
633
634impl hash::Hash for PublicKey {
635 fn hash<H: Hasher>(&self, state: &mut H) {
636 self.1.hash(state);
637 }
638}
639
640impl Randomizable for PublicKey {
641 fn random() -> Self {
644 let (_, cp) = random_group_element();
645 cp.into()
646 }
647}
648
649impl Debug for PublicKey {
650 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
651 write!(f, "{}", self.to_hex())
652 }
653}
654
655impl Display for PublicKey {
656 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
657 write!(f, "{}", self.to_hex())
658 }
659}
660
661impl TryFrom<&[u8]> for PublicKey {
662 type Error = GeneralError;
663
664 fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
665 match value.len() {
666 Self::SIZE_UNCOMPRESSED => {
667 let key = elliptic_curve::PublicKey::<Secp256k1>::from_sec1_bytes(value)
669 .map_err(|_| GeneralError::ParseError("invalid secp256k1 point".into()))?;
670
671 Ok(key.to_nonidentity().into())
672 }
673 Self::SIZE_UNCOMPRESSED_PLAIN => {
674 let key = elliptic_curve::PublicKey::<Secp256k1>::from_sec1_bytes(&[&[4u8], value].concat())
676 .map_err(|_| GeneralError::ParseError("invalid secp256k1 point".into()))?;
677
678 Ok(key.to_nonidentity().into())
679 }
680 Self::SIZE_COMPRESSED => {
681 let key = elliptic_curve::PublicKey::<Secp256k1>::from_sec1_bytes(value)
683 .map_err(|_| GeneralError::ParseError("invalid secp256k1 point".into()))?;
684
685 Ok(key.to_nonidentity().into())
686 }
687 _ => Err(GeneralError::ParseError("invalid secp256k1 point".into())),
688 }
689 }
690}
691
692impl AsRef<Address> for PublicKey {
693 fn as_ref(&self) -> &Address {
694 &self.2
695 }
696}
697
698impl AsRef<NonIdentity<AffinePoint>> for PublicKey {
699 fn as_ref(&self) -> &NonIdentity<AffinePoint> {
700 &self.0
701 }
702}
703
704impl AsRef<[u8]> for PublicKey {
705 fn as_ref(&self) -> &[u8] {
706 &self.1
707 }
708}
709
710impl BytesRepresentable for PublicKey {
711 const SIZE: usize = PublicKey::SIZE_COMPRESSED;
712}
713
714impl From<NonIdentity<AffinePoint>> for PublicKey {
715 fn from(value: NonIdentity<AffinePoint>) -> Self {
716 let mut compressed = [0u8; PublicKey::SIZE_COMPRESSED];
717 compressed.copy_from_slice(value.to_encoded_point(true).as_bytes());
718 Self(value, compressed, affine_point_to_address(&value))
719 }
720}
721
722impl From<PublicKey> for NonIdentity<AffinePoint> {
723 fn from(value: PublicKey) -> Self {
724 value.0
725 }
726}
727
728impl TryFrom<AffinePoint> for PublicKey {
729 type Error = CryptoError;
730
731 fn try_from(value: AffinePoint) -> std::result::Result<Self, Self::Error> {
732 Ok(NonIdentity::new(value)
733 .into_option()
734 .ok_or(CryptoError::InvalidPublicKey)?
735 .into())
736 }
737}
738
739impl From<&PublicKey> for k256::ProjectivePoint {
741 fn from(value: &PublicKey) -> Self {
742 (*value.0.as_ref()).into()
743 }
744}
745
746impl<'a> From<&'a PublicKey> for &'a GenericArray<u8, typenum::U33> {
747 fn from(value: &'a PublicKey) -> &'a GenericArray<u8, typenum::U33> {
748 GenericArray::from_slice(&value.1)
749 }
750}
751
752#[derive(Clone, Copy, Debug, PartialEq, Eq)]
755#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
756pub struct Response(#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] [u8; Self::SIZE]);
757
758impl Default for Response {
759 fn default() -> Self {
760 Self(HalfKey::default().0)
761 }
762}
763
764impl Display for Response {
765 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
766 write!(f, "{}", self.to_hex())
767 }
768}
769
770#[allow(deprecated)] impl Response {
772 pub fn to_challenge(&self) -> Result<Challenge> {
779 PublicKey::from_privkey(&self.0).map(|pk| Challenge(pk.into()))
781 }
782
783 pub fn from_half_keys(first: &HalfKey, second: &HalfKey) -> Result<Self> {
788 let first = NonZeroScalar::<Secp256k1>::try_from(first.as_ref()).map_err(|_| InvalidInputValue("first"))?;
789 let second = NonZeroScalar::<Secp256k1>::try_from(second.as_ref()).map_err(|_| InvalidInputValue("second"))?;
790
791 let res = first.as_ref() + second.as_ref();
793 if res.is_zero().into() {
794 return Err(InvalidInputValue("invalid half-key"));
795 }
796
797 let mut ret = [0u8; Self::SIZE];
798 ret.copy_from_slice(res.to_bytes().as_slice());
799 Ok(Self(ret))
800 }
801}
802
803impl AsRef<[u8]> for Response {
804 fn as_ref(&self) -> &[u8] {
805 &self.0
806 }
807}
808
809impl TryFrom<&[u8]> for Response {
810 type Error = GeneralError;
811
812 fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
813 Ok(Self(value.try_into().map_err(|_| ParseError("Response".into()))?))
814 }
815}
816
817impl BytesRepresentable for Response {
818 const SIZE: usize = 32;
820}
821
822impl From<[u8; Self::SIZE]> for Response {
823 fn from(value: [u8; Self::SIZE]) -> Self {
824 Self(value)
825 }
826}
827
828pub trait Pseudonym: BytesRepresentable + hash::Hash + Eq + Display + Randomizable {}
834
835#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
837#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
838pub struct SimplePseudonym(#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] pub [u8; Self::SIZE]);
839
840impl Display for SimplePseudonym {
841 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
842 write!(f, "{}", self.to_hex())
843 }
844}
845
846impl Debug for SimplePseudonym {
847 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
848 write!(f, "{}", self.to_hex())
849 }
850}
851
852impl BytesRepresentable for SimplePseudonym {
853 const SIZE: usize = 10;
854}
855
856impl AsRef<[u8]> for SimplePseudonym {
857 fn as_ref(&self) -> &[u8] {
858 &self.0
859 }
860}
861
862impl<'a> TryFrom<&'a [u8]> for SimplePseudonym {
863 type Error = GeneralError;
864
865 fn try_from(value: &'a [u8]) -> result::Result<Self, Self::Error> {
866 value
867 .try_into()
868 .map(Self)
869 .map_err(|_| ParseError("SimplePseudonym".into()))
870 }
871}
872
873impl Randomizable for SimplePseudonym {
874 fn random() -> Self {
876 let mut data = vec![0u8; Self::SIZE];
877 hopr_crypto_random::random_fill(&mut data);
878 Self::try_from(data.as_slice()).unwrap()
879 }
880}
881
882impl Pseudonym for SimplePseudonym {}
883
884#[cfg(test)]
885mod tests {
886 use std::str::FromStr;
887
888 use hex_literal::hex;
889 use hopr_crypto_random::Randomizable;
890 use hopr_primitive_types::prelude::*;
891 use k256::AffinePoint;
892 use libp2p_identity::PeerId;
893
894 use crate::{
895 keypairs::{Keypair, OffchainKeypair},
896 types::{Challenge, HalfKey, HalfKeyChallenge, Hash, OffchainPublicKey, PublicKey, Response},
897 };
898
899 const PUBLIC_KEY: [u8; 33] = hex!("021464586aeaea0eb5736884ca1bf42d165fc8e2243b1d917130fb9e321d7a93b8");
900 const PUBLIC_KEY_UNCOMPRESSED_PLAIN: [u8; 64] = hex!("1464586aeaea0eb5736884ca1bf42d165fc8e2243b1d917130fb9e321d7a93b8fb0699d4f177f9c84712f6d7c5f6b7f4f6916116047fa25c79ef806fc6c9523e");
901 const PUBLIC_KEY_UNCOMPRESSED: [u8; 65] = hex!("041464586aeaea0eb5736884ca1bf42d165fc8e2243b1d917130fb9e321d7a93b8fb0699d4f177f9c84712f6d7c5f6b7f4f6916116047fa25c79ef806fc6c9523e");
902 const PRIVATE_KEY: [u8; 32] = hex!("e17fe86ce6e99f4806715b0c9412f8dad89334bf07f72d5834207a9d8f19d7f8");
903
904 #[test]
905 fn test_public_key_to_hex() -> anyhow::Result<()> {
906 let pk = PublicKey::from_privkey(&hex!(
907 "492057cf93e99b31d2a85bc5e98a9c3aa0021feec52c227cc8170e8f7d047775"
908 ))?;
909
910 assert_eq!("0x0439d1bc2291826eaed86567d225cf243ebc637275e0a5aedb0d6b1dc82136a38e428804340d4c949a029846f682711d046920b4ca8b8ebeb9d1192b5bdaa54dba",
911 pk.to_uncompressed_hex());
912 assert_eq!(
913 "0x0239d1bc2291826eaed86567d225cf243ebc637275e0a5aedb0d6b1dc82136a38e",
914 pk.to_hex()
915 );
916
917 Ok(())
918 }
919
920 #[test]
921 fn test_public_key_serialize() -> anyhow::Result<()> {
922 let pk1 = PublicKey::try_from(PUBLIC_KEY.as_ref())?;
923 let pk2 = PublicKey::try_from(pk1.as_ref())?;
924 let pk3 = PublicKey::try_from(pk1.to_uncompressed_bytes().as_ref())?;
925
926 assert_eq!(pk1, pk2, "pub keys 1 2 don't match");
927 assert_eq!(pk2, pk3, "pub keys 2 3 don't match");
928
929 let pk1 = PublicKey::try_from(PUBLIC_KEY.as_ref())?;
930 let pk2 = PublicKey::try_from(PUBLIC_KEY_UNCOMPRESSED.as_ref())?;
931 let pk3 = PublicKey::try_from(PUBLIC_KEY_UNCOMPRESSED_PLAIN.as_ref())?;
932
933 assert_eq!(pk1, pk2, "pubkeys don't match");
934 assert_eq!(pk2, pk3, "pubkeys don't match");
935
936 let compressed: &[u8] = pk1.as_ref();
937 assert_eq!(PublicKey::SIZE_COMPRESSED, compressed.len());
938 assert_eq!(PublicKey::SIZE_UNCOMPRESSED, pk1.to_uncompressed_bytes().len());
939
940 let shorter = hex!("f85e38b056284626a7aed0acc5d474605a408e6cccf76d7241ec7b4dedb31929b710e034f4f9a7dba97743b01e1cc35a45a60bebb29642cb0ba6a7fe8433316c");
941 let s1 = PublicKey::try_from(shorter.as_ref())?;
942 let s2 = PublicKey::try_from(s1.to_uncompressed_bytes().as_ref())?;
943 assert_eq!(s1, s2);
944
945 Ok(())
946 }
947
948 #[test]
949 fn test_public_key_should_not_accept_identity() -> anyhow::Result<()> {
950 PublicKey::try_from(AffinePoint::IDENTITY).expect_err("must fail for identity point");
951 Ok(())
952 }
953
954 #[test]
955 fn test_public_key_from_privkey() -> anyhow::Result<()> {
956 let pk1 = PublicKey::from_privkey(&PRIVATE_KEY)?;
957 let pk2 = PublicKey::try_from(PUBLIC_KEY.as_ref())?;
958
959 assert_eq!(pk1, pk2, "failed to match deserialized pub key");
960
961 Ok(())
962 }
963
964 #[test]
965 fn test_offchain_public_key() -> anyhow::Result<()> {
966 let (s, pk1) = OffchainKeypair::random().unzip();
967
968 let pk2 = OffchainPublicKey::from_privkey(s.as_ref())?;
969 assert_eq!(pk1, pk2, "from privkey failed");
970
971 let pk3 = OffchainPublicKey::try_from(pk1.as_ref())?;
972 assert_eq!(pk1, pk3, "from bytes failed");
973
974 Ok(())
975 }
976
977 #[test]
978 fn test_offchain_public_key_peerid() -> anyhow::Result<()> {
979 let valid_peerid = PeerId::from_str("12D3KooWLYKsvDB4xEELYoHXxeStj2gzaDXjra2uGaFLpKCZkJHs")?;
980 let valid = OffchainPublicKey::from_peerid(&valid_peerid)?;
981 assert_eq!(valid_peerid, valid.into(), "must work with ed25519 peer ids");
982
983 let invalid_peerid = PeerId::from_str("16Uiu2HAmPHGyJ7y1Rj3kJ64HxJQgM9rASaeT2bWfXF9EiX3Pbp3K")?;
984 let invalid = OffchainPublicKey::from_peerid(&invalid_peerid);
985 assert!(invalid.is_err(), "must not work with secp256k1 peer ids");
986
987 let invalid_peerid_2 = PeerId::from_str("QmWvEwidPYBbLHfcZN6ATHdm4NPM4KbUx72LZnZRoRNKEN")?;
988 let invalid_2 = OffchainPublicKey::from_peerid(&invalid_peerid_2);
989 assert!(invalid_2.is_err(), "must not work with rsa peer ids");
990
991 Ok(())
992 }
993
994 #[test]
995 pub fn test_response() -> anyhow::Result<()> {
996 let r1 = Response([0u8; Response::SIZE]);
997 let r2 = Response::try_from(r1.as_ref())?;
998 assert_eq!(r1, r2, "deserialized response does not match");
999
1000 Ok(())
1001 }
1002
1003 #[test]
1004 fn test_half_key() -> anyhow::Result<()> {
1005 let hk1 = HalfKey([0u8; HalfKey::SIZE]);
1006 let hk2 = HalfKey::try_from(hk1.as_ref())?;
1007
1008 assert_eq!(hk1, hk2, "failed to match deserialized half-key");
1009
1010 Ok(())
1011 }
1012
1013 #[test]
1014 fn test_half_key_challenge() -> anyhow::Result<()> {
1015 let hkc1 = HalfKeyChallenge::try_from(PUBLIC_KEY.as_ref())?;
1016 let hkc2 = HalfKeyChallenge::try_from(hkc1.as_ref())?;
1017 assert_eq!(hkc1, hkc2, "failed to match deserialized half key challenge");
1018
1019 Ok(())
1020 }
1021
1022 #[test]
1023 fn test_challenge_response_flow() -> anyhow::Result<()> {
1024 let hk1 = HalfKey::random();
1025 let hk2 = HalfKey::random();
1026
1027 let response = Response::from_half_keys(&hk1, &hk2)?;
1028
1029 let half_chal1 = hk1.to_challenge()?;
1030 let half_chal2 = hk2.to_challenge()?;
1031
1032 let challenge1 = Challenge::from_hint_and_share(&half_chal1, &half_chal2)?;
1033 assert_eq!(challenge1, Challenge::from_hint_and_share(&half_chal2, &half_chal1)?);
1034 assert_eq!(challenge1, Challenge::from_own_share_and_half_key(&half_chal1, &hk2)?);
1035
1036 let challenge2 = response.to_challenge()?;
1037 assert_eq!(challenge1, challenge2);
1038 assert_eq!(challenge1.to_ethereum_challenge(), challenge2.to_ethereum_challenge());
1039 Ok(())
1040 }
1041
1042 #[test]
1043 fn test_hash() -> anyhow::Result<()> {
1044 let hash1 = Hash::create(&[b"msg"]);
1045 assert_eq!(
1046 "0x92aef1b955b9de564fc50e31a55b470b0c8cdb931f186485d620729fb03d6f2c",
1047 hash1.to_hex(),
1048 "hash test vector failed to match"
1049 );
1050
1051 let hash2 = Hash::try_from(hash1.as_ref())?;
1052 assert_eq!(hash1, hash2, "failed to match deserialized hash");
1053
1054 assert_eq!(
1055 hash1.hash(),
1056 Hash::try_from(hex!("1c4d8d521eccee7225073ea180e0fa075a6443afb7ca06076a9566b07d29470f").as_ref())?
1057 );
1058
1059 Ok(())
1060 }
1061}