Ensure key ID version contains only valid characters.

This commit is contained in:
Jimmy Cuadra 2019-07-09 16:29:45 -07:00
parent 30f830bd5e
commit 8c756141e1

View File

@ -42,7 +42,8 @@ impl Signature {
/// Returns an error if the key identifier is invalid. /// Returns an error if the key identifier is invalid.
pub fn new(id: &str, bytes: &[u8]) -> Result<Self, Error> { pub fn new(id: &str, bytes: &[u8]) -> Result<Self, Error> {
let (algorithm, version) = split_id(id).map_err(|split_error| match split_error { let (algorithm, version) = split_id(id).map_err(|split_error| match split_error {
SplitError::InvalidLength(_) => Error::new("malformed signature ID"), SplitError::InvalidLength(length) => Error::new(format!("malformed signature ID: expected exactly 2 segment separated by a colon, found {}", length)),
SplitError::InvalidVersion(version) => Error::new(format!("malformed signature ID: expected version to contain only characters in the character set `[a-zA-Z0-9_]`, found `{}`", version)),
SplitError::UnknownAlgorithm(algorithm) => { SplitError::UnknownAlgorithm(algorithm) => {
Error::new(format!("unknown algorithm: {}", algorithm)) Error::new(format!("unknown algorithm: {}", algorithm))
} }
@ -347,6 +348,9 @@ impl<'de> Visitor<'de> for SignatureSetVisitor {
while let Some((key, value)) = visitor.next_entry::<String, String>()? { while let Some((key, value)) = visitor.next_entry::<String, String>()? {
let (algorithm, version) = split_id(&key).map_err(|split_error| match split_error { let (algorithm, version) = split_id(&key).map_err(|split_error| match split_error {
SplitError::InvalidLength(length) => M::Error::invalid_length(length, &self), SplitError::InvalidLength(length) => M::Error::invalid_length(length, &self),
SplitError::InvalidVersion(version) => {
M::Error::invalid_value(Unexpected::Str(version), &self)
}
SplitError::UnknownAlgorithm(algorithm) => { SplitError::UnknownAlgorithm(algorithm) => {
M::Error::invalid_value(Unexpected::Str(algorithm), &self) M::Error::invalid_value(Unexpected::Str(algorithm), &self)
} }
@ -373,8 +377,10 @@ impl<'de> Visitor<'de> for SignatureSetVisitor {
/// An error when trying to extract the algorithm and version from a key identifier. /// An error when trying to extract the algorithm and version from a key identifier.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
enum SplitError<'a> { enum SplitError<'a> {
/// The signature's ID has an invalid length. /// The signature's ID does not have exactly two components separated by a colon.
InvalidLength(usize), InvalidLength(usize),
/// The signature's ID contains invalid characters in its version.
InvalidVersion(&'a str),
/// The signature uses an unknown algorithm. /// The signature uses an unknown algorithm.
UnknownAlgorithm(&'a str), UnknownAlgorithm(&'a str),
} }
@ -392,6 +398,19 @@ fn split_id(id: &str) -> Result<(Algorithm, String), SplitError<'_>> {
return Err(SplitError::InvalidLength(signature_id_length)); return Err(SplitError::InvalidLength(signature_id_length));
} }
let version = signature_id[1];
let invalid_character_index = version.find(|ch| {
!((ch >= 'a' && ch <= 'z')
|| (ch >= 'A' && ch <= 'Z')
|| (ch >= '0' && ch <= '9')
|| ch == '_')
});
if invalid_character_index.is_some() {
return Err(SplitError::InvalidVersion(version));
}
let algorithm_input = signature_id[0]; let algorithm_input = signature_id[0];
let algorithm = match algorithm_input { let algorithm = match algorithm_input {
@ -413,3 +432,28 @@ fn server_name_to_host(server_name: &str) -> Result<Host, Error> {
None => Err(Error::new(format!("invalid server name: {}", server_name))), None => Err(Error::new(format!("invalid server name: {}", server_name))),
} }
} }
#[cfg(test)]
mod tests {
use super::Signature;
#[test]
fn valid_key_id() {
assert!(Signature::new("ed25519:abcdef", &[]).is_ok());
}
#[test]
fn invalid_valid_key_id_length() {
assert!(Signature::new("ed25519:abcdef:123456", &[]).is_err());
}
#[test]
fn invalid_key_id_version() {
assert!(Signature::new("ed25519:abc!def", &[]).is_err());
}
#[test]
fn invalid_key_id_algorithm() {
assert!(Signature::new("foobar:abcdef", &[]).is_err());
}
}