rpki/src/storage/keys.rs

196 lines
5.6 KiB
Rust

use serde::{Serialize, de::DeserializeOwned};
use crate::data_model::common::der_take_tlv;
use super::config::*;
use super::pack::PackTime;
use super::{StorageError, StorageResult};
pub(super) fn repository_view_key(rsync_uri: &str) -> String {
format!("{REPOSITORY_VIEW_KEY_PREFIX}{rsync_uri}")
}
pub(super) fn repository_view_prefix(rsync_uri_prefix: &str) -> String {
format!("{REPOSITORY_VIEW_KEY_PREFIX}{rsync_uri_prefix}")
}
pub(super) fn raw_by_hash_key(sha256_hex: &str) -> String {
format!("{RAW_BY_HASH_KEY_PREFIX}{sha256_hex}")
}
pub(super) fn raw_blob_key(sha256_hex: &str) -> String {
format!("{RAW_BLOB_KEY_PREFIX}{sha256_hex}")
}
pub(super) fn vcir_key(manifest_rsync_uri: &str) -> String {
format!("{VCIR_KEY_PREFIX}{manifest_rsync_uri}")
}
pub(super) fn manifest_replay_meta_key(manifest_rsync_uri: &str) -> String {
format!("{MANIFEST_REPLAY_META_KEY_PREFIX}{manifest_rsync_uri}")
}
pub(super) fn rrdp_source_key(notify_uri: &str) -> String {
format!("{RRDP_SOURCE_KEY_PREFIX}{notify_uri}")
}
pub(super) fn rrdp_source_member_key(notify_uri: &str, rsync_uri: &str) -> String {
format!("{RRDP_SOURCE_MEMBER_KEY_PREFIX}{notify_uri}:{rsync_uri}")
}
pub(super) fn rrdp_source_member_prefix(notify_uri: &str) -> String {
format!("{RRDP_SOURCE_MEMBER_KEY_PREFIX}{notify_uri}:")
}
pub(super) fn rrdp_uri_owner_key(rsync_uri: &str) -> String {
format!("{RRDP_URI_OWNER_KEY_PREFIX}{rsync_uri}")
}
pub(super) fn encode_cbor<T: Serialize>(value: &T, entity: &'static str) -> StorageResult<Vec<u8>> {
serde_cbor::to_vec(value).map_err(|e| StorageError::Codec {
entity,
detail: e.to_string(),
})
}
pub(super) fn decode_cbor<T: DeserializeOwned>(
bytes: &[u8],
entity: &'static str,
) -> StorageResult<T> {
serde_cbor::from_slice(bytes).map_err(|e| StorageError::Codec {
entity,
detail: e.to_string(),
})
}
pub(super) fn validate_non_empty(field: &'static str, value: &str) -> StorageResult<()> {
if value.is_empty() {
return Err(StorageError::InvalidData {
entity: field,
detail: "must not be empty".to_string(),
});
}
Ok(())
}
pub(super) fn validate_sha256_hex(field: &'static str, value: &str) -> StorageResult<()> {
if value.len() != 64 || !value.as_bytes().iter().all(u8::is_ascii_hexdigit) {
return Err(StorageError::InvalidData {
entity: field,
detail: "must be a 64-character lowercase or uppercase SHA-256 hex string".to_string(),
});
}
Ok(())
}
pub(super) fn decode_sha256_hex_32(field: &'static str, value: &str) -> StorageResult<[u8; 32]> {
validate_sha256_hex(field, value)?;
let mut out = [0u8; 32];
hex::decode_to_slice(value, &mut out).map_err(|e| StorageError::InvalidData {
entity: field,
detail: format!("hex decode failed: {e}"),
})?;
Ok(out)
}
pub(super) fn validate_manifest_number_be(field: &'static str, value: &[u8]) -> StorageResult<()> {
if value.is_empty() {
return Err(StorageError::InvalidData {
entity: field,
detail: "must not be empty".to_string(),
});
}
if value.len() > 20 {
return Err(StorageError::InvalidData {
entity: field,
detail: "must be at most 20 octets".to_string(),
});
}
if value.len() > 1 && value[0] == 0 {
return Err(StorageError::InvalidData {
entity: field,
detail: "must be minimal big-endian without leading zeros".to_string(),
});
}
Ok(())
}
pub(super) fn validate_sha256_digest_bytes(field: &'static str, value: &[u8]) -> StorageResult<()> {
if value.len() != 32 {
return Err(StorageError::InvalidData {
entity: field,
detail: format!("must be 32 bytes, got {}", value.len()),
});
}
Ok(())
}
pub(super) fn validate_fixed_len_bytes(
field: &'static str,
value: &[u8],
expected_len: usize,
) -> StorageResult<()> {
if value.len() != expected_len {
return Err(StorageError::InvalidData {
entity: field,
detail: format!("must be {expected_len} bytes, got {}", value.len()),
});
}
Ok(())
}
pub(super) fn validate_sorted_unique_fixed_len_bytes(
field: &'static str,
values: &[Vec<u8>],
expected_len: usize,
) -> StorageResult<()> {
for value in values {
validate_fixed_len_bytes(field, value, expected_len)?;
}
for window in values.windows(2) {
if window[0] >= window[1] {
return Err(StorageError::InvalidData {
entity: field,
detail: "must be strictly sorted and unique".to_string(),
});
}
}
Ok(())
}
pub(super) fn validate_full_der_with_tag(
field: &'static str,
der: &[u8],
expected_tag: Option<u8>,
) -> StorageResult<()> {
let (tag, _value, rem) = der_take_tlv(der).map_err(|detail| StorageError::InvalidData {
entity: field,
detail,
})?;
if !rem.is_empty() {
return Err(StorageError::InvalidData {
entity: field,
detail: "trailing bytes after DER object".to_string(),
});
}
if let Some(expected_tag) = expected_tag {
if tag != expected_tag {
return Err(StorageError::InvalidData {
entity: field,
detail: format!("unexpected tag 0x{tag:02X}, expected 0x{expected_tag:02X}"),
});
}
}
Ok(())
}
pub(super) fn parse_time(
field: &'static str,
value: &PackTime,
) -> StorageResult<time::OffsetDateTime> {
value.parse().map_err(|detail| StorageError::InvalidData {
entity: field,
detail,
})
}