RFC 9421 HTTP Message Signatures canonicalization, HMAC signing, verification, and policy toolkit for MoonBit.
Dependencies
Project/module: xiguaAp6y3/moon-httpsig Repository: https://github.com/xiguaAp6y3/moon-httpsig Version: 0.1.0 License: Apache-2.0
| 功能 | 状态 |
|---|---|
| HTTP 消息模型(请求/响应/Header/Trailer) | 已实现并测试 |
| RFC 9651 子集解析与序列化 | 已实现并测试 |
| 派生组件解析(含 @query-param、@status、req) | 已实现并测试 |
| 字段组件解析(sf/bs/key/tr/req) | 已实现并测试 |
| 签名基构造(逐字节) | 已实现并测试 |
| HMAC-SHA256 签名/验签 | 已实现并测试 |
| 常量时间比较 | 已实现并测试 |
| 验证策略(白名单/时间/required/keyid/nonce/tag) | 已实现并测试 |
| 防重放(NonceStore) | 已实现并测试 |
| Content-Digest 绑定 | 已实现并测试 |
| 多签名策略 | 已实现并测试 |
| CLI httpsig-tool | 已实现并测试 |
| 6 个可执行示例 | 已实现并测试 |
| HTTP/1.1 文本适配器 | 已实现并测试 |
| rsa-pss-sha512 / ecdsa-* / ed25519 | 未实现(返回 AlgorithmNotAllowed) |
| 远程 Key Resolver / JWKS / DID | 不支持(按设计禁止) |
git clone https://github.com/xiguaAp6y3/moon-httpsig.git
cd moon-httpsig
moon add gmlewis/sha256 # 已写入 moon.mod,无需重复执行
moon test # 运行全部测试(默认目标)
moon run cmd/httpsig-tool -- --helpmoon check --target wasm-gc && moon build --target wasm-gc && moon test --target wasm-gc
moon check --target js && moon build --target js && moon test --target js
moon check --target native && moon build --target native && moon test --target nativepowershell -ExecutionPolicy Bypass -File scripts\verify_all.ps1let options : SignOptions = {
label: "sig1",
components: [
covered_derived(DerivedComponent::Method),
covered_field("content-type"),
],
parameters: params, // created/keyid/...
algorithm: "hmac-sha256",
key,
}
let signed = sign_request(request, options, Limits::default()).unwrap()
// signed.signature_input / signed.signature / signed.signature_baselet report = verify_request(
request,
signed.signature_input,
signed.signature,
resolver, // InMemoryKeyResolver(生产环境应替换为共享存储)
policy, // VerificationPolicy
clock, // FixedClock / SystemClock
nonces, // InMemoryNonceStore
Limits::default(),
MultiSignaturePolicy::AnyValid,
).unwrap()
// report.verified / report.rejectedlet req_component = covered_derived_with_params(
DerivedComponent::Method,
{ sf: false, key: None, bs: false, tr: false, req: true, name: None },
)
let signed = sign_response(response, options_with(req_component), Limits::default()).unwrap()moon run cmd/httpsig-tool -- --help
moon run cmd/httpsig-tool -- --version
moon run cmd/httpsig-tool -- parse-input --signature-input 'sig1=("@method" "@target-uri");created=1618884473;keyid="k1"'
moon run cmd/httpsig-tool -- parse-signature --signature 'sig1=:dGVzdA==:'
moon run cmd/httpsig-tool -- parse-accept --accept-signature 'a=("@method");created;keyid="k"'
moon run cmd/httpsig-tool -- build-base --method POST --path /foo --header 'content-type=application/json' --signature-input 'sig1=("@method" "content-type");created=1618884473;keyid="k1";alg="hmac-sha256"'
moon run cmd/httpsig-tool -- sign-hmac --method POST --path /foo --header 'content-type=application/json' --secret-hex 736563726574 --keyid k1 --created 1700000000 --component @method
moon run cmd/httpsig-tool -- verify-hmac --method POST --path /foo --header 'content-type=application/json' --secret-hex 736563726574 --keyid k1 --now 1700000000 --signature-input 'sig1=("@method");created=1700000000;keyid="k1";alg="hmac-sha256"' --signature 'sig1=:...:'
moon run cmd/httpsig-tool -- inspect --signature-input 'sig1=("@method");created=1618884473;keyid="k1"'
moon run cmd/httpsig-tool -- check-policy --signature-input 'sig1=("@method");created=1618884473;keyid="k1"' --required-component @method
moon run cmd/httpsig-tool -- rfc-examplemoon run examples/sign_request
moon run examples/verify_request
moon run examples/sign_response
moon run examples/multiple_signatures
moon run examples/replay_policy
moon run examples/content_digest_binding├── cmd/httpsig-tool/ CLI
├── adapters/http11/ HTTP/1.1 文本适配器
├── docs/ 文档
├── examples/ 6 个可执行示例
├── scripts/ 行数统计、fixture 生成/校验、一键验证
├── testdata/rfc9421/ RFC 测试向量
├── moon.mod / moon.pkg
└── *.mbt 核心库pub(open) trait Clock {
fn now_unix_seconds(Self) -> Int64
}pub(open) trait NonceStore {
fn check_and_store(Self, keyid : String, nonce : String, expires : Int64?) -> Result[Unit, HsError]
}pub(open) trait SignatureAlgorithm {
fn name(Self) -> String
fn sign(Self, Bytes, KeyMaterial) -> Result[Bytes, HsError]
fn verify(Self, Bytes, Bytes, KeyMaterial) -> Result[Bool, HsError]
}pub(all) struct AcceptSignatureEntry {
label : String
covered_components : Array[CoveredComponent]
request : AcceptSignatureRequest
}pub(all) struct AcceptSignatureRequest {
request_created : Bool
request_expires : Bool
keyid : String?
alg : String?
nonce : String?
tag : String?
extensions : Array[SfParameter]
}impl DigestBinding for CallbackDigestBindingfn validate(self : CallbackDigestBinding, headers : OrderedHeaders, body : Bytes) -> Result[Unit, HsError]fn CallbackDigestBinding::new(callback : (OrderedHeaders, Bytes) -> Result[Unit, HsError]) -> CallbackDigestBindingpub(all) struct ComponentParameters {
sf : Bool
key : String?
bs : Bool
tr : Bool
req : Bool
name : String?
}pub(all) enum CoveredComponent {
Derived(DerivedComponent, ComponentParameters)
Field(FieldComponent)
}pub(all) enum DerivedComponent {
SignatureParams
Method
TargetUri
Authority
Scheme
RequestTarget
Path
Query
QueryParam(String)
Status
}pub struct FixedClock {
fixed : Int64
}impl Clock for FixedClockpub(all) struct HeaderField {
name : String
value : String
trailer : Bool
}pub enum HmacSha256 {
HmacSha256
}impl SignatureAlgorithm for HmacSha256fn verify(_self : HmacSha256, message : Bytes, signature : Bytes, key : KeyMaterial) -> Result[Bool, HsError]fn HmacSha256::verify_hmac(_self : HmacSha256, message : Bytes, signature : Bytes, key : Bytes) -> Boolpub(all) enum HsErrorKind {
UnexpectedEnd
UnexpectedByte(Byte)
InvalidHeaderName
InvalidHeaderValue
InvalidMethod
InvalidScheme
InvalidAuthority
InvalidPath
InvalidStatus
DuplicateLabel
MissingSignatureInput
MissingSignature
LabelMismatch
InvalidSignatureInput
InvalidSignatureField
InvalidAcceptSignature
InvalidCoveredComponent
UnsupportedDerivedComponent
UnsupportedComponentParameter
MissingComponent
InvalidComponentCombination
InvalidStructuredField
InvalidBase64
InvalidInteger
InvalidTimestamp
CreatedInFuture
SignatureExpired
SignatureTooOld
MissingKeyId
KeyIdTooLong
KeyNotFound
AlgorithmMissing
AlgorithmNotAllowed
AlgorithmMismatch
InvalidKeyMaterial
SignatureMismatch
MissingRequiredComponent
DuplicateNonce
NonceRequired
NonceTooLong
InvalidTag
ContentDigestRequired
ContentDigestNotCovered
ContentDigestInvalid
InputTooLarge
TooManyHeaders
TooManySignatures
TooManyComponents
TooManyParameters
SerializationFailure
}pub(all) enum HsErrorStage {
MessageConstruction
StructuredFieldParsing
SignatureInputParsing
SignatureFieldParsing
ComponentResolution
SignatureBaseConstruction
Signing
KeyResolution
PolicyValidation
CryptographicVerification
ReplayProtection
DigestBinding
}impl KeyResolver for InMemoryKeyResolverfn InMemoryKeyResolver::add(self : InMemoryKeyResolver, record : KeyRecord, limits : Limits) -> Result[Unit, HsError]impl NonceStore for InMemoryNonceStorefn check_and_store(self : InMemoryNonceStore, keyid : String, nonce : String, expires : Int64?) -> Result[Unit, HsError]pub(all) enum KeyMaterial {
SharedSecret(Bytes)
ExternalKey(String)
}pub(all) struct Limits {
max_header_count : Int
max_header_name_bytes : Int
max_header_value_bytes : Int
max_signature_field_bytes : Int
max_signature_count : Int
max_components_per_signature : Int
max_parameter_count : Int
max_keyid_bytes : Int
max_nonce_bytes : Int
max_tag_bytes : Int
max_body_bytes_for_digest : Int
}pub(all) enum MultiSignaturePolicy {
AnyValid
AllPresentValid
SpecificLabel(String)
}pub enum NoDigestBinding {
NoDigestBinding
}impl DigestBinding for NoDigestBindingfn validate(_self : NoDigestBinding, _headers : OrderedHeaders, _body : Bytes) -> Result[Unit, HsError]fn OrderedHeaders::append(self : OrderedHeaders, name : String, value : String) -> Result[Unit, HsError]fn OrderedHeaders::append_field(self : OrderedHeaders, field : HeaderField) -> Result[Unit, HsError]fn OrderedHeaders::append_trailer(self : OrderedHeaders, name : String, value : String) -> Result[Unit, HsError]fn OrderedHeaders::combined_value_with(self : OrderedHeaders, name : String, joiner : String) -> Result[String, HsError]fn OrderedHeaders::set(self : OrderedHeaders, name : String, value : String) -> Result[Unit, HsError]pub struct Prng {
state : Int64
}fn Prng::generate_sign_options(_self : Prng, case : Int, components : Array[CoveredComponent]) -> SignOptionspub(all) struct RequestContext {
method : String
scheme : String
authority : String
path : String
query : String?
headers : OrderedHeaders
body : Bytes?
}fn RequestContext::new(method : String, scheme : String, authority : String, path : String, query : String?, headers : OrderedHeaders, body : Bytes?, limits : Limits) -> Result[RequestContext, HsError]fn RequestContext::new_default(method : String, scheme : String, authority : String, path : String, query : String?, headers : OrderedHeaders, body : Bytes?) -> Result[RequestContext, HsError]impl DigestBinding for RequireCoveredContentDigestfn validate(self : RequireCoveredContentDigest, headers : OrderedHeaders, body : Bytes) -> Result[Unit, HsError]fn RequireCoveredContentDigest::new(covered : Array[CoveredComponent], limits : Limits) -> RequireCoveredContentDigestpub(all) struct ResponseContext {
status : Int
headers : OrderedHeaders
body : Bytes?
related_request : RequestContext?
}fn ResponseContext::new(status : Int, headers : OrderedHeaders, body : Bytes?, related_request : RequestContext?, limits : Limits) -> Result[ResponseContext, HsError]fn ResponseContext::new_default(status : Int, headers : OrderedHeaders, body : Bytes?, related_request : RequestContext?) -> Result[ResponseContext, HsError]pub(all) enum SfBareItem {
SfString(String)
SfToken(String)
SfInteger(Int64)
SfByteSequence(Bytes)
SfBoolean(Bool)
}pub(all) struct SignOptions {
label : String
components : Array[CoveredComponent]
parameters : SignatureParameters
algorithm : String
key : KeyRecord
}fn sign(self : SignatureAlgorithmProvider, message : Bytes, key : KeyMaterial) -> Result[Bytes, HsError]fn verify(self : SignatureAlgorithmProvider, message : Bytes, signature : Bytes, key : KeyMaterial) -> Result[Bool, HsError]pub(all) struct SignatureEntry {
label : String
value : Bytes
}pub(all) struct SignatureInputEntry {
label : String
covered_components : Array[CoveredComponent]
parameters : SignatureParameters
}pub(all) struct SignatureParameters {
created : Int64?
expires : Int64?
keyid : String?
alg : String?
nonce : String?
tag : String?
extensions : Array[SfParameter]
}pub(all) struct SignedFields {
signature_input : String
signature : String
signature_base : SignatureBase
}pub enum SystemClock {
SystemClock
}impl Clock for SystemClockpub struct UnsupportedAlgorithm {
algorithm : String
}impl SignatureAlgorithm for UnsupportedAlgorithmfn verify(self : UnsupportedAlgorithm, message : Bytes, signature : Bytes, key : KeyMaterial) -> Result[Bool, HsError]fn UnsupportedAlgorithm::sign(self : UnsupportedAlgorithm, _message : Bytes, _key : KeyMaterial) -> Result[Bytes, HsError]fn UnsupportedAlgorithm::verify(self : UnsupportedAlgorithm, _message : Bytes, _signature : Bytes, _key : KeyMaterial) -> Result[Bool, HsError]pub(all) struct VerificationPolicy {
allowed_algorithms : Array[String]
required_components : Array[CoveredComponent]
require_created : Bool
require_expires : Bool
require_keyid : Bool
require_nonce : Bool
max_signature_age_seconds : Int64?
allowed_clock_skew_seconds : Int64
max_future_seconds : Int64
expected_tag : String?
reject_unknown_parameters : Bool
require_content_digest : Bool
require_content_digest_covered : Bool
}pub(all) struct VerificationReport {
verified : Array[VerifiedSignature]
rejected : Array[RejectedSignature]
}pub(all) struct VerifiedSignature {
label : String
keyid : String
algorithm : String
covered_components : Array[CoveredComponent]
created : Int64?
expires : Int64?
nonce : String?
tag : String?
}fn append_signature(field : SignatureField, entry : SignatureEntry) -> Result[SignatureField, HsError]fn append_signature_input(input : SignatureInput, entry : SignatureInputEntry) -> Result[SignatureInput, HsError]fn assert_bytes_eq(a : Bytes, b : Bytes, what : String) -> Unitfn base64_encode_bytes(bytes : Bytes) -> Stringfn build_request_signature_base(request : RequestContext, entry : SignatureInputEntry, limits : Limits) -> Result[SignatureBase, HsError]fn build_response_signature_base(response : ResponseContext, entry : SignatureInputEntry, limits : Limits) -> Result[SignatureBase, HsError]fn build_signature_base(target : SignTarget, entry : SignatureInputEntry, limits : Limits) -> Result[SignatureBase, HsError]fn constant_time_equal(a : Bytes, b : Bytes) -> Boolfn contains_cr_or_lf(s : String) -> Boolfn covered_derived_with_params(d : DerivedComponent, parameters : ComponentParameters) -> CoveredComponentfn get_signature_input(input : SignatureInput, label : String) -> Result[SignatureInputEntry, HsError]fn hex_encode_bytes(bytes : Bytes) -> Stringfn hmac_sha256(key : Bytes, message : Bytes) -> Bytesfn is_tchar(s : String) -> Boolfn is_valid_field_name(s : String) -> Boolfn make_test_request(method : String, path : String, query : String?, headers : OrderedHeaders) -> Result[RequestContext, HsError]fn make_verify_ctx(key : KeyRecord, created : Int64) -> (InMemoryKeyResolver, VerificationPolicy, FixedClock, InMemoryNonceStore, Limits)fn parse_accept_request_parameters(params : Array[SfParameter]) -> Result[AcceptSignatureRequest, HsError]fn parse_covered_components(items : Array[SfItem], max_components : Int) -> Result[Array[CoveredComponent], HsError]fn parse_int64_cli(s : String) -> Int64?fn parse_sf_dictionary_string(input : String, limits : Limits) -> Result[Array[SfDictionaryEntry], HsError]fn resolve_derived_component(component : DerivedComponent, target : SignTarget, use_req : Bool) -> Result[String, HsError]fn resolve_field_component(component : FieldComponent, target : SignTarget) -> Result[String, HsError]fn sha256_raw(data : Bytes) -> Bytesfn sign_request(request : RequestContext, options : SignOptions, limits : Limits) -> Result[SignedFields, HsError]fn sign_response(response : ResponseContext, options : SignOptions, limits : Limits) -> Result[SignedFields, HsError]fn sign_target(target : SignTarget, options : SignOptions, limits : Limits) -> Result[SignedFields, HsError]fn sign_test_request(components : Array[CoveredComponent], keyid : String, created : Int64) -> Result[SignedFields, HsError]fn strip_ows(s : String) -> Stringfn validate_content_digest(covered : Array[CoveredComponent], headers : OrderedHeaders, body : Bytes, limits : Limits) -> Result[Unit, HsError]fn validate_signature_labels(input : SignatureInput, field : SignatureField) -> Result[Unit, HsError]fn verify_label(target : SignTarget, entry : SignatureInputEntry, signature_bytes : Bytes, resolver : InMemoryKeyResolver, policy : VerificationPolicy, clock : FixedClock, nonce_store : InMemoryNonceStore, limits : Limits) -> Result[VerifiedSignature, HsError]fn verify_label_outcome(ctx : (InMemoryKeyResolver, VerificationPolicy, FixedClock, InMemoryNonceStore, Limits), target : SignTarget, entry : SignatureInputEntry, sig : Bytes) -> Stringfn verify_label_with_ctx(ctx : (InMemoryKeyResolver, VerificationPolicy, FixedClock, InMemoryNonceStore, Limits), target : SignTarget, entry : SignatureInputEntry, sig : Bytes) -> Result[VerifiedSignature, HsError]fn verify_outcome(ctx : (InMemoryKeyResolver, VerificationPolicy, FixedClock, InMemoryNonceStore, Limits), req : RequestContext, input : String, sig : String) -> Stringfn verify_request(request : RequestContext, signature_input : String, signature : String, resolver : InMemoryKeyResolver, policy : VerificationPolicy, clock : FixedClock, nonce_store : InMemoryNonceStore, limits : Limits, multi : MultiSignaturePolicy) -> Result[VerificationReport, HsError]fn verify_response(response : ResponseContext, signature_input : String, signature : String, resolver : InMemoryKeyResolver, policy : VerificationPolicy, clock : FixedClock, nonce_store : InMemoryNonceStore, limits : Limits, multi : MultiSignaturePolicy) -> Result[VerificationReport, HsError]fn verify_target(target : SignTarget, signature_input : String, signature : String, resolver : InMemoryKeyResolver, policy : VerificationPolicy, clock : FixedClock, nonce_store : InMemoryNonceStore, limits : Limits, multi : MultiSignaturePolicy) -> Result[VerificationReport, HsError]fn verify_test_request(request : RequestContext, signed : SignedFields, key : KeyRecord, now : Int64) -> Result[VerificationReport, HsError]fn verify_with_ctx(ctx : (InMemoryKeyResolver, VerificationPolicy, FixedClock, InMemoryNonceStore, Limits), req : RequestContext, input : String, sig : String, multi : MultiSignaturePolicy) -> Result[VerificationReport, HsError]RFC 9421 HTTP Message Signatures canonicalization, HMAC signing, verification, and policy toolkit for MoonBit.
Dependencies