cratestack_cose/thumbprint.rs
1//! RFC 9679 COSE Key Thumbprints, and the 8-byte `kid` derived from them
2//! (ADR 0006 §3).
3//!
4//! A thumbprint is SHA-256 over the deterministic CBOR encoding (RFC 8949
5//! §4.2.1) of a COSE_Key holding **only** the key type's required
6//! parameters (RFC 9679 §4). Deterministic map order sorts keys by their
7//! encoded bytes, so `1` (`0x01`) precedes `-1` (`0x20`), `-2` (`0x21`) and
8//! `-3` (`0x22`).
9//!
10//! Symmetric keys: RFC 9679 §7 allows their thumbprints only for keys with
11//! enough entropy to rule out precomputed tables. [`HmacSecret`] refuses
12//! secrets shorter than 32 bytes for exactly that reason (P0 scoping
13//! decision), and only the first 8 bytes of the digest ever reach the wire.
14//!
15//! [`HmacSecret`]: crate::HmacSecret
16
17use sha2::{Digest, Sha256};
18
19use crate::cbor::write::{self, MAJOR_MAP};
20
21/// The length of a `kid` on the wire (§3: `bstr .size 8`).
22pub const KID_LEN: usize = 8;
23
24/// COSE key type OKP (`kty` 1), curve Ed25519 (`crv` 6).
25pub fn okp_ed25519_thumbprint(x: &[u8; 32]) -> [u8; 32] {
26 let mut key = Vec::with_capacity(41);
27 write::head(&mut key, MAJOR_MAP, 3);
28 key.extend_from_slice(&[0x01, 0x01, 0x20, 0x06, 0x21]);
29 write::bstr(&mut key, x);
30 Sha256::digest(&key).into()
31}
32
33/// COSE key type EC2 (`kty` 2), curve P-256 (`crv` 1), from the
34/// uncompressed coordinates (RFC 9679 §4.2 requires the uncompressed form).
35pub fn ec2_p256_thumbprint(x: &[u8; 32], y: &[u8; 32]) -> [u8; 32] {
36 let mut key = Vec::with_capacity(77);
37 write::head(&mut key, MAJOR_MAP, 4);
38 key.extend_from_slice(&[0x01, 0x02, 0x20, 0x01, 0x21]);
39 write::bstr(&mut key, x);
40 key.push(0x22);
41 write::bstr(&mut key, y);
42 Sha256::digest(&key).into()
43}
44
45/// COSE key type Symmetric (`kty` 4), from the secret `k`.
46pub fn symmetric_thumbprint(k: &[u8]) -> [u8; 32] {
47 let mut key = Vec::with_capacity(4 + write::bstr_len(k.len()));
48 write::head(&mut key, MAJOR_MAP, 2);
49 key.extend_from_slice(&[0x01, 0x04, 0x20]);
50 write::bstr(&mut key, k);
51 Sha256::digest(&key).into()
52}
53
54/// The `kid` for a thumbprint: its first [`KID_LEN`] bytes. The birthday
55/// bound is about 2³² keys, and a resolver returns several candidates on a
56/// collision (§3), so the full thumbprint, not the `kid`, is what names a
57/// key unambiguously ([`Opened::key_thumbprint`](crate::Opened)).
58pub fn kid_from_thumbprint(thumbprint: &[u8; 32]) -> [u8; KID_LEN] {
59 let mut kid = [0; KID_LEN];
60 kid.copy_from_slice(&thumbprint[..KID_LEN]);
61 kid
62}