1use crate::{Error, Result};
51use p256::ecdsa::{Signature, VerifyingKey, signature::Verifier};
52use sha2::{Digest, Sha256};
53
54pub const PUBLIC_KEY_LEN: usize = 65;
60
61pub const SIGNATURE_LEN: usize = 64;
76
77pub const DEVICE_ID_LEN: usize = 32;
79
80#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
95pub struct DeviceId([u8; DEVICE_ID_LEN]);
96
97impl DeviceId {
98 #[must_use]
100 pub fn from_public_key(public_key: &DevicePublicKey) -> Self {
101 let digest = Sha256::digest(public_key.as_sec1_bytes());
102 let mut bytes = [0_u8; DEVICE_ID_LEN];
103 bytes.copy_from_slice(&digest);
104 Self(bytes)
105 }
106
107 #[must_use]
109 pub const fn from_bytes(bytes: [u8; DEVICE_ID_LEN]) -> Self {
110 Self(bytes)
111 }
112
113 #[must_use]
115 pub const fn as_bytes(&self) -> &[u8; DEVICE_ID_LEN] {
116 &self.0
117 }
118
119 #[must_use]
121 pub fn to_hex(&self) -> String {
122 let mut out = String::with_capacity(DEVICE_ID_LEN * 2);
123 for byte in self.0 {
124 out.push(char::from_digit(u32::from(byte >> 4), 16).unwrap_or('0'));
126 out.push(char::from_digit(u32::from(byte & 0x0F), 16).unwrap_or('0'));
127 }
128 out
129 }
130}
131
132impl core::fmt::Display for DeviceId {
133 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
134 f.write_str(&self.to_hex())
135 }
136}
137
138impl core::fmt::Debug for DeviceId {
139 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
144 write!(f, "DeviceId({})", self.to_hex())
145 }
146}
147
148#[derive(Clone, PartialEq, Eq)]
153pub struct DevicePublicKey {
154 sec1: [u8; PUBLIC_KEY_LEN],
156}
157
158impl DevicePublicKey {
159 pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self> {
176 if bytes.len() != PUBLIC_KEY_LEN {
177 return Err(Error::DevicePublicKeyInvalid {
178 reason: "长度不是 65 字节的 SEC1 未压缩点",
179 });
180 }
181 if bytes[0] != 0x04 {
182 return Err(Error::DevicePublicKeyInvalid {
183 reason: "SEC1 前缀不是 0x04(未压缩形式)",
184 });
185 }
186 if VerifyingKey::from_sec1_bytes(bytes).is_err() {
188 return Err(Error::DevicePublicKeyInvalid {
189 reason: "该点不在 P-256 曲线上",
190 });
191 }
192
193 let mut sec1 = [0_u8; PUBLIC_KEY_LEN];
194 sec1.copy_from_slice(bytes);
195 Ok(Self { sec1 })
196 }
197
198 #[must_use]
200 pub const fn as_sec1_bytes(&self) -> &[u8; PUBLIC_KEY_LEN] {
201 &self.sec1
202 }
203
204 #[must_use]
206 pub fn device_id(&self) -> DeviceId {
207 DeviceId::from_public_key(self)
208 }
209}
210
211impl core::fmt::Debug for DevicePublicKey {
212 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
214 write!(f, "DevicePublicKey(")?;
215 for byte in self.sec1 {
216 write!(f, "{byte:02x}")?;
217 }
218 write!(f, ")")
219 }
220}
221
222#[derive(Clone, PartialEq, Eq)]
224pub struct DeviceSignature([u8; SIGNATURE_LEN]);
225
226impl DeviceSignature {
227 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
237 let raw: [u8; SIGNATURE_LEN] =
238 bytes
239 .try_into()
240 .map_err(|_| Error::DeviceSignatureInvalid {
241 reason: "长度不是 64 字节的原始 r||s",
242 })?;
243 Ok(Self(raw))
244 }
245
246 #[must_use]
248 pub const fn as_bytes(&self) -> &[u8; SIGNATURE_LEN] {
249 &self.0
250 }
251}
252
253impl core::fmt::Debug for DeviceSignature {
254 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
255 write!(f, "DeviceSignature(")?;
256 for byte in self.0 {
257 write!(f, "{byte:02x}")?;
258 }
259 write!(f, ")")
260 }
261}
262
263#[must_use = "验证结果必须被检查,丢弃它等于没有验证"]
281pub fn verify(
282 public_key: &DevicePublicKey,
283 message: &[u8],
284 signature: &DeviceSignature,
285) -> Result<()> {
286 let verifying_key =
289 VerifyingKey::from_sec1_bytes(public_key.as_sec1_bytes()).map_err(|_| {
290 Error::DeviceSignatureInvalid {
291 reason: "公钥无法解析(不应发生:构造时已验证)",
292 }
293 })?;
294
295 let signature =
296 Signature::from_slice(signature.as_bytes()).map_err(|_| Error::DeviceSignatureInvalid {
297 reason: "签名不是合法的 P-256 r||s",
298 })?;
299
300 verifying_key
301 .verify(message, &signature)
302 .map_err(|_| Error::DeviceSignatureInvalid {
303 reason: "签名与公钥或消息不匹配",
304 })
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310 use p256::ecdsa::{SigningKey, signature::Signer};
311
312 fn test_key() -> (SigningKey, DevicePublicKey) {
314 let signing = SigningKey::from_slice(&[7_u8; 32]).expect("合法标量");
316 let sec1 = signing.verifying_key().to_sec1_point(false);
317 let public = DevicePublicKey::from_sec1_bytes(sec1.as_bytes()).expect("合法点");
318 (signing, public)
319 }
320
321 fn sign(signing: &SigningKey, message: &[u8]) -> DeviceSignature {
323 let sig: Signature = signing.sign(message);
324 DeviceSignature::from_bytes(&sig.to_bytes()).expect("64 字节")
325 }
326
327 #[test]
329 fn valid_signature_verifies() {
330 let (signing, public) = test_key();
331 let message = b"\x01Qualia2FA test message";
332 let signature = sign(&signing, message);
333 assert!(verify(&public, message, &signature).is_ok());
334 }
335
336 #[test]
338 fn modified_message_fails() {
339 let (signing, public) = test_key();
340 let mut message = *b"hello qualia2fa!";
341 let signature = sign(&signing, &message);
342 message[0] ^= 0x01;
343 assert!(verify(&public, &message, &signature).is_err());
344 }
345
346 #[test]
348 fn modified_signature_fails() {
349 let (signing, public) = test_key();
350 let message = b"hello qualia2fa!";
351 let signature = sign(&signing, message);
352 for index in [0_usize, 31, 32, 63] {
353 let mut raw = *signature.as_bytes();
354 raw[index] ^= 0x01;
355 let tampered = DeviceSignature::from_bytes(&raw).expect("仍是 64 字节");
356 assert!(
357 verify(&public, message, &tampered).is_err(),
358 "第 {index} 字节被改动却通过了验证"
359 );
360 }
361 }
362
363 #[test]
365 fn wrong_public_key_fails() {
366 let (signing, _) = test_key();
367 let other = SigningKey::from_slice(&[9_u8; 32]).expect("合法标量");
368 let other_sec1 = other.verifying_key().to_sec1_point(false);
369 let other_public = DevicePublicKey::from_sec1_bytes(other_sec1.as_bytes()).expect("合法点");
370
371 let message = b"hello qualia2fa!";
372 let signature = sign(&signing, message);
373 assert!(verify(&other_public, message, &signature).is_err());
374 }
375
376 #[test]
378 fn off_curve_point_is_rejected() {
379 let mut bytes = [0_u8; PUBLIC_KEY_LEN];
381 bytes[0] = 0x04;
382 bytes[32] = 1;
383 bytes[64] = 1;
384 assert!(
385 DevicePublicKey::from_sec1_bytes(&bytes).is_err(),
386 "不在曲线上的点被接受了——这会导致非法曲线攻击"
387 );
388 }
389
390 #[test]
392 fn malformed_public_key_is_rejected() {
393 assert!(DevicePublicKey::from_sec1_bytes(&[]).is_err());
394 assert!(DevicePublicKey::from_sec1_bytes(&[0x04; 64]).is_err());
395 assert!(DevicePublicKey::from_sec1_bytes(&[0x04; 66]).is_err());
396
397 let (_, public) = test_key();
399 let mut compressed = *public.as_sec1_bytes();
400 compressed[0] = 0x02;
401 assert!(
402 DevicePublicKey::from_sec1_bytes(&compressed).is_err(),
403 "压缩形式不应被接受"
404 );
405 }
406
407 #[test]
409 fn malformed_signature_is_rejected() {
410 assert!(DeviceSignature::from_bytes(&[]).is_err());
411 assert!(DeviceSignature::from_bytes(&[0_u8; 63]).is_err());
412 assert!(DeviceSignature::from_bytes(&[0_u8; 65]).is_err());
413 }
414
415 #[test]
417 fn device_id_is_derived_from_public_key() {
418 let (_, public) = test_key();
419 let id = public.device_id();
420 assert_eq!(id, DeviceId::from_public_key(&public));
421 assert_eq!(id.as_bytes().len(), DEVICE_ID_LEN);
422 assert_eq!(id.to_hex().len(), DEVICE_ID_LEN * 2);
423
424 let (_, other) = {
426 let signing = SigningKey::from_slice(&[9_u8; 32]).expect("合法标量");
427 let sec1 = signing.verifying_key().to_sec1_point(false);
428 let public = DevicePublicKey::from_sec1_bytes(sec1.as_bytes()).expect("合法点");
429 (signing, public)
430 };
431 assert_ne!(id, other.device_id());
432 }
433
434 #[test]
436 fn debug_shows_hex() {
437 let (_, public) = test_key();
438 let text = format!("{public:?}");
439 assert!(text.starts_with("DevicePublicKey(04"), "实际: {text}");
440 assert_eq!(
441 text.len(),
442 "DevicePublicKey(".len() + PUBLIC_KEY_LEN * 2 + 1
443 );
444 assert_eq!(public.device_id().to_string().len(), DEVICE_ID_LEN * 2);
445 }
446}