kcode_k1_http_signature/
lib.rs1use ed25519_dalek::{Signature, VerifyingKey};
2use std::error::Error;
3use std::fmt::{self, Display, Formatter};
4
5const MAGIC: &[u8; 9] = b"K1-HTTP-1";
6const FIXED_ENCODED_LENGTH: usize = MAGIC.len() + 6 * 8 + 8 + 16 + 32;
7
8#[derive(Clone, Debug, Eq, Hash, PartialEq)]
9pub struct CanonicalUsername(String);
10
11impl CanonicalUsername {
12 pub fn parse(value: &str) -> Result<Self, UsernameError> {
13 if value.is_empty() {
14 return Err(UsernameError::Empty);
15 }
16 if value.len() > 64 {
17 return Err(UsernameError::TooLong);
18 }
19 if !value.is_ascii() {
20 return Err(UsernameError::NonAscii);
21 }
22 if value.bytes().any(|byte| byte.is_ascii_control()) {
23 return Err(UsernameError::ControlCharacter);
24 }
25 Ok(Self(value.to_ascii_lowercase()))
26 }
27
28 pub fn as_str(&self) -> &str {
29 &self.0
30 }
31}
32
33#[derive(Clone, Copy, Debug, Eq, PartialEq)]
34pub enum UsernameError {
35 Empty,
36 TooLong,
37 NonAscii,
38 ControlCharacter,
39}
40
41impl Display for UsernameError {
42 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
43 let message = match self {
44 Self::Empty => "username must contain at least one byte",
45 Self::TooLong => "username must contain at most 64 bytes",
46 Self::NonAscii => "username must contain only ASCII bytes",
47 Self::ControlCharacter => "username must not contain ASCII control characters",
48 };
49 formatter.write_str(message)
50 }
51}
52
53impl Error for UsernameError {}
54
55pub struct RequestBinding<'a> {
56 pub server_id: &'a str,
57 pub public_origin: &'a str,
58 pub username: &'a CanonicalUsername,
59 pub epoch: u64,
60 pub nonce: [u8; 16],
61 pub method: &'a str,
62 pub target: &'a str,
63 pub content_type: &'a str,
64 pub body_sha256: [u8; 32],
65}
66
67#[derive(Clone, Copy, Debug, Eq, PartialEq)]
68pub enum SignatureError {
69 MalformedPublicKey,
70 VerificationFailed,
71}
72
73impl Display for SignatureError {
74 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
75 let message = match self {
76 Self::MalformedPublicKey => "malformed Ed25519 public key",
77 Self::VerificationFailed => "Ed25519 signature verification failed",
78 };
79 formatter.write_str(message)
80 }
81}
82
83impl Error for SignatureError {}
84
85pub fn encode(binding: &RequestBinding<'_>) -> Vec<u8> {
86 let variable_length = binding.server_id.len()
87 + binding.public_origin.len()
88 + binding.username.as_str().len()
89 + binding.method.len()
90 + binding.target.len()
91 + binding.content_type.len();
92 let mut encoded = Vec::with_capacity(FIXED_ENCODED_LENGTH + variable_length);
93 encoded.extend_from_slice(MAGIC);
94 append_string(&mut encoded, binding.server_id);
95 append_string(&mut encoded, binding.public_origin);
96 append_string(&mut encoded, binding.username.as_str());
97 encoded.extend_from_slice(&binding.epoch.to_be_bytes());
98 encoded.extend_from_slice(&binding.nonce);
99 append_string(&mut encoded, binding.method);
100 append_string(&mut encoded, binding.target);
101 append_string(&mut encoded, binding.content_type);
102 encoded.extend_from_slice(&binding.body_sha256);
103 encoded
104}
105
106pub fn verify(
107 binding: &RequestBinding<'_>,
108 public_key: &[u8; 32],
109 signature: &[u8; 64],
110) -> Result<(), SignatureError> {
111 let verifying_key =
112 VerifyingKey::from_bytes(public_key).map_err(|_| SignatureError::MalformedPublicKey)?;
113 let parsed_signature = Signature::from_bytes(signature);
114 verifying_key
115 .verify_strict(&encode(binding), &parsed_signature)
116 .map_err(|_| SignatureError::VerificationFailed)
117}
118
119fn append_string(encoded: &mut Vec<u8>, value: &str) {
120 let length = u64::try_from(value.len()).expect("string length must fit in a u64");
121 encoded.extend_from_slice(&length.to_be_bytes());
122 encoded.extend_from_slice(value.as_bytes());
123}