bee_block/signature/
ed25519.rs

1// Copyright 2020-2021 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use core::{fmt, ops::Deref};
5
6use crypto::{
7    hashes::{blake2b::Blake2b256, Digest},
8    signatures::ed25519::{PublicKey, Signature, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH},
9};
10
11use crate::{address::Ed25519Address, Error};
12
13/// An Ed25519 signature.
14#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, packable::Packable)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub struct Ed25519Signature {
17    public_key: [u8; Self::PUBLIC_KEY_LENGTH],
18    #[cfg_attr(feature = "serde", serde(with = "serde_big_array::BigArray"))]
19    signature: [u8; Self::SIGNATURE_LENGTH],
20}
21
22impl Ed25519Signature {
23    /// The signature kind of an [`Ed25519Signature`].
24    pub const KIND: u8 = 0;
25    /// Length of an ED25519 public key.
26    pub const PUBLIC_KEY_LENGTH: usize = PUBLIC_KEY_LENGTH;
27    /// Length of an ED25519 signature.
28    pub const SIGNATURE_LENGTH: usize = SIGNATURE_LENGTH;
29
30    /// Creates a new [`Ed25519Signature`].
31    pub fn new(public_key: [u8; Self::PUBLIC_KEY_LENGTH], signature: [u8; Self::SIGNATURE_LENGTH]) -> Self {
32        Self { public_key, signature }
33    }
34
35    /// Returns the public key of an [`Ed25519Signature`].
36    pub fn public_key(&self) -> &[u8; Self::PUBLIC_KEY_LENGTH] {
37        &self.public_key
38    }
39
40    /// Return the actual signature of an [`Ed25519Signature`].
41    pub fn signature(&self) -> &[u8; Self::SIGNATURE_LENGTH] {
42        &self.signature
43    }
44
45    /// Verifies the [`Ed25519Signature`] for a message against an [`Ed25519Address`].
46    pub fn is_valid(&self, message: &[u8], address: &Ed25519Address) -> Result<(), Error> {
47        let signature_address: [u8; PUBLIC_KEY_LENGTH] = Blake2b256::digest(self.public_key).into();
48
49        if address.deref() != &signature_address {
50            return Err(Error::SignaturePublicKeyMismatch {
51                expected: prefix_hex::encode(address.as_ref()),
52                actual: prefix_hex::encode(signature_address),
53            });
54        }
55
56        if !PublicKey::try_from_bytes(self.public_key)?.verify(&Signature::from_bytes(self.signature), message) {
57            return Err(Error::InvalidSignature);
58        }
59
60        Ok(())
61    }
62}
63
64impl fmt::Debug for Ed25519Signature {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        #[repr(transparent)]
67        struct UnquotedStr<'a>(&'a str);
68
69        impl<'a> fmt::Debug for UnquotedStr<'a> {
70            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71                write!(f, "{}", self.0)
72            }
73        }
74
75        f.debug_struct("Ed25519Signature")
76            .field("public_key", &UnquotedStr(&prefix_hex::encode(self.public_key)))
77            .field("signature", &UnquotedStr(&prefix_hex::encode(self.signature)))
78            .finish()
79    }
80}
81
82#[cfg(feature = "dto")]
83#[allow(missing_docs)]
84pub mod dto {
85    use serde::{Deserialize, Serialize};
86
87    use super::*;
88    use crate::error::dto::DtoError;
89
90    /// Defines an Ed25519 signature.
91    #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
92    pub struct Ed25519SignatureDto {
93        #[serde(rename = "type")]
94        pub kind: u8,
95        #[serde(rename = "publicKey")]
96        pub public_key: String,
97        pub signature: String,
98    }
99
100    impl From<&Ed25519Signature> for Ed25519SignatureDto {
101        fn from(value: &Ed25519Signature) -> Self {
102            Ed25519SignatureDto {
103                kind: Ed25519Signature::KIND,
104                public_key: prefix_hex::encode(value.public_key),
105                signature: prefix_hex::encode(value.signature),
106            }
107        }
108    }
109
110    impl TryFrom<&Ed25519SignatureDto> for Ed25519Signature {
111        type Error = DtoError;
112
113        fn try_from(value: &Ed25519SignatureDto) -> Result<Self, Self::Error> {
114            Ok(Ed25519Signature::new(
115                prefix_hex::decode(&value.public_key).map_err(|_| DtoError::InvalidField("publicKey"))?,
116                prefix_hex::decode(&value.signature).map_err(|_| DtoError::InvalidField("signature"))?,
117            ))
118        }
119    }
120}