Clean up keys api

This commit is contained in:
Bruce Leidl 2019-01-05 20:38:57 -05:00
parent b59188311d
commit dc9a49fd8a
5 changed files with 97 additions and 66 deletions

View File

@ -150,6 +150,11 @@ impl UpdateBuilder {
let metainfo = self.generate_metainfo();
fs::write(self.config.workdir_path("metainfo"), &metainfo)?;
hdr.set_metainfo_bytes(&metainfo);
if self.config.channel() == "dev" {
let sig = devkeys().sign(&metainfo);
hdr.set_signature(sig.to_bytes())?;
}
Ok(hdr)
}

View File

@ -1,18 +1,17 @@
use std::process::Command;
use libcitadel::{BlockDev,CommandLine,Config,ImageHeader,Partition,Result,verity};
use libcitadel::{BlockDev,CommandLine,Partition,Result,verity};
use std::path::Path;
use std::process::Stdio;
use BootSelection;
use ResourceImage;
pub struct Rootfs {
config: Config,
}
impl Rootfs {
pub fn new(config: Config) -> Rootfs {
Rootfs { config }
pub fn new() -> Rootfs {
Rootfs {}
}
pub fn setup(&self) -> Result<()> {
@ -53,17 +52,8 @@ impl Rootfs {
self.setup_linear_mapping(&loopdev)
}
fn maybe_check_signature(&self, hdr: &ImageHeader) -> Result<()> {
if !CommandLine::nosignatures() {
let signature = hdr.signature();
let metainfo = hdr.metainfo()?;
metainfo.verify(&self.config, &signature)?;
}
Ok(())
}
fn setup_resource_verified(&self, img: &ResourceImage) -> Result<()> {
let _ = img.setup_verity_device(&self.config)?;
let _ = img.setup_verity_device()?;
Ok(())
}
@ -74,7 +64,10 @@ impl Rootfs {
fn setup_partition_verified(&self, partition: &Partition) -> Result<()> {
info!("Creating /dev/mapper/rootfs dm-verity device");
self.maybe_check_signature(partition.header())?;
if !CommandLine::nosignatures() {
partition.header().verify_signature()?;
info!("Image signature is valid for channel {}", partition.metainfo().channel());
}
verity::setup_partition_device(partition)?;
Ok(())
}

View File

@ -8,7 +8,7 @@ use failure::ResultExt;
use toml;
use blockdev::AlignedBuffer;
use {BlockDev, Channel, Config, Result};
use {BlockDev,Result,public_key_for_channel};
/// Expected magic value in header
const MAGIC: &[u8] = b"SGOS";
@ -217,24 +217,25 @@ impl ImageHeader {
self.read_bytes(METAINFO_OFFSET + mlen, SIGNATURE_LENGTH)
}
pub fn sign_metainfo(&self, channel: &Channel) -> Result<()> {
pub fn set_signature(&self, signature: &[u8]) -> Result<()> {
if signature.len() != SIGNATURE_LENGTH {
bail!("Signature has invalid length: {}", signature.len());
}
let mlen = self.metainfo_len();
// XXX assert mlen is good
let sig = channel.sign(&self.0.borrow()[8..8 + mlen])?;
self.write_bytes(8 + mlen, sig.to_bytes());
self.write_bytes(8 + mlen, signature);
Ok(())
}
pub fn verify_signature(&self, config: &Config) -> Result<()> {
pub fn verify_signature(&self) -> Result<()> {
let metainfo = self.metainfo()?;
let channel = match config.channel(metainfo.channel()) {
Some(channel) => channel,
None => bail!("Cannot verify signature for channel '{}' because it does not exist in configuration file", metainfo.channel()),
};
channel
.verify(metainfo.bytes(), &self.signature())
.context("failed to verify header signature")?;
Ok(())
if let Some(pubkey) = public_key_for_channel(metainfo.channel())? {
if !pubkey.verify(&self.metainfo_bytes(), &self.signature()) {
bail!("Header signature verification failed");
}
return Ok(())
}
Err(format_err!("Cannot verify signature because no public key found for channel '{}'", metainfo.channel()))
}
pub fn write_header<W: Write>(&self, mut writer: W) -> Result<()> {
@ -315,10 +316,6 @@ impl MetaInfo {
}
}
fn bytes(&self) -> &[u8] {
&self.bytes
}
pub fn parse_toml(&mut self) -> Result<()> {
if !self.is_parsed {
self.is_parsed = true;
@ -329,18 +326,6 @@ impl MetaInfo {
Ok(())
}
pub fn verify(&self, config: &Config, signature: &[u8]) -> Result<()> {
let channel = match config.channel(self.channel()) {
Some(channel) => channel,
None => bail!("Channel '{}' not found in config file", self.channel()),
};
channel
.verify(&self.bytes, signature)
.context("Bad metainfo signature in header")?;
Ok(())
}
fn toml(&self) -> &MetaInfoToml {
self.toml.as_ref().unwrap()
}

View File

@ -16,17 +16,34 @@ pub struct KeyPair([u8; ED25519_PKCS8_V2_LEN]);
pub struct Signature(signature::Signature);
impl PublicKey {
pub fn from_bytes(bytes: &[u8]) -> Result<PublicKey> {
pub fn from_hex(hex: &str) -> Result<PublicKey> {
let bytes = hex.from_hex()?;
if bytes.len() != ED25519_PUBLIC_KEY_LEN {
bail!("Hex encoded public key has invalid length: {}", bytes.len());
}
Ok(PublicKey::from_bytes(&bytes))
}
pub fn to_hex(&self) -> String {
self.0.to_hex()
}
fn from_bytes(bytes: &[u8]) -> PublicKey {
let mut key = [0u8; ED25519_PUBLIC_KEY_LEN];
key.copy_from_slice(bytes);
Ok(PublicKey(key))
PublicKey(key)
}
pub fn verify(&self, data: &[u8], signature: &[u8]) -> Result<()> {
pub fn verify(&self, data: &[u8], signature: &[u8]) -> bool {
let signature = Input::from(signature);
let data = Input::from(data);
let pubkey = Input::from(&self.0);
signature::verify(&signature::ED25519, pubkey, data, signature)?;
Ok(())
match signature::verify(&signature::ED25519, pubkey, data, signature) {
Ok(()) => true,
Err(_) => false,
}
}
}
@ -40,7 +57,6 @@ impl KeyPair {
let rng = rand::SystemRandom::new();
let bytes = Ed25519KeyPair::generate_pkcs8(&rng)?;
KeyPair::from_bytes(&bytes)
}
pub fn from_hex(hex: &str) -> Result<KeyPair> {
@ -50,30 +66,32 @@ impl KeyPair {
fn from_bytes(bytes: &[u8]) -> Result<KeyPair> {
let mut pair = [0u8; ED25519_PKCS8_V2_LEN];
pair.copy_from_slice(bytes);
let _ = Ed25519KeyPair::from_pkcs8(Input::from(&pair))?;
Ok(KeyPair(pair))
}
pub fn public_key_bytes(&self) -> Vec<u8> {
let pair = Ed25519KeyPair::from_pkcs8(Input::from(&self.0)).expect("failed to parse pkcs8 key");
pair.public_key_bytes().to_vec()
fn get_keys(&self) -> Ed25519KeyPair {
Ed25519KeyPair::from_pkcs8(Input::from(&self.0))
.expect("failed to parse pkcs8 key")
}
pub fn private_key_bytes(&self) -> Vec<u8> {
self.0.to_vec()
pub fn public_key(&self) -> PublicKey {
let keys = self.get_keys();
PublicKey::from_bytes(keys.public_key_bytes())
}
pub fn private_key_hex(&self) -> String {
self.0.to_hex()
}
pub fn public_key_hex(&self) -> String {
let pair = Ed25519KeyPair::from_pkcs8(Input::from(&self.0)).expect("failed to parse pkcs8 key");
pair.public_key_bytes().to_hex()
pub fn sign(&self, data: &[u8]) -> Signature {
let keys = self.get_keys();
let signature = keys.sign(data);
Signature(signature)
}
pub fn sign(&self, data: &[u8]) -> Result<Signature> {
let pair = Ed25519KeyPair::from_pkcs8(Input::from(&self.0))?;
let signature = pair.sign(data);
Ok(Signature(signature))
pub fn verify(&self, data: &[u8], signature: &[u8]) -> bool {
self.public_key().verify(data, signature)
}
}

View File

@ -66,16 +66,46 @@ pub mod util;
pub mod verity;
mod mount;
pub use config::Config;
pub use config::Channel;
pub use config::OsRelease;
pub use blockdev::BlockDev;
pub use cmdline::CommandLine;
pub use header::{ImageHeader,MetaInfo};
pub use partition::Partition;
pub use resource::ResourceImage;
pub use keys::KeyPair;
pub use keys::{KeyPair,PublicKey};
pub use mount::Mount;
const DEVKEYS_HEX: &str =
"3053020101300506032b6570042204206ed2849c6c5168e1aebc50005ac3d4a4e84af4889e4e0189bb4c787e6ee0be49a1230321006b652764c62a1de35e7e37af2b743e9a5b82cee2211cf3091d2514441b417f5f";
pub fn devkeys() -> KeyPair {
KeyPair::from_hex(&DEVKEYS_HEX)
.expect("Error parsing built in dev channel keys")
}
pub fn public_key_for_channel(channel: &str) -> Result<Option<PublicKey>> {
if channel == "dev" {
return Ok(Some(devkeys().public_key()));
}
// Look in /etc/os-release
if Some(channel) == OsRelease::citadel_channel() {
if let Some(hex) = OsRelease::citadel_image_pubkey() {
let pubkey = PublicKey::from_hex(hex)?;
return Ok(Some(pubkey));
}
}
// Does kernel command line have citadel.channel=name:[hex encoded pubkey]
if Some(channel) == CommandLine::channel_name() {
if let Some(hex) = CommandLine::channel_pubkey() {
let pubkey = PublicKey::from_hex(hex)?;
return Ok(Some(pubkey))
}
}
Ok(None)
}
pub type Result<T> = result::Result<T,Error>;