bc_components/ec_key/ec_public_key.rs
1use anyhow::{bail, Result};
2use bc_crypto::ECDSA_SIGNATURE_SIZE;
3use bc_ur::prelude::*;
4
5use crate::{tags, ECKey, ECKeyBase, ECPublicKeyBase, Signature, Verifier};
6
7/// The size of an ECDSA compressed public key in bytes (33 bytes).
8pub const ECDSA_PUBLIC_KEY_SIZE: usize = bc_crypto::ECDSA_PUBLIC_KEY_SIZE;
9
10/// A compressed elliptic curve digital signature algorithm (ECDSA) public key.
11///
12/// An `ECPublicKey` is a 33-byte compressed representation of a public key on the
13/// secp256k1 curve. The first byte is a prefix (0x02 or 0x03) that indicates the
14/// parity of the y-coordinate, followed by the 32-byte x-coordinate.
15///
16/// These public keys are used to:
17/// - Verify ECDSA signatures
18/// - Identify the owner of a private key without revealing the private key
19/// - Derive shared secrets (when combined with another party's private key)
20///
21/// Unlike the larger 65-byte uncompressed format (`ECUncompressedPublicKey`),
22/// compressed public keys save space while providing the same cryptographic security.
23///
24/// # Examples
25///
26/// Verifying an ECDSA signature:
27///
28/// ```
29/// use bc_components::{ECPrivateKey, ECPublicKey, ECKey};
30///
31/// // Generate a keypair
32/// let private_key = ECPrivateKey::new();
33/// let public_key = private_key.public_key();
34///
35/// // Sign a message
36/// let message = b"Hello, world!";
37/// let signature = private_key.ecdsa_sign(message);
38///
39/// // Verify the signature
40/// assert!(public_key.verify(&signature, message));
41/// ```
42#[derive(Clone, PartialEq, Eq, Hash)]
43pub struct ECPublicKey([u8; ECDSA_PUBLIC_KEY_SIZE]);
44
45impl ECPublicKey {
46    /// Restores an ECDSA public key from an array of bytes.
47    ///
48    /// This method performs no validation on the input data.
49    pub const fn from_data(data: [u8; ECDSA_PUBLIC_KEY_SIZE]) -> Self {
50        Self(data)
51    }
52
53    /// Returns the ECDSA public key as an array of bytes.
54    pub fn data(&self) -> &[u8; ECDSA_PUBLIC_KEY_SIZE] {
55        &self.0
56    }
57}
58
59impl ECPublicKey {
60    /// Verifies an ECDSA signature for a message using this public key.
61    ///
62    /// Returns `true` if the signature is valid for the given message and this public key,
63    /// and `false` otherwise.
64    ///
65    /// # Parameters
66    /// - `signature`: A 70-72 byte DER-encoded ECDSA signature
67    /// - `message`: The message that was signed
68    pub fn verify(&self, signature: &[u8; ECDSA_SIGNATURE_SIZE], message: impl AsRef<[u8]>) -> bool {
69        bc_crypto::ecdsa_verify(&self.0, signature, message)
70    }
71}
72
73/// Formats the key as a hexadecimal string.
74impl std::fmt::Display for ECPublicKey {
75    /// Displays the key as a hexadecimal string.
76    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77        write!(f, "{}", self.hex())
78    }
79}
80
81/// Formats the key for debugging, showing type name and hexadecimal value.
82impl std::fmt::Debug for ECPublicKey {
83    /// Displays the key with type information and hexadecimal value.
84    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85        write!(f, "ECPublicKey({})", self.hex())
86    }
87}
88
89/// Implements the `ECKeyBase` trait methods for `ECPublicKey`.
90impl ECKeyBase for ECPublicKey {
91    /// The size of an EC compressed public key (33 bytes).
92    const KEY_SIZE: usize = bc_crypto::ECDSA_PUBLIC_KEY_SIZE;
93
94    /// Creates a key from a byte slice, with validation.
95    fn from_data_ref(data: impl AsRef<[u8]>) -> Result<Self> where Self: Sized {
96        let data = data.as_ref();
97        if data.len() != ECDSA_PUBLIC_KEY_SIZE {
98            bail!("Invalid ECDSA public key size");
99        }
100        let mut key = [0u8; ECDSA_PUBLIC_KEY_SIZE];
101        key.copy_from_slice(data);
102        Ok(Self(key))
103    }
104
105    /// Returns the key as a byte slice.
106    fn data(&self) -> &[u8] {
107        self.into()
108    }
109}
110
111/// Implements the `Verifier` trait for verifying signatures.
112impl Verifier for ECPublicKey {
113    /// Verifies a signature for a message using this public key.
114    ///
115    /// Only supports ECDSA signatures; returns `false` for other signature types.
116    fn verify(&self, signature: &Signature, message: &dyn AsRef<[u8]>) -> bool {
117        match signature {
118            Signature::ECDSA(sig) => self.verify(sig, message),
119            _ => false,
120        }
121    }
122}
123
124/// Implements the `ECKey` trait for `ECPublicKey`.
125impl ECKey for ECPublicKey {
126    /// Returns the public key (self).
127    fn public_key(&self) -> ECPublicKey {
128        self.clone()
129    }
130}
131
132/// Implements the `ECPublicKeyBase` trait for converting to uncompressed format.
133impl ECPublicKeyBase for ECPublicKey {
134    /// Converts this compressed public key to its uncompressed form.
135    fn uncompressed_public_key(&self) -> crate::ECUncompressedPublicKey {
136        bc_crypto::ecdsa_decompress_public_key(&self.0).into()
137    }
138}
139
140/// Converts a reference to an `ECPublicKey` to a reference to a fixed-size byte array.
141impl<'a> From<&'a ECPublicKey> for &'a [u8; ECPublicKey::KEY_SIZE] {
142    /// Returns a reference to the underlying byte array.
143    fn from(value: &'a ECPublicKey) -> Self {
144        &value.0
145    }
146}
147
148/// Converts a fixed-size byte array to an `ECPublicKey`.
149impl From<[u8; ECDSA_PUBLIC_KEY_SIZE]> for ECPublicKey {
150    /// Converts a 33-byte array into an EC public key.
151    fn from(value: [u8; ECDSA_PUBLIC_KEY_SIZE]) -> Self {
152        Self::from_data(value)
153    }
154}
155
156/// Converts a reference to an `ECPublicKey` to a reference to a byte slice.
157impl<'a> From<&'a ECPublicKey> for &'a [u8] {
158    /// Returns a reference to the key as a byte slice.
159    fn from(value: &'a ECPublicKey) -> Self {
160        &value.0
161    }
162}
163
164/// Defines CBOR tags for EC keys.
165impl CBORTagged for ECPublicKey {
166    /// Returns the CBOR tags for EC keys.
167    fn cbor_tags() -> Vec<Tag> {
168        tags_for_values(&[tags::TAG_EC_KEY, tags::TAG_EC_KEY_V1])
169    }
170}
171
172/// Converts an `ECPublicKey` to CBOR.
173impl From<ECPublicKey> for CBOR {
174    /// Converts to tagged CBOR.
175    fn from(value: ECPublicKey) -> Self {
176        value.tagged_cbor()
177    }
178}
179
180/// Implements CBOR encoding for EC public keys.
181impl CBORTaggedEncodable for ECPublicKey {
182    /// Creates the untagged CBOR representation.
183    ///
184    /// The format is a map with:
185    /// - Key 3: byte string of the key data
186    ///   (Note the absence of key 2, which would indicate a private key)
187    fn untagged_cbor(&self) -> CBOR {
188        let mut m = Map::new();
189        m.insert(3, CBOR::to_byte_string(self.0));
190        m.into()
191    }
192}