Refactors for reusability across supported recipients

This commit is contained in:
Jack Grigg
2025-12-08 02:34:04 +00:00
parent 1f1f257ede
commit 5b44faec44
8 changed files with 135 additions and 62 deletions
+16 -20
View File
@@ -11,12 +11,14 @@ use p256::{
use rand::rngs::OsRng;
use sha2::Sha256;
use crate::{key::Connection, p256::Recipient};
use crate::{key::Connection, recipient::TAG_BYTES, util::base64_arg};
mod recipient;
pub(crate) use recipient::Recipient;
const STANZA_TAG: &str = "piv-p256";
pub(crate) const STANZA_KEY_LABEL: &[u8] = b"piv-p256";
const TAG_BYTES: usize = 4;
const EPK_BYTES: usize = 33;
const ENCRYPTED_FILE_KEY_BYTES: usize = 32;
@@ -81,17 +83,6 @@ impl RecipientLine {
return None;
}
fn base64_arg<A: AsRef<[u8]>, B: AsMut<[u8]>>(arg: &A, mut buf: B) -> Option<B> {
if arg.as_ref().len() != ((4 * buf.as_mut().len()) + 2) / 3 {
return None;
}
BASE64_STANDARD_NO_PAD
.decode_slice_unchecked(arg, buf.as_mut())
.ok()
.and_then(|len| (len == buf.as_mut().len()).then_some(buf))
}
let (tag, epk_bytes) = match &s.args[..] {
[tag, epk_bytes] => (
base64_arg(tag, [0; TAG_BYTES]),
@@ -110,15 +101,17 @@ impl RecipientLine {
_ => Err(()),
})
}
}
pub(crate) fn wrap_file_key(file_key: &FileKey, pk: &Recipient) -> Self {
impl Recipient {
pub(crate) fn wrap_file_key(&self, file_key: &FileKey) -> RecipientLine {
let esk = EphemeralSecret::random(&mut OsRng);
let epk = esk.public_key();
let epk_bytes = EphemeralKeyBytes::from_public_key(&epk);
let shared_secret = esk.diffie_hellman(pk.public_key());
let shared_secret = esk.diffie_hellman(self.public_key());
let salt = salt(&epk_bytes, pk);
let salt = salt(&epk_bytes, self);
let enc_key = {
let mut okm = [0; 32];
@@ -136,21 +129,24 @@ impl RecipientLine {
};
RecipientLine {
tag: pk.tag(),
tag: self.tag(),
epk_bytes,
encrypted_file_key,
}
}
}
impl RecipientLine {
pub(crate) fn unwrap_file_key(&self, conn: &mut Connection) -> Result<FileKey, ()> {
assert_eq!(self.tag, conn.recipient().tag());
let crate::recipient::Recipient::PivP256(recipient) = conn.recipient();
assert_eq!(self.tag, recipient.tag());
let salt = salt(&self.epk_bytes, recipient);
// The YubiKey API for performing scalar multiplication takes the point in its
// uncompressed SEC-1 encoding.
let shared_secret = conn.p256_ecdh(self.epk_bytes.decompress().as_bytes())?;
let salt = salt(&self.epk_bytes, conn.recipient());
let enc_key = hkdf(&salt, STANZA_KEY_LABEL, shared_secret.as_ref());
// A failure to decrypt is fatal, because we assume that we won't