bc_components/ec_key/ec_uncompressed_public_key.rs
1use anyhow::{Result, bail};
2use bc_ur::prelude::*;
3
4use crate::{ECKey, ECKeyBase, ECPublicKey, ECPublicKeyBase, tags};
5
6/// The size of an ECDSA uncompressed public key in bytes (65 bytes).
7pub const ECDSA_UNCOMPRESSED_PUBLIC_KEY_SIZE: usize =
8 bc_crypto::ECDSA_UNCOMPRESSED_PUBLIC_KEY_SIZE;
9
10/// An uncompressed elliptic curve digital signature algorithm (ECDSA) public
11/// key.
12///
13/// An `ECUncompressedPublicKey` is a 65-byte representation of a public key on
14/// the secp256k1 curve. It consists of:
15///
16/// - 1 byte prefix (0x04)
17/// - 32 bytes for the x-coordinate
18/// - 32 bytes for the y-coordinate
19///
20/// This format explicitly includes both coordinates of the elliptic curve
21/// point, unlike the compressed format which only includes the x-coordinate and
22/// a single byte to indicate the parity of the y-coordinate.
23///
24/// This is considered a legacy key type and is not recommended for general use.
25/// The compressed format (`ECPublicKey`) is more space-efficient and provides
26/// the same cryptographic security. However, some legacy systems or protocols
27/// might require the uncompressed format.
28///
29/// # Examples
30///
31/// Converting between compressed and uncompressed formats:
32///
33/// ```
34/// use bc_components::{
35/// ECKey, ECPrivateKey, ECPublicKey, ECPublicKeyBase,
36/// ECUncompressedPublicKey,
37/// };
38///
39/// // Generate a keypair
40/// let private_key = ECPrivateKey::new();
41/// let compressed_key = private_key.public_key();
42///
43/// // Convert to uncompressed format
44/// let uncompressed_key = compressed_key.uncompressed_public_key();
45///
46/// // Convert back to compressed format
47/// let compressed_again = uncompressed_key.public_key();
48///
49/// // They should be equal
50/// assert_eq!(compressed_key, compressed_again);
51/// ```
52#[derive(Clone, PartialEq, Eq, Hash)]
53pub struct ECUncompressedPublicKey([u8; ECDSA_UNCOMPRESSED_PUBLIC_KEY_SIZE]);
54
55impl ECUncompressedPublicKey {
56 /// Restores an ECDSA uncompressed public key from an array of bytes.
57 ///
58 /// This method performs no validation on the input data.
59 pub const fn from_data(
60 data: [u8; ECDSA_UNCOMPRESSED_PUBLIC_KEY_SIZE],
61 ) -> Self {
62 Self(data)
63 }
64}
65
66/// Formats the key as a hexadecimal string.
67impl std::fmt::Display for ECUncompressedPublicKey {
68 /// Displays the key as a hexadecimal string.
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 write!(f, "{}", self.hex())
71 }
72}
73
74/// Formats the key for debugging, showing type name and hexadecimal value.
75impl std::fmt::Debug for ECUncompressedPublicKey {
76 /// Displays the key with type information and hexadecimal value.
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 write!(f, "ECUncompressedPublicKey({})", self.hex())
79 }
80}
81
82/// Implements the `ECKeyBase` trait methods for `ECUncompressedPublicKey`.
83impl ECKeyBase for ECUncompressedPublicKey {
84 /// The size of an EC uncompressed public key (65 bytes).
85 const KEY_SIZE: usize = bc_crypto::ECDSA_UNCOMPRESSED_PUBLIC_KEY_SIZE;
86
87 /// Creates a key from a byte slice, with validation.
88 fn from_data_ref(data: impl AsRef<[u8]>) -> Result<Self>
89 where
90 Self: Sized,
91 {
92 let data = data.as_ref();
93 if data.len() != ECDSA_UNCOMPRESSED_PUBLIC_KEY_SIZE {
94 bail!("Invalid ECDSA uncompressed public key size");
95 }
96 let mut key = [0u8; ECDSA_UNCOMPRESSED_PUBLIC_KEY_SIZE];
97 key.copy_from_slice(data);
98 Ok(Self(key))
99 }
100
101 /// Returns the key as a byte slice.
102 fn data(&self) -> &[u8] { &self.0 }
103}
104
105/// Implements the `ECKey` trait for converting to compressed format.
106impl ECKey for ECUncompressedPublicKey {
107 /// Converts this uncompressed public key to its compressed form.
108 fn public_key(&self) -> ECPublicKey {
109 bc_crypto::ecdsa_compress_public_key(&self.0).into()
110 }
111}
112
113/// Implements the `ECPublicKeyBase` trait.
114impl ECPublicKeyBase for ECUncompressedPublicKey {
115 /// Returns this uncompressed public key (self).
116 fn uncompressed_public_key(&self) -> ECUncompressedPublicKey {
117 self.clone()
118 }
119}
120
121/// Converts a fixed-size byte array to an `ECUncompressedPublicKey`.
122impl From<[u8; ECDSA_UNCOMPRESSED_PUBLIC_KEY_SIZE]>
123 for ECUncompressedPublicKey
124{
125 /// Converts a 65-byte array into an EC uncompressed public key.
126 fn from(value: [u8; ECDSA_UNCOMPRESSED_PUBLIC_KEY_SIZE]) -> Self {
127 Self::from_data(value)
128 }
129}
130
131/// Provides a reference to the key data as a byte slice.
132impl AsRef<[u8]> for ECUncompressedPublicKey {
133 /// Returns a reference to the key as a byte slice.
134 fn as_ref(&self) -> &[u8] { self.data() }
135}
136
137/// Defines CBOR tags for EC keys.
138impl CBORTagged for ECUncompressedPublicKey {
139 /// Returns the CBOR tags for EC keys.
140 fn cbor_tags() -> Vec<Tag> {
141 tags_for_values(&[tags::TAG_EC_KEY, tags::TAG_EC_KEY_V1])
142 }
143}
144
145/// Converts an `ECUncompressedPublicKey` to CBOR.
146impl From<ECUncompressedPublicKey> for CBOR {
147 /// Converts to tagged CBOR.
148 fn from(value: ECUncompressedPublicKey) -> Self { value.tagged_cbor() }
149}
150
151/// Implements CBOR encoding for EC uncompressed public keys.
152impl CBORTaggedEncodable for ECUncompressedPublicKey {
153 /// Creates the untagged CBOR representation.
154 ///
155 /// The format is a map with:
156 /// - Key 3: byte string of the key data
157 fn untagged_cbor(&self) -> CBOR {
158 let mut m = Map::new();
159 m.insert(3, CBOR::to_byte_string(self.0));
160 m.into()
161 }
162}