use crate::Error;
pub const MAX_KEY_NAME_LENGTH: usize = 64;
pub const MAX_SIGN_DATA_SIZE: usize = 1024 * 1024;
pub const MAX_PUBLIC_KEY_FILTER_LENGTH: usize = 256;
pub const MIN_PUBLIC_KEY_FILTER_LENGTH: usize = 20;
pub fn validate_key_name(key_name: &str) -> Result<(), Error> {
if key_name.is_empty() {
return Err(Error::Validation("Key name cannot be empty".to_string()));
}
let byte_len = key_name.len();
if byte_len > MAX_KEY_NAME_LENGTH {
return Err(Error::Validation(format!(
"Key name exceeds maximum length of {} bytes (got {} bytes)",
MAX_KEY_NAME_LENGTH, byte_len
)));
}
if !key_name
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
{
return Err(Error::Validation(
"Key name must contain only alphanumeric characters, hyphens, and underscores"
.to_string(),
));
}
Ok(())
}
pub fn validate_sign_data_size(data: &[u8]) -> Result<(), Error> {
let data_len = data.len();
if data_len > MAX_SIGN_DATA_SIZE {
return Err(Error::Validation(format!(
"Data to sign exceeds maximum size of {} bytes (got {} bytes)",
MAX_SIGN_DATA_SIZE, data_len
)));
}
if data_len == 0 {
return Err(Error::Validation(
"Data to sign cannot be empty".to_string(),
));
}
Ok(())
}
pub fn validate_public_key_filter(public_key: &str) -> Result<(), Error> {
if public_key.is_empty() {
return Err(Error::Validation(
"Public key filter cannot be empty".to_string(),
));
}
let trimmed = public_key.trim();
if trimmed.is_empty() {
return Err(Error::Validation(
"Public key filter cannot be empty or whitespace only".to_string(),
));
}
let no_whitespace: String = trimmed.chars().filter(|c| !c.is_whitespace()).collect();
if no_whitespace.len() < MIN_PUBLIC_KEY_FILTER_LENGTH {
return Err(Error::Validation(format!(
"Public key filter is too short (minimum {} characters, got {})",
MIN_PUBLIC_KEY_FILTER_LENGTH,
no_whitespace.len()
)));
}
if no_whitespace.len() > MAX_PUBLIC_KEY_FILTER_LENGTH {
return Err(Error::Validation(format!(
"Public key filter exceeds maximum length of {} characters (got {})",
MAX_PUBLIC_KEY_FILTER_LENGTH,
no_whitespace.len()
)));
}
let base64_chars: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
if !no_whitespace.bytes().all(|b| base64_chars.contains(&b)) {
return Err(Error::Validation(
"Public key filter contains invalid characters (must be base64 encoded)".to_string(),
));
}
let padding_count = no_whitespace
.chars()
.rev()
.take_while(|&c| c == '=')
.count();
if padding_count > 2 {
return Err(Error::Validation(
"Public key filter has invalid base64 padding (maximum 2 padding characters)"
.to_string(),
));
}
if padding_count > 0 {
let without_padding = &no_whitespace[..no_whitespace.len() - padding_count];
if without_padding.contains('=') {
return Err(Error::Validation(
"Public key filter has invalid base64 padding (padding must be at the end)"
.to_string(),
));
}
}
if no_whitespace.len() % 4 != 0 {
return Err(Error::Validation(
"Public key filter has invalid base64 format (total length must be multiple of 4)"
.to_string(),
));
}
let data_len = no_whitespace.len() - padding_count;
if data_len == 0 {
return Err(Error::Validation(
"Public key filter is invalid (empty after removing padding)".to_string(),
));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_key_names() {
assert!(validate_key_name("my-key").is_ok());
assert!(validate_key_name("my_key").is_ok());
assert!(validate_key_name("key123").is_ok());
assert!(validate_key_name("a").is_ok());
assert!(validate_key_name(&"a".repeat(64)).is_ok());
}
#[test]
fn test_invalid_key_names() {
assert!(validate_key_name("").is_err());
assert!(validate_key_name(&"a".repeat(65)).is_err());
assert!(validate_key_name("key with spaces").is_err());
assert!(validate_key_name("key@name").is_err());
assert!(validate_key_name("key.name").is_err());
assert!(validate_key_name("key/name").is_err());
assert!(validate_key_name("key\\name").is_err());
assert!(validate_key_name("key name").is_err());
assert!(validate_key_name("key-ñame").is_err());
assert!(validate_key_name("key-名字").is_err());
}
#[test]
fn test_key_name_length_in_bytes() {
assert!(validate_key_name("ñ").is_err());
let max_ascii = "a".repeat(64);
assert!(validate_key_name(&max_ascii).is_ok());
let too_long = "a".repeat(65);
assert!(validate_key_name(&too_long).is_err());
}
#[test]
fn test_valid_sign_data_sizes() {
assert!(validate_sign_data_size(&[0u8; 1]).is_ok());
assert!(validate_sign_data_size(&[0u8; 100]).is_ok());
let max_data = vec![0u8; MAX_SIGN_DATA_SIZE];
assert!(validate_sign_data_size(&max_data).is_ok());
}
#[test]
fn test_invalid_sign_data_sizes() {
assert!(validate_sign_data_size(&[]).is_err());
let too_large = vec![0u8; MAX_SIGN_DATA_SIZE + 1];
assert!(validate_sign_data_size(&too_large).is_err());
let very_large = vec![0u8; 10 * 1024 * 1024]; assert!(validate_sign_data_size(&very_large).is_err());
}
#[test]
fn test_valid_public_key_filters() {
assert!(validate_public_key_filter("dGVzdGtleTEyMzQ1Njc4OQ==").is_ok());
assert!(validate_public_key_filter("dGVzdGtleTEyMzQ1Njc=").is_ok());
assert!(
validate_public_key_filter("A3B4C5D6E7F8G9H0I1J2K3L4M5N6O7P8Q9R0S1T2U3V4W5X6Y7Z8")
.is_ok()
);
let valid_key = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE".repeat(2);
assert!(validate_public_key_filter(&valid_key).is_ok());
assert!(validate_public_key_filter(" dGVzdGtleTEyMzQ1Njc4OQ== ").is_ok());
let max_key = "A".repeat(256);
assert!(validate_public_key_filter(&max_key).is_ok());
}
#[test]
fn test_invalid_public_key_filters() {
assert!(validate_public_key_filter("").is_err());
assert!(validate_public_key_filter(" ").is_err());
assert!(validate_public_key_filter("short").is_err());
assert!(validate_public_key_filter("dGVzdA==").is_err());
let too_long = "A".repeat(MAX_PUBLIC_KEY_FILTER_LENGTH + 1);
assert!(validate_public_key_filter(&too_long).is_err());
assert!(validate_public_key_filter("dGVzdGtleTEyMzQ1Njc4OQ==@").is_err());
assert!(validate_public_key_filter("dGVzdGtleTEyMzQ1Njc4OQ==#").is_err());
assert!(validate_public_key_filter("dGVzdGtleTEyMzQ1Njc4OQ== ").is_ok());
assert!(validate_public_key_filter("dGVzdGtleTEyMzQ1Njc4OQ===").is_err()); assert!(validate_public_key_filter("dGVzdGtleTEyMzQ1Njc4O=Q=").is_err());
assert!(validate_public_key_filter("dGVzdGtleTEyMzQ1Njc4OQ").is_err()); }
}