examples/http_signature.v
Rama master · réplica de lectura del repositorio original.
// vtest build: !(openbsd && gcc) && !(sanitize-memory-clang || docker-ubuntu-musl)
// HTTP Message Signatures example: signs an outbound request with
// Ed25519 (using the RFC 9421 §B.1.4 test key in PEM form) and then
// verifies the result with the matching public key.
//
// Run with: v run examples/http_signature.v
import net.http
import net.http.signature
const ed25519_private_pem = '-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIJ+DYvh6SEqVTm50DFtMDoQikTmiCqirVv9mWG9qfSnF
-----END PRIVATE KEY-----'
const ed25519_public_pem = '-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAJrQLj5P/89iXES9+vFgrIy29clF9CC/oPPsw3c5D0bs=
-----END PUBLIC KEY-----'
fn main() {
demo_sign_and_verify_request()!
demo_two_signatures()!
}
// demo_sign_and_verify_request walks through the common path: the
// client signs a request before sending; the server verifies before
// processing.
fn demo_sign_and_verify_request() ! {
priv := signature.Key.from_pem(ed25519_private_pem)!.with_keyid('test-key-ed25519')
pub_key := signature.Key.from_pem(ed25519_public_pem)!
mut req := http.Request{
method: .post
url: 'https://example.com/foo'
}
req.header.add_custom('Host', 'example.com')!
req.header.add_custom('Date', 'Tue, 20 Apr 2021 02:07:55 GMT')!
req.header.add_custom('Content-Type', 'application/json')!
req.header.add_custom('Content-Length', '18')!
signature.sign_request(mut req, priv,
components: ['date', '@method', '@path', '@authority', 'content-type', 'content-length']
created: 1618884473
label: 'sig-b26'
)!
si := req.header.get_custom('Signature-Input') or { '' }
sig := req.header.get_custom('Signature') or { '' }
println('Signature-Input: ${si}')
println('Signature: ${sig}')
signature.verify_request(req, pub_key,
required_components: ['date', '@method', '@path', '@authority', 'content-type', 'content-length']
)!
println(' ✓ verified with the matching public key')
}
// demo_two_signatures shows a TLS-terminating proxy scenario: the
// client signs the original request, the proxy adds its own signature
// over the same message under a different label, and the backend
// verifies both independently.
fn demo_two_signatures() ! {
client_key := signature.Key.hmac_sha256('client-shared-secret'.bytes())!.with_keyid('client')
proxy_key := signature.Key.hmac_sha256('proxy-shared-secret'.bytes())!.with_keyid('proxy')
mut req := http.Request{
method: .get
url: 'https://api.example.com/orders/42'
}
req.header.add_custom('Host', 'api.example.com')!
req.header.add_custom('Date', 'Tue, 20 Apr 2021 02:07:55 GMT')!
// `created` defaults to `time.now().unix()` when not set —
// fine for a real client. Pinned here so the example output
// is reproducible across runs.
signature.sign_request(mut req, client_key,
components: ['@method', '@target-uri', 'date']
label: 'client-sig'
created: 1618884473
)!
signature.sign_request(mut req, proxy_key,
components: ['@method', '@authority', 'date']
label: 'proxy-sig'
created: 1618884480
)!
si := req.header.get_custom('Signature-Input') or { '' }
println('\nMerged Signature-Input: ${si}')
signature.verify_request(req, client_key,
label: 'client-sig'
required_components: ['@method', '@target-uri', 'date']
)!
signature.verify_request(req, proxy_key,
label: 'proxy-sig'
required_components: ['@method', '@authority', 'date']
)!
println(' ✓ both labelled signatures verified')
}