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}