diff --git a/Cargo.lock b/Cargo.lock index 2d81c4d7f..2dca99033 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2311,7 +2311,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.102", ] [[package]] @@ -7037,7 +7037,6 @@ name = "prism-keys" version = "0.1.0" dependencies = [ "alloy-primitives", - "anyhow", "ed25519", "ed25519-consensus", "getrandom 0.2.16", @@ -7050,6 +7049,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.8", + "thiserror 2.0.12", "utoipa", ] diff --git a/crates/keys/Cargo.toml b/crates/keys/Cargo.toml index 7e35722dc..44cd2b997 100644 --- a/crates/keys/Cargo.toml +++ b/crates/keys/Cargo.toml @@ -27,8 +27,8 @@ alloy-primitives.workspace = true ripemd.workspace = true # misc -anyhow.workspace = true sha2.workspace = true +thiserror.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] rand = { version = "0.8.5", features = ["std"] } diff --git a/crates/keys/src/algorithm.rs b/crates/keys/src/algorithm.rs index 49181198c..0fed879d8 100644 --- a/crates/keys/src/algorithm.rs +++ b/crates/keys/src/algorithm.rs @@ -1,4 +1,4 @@ -use anyhow::bail; +use crate::{CryptoError, Result, errors::SignatureError}; use pkcs8::{AlgorithmIdentifierRef, ObjectIdentifier}; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; @@ -33,16 +33,16 @@ impl CryptoAlgorithm { } impl std::str::FromStr for CryptoAlgorithm { - type Err = (); + type Err = CryptoError; - fn from_str(input: &str) -> Result { + fn from_str(input: &str) -> Result { match input.to_lowercase().as_str() { "ed25519" => Ok(CryptoAlgorithm::Ed25519), "secp256k1" => Ok(CryptoAlgorithm::Secp256k1), "secp256r1" => Ok(CryptoAlgorithm::Secp256r1), "eip191" => Ok(CryptoAlgorithm::Eip191), "cosmos_adr36" => Ok(CryptoAlgorithm::CosmosAdr36), - _ => Err(()), + _ => Err(SignatureError::AlgorithmError(input.to_string()).into()), } } } @@ -60,24 +60,31 @@ pub const SECP256K1_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.13 pub const SECP256R1_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7"); impl<'a> TryFrom> for CryptoAlgorithm { - type Error = anyhow::Error; + type Error = CryptoError; - fn try_from(algorithm_identifier: AlgorithmIdentifierRef<'a>) -> Result { + fn try_from(algorithm_identifier: AlgorithmIdentifierRef<'a>) -> Result { let oid = algorithm_identifier.oid; if oid == ED25519_OID { Ok(CryptoAlgorithm::Ed25519) } else if oid == ELLIPTIC_CURVE_OID || oid == ECDSA_SHA256_OID { - let parameter_oid = algorithm_identifier.parameters_oid()?; + let parameter_oid = algorithm_identifier + .parameters_oid() + .map_err(|e| SignatureError::AlgorithmError(e.to_string()))?; if parameter_oid == SECP256K1_OID { Ok(CryptoAlgorithm::Secp256k1) } else if parameter_oid == SECP256R1_OID { Ok(CryptoAlgorithm::Secp256r1) } else { - bail!("Unsupported elliptic curve OID") + return Err(SignatureError::AlgorithmError( + "Unsupported elliptic curve OID".to_string(), + ) + .into()); } } else { - bail!("Unsupported algorithm OID") + return Err( + SignatureError::AlgorithmError("Unsupported algorithm OID".to_string()).into(), + ); } } } diff --git a/crates/keys/src/cosmos.rs b/crates/keys/src/cosmos.rs index 3e557d805..8927927b8 100644 --- a/crates/keys/src/cosmos.rs +++ b/crates/keys/src/cosmos.rs @@ -1,4 +1,7 @@ -use anyhow::Result; +use crate::{ + Result, + errors::{ParseError, SignatureError}, +}; use k256::ecdsa::VerifyingKey as Secp256k1VerifyingKey; use prism_serde::{bech32::ToBech32, raw_or_b64}; use ripemd::Ripemd160; @@ -75,7 +78,8 @@ pub fn cosmos_adr36_hash_message( const ADDRESS_PREFIX: &str = "cosmos"; let signer = signer_from_key(ADDRESS_PREFIX, verifying_key)?; - let serialized_sign_doc = create_serialized_adr36_sign_doc(message.as_ref().to_vec(), signer)?; + let serialized_sign_doc = create_serialized_adr36_sign_doc(message.as_ref().to_vec(), signer) + .map_err(|e| ParseError::GeneralError(e.to_string()))?; let hashed_sign_doc = Sha256::digest(&serialized_sign_doc).to_vec(); Ok(hashed_sign_doc) } @@ -95,7 +99,8 @@ pub fn cosmos_adr36_hash_message( fn create_serialized_adr36_sign_doc(data: Vec, signer: String) -> Result> { let adr36_sign_doc = CosmosSignDoc::new(signer, data); - let sign_doc_str = serde_json::to_string(&adr36_sign_doc)? + let sign_doc_str = serde_json::to_string(&adr36_sign_doc) + .map_err(|e| ParseError::GeneralError(e.to_string()))? .replace("<", "\\u003c") .replace(">", "\\u003e") .replace("&", "\\u0026"); @@ -121,6 +126,8 @@ fn signer_from_key(address_prefix: &str, verifying_key: &Secp256k1VerifyingKey) let hashed_key_bytes = Sha256::digest(verifying_key_bytes); let cosmos_address = Ripemd160::digest(hashed_key_bytes); - let signer = cosmos_address.to_bech32(address_prefix)?; + let signer = cosmos_address + .to_bech32(address_prefix) + .map_err(|e| SignatureError::CosmosError(e.to_string()))?; Ok(signer) } diff --git a/crates/keys/src/errors.rs b/crates/keys/src/errors.rs new file mode 100644 index 000000000..c0347d1b9 --- /dev/null +++ b/crates/keys/src/errors.rs @@ -0,0 +1,78 @@ +use thiserror::Error; + +pub type Result = std::result::Result; + +#[derive(Error, Debug, Clone)] +pub enum CryptoError { + #[error("Signature error: {0}")] + SignatureError(#[from] SignatureError), + + #[error("Parse error: {0}")] + ParseError(#[from] ParseError), + + #[error("Verification error: {0}")] + VerificationError(#[from] VerificationError), +} + +#[derive(Error, Clone, Debug)] +pub enum SignatureError { + #[error("No {0} specific signatures implemented")] + UnsupportedFormatError(String), + + #[error("Malformed signature")] + MalformedSignError, + + #[error("Algorithm Error: {0}")] + AlgorithmError(String), + + #[error("Invalid signature type")] + InvalidSignError, + + #[error("Signing operation failed: {0}")] + SigningError(String), + + #[error("Cosmos Error: {0}")] + CosmosError(String), +} + +#[derive(Error, Clone, Debug)] +pub enum ParseError { + #[error("Creating PKCS8 DER failed")] + DerCreationError, + + #[error("Creating PKCS8 PEM failed")] + PemCreationError, + + #[error("Parsing key algorithm from PKCS#8 DER failed")] + DerParseError, + + #[error("Invalid PEM label")] + PemLabelError, + + #[error("Invalid key bytes for algorithm: {0}")] + InvalidKeyBytes(String), + + #[error("A parsing error occured: {0}")] + GeneralError(String), +} + +#[derive(Error, Clone, Debug)] +pub enum VerificationError { + #[error("Failed to verify {0} signature: {1}")] + VerifyError(String, String), + + #[error("Verifying key for {0} can only verify secp256k1 signatures")] + SignatureError(String), + + #[error("Creating {0} failed")] + VKCreationError(String), + + #[error("{0} vk from DER format failed: {1}")] + IntoRefError(String, String), + + #[error("{0} vk {1} DER format is not implemented")] + NotImplementedError(String, String), + + #[error("A verification error occured: {0}")] + GeneralError(String), +} diff --git a/crates/keys/src/lib.rs b/crates/keys/src/lib.rs index 68667829a..2e18fc5ab 100644 --- a/crates/keys/src/lib.rs +++ b/crates/keys/src/lib.rs @@ -1,6 +1,8 @@ mod algorithm; mod cosmos; mod der; +pub mod errors; +pub use errors::{CryptoError, ParseError, Result, SignatureError, VerificationError}; mod payload; mod signatures; mod signing_keys; diff --git a/crates/keys/src/signatures.rs b/crates/keys/src/signatures.rs index 32017e783..1aa7f96f2 100644 --- a/crates/keys/src/signatures.rs +++ b/crates/keys/src/signatures.rs @@ -1,4 +1,4 @@ -use anyhow::{Result, bail}; +use crate::{CryptoError, Result, errors::SignatureError}; use ed25519_consensus::Signature as Ed25519Signature; use k256::ecdsa::Signature as Secp256k1Signature; use p256::ecdsa::Signature as Secp256r1Signature; @@ -41,18 +41,20 @@ impl Signature { pub fn from_algorithm_and_bytes(algorithm: CryptoAlgorithm, bytes: &[u8]) -> Result { match algorithm { - CryptoAlgorithm::Ed25519 => { - Ed25519Signature::try_from(bytes).map(Signature::Ed25519).map_err(|e| e.into()) - } + CryptoAlgorithm::Ed25519 => Ed25519Signature::try_from(bytes) + .map(Signature::Ed25519) + .map_err(|e| SignatureError::AlgorithmError(e.to_string()).into()), CryptoAlgorithm::Secp256k1 => Secp256k1Signature::from_slice(bytes) .map(Signature::Secp256k1) - .map_err(|e| e.into()), + .map_err(|e| SignatureError::AlgorithmError(e.to_string()).into()), CryptoAlgorithm::Secp256r1 => Secp256r1Signature::from_slice(bytes) .map(Signature::Secp256r1) - .map_err(|e| e.into()), - CryptoAlgorithm::Eip191 => bail!("No EIP-191 specific signatures implemented"), + .map_err(|e| SignatureError::AlgorithmError(e.to_string()).into()), + CryptoAlgorithm::Eip191 => { + Err(SignatureError::UnsupportedFormatError("EIP-191".to_string()).into()) + } CryptoAlgorithm::CosmosAdr36 => { - bail!("No cosmos ADR-36 specific signatures implemented") + Err(SignatureError::UnsupportedFormatError("ADR-36".to_string()).into()) } } } @@ -87,22 +89,31 @@ impl Signature { let mut der_bytes = Vec::with_capacity(2 + signature_bytes.len()); der_bytes.push(0x04); // octet stream - der_bytes.push(signature_bytes.len().try_into()?); // length of signature bytes + der_bytes.push( + signature_bytes + .len() + .try_into() + .map_err(|_| SignatureError::AlgorithmError("Map conversion failed".to_string()))?, + ); // length of signature bytes der_bytes.extend_from_slice(&signature_bytes); let signature_info = SignatureInfoRef { algorithm: self.algorithm_identifier(), - signature: OctetStringRef::new(&der_bytes)?, + signature: OctetStringRef::new(&der_bytes) + .map_err(|e| SignatureError::AlgorithmError(e.to_string()))?, }; - let doc = SecretDocument::encode_msg(&signature_info)?; + let doc = SecretDocument::encode_msg(&signature_info) + .map_err(|e| SignatureError::AlgorithmError(e.to_string()))?; der_bytes.zeroize(); Ok(doc.as_bytes().to_vec()) } pub fn from_prism_der(bytes: &[u8]) -> Result { - let signature_info = SignatureInfoRef::from_der(bytes)?; - let algorithm = CryptoAlgorithm::try_from(signature_info.algorithm)?; + let signature_info = SignatureInfoRef::from_der(bytes) + .map_err(|e| SignatureError::AlgorithmError(e.to_string()))?; + let algorithm = CryptoAlgorithm::try_from(signature_info.algorithm) + .map_err(|e| SignatureError::AlgorithmError(e.to_string()))?; // Signature byte representation: // 1st byte: 0x04 (type OCTET STRING) @@ -112,13 +123,13 @@ impl Signature { [0x04, _, signature_bytes @ ..] => { Signature::from_algorithm_and_bytes(algorithm, signature_bytes) } - _ => bail!("Malformed signature"), + _ => Err(SignatureError::MalformedSignError.into()), } } } impl TryFrom for Signature { - type Error = anyhow::Error; + type Error = CryptoError; fn try_from(value: CryptoPayload) -> std::result::Result { Signature::from_algorithm_and_bytes(value.algorithm, &value.bytes) diff --git a/crates/keys/src/signing_keys.rs b/crates/keys/src/signing_keys.rs index db3816b44..1347a6ec0 100644 --- a/crates/keys/src/signing_keys.rs +++ b/crates/keys/src/signing_keys.rs @@ -1,5 +1,5 @@ +use crate::{CryptoError, Result, SignatureError, errors::ParseError}; use alloy_primitives::eip191_hash_message; -use anyhow::{Result, anyhow}; use ed25519::{ PublicKeyBytes as Ed25519PublicKeyBytes, pkcs8::KeypairBytes as Ed25519KeypairBytes, }; @@ -104,7 +104,7 @@ impl SigningKey { SigningKey::Eip191(sk) => sk.to_pkcs8_der(), SigningKey::CosmosAdr36(sk) => sk.to_pkcs8_der(), } - .map_err(|_| anyhow!("Creating PKCS8 DER failed")) + .map_err(|_| ParseError::DerCreationError.into()) } pub fn to_pkcs8_der(&self) -> Result> { @@ -114,65 +114,67 @@ impl SigningKey { pub fn to_pkcs8_pem_file(&self, filename: impl AsRef) -> Result<()> { self.to_pkcs8_der_doc()? .write_pem_file(filename, PrivateKeyInfo::PEM_LABEL, LineEnding::LF) - .map_err(|_| anyhow!("Creating PKCS8 PEM file failed")) + .map_err(|_| ParseError::PemCreationError.into()) } pub fn from_algorithm_and_bytes(algorithm: CryptoAlgorithm, bytes: &[u8]) -> Result { match algorithm { - CryptoAlgorithm::Ed25519 => { - Ed25519SigningKey::try_from(bytes).map(SigningKey::Ed25519).map_err(|e| e.into()) - } + CryptoAlgorithm::Ed25519 => Ed25519SigningKey::try_from(bytes) + .map(SigningKey::Ed25519) + .map_err(|e| ParseError::InvalidKeyBytes(e.to_string()).into()), CryptoAlgorithm::Secp256k1 => Secp256k1SigningKey::from_slice(bytes) .map(SigningKey::Secp256k1) - .map_err(|e| e.into()), + .map_err(|e| ParseError::InvalidKeyBytes(e.to_string()).into()), CryptoAlgorithm::Secp256r1 => Secp256r1SigningKey::from_slice(bytes) .map(SigningKey::Secp256r1) - .map_err(|e| e.into()), - CryptoAlgorithm::Eip191 => { - Secp256k1SigningKey::from_slice(bytes).map(SigningKey::Eip191).map_err(|e| e.into()) - } + .map_err(|e| ParseError::InvalidKeyBytes(e.to_string()).into()), + CryptoAlgorithm::Eip191 => Secp256k1SigningKey::from_slice(bytes) + .map(SigningKey::Eip191) + .map_err(|e| ParseError::InvalidKeyBytes(e.to_string()).into()), CryptoAlgorithm::CosmosAdr36 => Secp256k1SigningKey::from_slice(bytes) .map(SigningKey::CosmosAdr36) - .map_err(|e| e.into()), + .map_err(|e| ParseError::InvalidKeyBytes(e.to_string()).into()), } } pub fn from_pkcs8_der_doc(doc: &Document) -> Result { let value = doc.as_bytes(); - let pk_info = PrivateKeyInfo::try_from(value)?; - let algorithm = CryptoAlgorithm::try_from(pk_info.algorithm) - .map_err(|_| anyhow!("Unable to parse key algorithm from PKCS#8 der"))?; + let pk_info = PrivateKeyInfo::try_from(value).map_err(|_| ParseError::DerParseError)?; + let algorithm = + CryptoAlgorithm::try_from(pk_info.algorithm).map_err(|_| ParseError::DerParseError)?; match algorithm { CryptoAlgorithm::Ed25519 => { - let ed25519_key_pair_bytes = Ed25519KeypairBytes::try_from(pk_info)?; + let ed25519_key_pair_bytes = Ed25519KeypairBytes::try_from(pk_info) + .map_err(|e| ParseError::InvalidKeyBytes(e.to_string()))?; let ed25519_signing_key = Ed25519SigningKey::from(ed25519_key_pair_bytes.secret_key); Ok(SigningKey::Ed25519(ed25519_signing_key)) } CryptoAlgorithm::Secp256k1 => Secp256k1SigningKey::try_from(pk_info) .map(SigningKey::Secp256k1) - .map_err(|e| e.into()), + .map_err(|e| ParseError::InvalidKeyBytes(e.to_string()).into()), CryptoAlgorithm::Secp256r1 => Secp256r1SigningKey::try_from(pk_info) .map(SigningKey::Secp256r1) - .map_err(|e| e.into()), - CryptoAlgorithm::Eip191 => { - Secp256k1SigningKey::try_from(pk_info).map(SigningKey::Eip191).map_err(|e| e.into()) - } + .map_err(|e| ParseError::InvalidKeyBytes(e.to_string()).into()), + CryptoAlgorithm::Eip191 => Secp256k1SigningKey::try_from(pk_info) + .map(SigningKey::Eip191) + .map_err(|e| ParseError::InvalidKeyBytes(e.to_string()).into()), CryptoAlgorithm::CosmosAdr36 => Secp256k1SigningKey::try_from(pk_info) .map(SigningKey::CosmosAdr36) - .map_err(|e| e.into()), + .map_err(|e| ParseError::InvalidKeyBytes(e.to_string()).into()), } } pub fn from_pkcs8_der(bytes: &[u8]) -> Result { - let document = pkcs8::Document::from_der(bytes)?; + let document = pkcs8::Document::from_der(bytes).map_err(|_| ParseError::DerParseError)?; Self::from_pkcs8_der_doc(&document) } pub fn from_pkcs8_pem_file(file_path: impl AsRef) -> Result { - let (label, document) = pkcs8::Document::read_pem_file(file_path)?; - PrivateKeyInfo::validate_pem_label(&label).map_err(|_| anyhow!("Incorrect PEM label"))?; + let (label, document) = + pkcs8::Document::read_pem_file(file_path).map_err(|_| ParseError::DerParseError)?; + PrivateKeyInfo::validate_pem_label(&label).map_err(|_| ParseError::PemLabelError)?; Self::from_pkcs8_der_doc(&document) } @@ -193,23 +195,32 @@ impl SigningKey { SigningKey::Secp256k1(sk) => { let mut digest = sha2::Sha256::new(); digest.update(message); - let sig: Secp256k1Signature = sk.try_sign_digest(digest)?; + let sig: Secp256k1Signature = sk + .try_sign_digest(digest) + .map_err(|e| SignatureError::SigningError(e.to_string()))?; Ok(Signature::Secp256k1(sig)) } SigningKey::Secp256r1(sk) => { let mut digest = sha2::Sha256::new(); digest.update(message); - let sig: Secp256r1Signature = sk.try_sign_digest(digest)?; + let sig: Secp256r1Signature = sk + .try_sign_digest(digest) + .map_err(|e| SignatureError::SigningError(e.to_string()))?; Ok(Signature::Secp256r1(sig)) } SigningKey::Eip191(sk) => { let message = eip191_hash_message(message); - let sig: Secp256k1Signature = sk.sign_prehash(message.as_slice())?; + let sig: Secp256k1Signature = sk + .sign_prehash(message.as_slice()) + .map_err(|e| SignatureError::SigningError(e.to_string()))?; Ok(Signature::Secp256k1(sig)) } SigningKey::CosmosAdr36(sk) => { - let message = cosmos_adr36_hash_message(message, sk.verifying_key())?; - let sig: Secp256k1Signature = sk.sign_prehash(message.as_slice())?; + let message = cosmos_adr36_hash_message(message, sk.verifying_key()) + .map_err(|e| SignatureError::SigningError(e.to_string()))?; + let sig: Secp256k1Signature = sk + .sign_prehash(message.as_slice()) + .map_err(|e| SignatureError::SigningError(e.to_string()))?; Ok(Signature::Secp256k1(sig)) } } @@ -230,7 +241,7 @@ impl PartialEq for SigningKey { } impl TryFrom for SigningKey { - type Error = anyhow::Error; + type Error = CryptoError; fn try_from(value: CryptoPayload) -> std::result::Result { SigningKey::from_algorithm_and_bytes(value.algorithm, &value.bytes) diff --git a/crates/keys/src/verifying_keys.rs b/crates/keys/src/verifying_keys.rs index 109203551..8d6c3b0ff 100644 --- a/crates/keys/src/verifying_keys.rs +++ b/crates/keys/src/verifying_keys.rs @@ -1,5 +1,8 @@ +use crate::{ + CryptoError, Result, + errors::{ParseError, SignatureError, VerificationError}, +}; use alloy_primitives::eip191_hash_message; -use anyhow::{Result, anyhow, bail}; use ed25519::PublicKeyBytes as Ed25519PublicKeyBytes; use ed25519_consensus::VerificationKey as Ed25519VerifyingKey; use k256::ecdsa::VerifyingKey as Secp256k1VerifyingKey; @@ -90,21 +93,31 @@ impl VerifyingKey { pub fn from_algorithm_and_bytes(algorithm: CryptoAlgorithm, bytes: &[u8]) -> Result { match algorithm { - CryptoAlgorithm::Ed25519 => Ed25519VerifyingKey::try_from(bytes) - .map(VerifyingKey::Ed25519) - .map_err(|e| e.into()), + CryptoAlgorithm::Ed25519 => { + Ed25519VerifyingKey::try_from(bytes).map(VerifyingKey::Ed25519).map_err(|e| { + VerificationError::VerifyError("ed25519".to_string(), e.to_string()).into() + }) + } CryptoAlgorithm::Secp256k1 => Secp256k1VerifyingKey::from_sec1_bytes(bytes) .map(VerifyingKey::Secp256k1) - .map_err(|e| e.into()), + .map_err(|e| { + VerificationError::VerifyError("secp256k1".to_string(), e.to_string()).into() + }), CryptoAlgorithm::Secp256r1 => Secp256r1VerifyingKey::from_sec1_bytes(bytes) .map(VerifyingKey::Secp256r1) - .map_err(|e| e.into()), - CryptoAlgorithm::Eip191 => Secp256k1VerifyingKey::from_sec1_bytes(bytes) - .map(VerifyingKey::Eip191) - .map_err(|e| e.into()), + .map_err(|e| { + VerificationError::VerifyError("secp256r1".to_string(), e.to_string()).into() + }), + CryptoAlgorithm::Eip191 => { + Secp256k1VerifyingKey::from_sec1_bytes(bytes).map(VerifyingKey::Eip191).map_err( + |e| VerificationError::VerifyError("eip191".to_string(), e.to_string()).into(), + ) + } CryptoAlgorithm::CosmosAdr36 => Secp256k1VerifyingKey::from_sec1_bytes(bytes) .map(VerifyingKey::CosmosAdr36) - .map_err(|e| e.into()), + .map_err(|e| { + VerificationError::VerifyError("cosmos adr36".to_string(), e.to_string()).into() + }), } } @@ -122,47 +135,56 @@ impl VerifyingKey { match self { VerifyingKey::Ed25519(vk) => { let Signature::Ed25519(signature) = signature else { - bail!("Invalid signature type"); + return Err(SignatureError::InvalidSignError.into()); }; - vk.verify(signature, message.as_ref()) - .map_err(|e| anyhow!("Failed to verify ed25519 signature: {}", e)) + vk.verify(signature, message.as_ref()).map_err(|e| { + VerificationError::VerifyError("ed25519".to_string(), e.to_string()).into() + }) } VerifyingKey::Secp256k1(vk) => { let Signature::Secp256k1(signature) = signature else { - bail!("Invalid signature type"); + return Err(SignatureError::InvalidSignError.into()); }; let mut digest = sha2::Sha256::new(); digest.update(message); - vk.verify_digest(digest, signature) - .map_err(|e| anyhow!("Failed to verify secp256k1 signature: {}", e)) + vk.verify_digest(digest, signature).map_err(|e| { + VerificationError::VerifyError("secp256k1".to_string(), e.to_string()).into() + }) } VerifyingKey::Secp256r1(vk) => { let Signature::Secp256r1(signature) = signature else { - bail!("Invalid signature type"); + return Err(SignatureError::InvalidSignError.into()); }; let mut digest = sha2::Sha256::new(); digest.update(message); - vk.verify_digest(digest, signature) - .map_err(|e| anyhow!("Failed to verify secp256r1 signature: {}", e)) + vk.verify_digest(digest, signature).map_err(|e| { + VerificationError::VerifyError("secp256r1".to_string(), e.to_string()).into() + }) } VerifyingKey::Eip191(vk) => { let Signature::Secp256k1(signature) = signature else { - bail!("Verifying key for EIP-191 can only verify secp256k1 signatures"); + return Err(VerificationError::SignatureError("EIP-191".to_string()).into()); }; let prehash = eip191_hash_message(message); - vk.verify_prehash(prehash.as_slice(), signature) - .map_err(|e| anyhow!("Failed to verify EIP-191 signature: {}", e)) + vk.verify_prehash(prehash.as_slice(), signature).map_err(|e| { + VerificationError::VerifyError("EIP-191".to_string(), e.to_string()).into() + }) } VerifyingKey::CosmosAdr36(vk) => { let Signature::Secp256k1(signature) = signature else { - bail!("Verifying key for cosmos ADR-36 can only verify secp256k1 signatures"); + return Err( + VerificationError::SignatureError("cosmos ADR-36".to_string()).into(), + ); }; - let prehash = cosmos_adr36_hash_message(message, vk)?; - vk.verify_prehash(&prehash, signature) - .map_err(|e| anyhow!("Failed to verify cosmos ADR-36 signature: {}", e)) + let prehash = cosmos_adr36_hash_message(message, vk) + .map_err(|e| VerificationError::GeneralError(e.to_string()))?; + vk.verify_prehash(&prehash, signature).map_err(|e| { + VerificationError::VerifyError("cosmos ADR-36".to_string(), e.to_string()) + .into() + }) } } } @@ -172,12 +194,21 @@ impl VerifyingKey { VerifyingKey::Ed25519(vk) => Ed25519PublicKeyBytes(vk.to_bytes()).to_public_key_der(), VerifyingKey::Secp256k1(vk) => vk.to_public_key_der(), VerifyingKey::Secp256r1(vk) => vk.to_public_key_der(), - VerifyingKey::Eip191(_) => bail!("EIP-191 vk to DER format is not implemented"), + VerifyingKey::Eip191(_) => { + return Err(CryptoError::VerificationError( + VerificationError::NotImplementedError("EIP-191".to_string(), "to".to_string()), + )); + } VerifyingKey::CosmosAdr36(_) => { - bail!("Cosmos ADR-36 vk to DER format is not implemented") + return Err(CryptoError::VerificationError( + VerificationError::NotImplementedError( + "cosmos ADR-36".to_string(), + "to".to_string(), + ), + )); } } - .map_err(|_| anyhow!("Creating SPKI DER failed")) + .map_err(|_| ParseError::DerCreationError.into()) } pub fn to_spki_der(&self) -> Result> { @@ -187,48 +218,65 @@ impl VerifyingKey { pub fn to_spki_pem_file(&self, filename: impl AsRef) -> Result<()> { self.to_spki_der_doc()? .write_pem_file(filename, SubjectPublicKeyInfoRef::PEM_LABEL, LineEnding::LF) - .map_err(|_| anyhow!("Creating PKCS8 PEM file failed")) + .map_err(|_| VerificationError::VKCreationError("PKCS8 PEM file".to_string()).into()) } fn from_spki(spki: SubjectPublicKeyInfoRef) -> Result { - let algorithm = CryptoAlgorithm::try_from(spki.algorithm)?; + let algorithm = CryptoAlgorithm::try_from(spki.algorithm) + .map_err(|e| ParseError::GeneralError(e.to_string()))?; match algorithm { CryptoAlgorithm::Ed25519 => { - let ed25519_spki = Ed25519PublicKeyBytes::try_from(spki)?; - let ed25519_key = Ed25519VerifyingKey::try_from(ed25519_spki.as_ref() as &[u8])?; + let ed25519_spki = Ed25519PublicKeyBytes::try_from(spki) + .map_err(|e| ParseError::GeneralError(e.to_string()))?; + let ed25519_key = Ed25519VerifyingKey::try_from(ed25519_spki.as_ref() as &[u8]) + .map_err(|e| { + VerificationError::IntoRefError("ed25519".to_string(), e.to_string()) + })?; Ok(VerifyingKey::Ed25519(ed25519_key)) } CryptoAlgorithm::Secp256k1 => { - let secp256k1_key = Secp256k1VerifyingKey::try_from(spki)?; + let secp256k1_key = Secp256k1VerifyingKey::try_from(spki).map_err(|e| { + VerificationError::IntoRefError("secp256k1".to_string(), e.to_string()) + })?; Ok(VerifyingKey::Secp256k1(secp256k1_key)) } CryptoAlgorithm::Secp256r1 => { - let secp256r1_key = Secp256r1VerifyingKey::try_from(spki)?; + let secp256r1_key = Secp256r1VerifyingKey::try_from(spki).map_err(|e| { + VerificationError::IntoRefError("secp256r1".to_string(), e.to_string()) + })?; Ok(VerifyingKey::Secp256r1(secp256r1_key)) } - CryptoAlgorithm::Eip191 => bail!("Eth vk from DER format is not implemented"), - CryptoAlgorithm::CosmosAdr36 => { - bail!("Cosmos ADR-36 vk from DER format is not implemented") - } + CryptoAlgorithm::Eip191 => Err(VerificationError::NotImplementedError( + "Eth".to_string(), + "from".to_string(), + ) + .into()), + CryptoAlgorithm::CosmosAdr36 => Err(VerificationError::NotImplementedError( + "Cosmos ADR-36".to_string(), + "from".to_string(), + ) + .into()), } } pub fn from_spki_der(bytes: &[u8]) -> Result { - let spki = SubjectPublicKeyInfoRef::from_der(bytes)?; + let spki = SubjectPublicKeyInfoRef::from_der(bytes) + .map_err(|e| ParseError::GeneralError(e.to_string()))?; Self::from_spki(spki) } pub fn from_spki_pem_file(filename: impl AsRef) -> Result { - let (label, doc) = Document::read_pem_file(filename)?; + let (label, doc) = Document::read_pem_file(filename) + .map_err(|e| VerificationError::GeneralError(e.to_string()))?; SubjectPublicKeyInfoRef::validate_pem_label(&label) - .map_err(|_| anyhow!("Incorrect PEM label"))?; + .map_err(|_| VerificationError::GeneralError("Incorrect PEM label".to_string()))?; Self::from_spki_der(doc.as_bytes()) } } impl TryFrom for VerifyingKey { - type Error = anyhow::Error; + type Error = CryptoError; fn try_from(value: CryptoPayload) -> std::result::Result { VerifyingKey::from_algorithm_and_bytes(value.algorithm, &value.bytes) @@ -257,24 +305,29 @@ impl From for VerifyingKey { } impl FromBase64 for VerifyingKey { - type Error = anyhow::Error; + type Error = CryptoError; - fn from_base64>(base64: T) -> Result { - let bytes = Vec::::from_base64(base64)?; + fn from_base64>(base64: T) -> Result { + let bytes = Vec::::from_base64(base64) + .map_err(|e| (ParseError::GeneralError(e.to_string())))?; match bytes.len() { 32 => { - let vk = Ed25519VerifyingKey::try_from(bytes.as_slice()) - .map_err(|e| anyhow!("Invalid Ed25519 key: {}", e))?; + let vk = Ed25519VerifyingKey::try_from(bytes.as_slice()).map_err(|e| { + VerificationError::GeneralError(format!("invalid ed25519 key: {}", e)) + })?; Ok(VerifyingKey::Ed25519(vk)) } - _ => Err(anyhow!("Only Ed25519 keys can be initialized from base64")), + _ => Err(VerificationError::GeneralError( + "Only Ed25519 keys can be initialized from base64".to_string(), + ) + .into()), } } } impl TryFrom for VerifyingKey { - type Error = anyhow::Error; + type Error = CryptoError; fn try_from(s: String) -> std::result::Result { Self::from_base64(s)