forked from brl/citadel-tools
Clean up keys api
This commit is contained in:
parent
b59188311d
commit
dc9a49fd8a
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user