1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! This library provides JSON Web Signature encoding, decoding, signing and verification
//! as described in [RFC 7515](https://tools.ietf.org/html/rfc7515).
//!
//! Currently, encoding and decoding is available only for the JWS Compact Serialization scheme in the
//! [`compact`] module.
//!
//! Signing and verifying is done through the [`Signer`] and [`Verifier`] traits.
//! The [`hmac`] module contains implementations for these traits that support the HMAC-SHA2 family of algorithms.
//!
//! # Example:
//! ```
//! use jws::{JsonObject, JsonValue};
//! use jws::compact::{decode_verify, encode_sign};
//! use jws::hmac::{Hs512Signer, HmacVerifier};
//!
//! fn encode_decode() -> jws::Result<()> {
//!   // Add custom header parameters.
//!   let mut header = JsonObject::new();
//!   header.insert(String::from("typ"), JsonValue::from("text/plain"));
//!
//!   // Encode and sign the message.
//!   let encoded = encode_sign(header, b"payload", &Hs512Signer::new(b"secretkey"))?;
//!
//!   // Decode and verify the message.
//!   let decoded = decode_verify(encoded.data().as_bytes(), &HmacVerifier::new(b"secretkey"))?;
//!
//!   assert_eq!(decoded.payload, b"payload");
//!   assert_eq!(decoded.header.get("typ").and_then(|x| x.as_str()), Some("text/plain"));
//!
//!   Ok(())
//! }
//!
//! ```

pub mod compact;
pub mod hmac;
mod error;
mod header;
mod combine;
pub mod none;

pub use crate::error::{Error, ErrorKind, Result};
pub use crate::header::{get_header_param, get_required_header_param, parse_required_header_param};

/// Re-exported [`serde_json::Value`].
pub type JsonValue  = serde_json::Value;

/// A JSON object.
pub type JsonObject = std::collections::BTreeMap<String, JsonValue>;

/// Create a JSON object.
///
/// This is similar to the `json` macro from serde,
/// except that this macro always creates a JSON object, rather than a JSON value.
///
/// # Example
/// ```
/// # use jws::json_object;
/// json_object!{
///   "alg": "HS256",
///   "typ": "jwt",
/// };
/// ```
#[macro_export]
macro_rules! json_object {
	{} => { $crate::JsonObject::new() };

	{ $( $name:tt : $value:expr, )+ } => {{
		let mut object = $crate::JsonObject::new();
		$(object.insert(String::from($name), $crate::JsonValue::from($value));)*
		object
	}};

	{ $( $name:tt : $value:expr ),+ } => {{
		let mut object = $crate::JsonObject::new();
		$(object.insert(String::from($name), $crate::JsonValue::from($value));)*
		object
	}};
}

/// A verifier for JWS messages.
pub trait Verifier: Sized {
	/// Verify the signature of a JWS message.
	///
	/// This function needs access to the decoded message headers in order to determine which MAC algorithm to use.
	/// It also needs access to the base64-url encoded parts to verify the signature.
	///
	/// If the signature is invalid, the function should return a [`Error::InvalidSignature`] error.
	/// If the algorithm is not supported by the verifier, it should return a [`Error::UnsupportedMacAlgorithm`] error.
	/// It may also report any of the other supported error variants.
	///
	/// # Args:
	///   - protected_header:   The parsed protected header, if any.
	///   - unprotected_header: The parsed unprotected header, if any.
	///   - encoded_header:     The base64-url encoded protected header, needed to compute the MAC. If there is no protected header, this is an empty slice.
	///   - encoded_payload:    The base64-url encoded payload, needed to compute the MAC.
	///   - signature:          The signature associated with the message, should be tested against the computed MAC.
	fn verify(
		&self,
		protected_header   : Option<&JsonObject>,
		unprotected_header : Option<&JsonObject>,
		encoded_header     : &[u8],
		encoded_payload    : &[u8],
		signature          : &[u8],
	) -> Result<()>;

	/// Create a new verifier that accepts a message if either this or the other verifier does.
	fn or<Other: Verifier>(self, other: Other) -> combine::OrVerifier<Self, Other> {
		combine::OrVerifier::new(self, other)
	}

	/// Create a new verifier that accepts a message if both this and the other verifier does.
	fn and<Other: Verifier>(self, other: Other) -> combine::AndVerifier<Self, Other> {
		combine::AndVerifier::new(self, other)
	}
}

/// A signer for JWS messages.
pub trait Signer {
	/// Set the header parameters to indicate how the message should be verified.
	///
	/// This is the first step in the signing process, since the encoded headers will end up in the signature if they are added to the protected header.
	fn set_header_params(&self, header: &mut JsonObject);

	/// Compute the Message Authentication Code for the encoded protected header and encoded payload.
	///
	/// The returned MAC must be plain bytes, not hex or base64 encoded.
	fn compute_mac(&self, encoded_protected_header: &[u8], encoded_payload: &[u8]) -> Result<Vec<u8>>;
}