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