#json #json-object #jwt #authentication

no-std jose

A JSON Object Signing and Encryption implementation

1 unstable release

new 0.0.1 Jan 31, 2025

#95 in #json-object

MIT/Apache

305KB
5.5K SLoC

JOSE: JSON Object Signing and Encryption

JOSE is mostly known for so called Jwt. Jwt stands for JsonWebToken and is actually only a subset of the whole JOSE specifications.

In the majority of cases, JsonWebToken are a signed token that act as small certificates for authentication. However, JOSE is way more complex than just a simple signed certificate.

Because of this complexity, most implementation are incomplete. Even worse, bad library design often lead to critical security vulnerabilities, such as the famous none Algorithm bug.

In order to prevent such vulnerabilities, this crate uses the Rust type system to make illegal states impossible and marks insecure or unverified /! states directly on the types themselves.

Example

Create a basic Jwt

In this crate, cryptographic keys are always represented as a type, never as a String or some other format that would lose essential information about a key, such as its algorithm.

One should usually use the JsonWebKey type, because it abstracts away the complexity of different key types. Your application should not care what kind of key it gets.

extern crate alloc;
use alloc::string::ToString;

use jose::{
    format::{Compact, DecodeFormat},
    jwk::{JwkSigner, JwkVerifier},
    jws::Unverified,
    jwt::Claims,
    policy::{Checkable, StandardPolicy},
    JsonWebKey, Jwk, Jwt, UntypedAdditionalProperties,
};


// This is a serialized asymmetric key. The private key part is stored in
// the `d` parameter
let serialized_private_key = r#"
{
    "crv": "P-256",
    "kty": "EC",
     "x": "1uiXGPoQ3eLR3VOsCfnx1YzIJZGUQLbVfbl1CpCHcs0",
     "y": "danaoyQqKi48vlB2jnCoFmq3PdIbYwIRJyNKWiindZM",
     "d": "eLGzm5zd242okyN9SQBvmaC_4EPvASCgMhFgwtBvf3k",
     "alg": "ES256"
}
"#;
let private_key: JsonWebKey = serde_json::from_str(serialized_private_key).expect("valid key");
// A JsonWebToken is just a specific version of a JsonWebSignature where
// the payload is defined as a JSON Object
// This JSON Object is easiest represented by the Claims struct of this crate.
// UntypedAdditionalProperties is used to keep any members of the JSON Object
// that are not directly handled in this implementation.
let claims: Claims<UntypedAdditionalProperties> = Claims {
    subject: Some("Erik".to_string()),
    issuer: Some("AuthenticationProvider".to_string()),
    // sets the rest to None
    ..Default::default()
};
// because the key has the private component, it is able to create signatures
assert!(private_key.is_signing_key());
// A policy is used to ensure a key uses secure algorithm, key size etc.
let policy = StandardPolicy::default();
// a signer can create signatures
let mut signer: JwkSigner = private_key
    .check(&policy)
    .expect("valid key")
    .try_into()
    .expect("key has algorithm");
let jwt = Jwt::builder_jwt().build(claims).expect("header valid");
// this creates a signed JWT that can be serialized.
let signed_jwt = jwt.sign(&mut signer).unwrap();
let serialized = signed_jwt.to_string();
// Note: not all signatures are deterministic, meaning their signature is
// not always the same. In this case, the signature is deterministic
assert_eq!(
    serialized,
    "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJBdXRoZW50aWNhdGlvblByb3ZpZGVyIiwic3ViIjoiRXJpayJ9.\
     UzoUnXXs9QF1FrQyAbs2PwO9GSAEc3r4gx7BKjeie0UzcmD0YsZ7DRjhiYXYNhq3XPRQ0E0_bIGA4OH9xjPNlw"
);
// The serialized public key. Note the missing `d` parameter
let serialized_public_key = r#"
{
    "crv": "P-256",
    "kty": "EC",
     "x": "1uiXGPoQ3eLR3VOsCfnx1YzIJZGUQLbVfbl1CpCHcs0",
     "y": "danaoyQqKi48vlB2jnCoFmq3PdIbYwIRJyNKWiindZM",
     "alg": "ES256"
}
"#;

let public_key: Jwk = serde_json::from_str(&serialized_public_key).unwrap();

// This key can only verify signatures
assert_eq!(public_key.is_signing_key(), false);

let mut verifier: JwkVerifier = public_key
    .check(&policy)
    .unwrap()
    .try_into()
    .unwrap();

// deserialize the JWT
let encoded: Compact = serialized.parse().expect("valid format");

// decode the JWT
// Note: this JWT is not yet verified!
let unverified: Unverified<Jwt<UntypedAdditionalProperties>> =
    Jwt::decode(encoded).expect("valid JWT");

// now the JWT is verified and we can trust the payload
let verified = unverified.verify(&mut verifier).expect("valid signature");
assert_eq!(verified.payload().subject, Some("Erik".to_string()));

This crate implements various RFCs related to JOSE:

  • RFC 7519: JSON Web Token (JWT)
  • RFC 7518: JSON Web Algorithms (JWA)
  • RFC 7517: JSON Web Key (JWK)
  • RFC 7516: JSON Web Encryption (JWE) (Encryption is not supported in this first release)
  • RFC 7515: JSON Web Signature (JWS)

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~13MB
~253K SLoC