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}