Skip to main content

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}