5 unstable releases

0.3.0 Sep 20, 2024
0.2.1 Aug 1, 2024
0.2.0 Jul 26, 2024
0.1.1 Sep 25, 2023
0.1.0 Dec 15, 2022

#1098 in Cryptography

Download history 765/week @ 2024-08-21 904/week @ 2024-08-28 1169/week @ 2024-09-04 1244/week @ 2024-09-11 1521/week @ 2024-09-18 1223/week @ 2024-09-25 977/week @ 2024-10-02 1182/week @ 2024-10-09 756/week @ 2024-10-16 807/week @ 2024-10-23 905/week @ 2024-10-30 658/week @ 2024-11-06 886/week @ 2024-11-13 786/week @ 2024-11-20 719/week @ 2024-11-27 601/week @ 2024-12-04

3,170 downloads per month
Used in 42 crates (18 directly)

Apache-2.0 and maybe GPL-3.0

320KB
7K SLoC

JSON Web Signature (JWS) implementation following RFC 7515 and RFC 7797 (Unencoded Payload Option).

Usage

The entry point to store and verify JWS is the [&Jws][Jws] type, borrowing the JWS, just like a &str borrows a text string. The JwsBuf type is the owned version of this type, owning the JWS, just like a String owns a text string.

Decoding & Verification

Use JwsSlice::verify to decode a JWS.

use serde_json::json;
use ssi_jwk::JWK;
use ssi_jws::Jws;

let jws = Jws::new(b"eyJhbGciOiJFUzI1NiJ9.cGF5bG9hZA.LW6XkHmgfNnb2CA-2qdeMVGpekAoxRNsAHoeLpnton3QMaQ3dMj-5G9SlP8dHj7cHf2HtRPdy6-9LbxYKvumKw").unwrap();

let jwk: JWK = json!({
    "kty": "EC",
    "use": "sig",
    "crv": "P-256",
    "x": "dxdB360AJqJFYhdctoKZD_a_P6vLGAxtEVaCLnyraXQ",
    "y": "iH6o0l5AECsfRuEw2Eghbrp-6Fob3j98-1Cbe1YOmwM",
    "alg": "ES256"
}).try_into().unwrap();

assert!(jws.verify(&jwk).await.unwrap().is_ok());

Internally JwsSlice::verify uses JwsSlice::decode to decode the JWS, then DecodedJws::verify to validate the signature.

let decoded_jws = jws.to_decoded().unwrap();
let verifiable_jws = decoded_jws.into_verifiable().await.unwrap();
assert_eq!(verifiable_jws.verify(&jwk).await.unwrap().is_ok());

You can use this method to decode the payload before the verification (using DecodedJws::try_map for instance) so it can be verified along the signature.

Signature

Use the JwsPayload::sign method to sign a payload into a compact JWS.

use serde_json::json;
use ssi_jwk::JWK;
use ssi_jws::JwsPayload;

let jwk: JWK = json!({
    "kty": "EC",
    "d": "3KSLs0_obYeQXfEI9I3BBH5y7aOm028bEx3rW6i5UN4",
    "use": "sig",
    "crv": "P-256",
    "x": "dxdB360AJqJFYhdctoKZD_a_P6vLGAxtEVaCLnyraXQ",
    "y": "iH6o0l5AECsfRuEw2Eghbrp-6Fob3j98-1Cbe1YOmwM",
    "alg": "ES256"
}).try_into().unwrap();

let jwt = "payload".sign(&jwk).await.unwrap();
assert_eq!(jwt, "eyJhbGciOiJFUzI1NiJ9.cGF5bG9hZA.LW6XkHmgfNnb2CA-2qdeMVGpekAoxRNsAHoeLpnton3QMaQ3dMj-5G9SlP8dHj7cHf2HtRPdy6-9LbxYKvumKw")

URL safety and JWS types

RFC 7515 originally defines JWS as URL safe strings due to the payload being base64 URL-safe encoded. However, RFC 7797 introduces a b64 header option that makes this encoding optional. If set to false, the JWS may not be URL-safe. In fact it may not be UTF-8 encoded at all.

To deal with these different encoding expectations this library provides three families of types for representing JWS:

  • [Jws] and JwsBuf: This is the most common type family that follows RFC 7515 to the letter, expecting an URL-safe JWS. It is still possible to use the b64 header to embed unencoded payloads but those payloads must use URL-safe base64 bytes/characters.
  • JwsStr and JwsString: This family relaxes the URL-safe payload constraint. Unencoded payloads may use bytes outside of the URL-safe base64 alphabet, but they must be valid UTF-8 strings. This guarantees that the overall JWS is a valid UTF-8 string, even if it is not URL-safe.
  • JwsSlice and JwsVec: This family does not imposes any constraint on unencoded payloads. There is no guaranty that the overall JWS will be an UTF-8 string.

Dependencies

~23–40MB
~676K SLoC