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();
|
let metainfo = self.generate_metainfo();
|
||||||
fs::write(self.config.workdir_path("metainfo"), &metainfo)?;
|
fs::write(self.config.workdir_path("metainfo"), &metainfo)?;
|
||||||
hdr.set_metainfo_bytes(&metainfo);
|
hdr.set_metainfo_bytes(&metainfo);
|
||||||
|
|
||||||
|
if self.config.channel() == "dev" {
|
||||||
|
let sig = devkeys().sign(&metainfo);
|
||||||
|
hdr.set_signature(sig.to_bytes())?;
|
||||||
|
}
|
||||||
Ok(hdr)
|
Ok(hdr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
use std::process::Command;
|
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::path::Path;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use BootSelection;
|
use BootSelection;
|
||||||
use ResourceImage;
|
use ResourceImage;
|
||||||
|
|
||||||
pub struct Rootfs {
|
pub struct Rootfs {
|
||||||
config: Config,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rootfs {
|
impl Rootfs {
|
||||||
pub fn new(config: Config) -> Rootfs {
|
pub fn new() -> Rootfs {
|
||||||
Rootfs { config }
|
Rootfs {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup(&self) -> Result<()> {
|
pub fn setup(&self) -> Result<()> {
|
||||||
@ -53,17 +52,8 @@ impl Rootfs {
|
|||||||
self.setup_linear_mapping(&loopdev)
|
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<()> {
|
fn setup_resource_verified(&self, img: &ResourceImage) -> Result<()> {
|
||||||
let _ = img.setup_verity_device(&self.config)?;
|
let _ = img.setup_verity_device()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +64,10 @@ impl Rootfs {
|
|||||||
|
|
||||||
fn setup_partition_verified(&self, partition: &Partition) -> Result<()> {
|
fn setup_partition_verified(&self, partition: &Partition) -> Result<()> {
|
||||||
info!("Creating /dev/mapper/rootfs dm-verity device");
|
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)?;
|
verity::setup_partition_device(partition)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use failure::ResultExt;
|
|||||||
use toml;
|
use toml;
|
||||||
|
|
||||||
use blockdev::AlignedBuffer;
|
use blockdev::AlignedBuffer;
|
||||||
use {BlockDev, Channel, Config, Result};
|
use {BlockDev,Result,public_key_for_channel};
|
||||||
|
|
||||||
/// Expected magic value in header
|
/// Expected magic value in header
|
||||||
const MAGIC: &[u8] = b"SGOS";
|
const MAGIC: &[u8] = b"SGOS";
|
||||||
@ -217,24 +217,25 @@ impl ImageHeader {
|
|||||||
self.read_bytes(METAINFO_OFFSET + mlen, SIGNATURE_LENGTH)
|
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();
|
let mlen = self.metainfo_len();
|
||||||
// XXX assert mlen is good
|
self.write_bytes(8 + mlen, signature);
|
||||||
let sig = channel.sign(&self.0.borrow()[8..8 + mlen])?;
|
|
||||||
self.write_bytes(8 + mlen, sig.to_bytes());
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_signature(&self, config: &Config) -> Result<()> {
|
pub fn verify_signature(&self) -> Result<()> {
|
||||||
let metainfo = self.metainfo()?;
|
let metainfo = self.metainfo()?;
|
||||||
let channel = match config.channel(metainfo.channel()) {
|
|
||||||
Some(channel) => channel,
|
if let Some(pubkey) = public_key_for_channel(metainfo.channel())? {
|
||||||
None => bail!("Cannot verify signature for channel '{}' because it does not exist in configuration file", metainfo.channel()),
|
if !pubkey.verify(&self.metainfo_bytes(), &self.signature()) {
|
||||||
};
|
bail!("Header signature verification failed");
|
||||||
channel
|
}
|
||||||
.verify(metainfo.bytes(), &self.signature())
|
return Ok(())
|
||||||
.context("failed to verify header signature")?;
|
}
|
||||||
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<()> {
|
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<()> {
|
pub fn parse_toml(&mut self) -> Result<()> {
|
||||||
if !self.is_parsed {
|
if !self.is_parsed {
|
||||||
self.is_parsed = true;
|
self.is_parsed = true;
|
||||||
@ -329,18 +326,6 @@ impl MetaInfo {
|
|||||||
Ok(())
|
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 {
|
fn toml(&self) -> &MetaInfoToml {
|
||||||
self.toml.as_ref().unwrap()
|
self.toml.as_ref().unwrap()
|
||||||
}
|
}
|
||||||
|
@ -16,17 +16,34 @@ pub struct KeyPair([u8; ED25519_PKCS8_V2_LEN]);
|
|||||||
pub struct Signature(signature::Signature);
|
pub struct Signature(signature::Signature);
|
||||||
|
|
||||||
impl PublicKey {
|
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];
|
let mut key = [0u8; ED25519_PUBLIC_KEY_LEN];
|
||||||
key.copy_from_slice(bytes);
|
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 signature = Input::from(signature);
|
||||||
let data = Input::from(data);
|
let data = Input::from(data);
|
||||||
let pubkey = Input::from(&self.0);
|
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 rng = rand::SystemRandom::new();
|
||||||
let bytes = Ed25519KeyPair::generate_pkcs8(&rng)?;
|
let bytes = Ed25519KeyPair::generate_pkcs8(&rng)?;
|
||||||
KeyPair::from_bytes(&bytes)
|
KeyPair::from_bytes(&bytes)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_hex(hex: &str) -> Result<KeyPair> {
|
pub fn from_hex(hex: &str) -> Result<KeyPair> {
|
||||||
@ -50,30 +66,32 @@ impl KeyPair {
|
|||||||
fn from_bytes(bytes: &[u8]) -> Result<KeyPair> {
|
fn from_bytes(bytes: &[u8]) -> Result<KeyPair> {
|
||||||
let mut pair = [0u8; ED25519_PKCS8_V2_LEN];
|
let mut pair = [0u8; ED25519_PKCS8_V2_LEN];
|
||||||
pair.copy_from_slice(bytes);
|
pair.copy_from_slice(bytes);
|
||||||
|
let _ = Ed25519KeyPair::from_pkcs8(Input::from(&pair))?;
|
||||||
Ok(KeyPair(pair))
|
Ok(KeyPair(pair))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn public_key_bytes(&self) -> Vec<u8> {
|
fn get_keys(&self) -> Ed25519KeyPair {
|
||||||
let pair = Ed25519KeyPair::from_pkcs8(Input::from(&self.0)).expect("failed to parse pkcs8 key");
|
Ed25519KeyPair::from_pkcs8(Input::from(&self.0))
|
||||||
pair.public_key_bytes().to_vec()
|
.expect("failed to parse pkcs8 key")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn private_key_bytes(&self) -> Vec<u8> {
|
pub fn public_key(&self) -> PublicKey {
|
||||||
self.0.to_vec()
|
let keys = self.get_keys();
|
||||||
|
PublicKey::from_bytes(keys.public_key_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn private_key_hex(&self) -> String {
|
pub fn private_key_hex(&self) -> String {
|
||||||
self.0.to_hex()
|
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");
|
pub fn sign(&self, data: &[u8]) -> Signature {
|
||||||
pair.public_key_bytes().to_hex()
|
let keys = self.get_keys();
|
||||||
|
let signature = keys.sign(data);
|
||||||
|
Signature(signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sign(&self, data: &[u8]) -> Result<Signature> {
|
pub fn verify(&self, data: &[u8], signature: &[u8]) -> bool {
|
||||||
let pair = Ed25519KeyPair::from_pkcs8(Input::from(&self.0))?;
|
self.public_key().verify(data, signature)
|
||||||
let signature = pair.sign(data);
|
|
||||||
Ok(Signature(signature))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,16 +66,46 @@ pub mod util;
|
|||||||
pub mod verity;
|
pub mod verity;
|
||||||
mod mount;
|
mod mount;
|
||||||
|
|
||||||
pub use config::Config;
|
pub use config::OsRelease;
|
||||||
pub use config::Channel;
|
|
||||||
pub use blockdev::BlockDev;
|
pub use blockdev::BlockDev;
|
||||||
pub use cmdline::CommandLine;
|
pub use cmdline::CommandLine;
|
||||||
pub use header::{ImageHeader,MetaInfo};
|
pub use header::{ImageHeader,MetaInfo};
|
||||||
pub use partition::Partition;
|
pub use partition::Partition;
|
||||||
pub use resource::ResourceImage;
|
pub use resource::ResourceImage;
|
||||||
pub use keys::KeyPair;
|
pub use keys::{KeyPair,PublicKey};
|
||||||
pub use mount::Mount;
|
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>;
|
pub type Result<T> = result::Result<T,Error>;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user