keyfork-shard: use hkdf for remote shard keys

This commit is contained in:
Ryan Heywood 2024-01-08 14:00:31 -05:00
parent 488e9f48da
commit 3df3caa235
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
4 changed files with 30 additions and 2 deletions

11
Cargo.lock generated
View File

@ -906,6 +906,15 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5491a308e0214554f07a81d8944abe45f552871c12e3c3c6e7e5d354039a6c4c"
[[package]]
name = "hkdf"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
dependencies = [
"hmac",
]
[[package]]
name = "hmac"
version = "0.12.1"
@ -1152,12 +1161,14 @@ dependencies = [
"anyhow",
"card-backend",
"card-backend-pcsc",
"hkdf",
"keyfork-derive-openpgp",
"keyfork-mnemonic-util",
"keyfork-prompt",
"openpgp-card",
"openpgp-card-sequoia",
"sequoia-openpgp",
"sha2",
"sharks",
"smex",
"thiserror",

View File

@ -31,3 +31,5 @@ openpgp-card-sequoia = { version = "0.2.0", optional = true }
openpgp-card = { version = "0.4.0", optional = true }
sequoia-openpgp = { version = "1.16.1", optional = true }
keyfork-prompt = { version = "0.1.0", path = "../keyfork-prompt", optional = true }
hkdf = { version = "0.12.4", features = ["std"] }
sha2 = "0.10.8"

View File

@ -7,6 +7,8 @@ use aes_gcm::{
aead::{Aead, AeadCore, OsRng},
Aes256Gcm, KeyInit,
};
use hkdf::Hkdf;
use sha2::Sha256;
use keyfork_mnemonic_util::{Mnemonic, Wordlist};
use keyfork_prompt::{qrencode, Message as PromptMessage, PromptManager};
use sharks::{Share, Sharks};
@ -89,8 +91,11 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
let shared_secret = our_key
.diffie_hellman(&PublicKey::from(their_key))
.to_bytes();
let hkdf = Hkdf::<Sha256>::new(None, &shared_secret);
let mut hkdf_output = [0u8; 256 / 8];
hkdf.expand(&[], &mut hkdf_output)?;
let shared_key =
Aes256Gcm::new_from_slice(&shared_secret)?;
Aes256Gcm::new_from_slice(&hkdf_output)?;
let payload = Mnemonic::from_str(&payload_mnemonic)?.entropy();
let payload =

View File

@ -10,6 +10,8 @@ use aes_gcm::{
aes::cipher::InvalidLength,
Aes256Gcm, Error as AesError, KeyInit, Nonce,
};
use hkdf::{Hkdf, InvalidLength as HkdfInvalidLength};
use sha2::Sha256;
use keyfork_derive_openpgp::derive_util::{
request::{DerivationAlgorithm, DerivationRequest},
DerivationPath,
@ -65,6 +67,9 @@ pub enum Error {
#[error("Invalid length of AES key: {0}")]
AesLength(#[from] InvalidLength),
#[error("Invalid KDF length: {0}")]
HkdfLength(#[from] HkdfInvalidLength),
#[error("Derived secret hash {0} != expected {1}")]
InvalidSecret(Fingerprint, Fingerprint),
@ -442,7 +447,12 @@ pub fn decrypt(
"invalid share length (too long, max {ENC_LEN} bytes)"
);
let shared_key = Aes256Gcm::new_from_slice(&shared_secret)?;
let hkdf = Hkdf::<Sha256>::new(None, &shared_secret);
let mut hkdf_output = [0u8; 256 / 8];
hkdf.expand(&[], &mut hkdf_output)?;
let shared_key =
Aes256Gcm::new_from_slice(&hkdf_output)?;
let bytes = shared_key.encrypt(their_nonce, share.as_slice())?;
shared_key.decrypt(their_nonce, &bytes[..])?;