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