16 releases

0.0.16 Aug 30, 2024
0.0.15 Apr 12, 2024
0.0.14 Mar 14, 2024
0.0.8 Feb 29, 2024

#286 in Cryptography

Download history 25/week @ 2024-08-21 200/week @ 2024-08-28 28/week @ 2024-09-04 124/week @ 2024-09-11 77/week @ 2024-09-18 115/week @ 2024-09-25 50/week @ 2024-10-02 29/week @ 2024-10-09 15/week @ 2024-10-16 9/week @ 2024-10-23 599/week @ 2024-10-30 2545/week @ 2024-11-06 1310/week @ 2024-11-13 1286/week @ 2024-11-20 1060/week @ 2024-11-27 965/week @ 2024-12-04

5,300 downloads per month
Used in httpsig-hyper

MIT license

92KB
2K SLoC

httpsig-rs

Work in Progress

httpsig httpsig License: MIT Unit Test

Implementation of IETF RFC 9421 of http message signatures.

This crates provides a basic library httpsig and its extension of hyper's http library. At this point, our library can sign and verify request and response messages of only hyper.

Supported Signature Algorithms

  • HMAC using SHA-256
  • Ed25519
  • ECDSA-P256 using SHA-256
  • ECDSA-P384 using SHA-384

- [ ] RSASSA-PSS using SHA-512

- [ ] RSASSA-PKCS1-v1_5 using SHA-256

At this point, we have no plan to support RSA signature due to the problem related to the non-constant time operation, i.e., Mervin Attack.

Usage of Extension for hyper (httpsig-hyper)

This is a case signing and verifying a signature generated with asymmetric cryptography (like EdDSA), where PUBLIC_KEY_STRING and SECRET_KEY_STRING is a public and private keys in PEM format, respectively. Generating and verifying a MAC through symmetric crypto (HMAC-SHA256) is also supported.

Signing and Verifying a Request

use http::Request;
use http_body_util::Full;
use httpsig_hyper::{prelude::*, *};

type SignatureName = String;
const COVERED_COMPONENTS: &[&str] = &["@method", "date", "content-type", "content-digest"];

/// Signer function that generates a request with a signature
async fn signer<B>(&mut req: Request<B>) -> HttpSigResult<()> {
  // build signature params that indicates objects to be signed
  let covered_components = COVERED_COMPONENTS
    .iter()
    .map(|v| message_component::HttpMessageComponentId::try_from(*v))
    .collect::<Result<Vec<_>, _>>()
    .unwrap();
  let mut signature_params = HttpSignatureParams::try_new(&covered_components).unwrap();

  // set signing/verifying key information, alg and keyid
  let secret_key = SecretKey::from_pem(SECRET_KEY_STRING).unwrap();
  signature_params.set_key_info(&secret_key);

  req
    .set_message_signature(&signature_params, &secret_key, Some("custom_sig_name"))
    .await
}

/// Validation function that verifies a request with a signature
async fn verifier<B>(req: &Request<B>) -> HttpSigResult<SignatureName> {
  let public_key = PublicKey::from_pem(PUBLIC_KEY_STRING).unwrap();
  let key_id = public_key.key_id();

  // verify signature with checking key_id
  req.verify_message_signature(&public_key, Some(&key_id)).await
}

#[tokio::main]
async fn main() {
  let mut request_from_sender = ...;
  let res = signer(request_from_sender).await;
  assert!(res.is_ok())

  // receiver verifies the request with a signature
  let verified_message = receiver(&request_from_sender).await;
  assert!(verification_res.is_ok());

  // if needed, content-digest can be verified separately
  let verified_request = request_from_sender.verify_content_digest().await;
  assert!(verified_request.is_ok());
}

Signing and Verifying a Response

use http::{Request, Response};
use http_body_util::Full;
use httpsig_hyper::{prelude::*, *};

type SignatureName = String;

/// This includes the method of the request corresponding to the request (the second element)
const COVERED_COMPONENTS: &[&str] = &["@status", "\"@method\";req", "date", "content-type", "content-digest"];

/// Signer function that generates a response with a signature from response itself and corresponding request
async fn signer<B>(&mut res: Response<B>, corresponding_req: &Request<B>) -> HttpSigResult<()> {
  // build signature params that indicates objects to be signed
  let covered_components = COVERED_COMPONENTS
    .iter()
    .map(|v| message_component::HttpMessageComponentId::try_from(*v))
    .collect::<Result<Vec<_>, _>>()
    .unwrap();
  let mut signature_params = HttpSignatureParams::try_new(&covered_components).unwrap();

  // set signing/verifying key information, alg and keyid
  let secret_key = SecretKey::from_pem(SECRET_KEY_STRING).unwrap();
  signature_params.set_key_info(&secret_key);

  req
    .set_message_signature(&signature_params, &secret_key, Some("custom_sig_name"), Some(corresponding_req))
    .await
}

/// Validation function that verifies a response with a signature from response itself and sent request
async fn verifier<B>(res: &Response<B>, sent_req: &Request<B>) -> HttpSigResult<SignatureName> {
  let public_key = PublicKey::from_pem(PUBLIC_KEY_STRING).unwrap();
  let key_id = public_key.key_id();

  // verify signature with checking key_id
  res.verify_message_signature(&public_key, Some(&key_id), Some(sent_req)).await
}

Examples

See ./httpsig-hyper/examples for detailed examples with hyper extension.

Dependencies

~8MB
~148K SLoC