// signature_base.mbt — Signature base construction (RFC 9421 §2.5).
//
// The signature base is the exact byte sequence that is signed. It is one
// line per covered component in order, each of the form
// `"component-id": value` followed by LF, then the final
// `"@signature-params": (...)`. There is no trailing LF. The @signature-params
// value must be byte-identical to the Signature-Input field serialization.
///|
/// The constructed signature base.
pub(all) struct SignatureBase {
/// The exact bytes that are fed to the signature algorithm.
bytes : Bytes
/// The signature base as a UTF-8 string.
text : String
/// The individual lines (including the @signature-params line).
lines : Array[String]
}
///|
/// Builds a signature base for a request.
pub fn build_request_signature_base(
request : RequestContext,
entry : SignatureInputEntry,
limits : Limits,
) -> Result[SignatureBase, HsError] {
build_signature_base(TargetRequest(request), entry, limits)
}
///|
/// Builds a signature base for a response.
pub fn build_response_signature_base(
response : ResponseContext,
entry : SignatureInputEntry,
limits : Limits,
) -> Result[SignatureBase, HsError] {
build_signature_base(TargetResponse(response), entry, limits)
}
///|
/// Builds a signature base for any target message.
pub fn build_signature_base(
target : SignTarget,
entry : SignatureInputEntry,
limits : Limits,
) -> Result[SignatureBase, HsError] {
Ok(build_signature_base_raise(target, entry, limits)) catch {
e => Err(e)
}
}
///|
/// Internal: builds the signature base, raising on failure.
fn build_signature_base_raise(
target : SignTarget,
entry : SignatureInputEntry,
limits : Limits,
) -> SignatureBase raise HsError {
if entry.covered_components.length() > limits.max_components_per_signature {
raise hs_error(
SignatureBaseConstruction,
TooManyComponents,
"too many covered components",
)
}
let lines : Array[String] = Array::new()
let seen : Array[String] = Array::new()
for cc in entry.covered_components {
let component_id = covered_component_string_raise(cc)
for s in seen {
if s == component_id {
raise hs_error(
SignatureBaseConstruction,
InvalidComponentCombination,
"duplicate component in signature base: " + component_id,
)
}
}
seen.push(component_id)
let value = match resolve_component(cc, target) {
Ok(v) => v
Err(e) => raise e
}
lines.push(component_id + ": " + value)
}
let sig_params = serialize_entry_inner_list_raise(entry)
let final_line = "\"@signature-params\": " + sig_params
lines.push(final_line)
let mut text = ""
for i, line in lines {
if i < lines.length() - 1 {
text = text + line + "\n"
} else {
text = text + line
}
}
let bytes = @utf8.encode(text)
{ bytes, text, lines }
}