matter_cert/
public_key.rs1use core::fmt;
7
8use crate::error::{Error, Result};
9
10#[derive(Clone, PartialEq, Eq, Hash)]
12pub struct PublicKey([u8; 65]);
13
14impl PublicKey {
15 pub fn new(bytes: [u8; 65]) -> Result<Self> {
22 if bytes[0] != 0x04 {
23 return Err(Error::BadPublicKeyPrefix);
24 }
25 Ok(Self(bytes))
26 }
27
28 pub fn from_slice(slice: &[u8]) -> Result<Self> {
35 let bytes: [u8; 65] = slice
36 .try_into()
37 .map_err(|_| Error::WrongPublicKeyLength(slice.len()))?;
38 Self::new(bytes)
39 }
40
41 #[must_use]
43 pub fn as_bytes(&self) -> &[u8; 65] {
44 &self.0
45 }
46
47 pub fn verify(&self, message: &[u8], signature: &crate::signature::Signature) -> Result<()> {
62 use ring::signature::{UnparsedPublicKey, ECDSA_P256_SHA256_FIXED};
63 let key = UnparsedPublicKey::new(&ECDSA_P256_SHA256_FIXED, &self.0[..]);
64 key.verify(message, signature.as_bytes())
65 .map_err(|_| Error::SignatureVerificationFailed)
66 }
67}
68
69impl fmt::Debug for PublicKey {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 write!(
72 f,
73 "PublicKey({:02x}{:02x}{:02x}{:02x}…)",
74 self.0[1], self.0[2], self.0[3], self.0[4]
75 )
76 }
77}
78
79#[cfg(test)]
80#[allow(clippy::unwrap_used)] mod tests {
82 use ring::rand::SystemRandom;
83 use ring::signature::{EcdsaKeyPair, KeyPair, ECDSA_P256_SHA256_FIXED_SIGNING};
84
85 use super::*;
86 use crate::signature::Signature;
87
88 fn make_keypair() -> (PublicKey, EcdsaKeyPair) {
91 let rng = SystemRandom::new();
92 let pkcs8 = EcdsaKeyPair::generate_pkcs8(&ECDSA_P256_SHA256_FIXED_SIGNING, &rng).unwrap();
93 let key_pair =
94 EcdsaKeyPair::from_pkcs8(&ECDSA_P256_SHA256_FIXED_SIGNING, pkcs8.as_ref(), &rng)
95 .unwrap();
96 let our_pub = PublicKey::from_slice(key_pair.public_key().as_ref()).unwrap();
97 (our_pub, key_pair)
98 }
99
100 fn sign(key_pair: &EcdsaKeyPair, message: &[u8]) -> Signature {
101 let rng = SystemRandom::new();
102 let sig = key_pair.sign(&rng, message).unwrap();
103 Signature::from_slice(sig.as_ref()).unwrap()
104 }
105
106 #[test]
107 fn verify_accepts_correct_signature() {
108 let (pub_key, key_pair) = make_keypair();
109 let message = b"matter-cert phase 2 test message";
110 let sig = sign(&key_pair, message);
111 assert!(pub_key.verify(message, &sig).is_ok());
112 }
113
114 #[test]
115 fn verify_rejects_signature_from_different_key() {
116 let (_, key_a) = make_keypair();
117 let (pub_b, _) = make_keypair();
118 let message = b"signed by A, verified against B";
119 let sig = sign(&key_a, message);
120 let err = pub_b.verify(message, &sig).unwrap_err();
121 assert!(matches!(err, Error::SignatureVerificationFailed));
122 }
123
124 #[test]
125 fn verify_rejects_signature_for_different_message() {
126 let (pub_key, key_pair) = make_keypair();
127 let sig = sign(&key_pair, b"signed message");
128 let err = pub_key.verify(b"different message", &sig).unwrap_err();
129 assert!(matches!(err, Error::SignatureVerificationFailed));
130 }
131
132 #[test]
133 fn verify_rejects_tampered_signature() {
134 let (pub_key, key_pair) = make_keypair();
135 let message = b"this is the original message";
136 let mut sig = sign(&key_pair, message);
137 let mut raw = *sig.as_bytes();
138 raw[0] ^= 0x01;
139 sig = Signature::from_slice(&raw).unwrap();
140 let err = pub_key.verify(message, &sig).unwrap_err();
141 assert!(matches!(err, Error::SignatureVerificationFailed));
142 }
143
144 #[test]
145 fn new_rejects_non_0x04_prefix() {
146 let mut bytes = [0u8; 65];
147 bytes[0] = 0x02;
148 assert!(matches!(
149 PublicKey::new(bytes),
150 Err(Error::BadPublicKeyPrefix)
151 ));
152 }
153
154 #[test]
155 fn new_accepts_0x04_prefix() {
156 let mut bytes = [0u8; 65];
157 bytes[0] = 0x04;
158 bytes[1] = 0xAB;
159 let key = PublicKey::new(bytes).unwrap();
160 assert_eq!(key.as_bytes(), &bytes);
161 }
162
163 #[test]
164 fn from_slice_rejects_wrong_length() {
165 let short = [0x04u8; 10];
166 assert!(matches!(
167 PublicKey::from_slice(&short),
168 Err(Error::WrongPublicKeyLength(10))
169 ));
170 }
171
172 #[test]
173 fn debug_format_does_not_leak_full_key() {
174 let mut bytes = [0u8; 65];
175 bytes[0] = 0x04;
176 bytes[1] = 0xAB;
177 bytes[2] = 0xCD;
178 bytes[3] = 0xEF;
179 bytes[4] = 0x12;
180 let key = PublicKey::new(bytes).unwrap();
181 let s = format!("{key:?}");
182 assert!(s.contains("abcdef12"));
183 assert!(!s.contains("00000000"));
184 }
185}