use crate::crypto::JwtSignatureAlgorithm;
use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, str::FromStr};
const NONSTANDARD_VARSIG_PREFIX: u64 = 0xd000;
const ES256K_VARSIG_PREFIX: u64 = 0xd0e7;
const BLS12381G1_VARSIG_PREFIX: u64 = 0xd0ea;
const BLS12381G2_VARSIG_PREFIX: u64 = 0xd0eb;
const EDDSA_VARSIG_PREFIX: u64 = 0xd0ed;
const ES256_VARSIG_PREFIX: u64 = 0xd01200;
const ES384_VARSIG_PREFIX: u64 = 0xd01201;
const ES512_VARSIG_PREFIX: u64 = 0xd01202;
const RS256_VARSIG_PREFIX: u64 = 0xd01205;
const EIP191_VARSIG_PREFIX: u64 = 0xd191;
#[derive(Debug, Eq, PartialEq)]
pub enum VarsigPrefix {
NonStandard,
ES256K,
BLS12381G1,
BLS12381G2,
EdDSA,
ES256,
ES384,
ES512,
RS256,
EIP191,
}
impl TryFrom<JwtSignatureAlgorithm> for VarsigPrefix {
type Error = anyhow::Error;
fn try_from(value: JwtSignatureAlgorithm) -> Result<Self, Self::Error> {
Ok(match value {
JwtSignatureAlgorithm::EdDSA => VarsigPrefix::EdDSA,
JwtSignatureAlgorithm::RS256 => VarsigPrefix::RS256,
JwtSignatureAlgorithm::ES256 => VarsigPrefix::ES256,
JwtSignatureAlgorithm::ES384 => VarsigPrefix::ES384,
JwtSignatureAlgorithm::ES512 => VarsigPrefix::ES512,
})
}
}
impl TryFrom<VarsigPrefix> for JwtSignatureAlgorithm {
type Error = anyhow::Error;
fn try_from(value: VarsigPrefix) -> Result<Self, Self::Error> {
Ok(match value {
VarsigPrefix::EdDSA => JwtSignatureAlgorithm::EdDSA,
VarsigPrefix::RS256 => JwtSignatureAlgorithm::RS256,
VarsigPrefix::ES256 => JwtSignatureAlgorithm::ES256,
VarsigPrefix::ES384 => JwtSignatureAlgorithm::ES384,
VarsigPrefix::ES512 => JwtSignatureAlgorithm::ES512,
_ => {
return Err(anyhow!(
"JWT signature algorithm name for {:?} is not known",
value
))
}
})
}
}
impl FromStr for VarsigPrefix {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
VarsigPrefix::try_from(JwtSignatureAlgorithm::from_str(s)?)
}
}
impl From<VarsigPrefix> for u64 {
fn from(value: VarsigPrefix) -> Self {
match value {
VarsigPrefix::NonStandard { .. } => NONSTANDARD_VARSIG_PREFIX,
VarsigPrefix::ES256K => ES256K_VARSIG_PREFIX,
VarsigPrefix::BLS12381G1 => BLS12381G1_VARSIG_PREFIX,
VarsigPrefix::BLS12381G2 => BLS12381G2_VARSIG_PREFIX,
VarsigPrefix::EdDSA => EDDSA_VARSIG_PREFIX,
VarsigPrefix::ES256 => ES256_VARSIG_PREFIX,
VarsigPrefix::ES384 => ES384_VARSIG_PREFIX,
VarsigPrefix::ES512 => ES512_VARSIG_PREFIX,
VarsigPrefix::RS256 => RS256_VARSIG_PREFIX,
VarsigPrefix::EIP191 => EIP191_VARSIG_PREFIX,
}
}
}
impl TryFrom<u64> for VarsigPrefix {
type Error = anyhow::Error;
fn try_from(value: u64) -> Result<Self, Self::Error> {
Ok(match value {
EDDSA_VARSIG_PREFIX => VarsigPrefix::EdDSA,
RS256_VARSIG_PREFIX => VarsigPrefix::RS256,
ES256K_VARSIG_PREFIX => VarsigPrefix::ES256K,
BLS12381G1_VARSIG_PREFIX => VarsigPrefix::BLS12381G1,
BLS12381G2_VARSIG_PREFIX => VarsigPrefix::BLS12381G2,
EIP191_VARSIG_PREFIX => VarsigPrefix::EIP191,
ES256_VARSIG_PREFIX => VarsigPrefix::ES256,
ES384_VARSIG_PREFIX => VarsigPrefix::ES384,
ES512_VARSIG_PREFIX => VarsigPrefix::ES512,
NONSTANDARD_VARSIG_PREFIX => VarsigPrefix::NonStandard,
_ => return Err(anyhow!("Signature does not have a recognized prefix")),
})
}
}
#[repr(transparent)]
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Signature(pub Vec<u8>);
impl Signature {
pub fn decode(&self) -> Result<(JwtSignatureAlgorithm, Vec<u8>)> {
let buffer = self.0.as_slice();
let (prefix, buffer) = unsigned_varint::decode::u64(buffer)?;
let (signature_length, buffer) = unsigned_varint::decode::usize(buffer)?;
let algorithm = JwtSignatureAlgorithm::try_from(VarsigPrefix::try_from(prefix)?)?;
let signature = buffer[..signature_length].to_vec();
Ok((algorithm, signature))
}
}
impl<T: AsRef<[u8]>> TryFrom<(JwtSignatureAlgorithm, T)> for Signature {
type Error = anyhow::Error;
fn try_from((algorithm, signature): (JwtSignatureAlgorithm, T)) -> Result<Self, Self::Error> {
let signature_bytes = signature.as_ref();
let prefix = VarsigPrefix::try_from(algorithm)?;
let mut prefix_buffer = unsigned_varint::encode::u64_buffer();
let prefix_bytes = unsigned_varint::encode::u64(prefix.into(), &mut prefix_buffer);
let mut size_buffer = unsigned_varint::encode::usize_buffer();
let size_bytes = unsigned_varint::encode::usize(signature_bytes.len(), &mut size_buffer);
Ok(Signature(
[prefix_bytes, size_bytes, signature_bytes].concat(),
))
}
}
#[cfg(test)]
mod tests {
use crate::{crypto::JwtSignatureAlgorithm, ipld::Signature};
use base64::Engine;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
#[cfg(target_arch = "wasm32")]
wasm_bindgen_test_configure!(run_in_browser);
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(not(target_arch = "wasm32"), test)]
fn it_can_convert_between_jwt_and_bytesprefix_form() {
let token_signature = "Ab-xfYRoqYEHuo-252MKXDSiOZkLD-h1gHt8gKBP0AVdJZ6Jruv49TLZOvgWy9QkCpiwKUeGVbHodKcVx-azCQ";
let signature_bytes = base64::engine::general_purpose::URL_SAFE_NO_PAD
.decode(token_signature)
.unwrap();
let bytesprefix_signature =
Signature::try_from((JwtSignatureAlgorithm::EdDSA, &signature_bytes)).unwrap();
let (decoded_algorithm, decoded_signature_bytes) = bytesprefix_signature.decode().unwrap();
assert_eq!(decoded_algorithm, JwtSignatureAlgorithm::EdDSA);
assert_eq!(decoded_signature_bytes, signature_bytes);
}
#[allow(dead_code)]
#[cfg_attr(not(target_arch = "wasm32"), test)]
#[ignore = "Support non-standard signature algorithms"]
fn it_can_convert_between_jwt_and_bytesprefix_for_nonstandard_signatures() {}
}