use const_oid::ObjectIdentifier;
pub const OID_SHA3_256: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.8");
pub const OID_SHA3_384: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.9");
pub const OID_SHA3_512: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.10");
pub const OID_RSASSA_PSS: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10");
pub const OID_ED25519: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.101.112");
pub const OID_ECDSA_WITH_SHA512: ObjectIdentifier =
ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.4");
pub const OID_MD5_WITH_RSA: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.4");
pub const OID_SHA1_WITH_RSA: ObjectIdentifier =
ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.5");
pub const OID_SHA224_WITH_RSA: ObjectIdentifier =
ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.14");
pub const OID_ECDSA_WITH_SHA1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.1");
pub const OID_DSA_WITH_SHA1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10040.4.3");
pub const OID_DSA_WITH_SHA256: ObjectIdentifier =
ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.3.2");
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum DigestAlgorithm {
#[default]
Sha256,
Sha384,
Sha512,
Sha3_256,
Sha3_384,
Sha3_512,
}
impl DigestAlgorithm {
pub fn oid(&self) -> ObjectIdentifier {
match self {
DigestAlgorithm::Sha256 => ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1"),
DigestAlgorithm::Sha384 => ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2"),
DigestAlgorithm::Sha512 => ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.3"),
DigestAlgorithm::Sha3_256 => OID_SHA3_256,
DigestAlgorithm::Sha3_384 => OID_SHA3_384,
DigestAlgorithm::Sha3_512 => OID_SHA3_512,
}
}
pub fn from_oid(oid: &ObjectIdentifier) -> Option<Self> {
if *oid == DigestAlgorithm::Sha256.oid() {
Some(DigestAlgorithm::Sha256)
} else if *oid == DigestAlgorithm::Sha384.oid() {
Some(DigestAlgorithm::Sha384)
} else if *oid == DigestAlgorithm::Sha512.oid() {
Some(DigestAlgorithm::Sha512)
}
else if *oid == OID_SHA3_256 {
Some(DigestAlgorithm::Sha3_256)
} else if *oid == OID_SHA3_384 {
Some(DigestAlgorithm::Sha3_384)
} else if *oid == OID_SHA3_512 {
Some(DigestAlgorithm::Sha3_512)
} else {
None
}
}
pub fn digest(&self, data: &[u8]) -> kryptering::Result<Vec<u8>> {
kryptering::digest::digest((*self).into(), data)
}
pub fn new_hasher(&self) -> kryptering::Result<DigestHasher> {
kryptering::digest::new_digest((*self).into()).map(DigestHasher::new)
}
pub fn output_size(&self) -> usize {
match self {
DigestAlgorithm::Sha256 | DigestAlgorithm::Sha3_256 => 32,
DigestAlgorithm::Sha384 | DigestAlgorithm::Sha3_384 => 48,
DigestAlgorithm::Sha512 | DigestAlgorithm::Sha3_512 => 64,
}
}
pub fn name(&self) -> &'static str {
match self {
DigestAlgorithm::Sha256 => "SHA-256",
DigestAlgorithm::Sha384 => "SHA-384",
DigestAlgorithm::Sha512 => "SHA-512",
DigestAlgorithm::Sha3_256 => "SHA3-256",
DigestAlgorithm::Sha3_384 => "SHA3-384",
DigestAlgorithm::Sha3_512 => "SHA3-512",
}
}
pub fn is_sha3(&self) -> bool {
matches!(
self,
DigestAlgorithm::Sha3_256 | DigestAlgorithm::Sha3_384 | DigestAlgorithm::Sha3_512
)
}
pub fn all() -> &'static [DigestAlgorithm] {
&[
DigestAlgorithm::Sha256,
DigestAlgorithm::Sha384,
DigestAlgorithm::Sha512,
DigestAlgorithm::Sha3_256,
DigestAlgorithm::Sha3_384,
DigestAlgorithm::Sha3_512,
]
}
}
impl From<DigestAlgorithm> for kryptering::HashAlgorithm {
fn from(value: DigestAlgorithm) -> Self {
match value {
DigestAlgorithm::Sha256 => Self::Sha256,
DigestAlgorithm::Sha384 => Self::Sha384,
DigestAlgorithm::Sha512 => Self::Sha512,
DigestAlgorithm::Sha3_256 => Self::Sha3_256,
DigestAlgorithm::Sha3_384 => Self::Sha3_384,
DigestAlgorithm::Sha3_512 => Self::Sha3_512,
}
}
}
impl std::fmt::Display for DigestAlgorithm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.name())
}
}
pub struct DigestHasher {
inner: Box<dyn kryptering::digest::DigestStream>,
}
impl DigestHasher {
fn new(inner: Box<dyn kryptering::digest::DigestStream>) -> Self {
Self { inner }
}
pub fn update(&mut self, data: &[u8]) {
self.inner.update(data);
}
pub fn finalize(self) -> kryptering::Result<Vec<u8>> {
self.inner.finalize()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SignatureAlgorithm {
RsaPkcs1v15,
RsaPss,
EcdsaP256,
EcdsaP384,
Ed25519,
}
impl SignatureAlgorithm {
pub fn name(&self) -> &'static str {
match self {
SignatureAlgorithm::RsaPkcs1v15 => "RSA-PKCS1-v1.5",
SignatureAlgorithm::RsaPss => "RSA-PSS",
SignatureAlgorithm::EcdsaP256 => "ECDSA-P256",
SignatureAlgorithm::EcdsaP384 => "ECDSA-P384",
SignatureAlgorithm::Ed25519 => "Ed25519",
}
}
pub fn all() -> &'static [SignatureAlgorithm] {
&[
SignatureAlgorithm::RsaPkcs1v15,
SignatureAlgorithm::RsaPss,
SignatureAlgorithm::EcdsaP256,
SignatureAlgorithm::EcdsaP384,
SignatureAlgorithm::Ed25519,
]
}
}
impl std::fmt::Display for SignatureAlgorithm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Clone)]
pub struct AlgorithmRegistry {
allowed_digests: Vec<DigestAlgorithm>,
allowed_signatures: Vec<SignatureAlgorithm>,
configured: bool,
}
impl AlgorithmRegistry {
pub fn new() -> Self {
Self {
allowed_digests: Vec::new(),
allowed_signatures: Vec::new(),
configured: true,
}
}
pub fn allow_all() -> Self {
Self {
allowed_digests: DigestAlgorithm::all().to_vec(),
allowed_signatures: SignatureAlgorithm::all().to_vec(),
configured: true,
}
}
pub fn standard() -> Self {
Self {
allowed_digests: vec![
DigestAlgorithm::Sha256,
DigestAlgorithm::Sha384,
DigestAlgorithm::Sha512,
],
allowed_signatures: vec![
SignatureAlgorithm::RsaPkcs1v15,
SignatureAlgorithm::RsaPss,
SignatureAlgorithm::EcdsaP256,
SignatureAlgorithm::EcdsaP384,
],
configured: true,
}
}
pub fn allow_digest(mut self, alg: DigestAlgorithm) -> Self {
if !self.allowed_digests.contains(&alg) {
self.allowed_digests.push(alg);
}
self
}
pub fn allow_signature(mut self, alg: SignatureAlgorithm) -> Self {
if !self.allowed_signatures.contains(&alg) {
self.allowed_signatures.push(alg);
}
self
}
pub fn is_digest_allowed(&self, alg: DigestAlgorithm) -> bool {
if !self.configured {
return true;
}
self.allowed_digests.contains(&alg)
}
pub fn is_signature_allowed(&self, alg: SignatureAlgorithm) -> bool {
if !self.configured {
return true;
}
self.allowed_signatures.contains(&alg)
}
pub fn validate(
&self,
sig_alg: SignatureAlgorithm,
digest_alg: DigestAlgorithm,
) -> Result<(), String> {
if !self.is_signature_allowed(sig_alg) {
return Err(format!(
"signature algorithm {} is not allowed by the algorithm registry",
sig_alg
));
}
if !self.is_digest_allowed(digest_alg) {
return Err(format!(
"digest algorithm {} is not allowed by the algorithm registry",
digest_alg
));
}
Ok(())
}
pub fn allowed_digests(&self) -> &[DigestAlgorithm] {
&self.allowed_digests
}
pub fn allowed_signatures(&self) -> &[SignatureAlgorithm] {
&self.allowed_signatures
}
}
impl Default for AlgorithmRegistry {
fn default() -> Self {
Self::allow_all()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_digest_algorithm_oid_roundtrip() {
for &alg in DigestAlgorithm::all() {
let oid = alg.oid();
let roundtrip = DigestAlgorithm::from_oid(&oid);
assert_eq!(roundtrip, Some(alg), "roundtrip failed for {alg:?}");
}
}
#[test]
fn test_digest_algorithm_from_unknown_oid() {
let unknown = ObjectIdentifier::new_unwrap("1.2.3.4.5.6.7.8.9");
assert_eq!(DigestAlgorithm::from_oid(&unknown), None);
}
#[test]
fn test_sha3_digests() {
let data = b"hello world";
let sha3_256 = DigestAlgorithm::Sha3_256.digest(data).unwrap();
assert_eq!(sha3_256.len(), 32);
let sha3_384 = DigestAlgorithm::Sha3_384.digest(data).unwrap();
assert_eq!(sha3_384.len(), 48);
let sha3_512 = DigestAlgorithm::Sha3_512.digest(data).unwrap();
assert_eq!(sha3_512.len(), 64);
let sha2_256 = DigestAlgorithm::Sha256.digest(data).unwrap();
assert_ne!(sha2_256, sha3_256, "SHA-256 and SHA3-256 should differ");
}
#[test]
fn test_sha3_streaming_hasher() {
let data = b"hello world";
for &alg in DigestAlgorithm::all() {
let one_shot = alg.digest(data).unwrap();
let mut hasher = alg.new_hasher().unwrap();
hasher.update(b"hello ");
hasher.update(b"world");
let streaming = hasher.finalize().unwrap();
assert_eq!(one_shot, streaming, "mismatch for {alg:?}");
}
}
#[test]
fn test_output_size() {
assert_eq!(DigestAlgorithm::Sha256.output_size(), 32);
assert_eq!(DigestAlgorithm::Sha384.output_size(), 48);
assert_eq!(DigestAlgorithm::Sha512.output_size(), 64);
assert_eq!(DigestAlgorithm::Sha3_256.output_size(), 32);
assert_eq!(DigestAlgorithm::Sha3_384.output_size(), 48);
assert_eq!(DigestAlgorithm::Sha3_512.output_size(), 64);
}
#[test]
fn test_is_sha3() {
assert!(!DigestAlgorithm::Sha256.is_sha3());
assert!(!DigestAlgorithm::Sha384.is_sha3());
assert!(!DigestAlgorithm::Sha512.is_sha3());
assert!(DigestAlgorithm::Sha3_256.is_sha3());
assert!(DigestAlgorithm::Sha3_384.is_sha3());
assert!(DigestAlgorithm::Sha3_512.is_sha3());
}
#[test]
fn test_display() {
assert_eq!(DigestAlgorithm::Sha256.to_string(), "SHA-256");
assert_eq!(DigestAlgorithm::Sha3_256.to_string(), "SHA3-256");
assert_eq!(SignatureAlgorithm::RsaPss.to_string(), "RSA-PSS");
assert_eq!(SignatureAlgorithm::Ed25519.to_string(), "Ed25519");
}
#[test]
fn test_default_registry_allows_all() {
let reg = AlgorithmRegistry::default();
for &alg in DigestAlgorithm::all() {
assert!(reg.is_digest_allowed(alg), "{alg:?} should be allowed");
}
for &alg in SignatureAlgorithm::all() {
assert!(reg.is_signature_allowed(alg), "{alg:?} should be allowed");
}
}
#[test]
fn test_empty_registry_allows_nothing() {
let reg = AlgorithmRegistry::new();
assert!(!reg.is_digest_allowed(DigestAlgorithm::Sha256));
assert!(!reg.is_signature_allowed(SignatureAlgorithm::RsaPkcs1v15));
}
#[test]
fn test_standard_registry() {
let reg = AlgorithmRegistry::standard();
assert!(reg.is_digest_allowed(DigestAlgorithm::Sha256));
assert!(reg.is_digest_allowed(DigestAlgorithm::Sha384));
assert!(reg.is_digest_allowed(DigestAlgorithm::Sha512));
assert!(!reg.is_digest_allowed(DigestAlgorithm::Sha3_256));
assert!(!reg.is_digest_allowed(DigestAlgorithm::Sha3_384));
assert!(!reg.is_digest_allowed(DigestAlgorithm::Sha3_512));
assert!(reg.is_signature_allowed(SignatureAlgorithm::RsaPkcs1v15));
assert!(reg.is_signature_allowed(SignatureAlgorithm::RsaPss));
assert!(reg.is_signature_allowed(SignatureAlgorithm::EcdsaP256));
assert!(reg.is_signature_allowed(SignatureAlgorithm::EcdsaP384));
assert!(!reg.is_signature_allowed(SignatureAlgorithm::Ed25519));
}
#[test]
fn test_custom_registry() {
let reg = AlgorithmRegistry::new()
.allow_digest(DigestAlgorithm::Sha3_256)
.allow_signature(SignatureAlgorithm::RsaPss);
assert!(reg.is_digest_allowed(DigestAlgorithm::Sha3_256));
assert!(!reg.is_digest_allowed(DigestAlgorithm::Sha256));
assert!(reg.is_signature_allowed(SignatureAlgorithm::RsaPss));
assert!(!reg.is_signature_allowed(SignatureAlgorithm::RsaPkcs1v15));
}
#[test]
fn test_validate_combination() {
let reg = AlgorithmRegistry::standard();
assert!(reg
.validate(SignatureAlgorithm::RsaPkcs1v15, DigestAlgorithm::Sha256)
.is_ok());
assert!(reg
.validate(SignatureAlgorithm::RsaPss, DigestAlgorithm::Sha384)
.is_ok());
let err = reg
.validate(SignatureAlgorithm::Ed25519, DigestAlgorithm::Sha256)
.unwrap_err();
assert!(err.contains("Ed25519"));
assert!(err.contains("not allowed"));
let err = reg
.validate(SignatureAlgorithm::RsaPss, DigestAlgorithm::Sha3_256)
.unwrap_err();
assert!(err.contains("SHA3-256"));
assert!(err.contains("not allowed"));
}
#[test]
fn test_no_duplicate_allow() {
let reg = AlgorithmRegistry::new()
.allow_digest(DigestAlgorithm::Sha256)
.allow_digest(DigestAlgorithm::Sha256)
.allow_digest(DigestAlgorithm::Sha256);
assert_eq!(reg.allowed_digests().len(), 1);
}
#[test]
fn test_sha3_oids_correct() {
assert_eq!(OID_SHA3_256.to_string(), "2.16.840.1.101.3.4.2.8");
assert_eq!(OID_SHA3_384.to_string(), "2.16.840.1.101.3.4.2.9");
assert_eq!(OID_SHA3_512.to_string(), "2.16.840.1.101.3.4.2.10");
}
#[test]
fn test_rsassa_pss_oid() {
assert_eq!(OID_RSASSA_PSS.to_string(), "1.2.840.113549.1.1.10");
}
#[test]
fn test_ed25519_oid() {
assert_eq!(OID_ED25519.to_string(), "1.3.101.112");
}
}