• Stars
    star
    222
  • Rank 179,176 (Top 4 %)
  • Language
    Rust
  • License
    Other
  • Created about 4 years ago
  • Updated 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A secure, standard-conformant, easy to use JWT implementation for Rust.

GitHub CI Docs.rs crates.io

JWT-Simple

A new JWT (JSON Web Tokens) implementation for Rust that focuses on simplicity, while avoiding common JWT security pitfalls.

jwt-simple is unopinionated and supports all commonly deployed authentication and signature algorithms:

JWT algorithm name Description
HS256 HMAC-SHA-256
HS384 HMAC-SHA-384
HS512 HMAC-SHA-512
RS256 RSA with PKCS#1v1.5 padding / SHA-256
RS384 RSA with PKCS#1v1.5 padding / SHA-384
RS512 RSA with PKCS#1v1.5 padding / SHA-512
PS256 RSA with PSS padding / SHA-256
PS384 RSA with PSS padding / SHA-384
PS512 RSA with PSS padding / SHA-512
ES256 ECDSA over p256 / SHA-256
ES384 ECDSA over p384 / SHA-384
ES256K ECDSA over secp256k1 / SHA-256
EdDSA Ed25519

jwt-simple uses only pure Rust implementations, and can be compiled out of the box to WebAssembly/WASI. It is fully compatible with Fastly's Compute@Edge service.

Important: JWT's purpose is to verify that data has been created by a party knowing a secret key. It does not provide any kind of confidentiality: JWT data is simply encoded as BASE64, and is not encrypted.

Usage

cargo.toml:

[dependencies]
jwt-simple = "0.10"

Rust:

use jwt_simple::prelude::*;

Errors are returned as jwt_simple::Error values (alias for the Error type of the thiserror crate).

Authentication (symmetric, HS* JWT algorithms) example

Authentication schemes use the same key for creating and verifying tokens. In other words, both parties need to ultimately trust each other, or else the verifier could also create arbitrary tokens.

Keys and tokens creation

Key creation:

use jwt_simple::prelude::*;

// create a new key for the `HS256` JWT algorithm
let key = HS256Key::generate();

A key can be exported as bytes with key.to_bytes(), and restored with HS256Key::from_bytes().

Token creation:

/// create claims valid for 2 hours
let claims = Claims::create(Duration::from_hours(2));
let token = key.authenticate(claims)?;

-> Done!

Token verification

let claims = key.verify_token::<NoCustomClaims>(&token, None)?;

-> Done! No additional steps required.

Key expiration, start time, authentication tags, etc. are automatically verified. The function fails with JWTError::InvalidAuthenticationTag if the authentication tag is invalid for the given key.

The full set of claims can be inspected in the claims object if necessary. NoCustomClaims means that only the standard set of claims is used by the application, but application-defined claims can also be supported.

Extra verification steps can optionally be enabled via the ValidationOptions structure:

let mut options = VerificationOptions::default();
// Accept tokens that will only be valid in the future
options.accept_future = true;
// Accept tokens even if they have expired up to 15 minutes after the deadline,
// and/or they will be valid within 15 minutes.
options.time_tolerance = Some(Duration::from_mins(15));
// Reject tokens if they were issued more than 1 hour ago
options.max_validity = Some(Duration::from_hours(1));
// Reject tokens if they don't include an issuer from that set
options.allowed_issuers = Some(HashSet::from_strings(&["example app"]));

// see the documentation for the full list of available options

let claims = key.verify_token::<NoCustomClaims>(&token, Some(options))?;

Note that allowed_issuers and allowed_audiences are not strings, but sets of strings (using the HashSet type from the Rust standard library), as the application can allow multiple return values.

Signatures (asymmetric, RS*, PS*, ES* and EdDSA algorithms) example

A signature requires a key pair: a secret key used to create tokens, and a public key, that can only verify them.

Always use a signature scheme if both parties do not ultimately trust each other, such as tokens exchanged between clients and API providers.

Key pairs and tokens creation

Key creation:

ES256

use jwt_simple::prelude::*;

// create a new key pair for the `ES256` JWT algorithm
let key_pair = ES256KeyPair::generate();

// a public key can be extracted from a key pair:
let public_key = key_pair.public_key();

ES384

use jwt_simple::prelude::*;

// create a new key pair for the `ES384` JWT algorithm
let key_pair = ES384KeyPair::generate();

// a public key can be extracted from a key pair:
let public_key = key_pair.public_key();

Keys can be exported as bytes for later reuse, and imported from bytes or, for RSA, from individual parameters, DER-encoded data or PEM-encoded data.

RSA key pair creation, using OpenSSL and PEM importation of the secret key:

openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
let key_pair = RS384KeyPair::from_pem(private_pem_file_content)?;
let public_key = RS384PublicKey::from_pem(public_pem_file_content)?;

Token creation and verification work the same way as with HS* algorithms, except that tokens are created with a key pair, and verified using the corresponding public key.

Token creation:

/// create claims valid for 2 hours
let claims = Claims::create(Duration::from_hours(2));
let token = key_pair.sign(claims)?;

Token verification:

let claims = public_key.verify_token::<NoCustomClaims>(&token, None)?;

Available verification options are identical to the ones used with symmetric algorithms.

Advanced usage

Custom claims

Claim objects support all the standard claims by default, and they can be set directly or via convenient helpers:

let claims = Claims::create(Duration::from_hours(2)).
    with_issuer("Example issuer").with_subject("Example subject");

But application-defined claims can also be defined. These simply have to be present in a serializable type (this requires the serde crate):

#[derive(Serialize, Deserialize)]
struct MyAdditionalData {
   user_is_admin: bool,
   user_country: String,
}
let my_additional_data = MyAdditionalData {
   user_is_admin: false,
   user_country: "FR".to_string(),
};

Claim creation with custom data:

let claims = Claims::with_custom_claims(my_additional_data, Duration::from_secs(30));

Claim verification with custom data. Note the presence of the custom data type:

let claims = public_key.verify_token::<MyAdditionalData>(&token, None)?;
let user_is_admin = claims.custom.user_is_admin;

Peeking at metadata before verification

Properties such as the key identifier can be useful prior to tag or signature verification in order to pick the right key out of a set.

let metadata = Token::decode_metadata(&token)?;
let key_id = metadata.key_id();
let algorithm = metadata.algorithm();
// all other standard properties are also accessible

IMPORTANT: neither the key ID nor the algorithm can be trusted. This is an unfixable design flaw of the JWT standard.

As a result, algorithm should be used only for debugging purposes, and never to select a key type. Similarly, key_id should be used only to select a key in a set of keys made for the same algorithm.

At the bare minimum, verification using HS* must be prohibited if a signature scheme was originally used to create the token.

Creating and attaching key identifiers

Key identifiers indicate to verifiers what public key (or shared key) should be used for verification. They can be attached at any time to existing shared keys, key pairs and public keys:

let public_key_with_id = public_key.with_key_id(&"unique key identifier");

Instead of delegating this to applications, jwt-simple can also create such an identifier for an existing key:

let key_id = public_key.create_key_id();

This creates an text-encoded identifier for the key, attaches it, and returns it.

If an identifier has been attached to a shared key or a key pair, tokens created with them will include it.

Mitigations against replay attacks

jwt-simple includes mechanisms to mitigate replay attacks:

  • Nonces can be created and attached to new tokens using the create_nonce() claim function. The verification procedure can later reject any token that doesn't include the expected nonce (required_nonce verification option).
  • The verification procedure can reject tokens created too long ago, no matter what their expiration date is. This prevents tokens from malicious (or compromised) signers from being used for too long.
  • The verification procedure can reject tokens created before a date. For a given user, the date of the last successful authentication can be stored in a database, and used later along with this option to reject older (replayed) tokens.

CWT (CBOR) support

The development code includes a cwt cargo feature that enables experimental parsing and validation of CWT tokens.

Please note that CWT doesn't support custom claims. The required identifiers haven't been standardized yet.

Also, the existing Rust crates for JSON and CBOR deserialization are not safe. An untrusted party can send a serialized object that requires a lot of memory and CPU to deserialize. Band-aids have been added for JSON, but with the current Rust tooling, it would be tricky to do for CBOR.

As a mitigation, we highly recommend rejecting tokens that would be too large in the context of your application. That can be done by with the max_token_length verification option.

Why yet another JWT crate

This crate is not an endorsement of JWT. JWT is an awful design, and one of the many examples that "but this is a standard" doesn't necessarily mean that it is good.

I would highly recommend PASETO or Biscuit instead if you control both token creation and verification.

However, JWT is still widely used in the industry, and remains absolutely mandatory to communicate with popular APIs.

This crate was designed to:

  • Be simple to use, even to people who are new to Rust
  • Avoid common JWT API pitfalls
  • Support features widely in use. I'd love to limit the algorithm choices to Ed25519, but other methods are required to connect to existing APIs, so just provide them (with the exception of the None signature method for obvious reasons).
  • Minimize code complexity and external dependencies
  • Automatically perform common tasks to prevent misuse. Signature verification and claims validation happen automatically instead of relying on applications.
  • Still allow power users to access everything JWT tokens include if they really need to
  • Be as portable as possible by using only Rust implementations of cryptographic primitives
  • Have no dependency on OpenSSL
  • Work out of the box in a WebAssembly environment, so that it can be used in function-as-a-service platforms.

More Repositories

1

libsodium

A modern, portable, easy to use crypto library.
C
12,131
star
2

dsvpn

A Dead Simple VPN.
C
5,068
star
3

piknik

Copy/paste anything over the network.
Go
2,342
star
4

minisign

A dead simple tool to sign files and verify digital signatures.
C
1,856
star
5

libsodium.js

libsodium compiled to Webassembly and pure JavaScript, with convenient wrappers.
HTML
899
star
6

libhydrogen

A lightweight, secure, easy-to-use crypto library suitable for constrained environments.
C
599
star
7

pure-ftpd

Pure FTP server
C
589
star
8

libsodium-php

The PHP extension for libsodium.
C
546
star
9

swift-sodium

Safe and easy to use crypto for iOS and macOS
C
518
star
10

edgedns

A high performance DNS cache designed for Content Delivery Networks
Rust
490
star
11

libpuzzle

A library to quickly find visually similar images
C
262
star
12

iptoasn-webservice

Web service to map IP addresses to AS information, using iptoasn.com
Rust
254
star
13

dnsblast

A simple and stupid load testing tool for DNS resolvers
C
233
star
14

as-wasi

An AssemblyScript API layer for WASI system calls.
TypeScript
232
star
15

wasm-crypto

A WebAssembly (via AssemblyScript) set of cryptographic primitives for building authentication and key exchange protocols.
TypeScript
214
star
16

rust-bloom-filter

A fast Bloom filter implementation in Rust
Rust
187
star
17

encpipe

The dum^H^H^Hsimplest encryption tool in the world.
C
182
star
18

Pincaster

A fast persistent nosql database with a HTTP/JSON interface, not only for geographical data.
C
171
star
19

UCarp

UCARP allows a couple of hosts to share common virtual IP addresses in order to provide automatic failover. It is a portable userland implementation of the secure and patent-free Common Address Redundancy Protocol (CARP, OpenBSD's alternative to the patents-bloated VRRP).
M4
170
star
20

blacknurse

BlackNurse attack PoC
C
170
star
21

libsodium-doc

Gitbook documentation for libsodium
Shell
166
star
22

bitbar-dnscrypt-proxy-switcher

BitBar plugin to control dnscrypt-proxy usage
Shell
148
star
23

charm

A really tiny crypto library.
C
148
star
24

witx-codegen

WITX code and documentation generator for AssemblyScript, Zig, Rust and more.
Rust
134
star
25

siphash-js

A Javascript implementation of SipHash-2-4
JavaScript
122
star
26

rust-ed25519-compact

Small, wasm-friendly, zero-dependencies Ed25519 and X25519 implementation for Rust.
Rust
120
star
27

rsign2

A command-line tool to sign files and verify signatures in pure Rust.
Rust
112
star
28

go-minisign

Minisign verification library for Golang.
Go
103
star
29

rust-nats

A simple NATS client library for Rust
Rust
102
star
30

zigly

The easiest way to write services for Fastly's Compute@Edge in Zig.
Zig
84
star
31

was-not-wasm

A hostile memory allocator to make WebAssembly applications more predictable.
Rust
81
star
32

webassembly-benchmarks

Libsodium WebAssembly benchmarks results.
79
star
33

rust-minisign

A pure Rust implementation of the Minisign signature tool.
Rust
78
star
34

zig-charm

A Zig version of the Charm crypto library.
Zig
74
star
35

rust-ffmpeg-wasi

ffmpeg 7 libraries precompiled for WebAsembly/WASI, as a Rust crate.
Rust
67
star
36

openssl-wasm

OpenSSL 3 compiled for WebAssembly/WASI (up-to-date, maintained)
C
66
star
37

rust-sthash

Very fast cryptographic hashing for large messages.
Rust
65
star
38

vtun

A mirror of VTUN, with some changes
C
63
star
39

iptrap

A simple, but damn fast sinkhole
Rust
62
star
40

boringssl-wasm

BoringSSL for WebAssembly/WASI
Zig
59
star
41

libsodium-signcryption

Signcryption using libsodium.
C
59
star
42

wasmsign

A tool to add and verify digital signatures to/from WASM binaries
Rust
56
star
43

libaegis

Portable C implementations of the AEGIS family of high-performance authenticated encryption algorithms.
C
56
star
44

blobcrypt

Authenticated encryption for streams and arbitrary large files using libsodium
C
54
star
45

rust-coarsetime

Time and duration crate optimized for speed
Rust
52
star
46

zig-minisign

Minisign reimplemented in Zig.
Zig
50
star
47

rust-siphash

SipHash (2-4, 1-3 + 128 bit variant) implementations for Rust
Rust
48
star
48

6Jack

A framework for analyzing/testing/fuzzing network applications.
C
46
star
49

rust-hyperloglog

A HyperLogLog implementation in Rust.
Rust
46
star
50

libchloride

Networking layer for libsodium, based on CurveCP
C
44
star
51

rust-qptrie

A qp-trie implementation in Rust
Rust
42
star
52

minicsv

A tiny, fast, simple, single-file, BSD-licensed CSV parsing library in C.
C
39
star
53

spake2-ee

A SPAKE2+ Elligator Edition implementation for libsodium 1.0.16+
C
36
star
54

massresolver

Mass DNS resolution tool
C
36
star
55

cpace

A CPace PAKE implementation using libsodium.
C
35
star
56

aegis-X

The AEGIS-128X and AEGIS-256X high performance ciphers.
Zig
34
star
57

rust-privdrop

A simple Rust crate to drop privileges
Rust
34
star
58

PureDB

PureDB is a portable and tiny set of libraries for creating and reading constant databases.
C
33
star
59

libclang_rt.builtins-wasm32.a

The missing libclang_rt.builtins-wasm32.a file to compile to WebAssembly.
32
star
60

c-ipcrypt

ipcrypt implementation in C
C
31
star
61

rust-dnsclient

A simple and secure DNS client crate for Rust.
Rust
29
star
62

rust-xoodyak

Xoodyak, a lightweight and versatile cryptographic scheme implemented in Rust.
Rust
29
star
63

fastly-terrarium-examples

Example code you can run in Fastly Terrarium: https://www.fastlylabs.com/
C
28
star
64

libsodium-xchacha20-siv

Deterministic/nonce-reuse resistant authenticated encryption scheme using XChaCha20, implemented on libsodium.
C
28
star
65

rust-geoip

GeoIP bindings for Rust
Rust
28
star
66

rust-minisign-verify

A small Rust crate to verify Minisign signatures.
Rust
27
star
67

whatsmyresolver

A tiny DNS server that returns the client (resolver) IP
Go
26
star
68

libsodium-sys-stable

Sodiumoxide's libsodium-sys crate, but that installs stable versions of libsodium.
Rust
26
star
69

spritz

A C implementation of Spritz, a spongy RC4-like stream cipher and hash function.
C
25
star
70

metrohash-c

C version of the MetroHash function
C
25
star
71

rust-hmac-sha256

A small, self-contained SHA256 and HMAC-SHA256 implementation.
Rust
25
star
72

rust-cpace

A Rust implementation of CPace, a balanced PAKE.
Rust
25
star
73

rust-clockpro-cache

CLOCK-Pro cache replacement algorithm for Rust
Rust
24
star
74

rust-blind-rsa-signatures

RSA blind signatures in Rust
Rust
24
star
75

PHP-OAuth2-Provider

Skyrock OAuth2 server
PHP
23
star
76

rust-aegis

AEGIS high performance ciphers for Rust.
Rust
23
star
77

system-tuning-for-crypto

System tuning recommendations for running cryptographic applications
23
star
78

hashseq

A simple proof of work, mainly designed to mitigate DDoS attacks.
C
23
star
79

dnssector

A DNS library for Rust.
Rust
23
star
80

openssl-family-bench

A quick benchmark of {Open,Libre,Boring}SSL
C
23
star
81

randen-rng

A port of the Google Randen fast backtracking-resistant random generator to the C language.
C
21
star
82

Blogbench

A filesystem benchmark tool that simulates a realistic load
C
21
star
83

vue-dnsstamp

DNS Stamp calculator component for VueJS
Vue
21
star
84

supercop

Always up-to-date mirror of the SUPERCOP cryptographic benchmark.
C
21
star
85

yaifo

YAIFO [remote OpenBSD installer] for OpenBSD-current
Shell
21
star
86

ratelimit

Plug-and-play IP rate limiter in C
C
21
star
87

draft-aegis-aead

The AEGIS cipher family - Draft.
20
star
88

go-hpke-compact

A small and easy to use HPKE implementation in Go.
Go
20
star
89

ipgrep

Extract, defang, resolve names and IPs from text
Python
20
star
90

zig-rocca-s

An implementation of the ROCCA-S encryption scheme.
Zig
19
star
91

Simple-Comet-Server

HTTP long-polling server and javascript client library.
Python
19
star
92

simpira384

An AES-based 384 bit permutation.
C
18
star
93

PHP-WebDAV-extension

The PHP WebDAV extension allows easy access to remote resources with PHP through the DAV protocol.
Shell
18
star
94

nonce-extension

Make AES-GCM safe to use with random nonces, for any practical number of messages.
Rust
17
star
95

rust-sealed_box

Sealed boxes implementation for Rust/WebAssembly.
Rust
16
star
96

zig-eddsa-key-blinding

A Zig implementation of EdDSA signatures with blind keys.
Zig
16
star
97

c-blind-rsa-signatures

Blind RSA signatures for OpenSSL/BoringSSL.
C
16
star
98

aes-torture

A software AES implementation to torture code generators.
C
16
star
99

js-base64-ct

Safe Base64 encoding/decoding in pure JavaScript.
TypeScript
16
star
100

rust-aes-wasm

Fast(er) AES-based constructions for WebAssembly and Rust.
Rust
16
star