blob: 6b3e2dc6e784cf79a6fe51564df89e5061d1baa0 [file] [log] [blame]
use chrono::{DateTime, UTC};
use data_encoding::HEXLOWER;
use json;
use ring;
use ring::digest::{digest, SHA256};
use ring::signature::ED25519;
use serde::de::{Deserialize, DeserializeOwned, Deserializer, Error as DeserializeError};
use std::collections::HashMap;
use std::fmt::{self, Display, Formatter, Debug};
use std::marker::PhantomData;
use std::str::FromStr;
use untrusted::Input;
use cjson::canonicalize;
use error::Error;
static HASH_PREFERENCES: &'static [HashType] = &[HashType::Sha512, HashType::Sha256];
#[derive(Eq, PartialEq, Deserialize, Debug)]
pub enum Role {
Root,
Targets,
Timestamp,
Snapshot,
}
impl FromStr for Role {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Root" => Ok(Role::Root),
"Snapshot" => Ok(Role::Snapshot),
"Targets" => Ok(Role::Targets),
"Timestamp" => Ok(Role::Timestamp),
role => Err(Error::UnknownRole(String::from(role))),
}
}
}
impl Display for Role {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match *self {
Role::Root => write!(f, "{}", "root"),
Role::Targets => write!(f, "{}", "targets"),
Role::Snapshot => write!(f, "{}", "snapshot"),
Role::Timestamp => write!(f, "{}", "timestamp"),
}
}
}
pub trait RoleType: Debug {
fn role() -> Role;
}
#[derive(Debug)]
pub struct Root {}
impl RoleType for Root {
fn role() -> Role {
Role::Root
}
}
#[derive(Debug)]
pub struct Targets {}
impl RoleType for Targets {
fn role() -> Role {
Role::Targets
}
}
#[derive(Debug)]
pub struct Timestamp {}
impl RoleType for Timestamp {
fn role() -> Role {
Role::Timestamp
}
}
#[derive(Debug)]
pub struct Snapshot {}
impl RoleType for Snapshot {
fn role() -> Role {
Role::Snapshot
}
}
#[derive(Debug)]
pub struct SignedMetadata<R: RoleType> {
pub signatures: Vec<Signature>,
pub signed: json::Value,
_role: PhantomData<R>,
}
impl<'de, R: RoleType> Deserialize<'de> for SignedMetadata<R> {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
if let json::Value::Object(mut object) = Deserialize::deserialize(de)? {
match (object.remove("signatures"), object.remove("signed")) {
(Some(a @ json::Value::Array(_)), Some(v @ json::Value::Object(_))) => {
Ok(SignedMetadata::<R> {
signatures: json::from_value(a).map_err(|e| {
DeserializeError::custom(format!("Bad signature data: {}", e))
})?,
signed: v.clone(),
_role: PhantomData,
})
}
_ => {
Err(DeserializeError::custom("Metadata missing 'signed' or 'signatures' \
section"))
}
}
} else {
Err(DeserializeError::custom("Metadata was not an object"))
}
}
}
pub trait Metadata<R: RoleType>: DeserializeOwned {
fn expires(&self) -> &DateTime<UTC>;
}
#[derive(Debug, PartialEq)]
pub struct RootMetadata {
// TODO consistent_snapshot: bool,
expires: DateTime<UTC>,
pub version: i32,
pub keys: HashMap<KeyId, Key>,
root: RoleDefinition,
targets: RoleDefinition,
timestamp: RoleDefinition,
snapshot: RoleDefinition,
}
impl RootMetadata {
pub fn role_definition<R: RoleType>(&self) -> &RoleDefinition {
match R::role() {
Role::Root => &self.root,
Role::Targets => &self.targets,
Role::Timestamp => &self.timestamp,
Role::Snapshot => &self.snapshot,
}
}
}
impl Metadata<Root> for RootMetadata {
fn expires(&self) -> &DateTime<UTC> {
&self.expires
}
}
impl<'de> Deserialize<'de> for RootMetadata {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
if let json::Value::Object(mut object) = Deserialize::deserialize(de)? {
let typ = json::from_value::<Role>(object.remove("_type")
.ok_or_else(|| DeserializeError::custom("Field '_type' missing"))?)
.map_err(|e| {
DeserializeError::custom(format!("Field '_type' not a valid role: {}", e))
})?;
if typ != Role::Root {
return Err(DeserializeError::custom("Field '_type' was not 'Root'"));
}
let keys = json::from_value(object.remove("keys")
.ok_or_else(|| DeserializeError::custom("Field 'keys' missing"))?).map_err(|e| {
DeserializeError::custom(format!("Field 'keys' not a valid key map: {}", e))
})?;
let expires = json::from_value(object.remove("expires")
.ok_or_else(|| DeserializeError::custom("Field 'expires' missing"))?).map_err(|e| {
DeserializeError::custom(format!("Field 'expires' did not have a valid format: {}", e))
})?;
let version = json::from_value(object.remove("version")
.ok_or_else(|| DeserializeError::custom("Field 'version' missing"))?).map_err(|e| {
DeserializeError::custom(format!("Field 'version' did not have a valid format: {}", e))
})?;
let mut roles = object.remove("roles")
.and_then(|v| match v {
json::Value::Object(o) => Some(o),
_ => None,
})
.ok_or_else(|| DeserializeError::custom("Field 'roles' missing"))?;
let root = json::from_value(roles.remove("root")
.ok_or_else(|| DeserializeError::custom("Role 'root' missing"))?)
.map_err(|e| {
DeserializeError::custom(format!("Root role definition error: {}", e))
})?;
let targets = json::from_value(roles.remove("targets")
.ok_or_else(|| DeserializeError::custom("Role 'targets' missing"))?)
.map_err(|e| {
DeserializeError::custom(format!("Targets role definition error: {}", e))
})?;
let timestamp = json::from_value(roles.remove("timestamp")
.ok_or_else(|| DeserializeError::custom("Role 'timestamp' missing"))?)
.map_err(|e| {
DeserializeError::custom(format!("Timetamp role definition error: {}", e))
})?;
let snapshot = json::from_value(roles.remove("snapshot")
.ok_or_else(|| DeserializeError::custom("Role 'shapshot' missing"))?)
.map_err(|e| {
DeserializeError::custom(format!("Snapshot role definition error: {}", e))
})?;
Ok(RootMetadata {
expires: expires,
version: version,
keys: keys,
root: root,
targets: targets,
timestamp: timestamp,
snapshot: snapshot,
})
} else {
Err(DeserializeError::custom("Role was not an object"))
}
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct RoleDefinition {
pub key_ids: Vec<KeyId>,
pub threshold: i32,
}
impl<'de> Deserialize<'de> for RoleDefinition {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
if let json::Value::Object(mut object) = Deserialize::deserialize(de)? {
let key_ids = json::from_value(object.remove("keyids")
.ok_or_else(|| DeserializeError::custom("Field 'keyids' missing"))?).map_err(|e| {
DeserializeError::custom(format!("Field 'keyids' not a valid array: {}", e))
})?;
let threshold = json::from_value(object.remove("threshold")
.ok_or_else(|| DeserializeError::custom("Field 'threshold' missing"))?).map_err(|e| {
DeserializeError::custom(format!("Field 'threshold' not a an int: {}", e))
})?;
if threshold <= 0 {
return Err(DeserializeError::custom("'threshold' must be >= 1"));
}
Ok(RoleDefinition {
key_ids: key_ids,
threshold: threshold,
})
} else {
Err(DeserializeError::custom("Role definition was not an object"))
}
}
}
#[derive(Debug)]
pub struct TargetsMetadata {
expires: DateTime<UTC>,
pub version: i32,
pub delegations: Option<Delegations>,
pub targets: HashMap<String, TargetInfo>,
}
impl Metadata<Targets> for TargetsMetadata {
fn expires(&self) -> &DateTime<UTC> {
&self.expires
}
}
impl<'de> Deserialize<'de> for TargetsMetadata {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
if let json::Value::Object(mut object) = Deserialize::deserialize(de)? {
let delegations = match object.remove("delegations") {
// TODO this should accept null / empty object too
// currently the options are "not present at all" or "completely correct"
// and everything else errors out
Some(value) => {
Some(json::from_value(value).map_err(|e| {
DeserializeError::custom(format!("Bad delegations format: {}", e))
})?)
}
None => None,
};
let expires = json::from_value(object.remove("expires")
.ok_or_else(|| DeserializeError::custom("Field 'expires' missing"))?).map_err(|e| {
DeserializeError::custom(format!("Field 'expires did not have a valid format: {}", e))
})?;
let version = json::from_value(object.remove("version")
.ok_or_else(|| DeserializeError::custom("Field 'version' missing"))?).map_err(|e| {
DeserializeError::custom(format!("Field 'version' did not have a valid format: {}", e))
})?;
match object.remove("targets") {
Some(t) => {
let targets =
json::from_value(t).map_err(|e| {
DeserializeError::custom(format!("Bad targets format: {}", e))
})?;
Ok(TargetsMetadata {
version: version,
expires: expires,
delegations: delegations,
targets: targets,
})
}
_ => Err(DeserializeError::custom("Signature missing fields".to_string())),
}
} else {
Err(DeserializeError::custom("Role was not an object"))
}
}
}
#[derive(Debug)]
pub struct TimestampMetadata {
expires: DateTime<UTC>,
pub version: i32,
pub meta: HashMap<String, MetadataMetadata>,
}
impl Metadata<Timestamp> for TimestampMetadata {
fn expires(&self) -> &DateTime<UTC> {
&self.expires
}
}
impl<'de> Deserialize<'de> for TimestampMetadata {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
if let json::Value::Object(mut object) = Deserialize::deserialize(de)? {
let expires = json::from_value(object.remove("expires")
.ok_or_else(|| DeserializeError::custom("Field 'expires' missing"))?).map_err(|e| {
DeserializeError::custom(format!("Field 'expires' did not have a valid format: {}", e))
})?;
let version = json::from_value(object.remove("version")
.ok_or_else(|| DeserializeError::custom("Field 'version' missing"))?).map_err(|e| {
DeserializeError::custom(format!("Field 'version' did not have a valid format: {}", e))
})?;
match object.remove("meta") {
Some(m) => {
let meta = json::from_value(m).map_err(|e| {
DeserializeError::custom(format!("Bad meta-meta format: {}", e))
})?;
Ok(TimestampMetadata {
expires: expires,
version: version,
meta: meta,
})
}
_ => Err(DeserializeError::custom("Signature missing fields".to_string())),
}
} else {
Err(DeserializeError::custom("Role was not an object"))
}
}
}
#[derive(Debug)]
pub struct SnapshotMetadata {
expires: DateTime<UTC>,
pub version: i32,
pub meta: HashMap<String, SnapshotMetadataMetadata>,
}
impl Metadata<Snapshot> for SnapshotMetadata {
fn expires(&self) -> &DateTime<UTC> {
&self.expires
}
}
impl<'de> Deserialize<'de> for SnapshotMetadata {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
if let json::Value::Object(mut object) = Deserialize::deserialize(de)? {
let expires = json::from_value(object.remove("expires")
.ok_or_else(|| DeserializeError::custom("Field 'expires' missing"))?).map_err(|e| {
DeserializeError::custom(format!("Field 'expires' did not have a valid format: {}", e))
})?;
let version = json::from_value(object.remove("version")
.ok_or_else(|| DeserializeError::custom("Field 'version' missing"))?).map_err(|e| {
DeserializeError::custom(format!("Field 'version' did not have a valid format: {}", e))
})?;
match object.remove("meta") {
Some(m) => {
let meta = json::from_value(m).map_err(|e| {
DeserializeError::custom(format!("Bad meta-meta format: {}", e))
})?;
Ok(SnapshotMetadata {
expires: expires,
version: version,
meta: meta,
})
}
_ => Err(DeserializeError::custom("Signature missing fields".to_string())),
}
} else {
Err(DeserializeError::custom("Role was not an object"))
}
}
}
/// A cryptographic signature.
#[derive(Clone, PartialEq, Debug)]
pub struct Signature {
pub key_id: KeyId,
pub method: SignatureScheme,
pub sig: SignatureValue,
}
impl<'de> Deserialize<'de> for Signature {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
if let json::Value::Object(mut object) = Deserialize::deserialize(de)? {
match (object.remove("keyid"), object.remove("method"), object.remove("sig")) {
(Some(k), Some(m), Some(s)) => {
let key_id =
json::from_value(k).map_err(|e| {
DeserializeError::custom(format!("Failed at keyid: {}", e))
})?;
let method =
json::from_value(m).map_err(|e| {
DeserializeError::custom(format!("Failed at method: {}", e))
})?;
let sig = json::from_value(s)
.map_err(|e| DeserializeError::custom(format!("Failed at sig: {}", e)))?;
Ok(Signature {
key_id: key_id,
method: method,
sig: sig,
})
}
_ => Err(DeserializeError::custom("Signature missing fields".to_string())),
}
} else {
Err(DeserializeError::custom("Signature was not an object".to_string()))
}
}
}
/// A public key
#[derive(Clone, PartialEq, Debug, Deserialize)]
pub struct Key {
#[serde(rename = "keytype")]
pub typ: KeyType,
#[serde(rename = "keyval")]
pub value: KeyValue,
}
impl Key {
pub fn verify(&self,
scheme: &SignatureScheme,
msg: &[u8],
sig: &SignatureValue)
-> Result<(), Error> {
if self.typ.supports(scheme) {
match self.typ {
KeyType::Unsupported(ref s) => Err(Error::UnsupportedKeyType(s.clone())),
_ => scheme.verify(&self.value, msg, sig),
}
} else {
Err(Error::Generic(format!("Signature scheme mismatch: Key {:?}, Scheme {:?}",
self,
scheme)))
}
}
}
/// Types of public keys.
#[derive(Clone, PartialEq, Debug)]
pub enum KeyType {
Ed25519,
Unsupported(String),
}
impl KeyType {
fn supports(&self, scheme: &SignatureScheme) -> bool {
match (self, scheme) {
(&KeyType::Ed25519, &SignatureScheme::Ed25519) => true,
_ => false,
}
}
}
impl FromStr for KeyType {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"ed25519" => Ok(KeyType::Ed25519),
typ => Ok(KeyType::Unsupported(typ.into())),
}
}
}
impl<'de> Deserialize<'de> for KeyType {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
if let json::Value::String(ref s) = Deserialize::deserialize(de)? {
s.parse().map_err(|_| unreachable!())
} else {
Err(DeserializeError::custom("Key type was not a string"))
}
}
}
/// The raw bytes of a public key.
#[derive(Clone, PartialEq, Debug)]
pub struct KeyValue(pub Vec<u8>);
impl KeyValue {
/// Calculates the `KeyId` of the public key.
pub fn key_id(&self) -> KeyId {
// TODO this only works because we're using ed25519
// and this will break when we add RSA.
let key_value_hex = canonicalize(json!(HEXLOWER.encode(&self.0))).unwrap(); // TODO unwrap
KeyId(HEXLOWER.encode(digest(&SHA256, &key_value_hex).as_ref()))
}
}
impl<'de> Deserialize<'de> for KeyValue {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
match Deserialize::deserialize(de)? {
json::Value::String(ref s) => {
// TODO this is shit because we can't tell what type of key it is
// e.g., ed25519 => hex, rsa => PEM
// need to add this into the type/struct so it can be accessed here
HEXLOWER.decode(s.as_ref())
.map(KeyValue)
.map_err(|e| DeserializeError::custom(format!("Key value was not hex: {}", e)))
}
json::Value::Object(mut object) => {
json::from_value::<KeyValue>(object.remove("public")
.ok_or_else(|| DeserializeError::custom("Field 'public' missing"))?)
.map_err(|e| {
DeserializeError::custom(format!("Field 'public' not encoded correctly: \
{}",
e))
})
}
_ => Err(DeserializeError::custom("Key value was not a string or object")),
}
}
}
/// The hex encoded ID of a public key.
#[derive(Clone, Hash, Eq, PartialEq, Debug)]
pub struct KeyId(pub String);
impl<'de> Deserialize<'de> for KeyId {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
match Deserialize::deserialize(de)? {
json::Value::String(s) => Ok(KeyId(s)),
_ => Err(DeserializeError::custom("Key ID was not a string")),
}
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct SignatureValue(Vec<u8>);
impl<'de> Deserialize<'de> for SignatureValue {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
match Deserialize::deserialize(de)? {
json::Value::String(ref s) => {
HEXLOWER.decode(s.as_ref())
.map(SignatureValue)
.map_err(|e| {
DeserializeError::custom(format!("Signature value was not hex: {}", e))
})
}
_ => Err(DeserializeError::custom("Signature value was not a string")),
}
}
}
#[derive(Clone, PartialEq, Debug)]
pub enum SignatureScheme {
Ed25519,
Unsupported(String),
}
impl SignatureScheme {
fn verify(&self, pub_key: &KeyValue, msg: &[u8], sig: &SignatureValue) -> Result<(), Error> {
match self {
&SignatureScheme::Ed25519 => {
ring::signature::verify(&ED25519,
Input::from(&pub_key.0),
Input::from(msg),
Input::from(&sig.0))
.map_err(|_| Error::VerificationFailure("Bad signature".into()))
}
&SignatureScheme::Unsupported(ref s) => {
Err(Error::UnsupportedSignatureScheme(s.clone()))
}
}
}
}
impl FromStr for SignatureScheme {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"ed25519" => Ok(SignatureScheme::Ed25519),
typ => Ok(SignatureScheme::Unsupported(typ.into())),
}
}
}
impl<'de> Deserialize<'de> for SignatureScheme {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
if let json::Value::String(ref s) = Deserialize::deserialize(de)? {
s.parse().map_err(|_| unreachable!())
} else {
Err(DeserializeError::custom("Key type was not a string"))
}
}
}
#[derive(Clone, PartialEq, Debug, Deserialize)]
pub struct MetadataMetadata {
pub length: i64,
pub hashes: HashMap<HashType, HashValue>,
pub version: i32,
}
#[derive(Clone, PartialEq, Debug, Deserialize)]
pub struct SnapshotMetadataMetadata {
pub length: Option<i64>,
pub hashes: Option<HashMap<HashType, HashValue>>,
pub version: i32,
}
#[derive(Clone, Hash, Eq, PartialEq, Debug)]
pub enum HashType {
Sha256,
Sha512,
Unsupported(String),
}
impl HashType {
pub fn preferences() -> &'static [HashType] {
HASH_PREFERENCES
}
}
impl FromStr for HashType {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"sha256" => Ok(HashType::Sha256),
"sha512" => Ok(HashType::Sha512),
typ => Ok(HashType::Unsupported(typ.into())),
}
}
}
impl<'de> Deserialize<'de> for HashType {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
if let json::Value::String(ref s) = Deserialize::deserialize(de)? {
s.parse().map_err(|_| unreachable!())
} else {
Err(DeserializeError::custom("Hash type was not a string"))
}
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct HashValue(pub Vec<u8>);
impl<'de> Deserialize<'de> for HashValue {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
match Deserialize::deserialize(de)? {
json::Value::String(ref s) => {
HEXLOWER.decode(s.as_ref())
.map(HashValue)
.map_err(|e| DeserializeError::custom(format!("Hash value was not hex: {}", e)))
}
_ => Err(DeserializeError::custom("Hash value was not a string")),
}
}
}
#[derive(Clone, Debug, Deserialize)]
// TODO this is a dumb name
pub struct TargetInfo {
pub length: i64,
pub hashes: HashMap<HashType, HashValue>,
pub custom: Option<HashMap<String, String>>, // TODO json value
}
#[derive(Clone, PartialEq, Debug, Deserialize)]
pub struct Delegations {
keys: Vec<KeyId>,
roles: Vec<DelegatedRole>,
}
#[derive(Clone, PartialEq, Debug, Deserialize)]
pub struct DelegatedRole {
name: String,
key_ids: Vec<KeyId>,
threshold: i32,
// TODO path_hash_prefixes
paths: Vec<String>,
}