use std::fmt::Display;
use chrono::TimeZone;
use jws::compact::{decode_verify, encode_sign};
use jws::hmac::{HmacVerifier, Hs512Signer};
use jws::{JsonObject, JsonValue};
use crate::open_badge::{BadgeAssertion, Type};
fn encode_decode() -> jws::Result<()> {
let mut header = JsonObject::new();
header.insert(String::from("typ"), JsonValue::from("text/plain"));
let encoded = encode_sign(header, b"payload", &Hs512Signer::new(b"secretkey"))?;
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 fn sign<S: AsRef<str> + Display, Tz1: TimeZone, Tz2: TimeZone>(
badge_assertion: &BadgeAssertion<S, Tz1, Tz2>,
secret_key: &[u8],
) -> jws::Result<String> {
let mut header = JsonObject::new();
let payload = badge_assertion.serialize();
let encoded = encode_sign(header, payload.as_bytes(), &Hs512Signer::new(secret_key))?;
let verify = true;
if verify {
let decoded = decode_verify(encoded.data().as_bytes(), &HmacVerifier::new(secret_key))?;
assert_eq!(decoded.payload, payload.as_bytes());
assert_eq!(
decoded.header.get("alg").and_then(|x| x.as_str()),
Some("HS512")
);
}
Ok(encoded.into_data())
}
#[cfg(test)]
mod tests {
use std::fs;
use super::*;
use crate::{box_err::BoxResult, constants, hash, open_badge};
use chrono::DateTime;
fn sign_and_verify<S: AsRef<str> + Display, Tz1: TimeZone, Tz2: TimeZone>(
badge_assertion: &BadgeAssertion<S, Tz1, Tz2>,
secret_key: &[u8],
public_key: &[u8],
) -> jws::Result<()> {
let encoded = sign(badge_assertion, secret_key)?;
let decoded = decode_verify(encoded.as_bytes(), &HmacVerifier::new(public_key))?;
assert_eq!(decoded.payload, badge_assertion.serialize().as_bytes());
assert_eq!(
decoded.header.get("alg").and_then(|x| x.as_str()),
Some("HS512")
);
Ok(())
}
#[test]
fn test_sign() -> BoxResult<()> {
let email_hash = hash::sha256(constants::BADGE_ASSERTION_RECIPIENT_EMAIL);
let badge_asser = open_badge::BadgeAssertion {
id: constants::BADGE_ASSERTION_WITH_KEY_ID,
badge_id: constants::BADGE_DEFINITION_WITH_KEY_ID,
recipient_salt: Some(constants::EMAIL_SALT),
recipient_hashed_email: &email_hash,
verification_public_key: Some(constants::KEY_ID),
issued_on: DateTime::parse_from_rfc3339(constants::DT_PAST)?,
expires: DateTime::parse_from_rfc3339(constants::DT_FAR_FUTURE)?,
};
let key_priv = fs::read_to_string(constants::ISSUER_KEY_PATH_PRIV)?;
let key_pub = fs::read_to_string(constants::ISSUER_KEY_PATH_PUB)?;
sign_and_verify(&badge_asser, key_priv.as_bytes(), key_pub.as_bytes()).map_err(|err| {
format!(
"Failed to sign because: {}: '{}'",
err.kind(),
err.message()
)
})?;
Ok(())
}
}