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}