blob: 6f93696b463650a07e6810340fe28f747699a468 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Implement key management of the top level asymmetric key pair, used to
//! protect the hibernate metadata encryption key.
use std::convert::TryInto;
use std::fs::create_dir;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
use anyhow::{Context, Result};
use log::{info, warn};
use openssl::derive::Deriver;
use openssl::pkey::{Id, PKey, Private, Public};
use zeroize::Zeroizing;
use crate::hibermeta::{HibernateMetadata, META_ASYMMETRIC_KEY_SIZE, META_SYMMETRIC_KEY_SIZE};
use crate::hiberutil::HibernateError;
/// Define the ramfs location where the hibernate public key is stored.
static PUBLIC_KEY_DIR: &str = "/run/hibernate/";
/// Define the name where the hibernate public key is stored.
static PUBLIC_KEY_NAME: &str = "pubkey";
/// Define the fixed test key seed, used when a developer wants to hibernate or
/// resume now with --test-keys.
const TEST_KEY_MATERIAL: &[u8; META_ASYMMETRIC_KEY_SIZE] = b"TestHibernateKeyMaterial12345678";
/// The HibernateKeyManager stores the public and private key pair. The public
/// side is used to encrypt the hibernate metadata at suspend time. The private
/// side is used to decrypt that same private metadata. An asymmetric key is
/// used so that the hibernate image decryption key is not sitting around in
/// memory during most of boot.
pub struct HibernateKeyManager {
private_key: Option<PKey<Private>>,
public_key: Option<PKey<Public>>,
}
impl HibernateKeyManager {
/// Create a new HibernateKeyManager, with no keys.
pub fn new() -> Self {
HibernateKeyManager {
private_key: None,
public_key: None,
}
}
/// Use the test keys.
pub fn use_test_keys(&mut self) -> Result<()> {
warn!("Using test keys: File a bug if you see this in production");
// Don't clobber already loaded keys.
assert!(self.private_key.is_none() && self.public_key.is_none());
self.set_private_key(&TEST_KEY_MATERIAL[..])?;
let public_bytes = self.private_key.as_ref().unwrap().raw_public_key().unwrap();
self.public_key = Some(PKey::public_key_from_raw_bytes(&public_bytes, Id::X25519).unwrap());
Ok(())
}
/// Create the private (and public) key based on secret seed material.
pub fn set_private_key(&mut self, key: &[u8]) -> Result<()> {
let private_key = PKey::private_key_from_raw_bytes(key, Id::X25519).unwrap();
self.private_key = Some(private_key);
Ok(())
}
/// Clear the private key out of the key manager so it's not floating around
/// in memory.
pub fn clear_private_key(&mut self) -> Result<()> {
self.set_private_key(&TEST_KEY_MATERIAL[..])?;
self.private_key = None;
Ok(())
}
/// Save the public key to a ramfs file so it can be used later by the
/// hibernate service.
pub fn save_public_key(&self) -> Result<()> {
let private_key = self.private_key.as_ref().ok_or_else(|| {
HibernateError::KeyManagerError("No private key to derive public key from".to_string())
})?;
if !Path::new(PUBLIC_KEY_DIR).exists() {
create_dir(PUBLIC_KEY_DIR).context("Cannot create directory to save public key")?;
}
let key_path = Path::new(PUBLIC_KEY_DIR).join(PUBLIC_KEY_NAME);
info!("Saving public key to {}", key_path.display());
let mut key_file = File::create(&key_path).context("Cannot create public key file")?;
let public_key = private_key.raw_public_key().unwrap();
assert!(public_key.len() == META_ASYMMETRIC_KEY_SIZE);
key_file
.write_all(&public_key)
.context("Failed to write public key")?;
Ok(())
}
/// Load the public key from a ramfs file, that was previously saved by a
/// different instance of this application calling save_public_key().
pub fn load_public_key(&mut self) -> Result<()> {
let key_path = Path::new(PUBLIC_KEY_DIR).join(PUBLIC_KEY_NAME);
info!("Loading public key from {}", key_path.display());
// Cryptohome should have handed the hibernate key to another instance
// of this program. If you see this message, that instance probably
// didn't launch, or never received keys from cryptohome.
let mut key_file = File::open(&key_path)
.context("No hibernate public key. Use --test-keys to hibernate now")?;
let mut public_key = [0u8; META_ASYMMETRIC_KEY_SIZE];
let bytes_read = key_file
.read(&mut public_key)
.context("Failed to read hibernate public key")?;
if bytes_read != public_key.len() {
return Err(HibernateError::KeyManagerError(
"Read too few bytes".to_string(),
))
.context("Failed to load hibernate public key");
}
self.public_key = Some(PKey::public_key_from_raw_bytes(&public_key, Id::X25519).unwrap());
Ok(())
}
/// Generate a new metadata key by generating a random ephemeral asymmetric
/// key and doing Diffie-Hellman to get a symmetric key. The caller must
/// have previously called load_public_key() or use_test_key(). On success,
/// the symmetric key will be installed in the given metadata instance.
pub fn install_new_metadata_key(&mut self, metadata: &mut HibernateMetadata) -> Result<()> {
// Use the public key. The private key will also do.
let public_key = match &self.public_key {
Some(k) => k,
None => {
return Err(HibernateError::KeyManagerError(
"Unpopulated public key".to_string(),
))
.context("Cannot install new metadata key");
}
};
// Create a new random key, and fire up a DH shared secret.
let ephemeral_key = PKey::generate_x25519().unwrap();
let mut deriver = Deriver::new(&ephemeral_key).unwrap();
deriver.set_peer(public_key).unwrap();
// Install the ephemeral public key into the metadata.
let ephemeral_public = ephemeral_key.raw_public_key().unwrap();
metadata.meta_eph_public = ephemeral_public.try_into().unwrap();
// Derive and install the derived key into the metadata.
self.install_derived_key(metadata, &mut deriver)
}
/// Generate the metadata key by doing Diffie-Hellman with the previously
/// saved ephemeral public key, and the auth public/private key. The caller
/// must have previously called set_private_key().
pub fn install_saved_metadata_key(&mut self, metadata: &mut HibernateMetadata) -> Result<()> {
if self.private_key.is_none() {
return Err(HibernateError::KeyManagerError(
"Unpopulated private key".to_string(),
))
.context("Cannot install saved metadata key");
}
// Load the ephemeral public key.
let ephemeral_public =
PKey::public_key_from_raw_bytes(&metadata.meta_eph_public, Id::X25519).unwrap();
// Fire up a DH and load both keys.
let mut deriver = Deriver::new(self.private_key.as_ref().unwrap()).unwrap();
deriver.set_peer(&ephemeral_public).unwrap();
// Derive and install the derived key into the metadata.
self.install_derived_key(metadata, &mut deriver)
}
/// Derive the shared key, and install it into the metadata for future
/// encryption or decryption of private data.
fn install_derived_key(
&self,
metadata: &mut HibernateMetadata,
deriver: &mut Deriver,
) -> Result<()> {
assert!(deriver.len().unwrap() >= META_ASYMMETRIC_KEY_SIZE);
let mut derived_metadata_key = Zeroizing::new(vec![0u8; deriver.len().unwrap()]);
let key_size = deriver
.derive(&mut derived_metadata_key)
.context("Failed to derive DH key")?;
if key_size != derived_metadata_key.len() {
return Err(HibernateError::KeyManagerError(format!(
"Derived size {}, expected {}",
key_size,
derived_metadata_key.len()
)))
.context("Failed to derive DH key");
}
// Install the shared key into the metadata.
metadata.set_metadata_key(
derived_metadata_key[..META_SYMMETRIC_KEY_SIZE]
.try_into()
.unwrap(),
);
Ok(())
}
}