Repository: CosmWasm/cosmwasm
Pull request #800 (2021-02-25 15:01:54)

Review and refine crypto errors
This makes the meaking of the errors much more specific.


Patch:
diff --git a/contracts/crypto-verify/src/contract.rs b/contracts/crypto-verify/src/contract.rs
index 1c0822a40..46f6e7791 100644
--- a/contracts/crypto-verify/src/contract.rs
+++ b/contracts/crypto-verify/src/contract.rs
@@ -325,7 +325,7 @@ mod tests {
         assert_eq!(
             res.unwrap_err(),
             StdError::VerificationErr {
-                source: VerificationError::PublicKeyErr
+                source: VerificationError::InvalidPubkeyFormat
             }
         )
     }
@@ -611,7 +611,7 @@ mod tests {
         assert_eq!(
             res.unwrap_err(),
             StdError::VerificationErr {
-                source: VerificationError::PublicKeyErr,
+                source: VerificationError::InvalidPubkeyFormat,
             }
         )
     }
@@ -676,7 +676,7 @@ mod tests {
         assert_eq!(
             res.unwrap_err(),
             StdError::VerificationErr {
-                source: VerificationError::PublicKeyErr
+                source: VerificationError::InvalidPubkeyFormat
             }
         )
     }
diff --git a/packages/crypto/src/ed25519.rs b/packages/crypto/src/ed25519.rs
index 43ab08214..71fa8a76e 100644
--- a/packages/crypto/src/ed25519.rs
+++ b/packages/crypto/src/ed25519.rs
@@ -1,23 +1,20 @@
-use ed25519_zebra as ed25519;
+use ed25519_zebra::{batch, Signature, VerificationKey};
 use rand_core::OsRng;
 use std::convert::TryFrom;
+use std::convert::TryInto;
 
 use crate::errors::{CryptoError, CryptoResult};
 
 /// Max length of a message for ed25519 verification in bytes.
 /// This is an arbitrary value, for performance / memory contraints. If you need to verify larger
 /// messages, let us know.
-pub const MESSAGE_MAX_LEN: usize = 131072;
+pub const MESSAGE_MAX_LEN: usize = 128 * 1024;
 
 /// Max number of batch messages / signatures / public_keys.
 /// This is an arbitrary value, for performance / memory contraints. If you need to batch-verify a
 /// larger number of signatures, let us know.
 pub const BATCH_MAX_LEN: usize = 256;
 
-/// EdDSA (ed25519) parameters
-/// Length of a serialized signature
-pub const EDDSA_SIGNATURE_LEN: usize = 64;
-
 /// Length of a serialized public key
 pub const EDDSA_PUBKEY_LEN: usize = 32;
 
@@ -33,37 +30,14 @@ pub const EDDSA_PUBKEY_LEN: usize = 32;
 /// - public key: raw ED25519 public key (32 bytes).
 pub fn ed25519_verify(message: &[u8], signature: &[u8], public_key: &[u8]) -> CryptoResult<bool> {
     // Validation
-    if message.len() > MESSAGE_MAX_LEN {
-        return Err(CryptoError::msg_err(format!(
-            "too large: {}",
-            message.len()
-        )));
-    }
-    if signature.len() != EDDSA_SIGNATURE_LEN {
-        return Err(CryptoError::sig_err(format!(
-            "wrong / unsupported length: {}",
-            signature.len()
-        )));
-    }
-    let pubkey_len = public_key.len();
-    if pubkey_len == 0 {
-        return Err(CryptoError::pubkey_err("empty"));
-    }
-    if pubkey_len != EDDSA_PUBKEY_LEN {
-        return Err(CryptoError::pubkey_err(format!(
-            "wrong / unsupported length: {}",
-            pubkey_len,
-        )));
-    }
-    // Deserialization
-    let signature = ed25519::Signature::try_from(signature)
-        .map_err(|err| CryptoError::generic_err(err.to_string()))?;
-
-    let public_key = ed25519::VerificationKey::try_from(public_key)
-        .map_err(|err| CryptoError::generic_err(err.to_string()))?;
+    check_message_length(message)?;
+    let signature = read_signature(signature)?;
+    let pubkey = read_pubkey(public_key)?;
 
     // Verification
-    match public_key.verify(&signature, &message) {
+    match VerificationKey::try_from(pubkey)
+        .and_then(|vk| vk.verify(&Signature::from(signature), &message))
+    {
         Ok(()) => Ok(true),
         Err(_) => Ok(false),
     }
@@ -126,48 +100,20 @@ pub fn ed25519_batch_verify(
     debug_assert_eq!(messages.len(), signatures_len);
     debug_assert_eq!(messages.len(), public_keys.len());
 
-    let mut batch = ed25519::batch::Verifier::new();
+    let mut batch = batch::Verifier::new();
 
-    for (((i, &message), &signature), &public_key) in
-        (1..).zip(&messages).zip(signatures).zip(&public_keys)
+    for ((&message, &signature), &public_key) in messages
+        .iter()
+        .zip(signatures.iter())
+        .zip(public_keys.iter())
     {
         // Validation
-        if message.len() > MESSAGE_MAX_LEN {
-            return Err(CryptoError::msg_err(format!(
-                "message {}: too large: {}",
-                i,
-                message.len()
-            )));
-        }
-        if signature.len() != EDDSA_SIGNATURE_LEN {
-            return Err(CryptoError::sig_err(format!(
-                "signature {}: wrong / unsupported length: {}",
-                i,
-                signature.len()
-            )));
-        }
-        let pubkey_len = public_key.len();
-        if pubkey_len == 0 {
-            return Err(CryptoError::pubkey_err(format!("public key {}: empty", i)));
-        }
-        if pubkey_len != EDDSA_PUBKEY_LEN {
-            return Err(CryptoError::pubkey_err(format!(
-                "public key {}: wrong / unsupported length: {}",
-                i, pubkey_len,
-            )));
-        }
-
-        // Deserialization
-        let signature = ed25519::Signature::try_from(signature).map_err(|err| {
-            CryptoError::generic_err(format!("signature {}: {}", i, err.to_string()))
-        })?;
-
-        let public_key = ed25519::VerificationKey::try_from(public_key).map_err(|err| {
-            CryptoError::generic_err(format!("public key {}: {}", i, err.to_string()))
-        })?;
+        check_message_length(message)?;
+        let signature = read_signature(signature)?;
+        let pubkey = read_pubkey(public_key)?;
 
         // Enqueing
-        batch.queue((public_key.into(), signature, message));
+        batch.queue((pubkey.into(), signature.into(), message));
     }
 
     // Batch verification
@@ -177,9 +123,58 @@ pub fn ed25519_batch_verify(
     }
 }
 
+/// Error raised when signature is not 64 bytes long
+struct InvalidEd25519SignatureFormat;
+
+impl From<InvalidEd25519SignatureFormat> for CryptoError {
+    fn from(_original: InvalidEd25519SignatureFormat) -> Self {
+        CryptoError::invalid_signature_format()
+    }
+}
+
+fn read_signature(data: &[u8]) -> Result<[u8; 64], InvalidEd25519SignatureFormat> {
+    data.try_into().map_err(|_| InvalidEd25519SignatureFormat)
+}
+
+/// Error raised when pubkey is not 32 bytes long
+struct InvalidEd25519PubkeyFormat;
+
+impl From<InvalidEd25519PubkeyFormat> for CryptoError {
+    fn from(_original: InvalidEd25519PubkeyFormat) -> Self {
+        CryptoError::invalid_pubkey_format()
+    }
+}
+
+fn read_pubkey(data: &[u8]) -> Result<[u8; 32], InvalidEd25519PubkeyFormat> {
+    data.try_into().map_err(|_| InvalidEd25519PubkeyFormat)
+}
+
+struct MessageTooLong {
+    limit: usize,
+    actual: usize,
+}
+
+impl From<MessageTooLong> for CryptoError {
+    fn from(original: MessageTooLong) -> Self {
+        CryptoError::message_too_long(original.limit, original.actual)
+    }
+}
+
+fn check_message_length(message: &[u8]) -> Result<(), MessageTooLong> {
+    if message.len() > MESSAGE_MAX_LEN {
+        Err(MessageTooLong {
+            limit: MESSAGE_MAX_LEN,
+            actual: message.len(),
+        })
+    } else {
+        Ok(())
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
+    use ed25519_zebra::SigningKey;
     use serde::Deserialize;
 
     // For generic signature verification
@@ -222,10 +217,10 @@ mod tests {
     fn test_ed25519_verify() {
         let message = MSG.as_bytes();
         // Signing
-        let secret_key = ed25519::SigningKey::new(&mut OsRng);
+        let secret_key = SigningKey::new(&mut OsRng);
         let signature = secret_key.sign(&message);
 
-        let public_key = ed25519::VerificationKey::from(&secret_key);
+        let public_key = VerificationKey::from(&secret_key);
 
         // Serialization. Types can be converted to raw byte arrays with From/Into
         let signature_bytes: [u8; 64] = signature.into();
@@ -239,21 +234,21 @@ mod tests {
         assert!(!ed25519_verify(&bad_message, &signature_bytes, &public_key_bytes).unwrap());
 
         // Other pubkey fails
-        let other_secret_key = ed25519::SigningKey::new(&mut OsRng);
-        let other_public_key = ed25519::VerificationKey::from(&other_secret_key);
+        let other_secret_key = SigningKey::new(&mut OsRng);
+        let other_public_key = VerificationKey::from(&other_secret_key);
         let other_public_key_bytes: [u8; 32] = other_public_key.into();
         assert!(!ed25519_verify(&message, &signature_bytes, &other_public_key_bytes).unwrap());
     }
 
     #[test]
     fn test_cosmos_ed25519_verify() {
-        let secret_key = ed25519::SigningKey::try_from(
+        let secret_key = SigningKey::try_from(
             hex::decode(COSMOS_ED25519_PRIVATE_KEY_HEX)
                 .unwrap()
                 .as_slice(),
         )
         .unwrap();
-        let public_key = ed25519::VerificationKey::try_from(
+        let public_key = VerificationKey::try_from(
             hex::decode(COSMOS_ED25519_PUBLIC_KEY_HEX)
                 .unwrap()
                 .as_slice(),
diff --git a/packages/crypto/src/errors.rs b/packages/crypto/src/errors.rs
index e269137d6..f340580f6 100644
--- a/packages/crypto/src/errors.rs
+++ b/packages/crypto/src/errors.rs
@@ -19,27 +19,25 @@ pub enum CryptoError {
         #[cfg(feature = "backtraces")]
         backtrace: Backtrace,
     },
-    #[error("Message error: {msg}")]
-    MessageError {
-        msg: String,
+    #[error("Invalid hash format")]
+    InvalidHashFormat {
         #[cfg(feature = "backtraces")]
         backtrace: Backtrace,
     },
-    #[error("Hash error: {msg}")]
-    HashErr {
-        msg: String,
+    #[error("Invalid public key format")]
+    InvalidPubkeyFormat {
         #[cfg(feature = "backtraces")]
         backtrace: Backtrace,
     },
-    #[error("Signature error: {msg}")]
-    SignatureErr {
-        msg: String,
+    #[error("Invalid signature format")]
+    InvalidSignatureFormat {
         #[cfg(feature = "backtraces")]
         backtrace: Backtrace,
     },
-    #[error("Public key error: {msg}")]
-    PublicKeyErr {
-        msg: String,
+    #[error("Message is longer than supported by this implementation (Limit: {limit}, actual length: {actual})")]
+    MessageTooLong {
+        limit: usize,
+        actual: usize,
         #[cfg(feature = "backtraces")]
         backtrace: Backtrace,
     },
@@ -67,33 +65,31 @@ impl CryptoError {
         }
     }
 
-    pub fn msg_err<S: Into<String>>(msg: S) -> Self {
-        CryptoError::MessageError {
-            msg: msg.into(),
+    pub fn invalid_hash_format() -> Self {
+        CryptoError::InvalidHashFormat {
             #[cfg(feature = "backtraces")]
             backtrace: Backtrace::capture(),
         }
     }
 
-    pub fn hash_err<S: Into<String>>(msg: S) -> Self {
-        CryptoError::HashErr {
-            msg: msg.into(),
+    pub fn invalid_pubkey_format() -> Self {
+        CryptoError::InvalidPubkeyFormat {
             #[cfg(feature = "backtraces")]
             backtrace: Backtrace::capture(),
         }
     }
 
-    pub fn sig_err<S: Into<String>>(msg: S) -> Self {
-        CryptoError::SignatureErr {
-            msg: msg.into(),
+    pub fn invalid_signature_format() -> Self {
+        CryptoError::InvalidSignatureFormat {
             #[cfg(feature = "backtraces")]
             backtrace: Backtrace::capture(),
         }
     }
 
-    pub fn pubkey_err<S: Into<String>>(msg: S) -> Self {
-        CryptoError::PublicKeyErr {
-            msg: msg.into(),
+    pub fn message_too_long(limit: usize, actual: usize) -> Self {
+        CryptoError::MessageTooLong {
+            limit,
+            actual,
             #[cfg(feature = "backtraces")]
             backtrace: Backtrace::capture(),
         }
@@ -110,10 +106,10 @@ impl CryptoError {
     /// contract VM boundary.
     pub fn code(&self) -> u32 {
         match self {
-            CryptoError::MessageError { .. } => 2,
-            CryptoError::HashErr { .. } => 3,
-            CryptoError::SignatureErr { .. } => 4,
-            CryptoError::PublicKeyErr { .. } => 5,
+            CryptoError::MessageTooLong { .. } => 2,
+            CryptoError::InvalidHashFormat { .. } => 3,
+            CryptoError::InvalidSignatureFormat { .. } => 4,
+            CryptoError::InvalidPubkeyFormat { .. } => 5,
             CryptoError::InvalidRecoveryParam { .. } => 6,
             CryptoError::BatchErr { .. } => 7,
             CryptoError::GenericErr { .. } => 10,
@@ -149,45 +145,40 @@ mod tests {
     }
 
     #[test]
-    fn msg_err_works() {
-        let error = CryptoError::msg_err("something went wrong with the msg");
+    fn invalid_hash_format_works() {
+        let error = CryptoError::invalid_hash_format();
         match error {
-            CryptoError::MessageError { msg, .. } => {
-                assert_eq!(msg, "something went wrong with the msg")
-            }
+            CryptoError::InvalidHashFormat { .. } => {}
             _ => panic!("wrong error type!"),
         }
     }
 
     #[test]
-    fn hash_err_works() {
-        let error = CryptoError::hash_err("something went wrong with the hash");
+    fn invalid_signature_format_works() {
+        let error = CryptoError::invalid_signature_format();
         match error {
-            CryptoError::HashErr { msg, .. } => {
-                assert_eq!(msg, "something went wrong with the hash")
-            }
+            CryptoError::InvalidSignatureFormat { .. } => {}
             _ => panic!("wrong error type!"),
         }
     }
 
     #[test]
-    fn sig_err_works() {
-        let error = CryptoError::sig_err("something went wrong with the sig");
+    fn message_too_long_works() {
+        let error = CryptoError::message_too_long(5, 7);
         match error {
-            CryptoError::SignatureErr { msg, .. } => {
-                assert_eq!(msg, "something went wrong with the sig")
+            CryptoError::MessageTooLong { limit, actual, .. } => {
+                assert_eq!(limit, 5);
+                assert_eq!(actual, 7);
             }
             _ => panic!("wrong error type!"),
         }
     }
 
     #[test]
-    fn pubkey_err_works() {
-        let error = CryptoError::pubkey_err("something went wrong with the pubkey");
+    fn invalid_pubkey_format_works() {
+        let error = CryptoError::invalid_pubkey_format();
         match error {
-            CryptoError::PublicKeyErr { msg, .. } => {
-                assert_eq!(msg, "something went wrong with the pubkey")
-            }
+            CryptoError::InvalidPubkeyFormat { .. } => {}
             _ => panic!("wrong error type!"),
         }
     }
diff --git a/packages/crypto/src/lib.rs b/packages/crypto/src/lib.rs
index 8e3cdfe60..ac2d6482c 100644
--- a/packages/crypto/src/lib.rs
+++ b/packages/crypto/src/lib.rs
@@ -12,7 +12,7 @@ mod secp256k1;
 #[doc(hidden)]
 pub use crate::ed25519::{ed25519_batch_verify, ed25519_verify};
 #[doc(hidden)]
-pub use crate::ed25519::{BATCH_MAX_LEN, EDDSA_PUBKEY_LEN, EDDSA_SIGNATURE_LEN, MESSAGE_MAX_LEN};
+pub use crate::ed25519::{BATCH_MAX_LEN, EDDSA_PUBKEY_LEN, MESSAGE_MAX_LEN};
 #[doc(hidden)]
 pub use crate::errors::{CryptoError, CryptoResult};
 #[doc(hidden)]
diff --git a/packages/crypto/src/secp256k1.rs b/packages/crypto/src/secp256k1.rs
index 1089ad229..5f4382c02 100644
--- a/packages/crypto/src/secp256k1.rs
+++ b/packages/crypto/src/secp256k1.rs
@@ -5,6 +5,7 @@ use k256::{
     ecdsa::{Signature, VerifyingKey},                   // type aliases
     elliptic_curve::sec1::ToEncodedPoint,
 };
+use std::convert::TryInto;
 
 use crate::errors::{CryptoError, CryptoResult};
 use crate::identity_digest::Identity256;
@@ -18,14 +19,8 @@ pub const MESSAGE_HASH_MAX_LEN: usize = 32;
 /// Length of a serialized signature
 pub const ECDSA_SIGNATURE_LEN: usize = 64;
 
-/// Compressed public key prefix (variant 1)
-const ECDSA_COMPRESSED_PUBKEY_PREFIX_1: u8 = 0x02;
-/// Compressed public key prefix (variant 2)
-const ECDSA_COMPRESSED_PUBKEY_PREFIX_2: u8 = 0x03;
 /// Length of a serialized compressed public key
 const ECDSA_COMPRESSED_PUBKEY_LEN: usize = 33;
-/// Uncompressed public key prefix
-const ECDSA_UNCOMPRESSED_PUBKEY_PREFIX: u8 = 0x04;
 /// Length of a serialized uncompressed public key
 const ECDSA_UNCOMPRESSED_PUBKEY_LEN: usize = 65;
 /// Max length of a serialized public key
@@ -46,40 +41,15 @@ pub fn secp256k1_verify(
     signature: &[u8],
     public_key: &[u8],
 ) -> CryptoResult<bool> {
-    if message_hash.len() != MESSAGE_HASH_MAX_LEN {
-        return Err(CryptoError::hash_err(format!(
-            "wrong length: {}",
-            message_hash.len()
-        )));
-    }
-    if signature.len() != ECDSA_SIGNATURE_LEN {
-        return Err(CryptoError::sig_err(format!(
-            "wrong / unsupported length: {}",
-            signature.len()
-        )));
-    }
-    let pubkey_len = public_key.len();
-    if pubkey_len == 0 {
-        return Err(CryptoError::pubkey_err("empty"));
-    }
-    let pubkey_fmt = public_key[0];
-    if !(pubkey_len == ECDSA_UNCOMPRESSED_PUBKEY_LEN
-        && pubkey_fmt == ECDSA_UNCOMPRESSED_PUBKEY_PREFIX
-        || pubkey_len == ECDSA_COMPRESSED_PUBKEY_LEN
-            && (pubkey_fmt == ECDSA_COMPRESSED_PUBKEY_PREFIX_1
-                || pubkey_fmt == ECDSA_COMPRESSED_PUBKEY_PREFIX_2))
-    {
-        return Err(CryptoError::pubkey_err(format!(
-            "wrong / unsupported length/format: {}/{}",
-            pubkey_len, pubkey_fmt,
-        )));
-    }
+    let message_hash = read_hash(message_hash)?;
+    let signature = read_signature(signature)?;
+    check_pubkey(public_key)?;
 
     // Already hashed, just build Digest container
     let message_digest = Identity256::new().chain(message_hash);
 
     let mut signature =
-        Signature::from_bytes(signature).map_err(|e| CryptoError::generic_err(e.to_string()))?;
+        Signature::from_bytes(&signature).map_err(|e| CryptoError::generic_err(e.to_string()))?;
     // Non low-S signatures require normalization
     signature
         .normalize_s()
@@ -110,25 +80,15 @@ pub fn secp256k1_recover_pubkey(
     signature: &[u8],
     recovery_param: u8,
 ) -> Result<Vec<u8>, CryptoError> {
-    if message_hash.len() != MESSAGE_HASH_MAX_LEN {
-        return Err(CryptoError::hash_err(format!(
-            "wrong length: {}",
-            message_hash.len()
-        )));
-    }
-    if signature.len() != ECDSA_SIGNATURE_LEN {
-        return Err(CryptoError::sig_err(format!(
-            "wrong / unsupported length: {}",
-            signature.len()
-        )));
-    }
+    let message_hash = read_hash(message_hash)?;
+    let signature = read_signature(signature)?;
 
     let id =
         recoverable::Id::new(recovery_param).map_err(|_| CryptoError::invalid_recovery_param())?;
 
     // Compose extended signature
     let signature =
-        Signature::from_bytes(signature).map_err(|e| CryptoError::generic_err(e.to_string()))?;
+        Signature::from_bytes(&signature).map_err(|e| CryptoError::generic_err(e.to_string()))?;
     let extended_signature = recoverable::Signature::new(&signature, id)
         .map_err(|e| CryptoError::generic_err(e.to_string()))?;
 
@@ -141,6 +101,56 @@ pub fn secp256k1_recover_pubkey(
     Ok(encoded)
 }
 
+/// Error raised when hash is not 32 bytes long
+struct InvalidSecp256k1HashFormat;
+
+impl From<InvalidSecp256k1HashFormat> for CryptoError {
+    fn from(_original: InvalidSecp256k1HashFormat) -> Self {
+        CryptoError::invalid_hash_format()
+    }
+}
+
+fn read_hash(data: &[u8]) -> Result<[u8; 32], InvalidSecp256k1HashFormat> {
+    data.try_into().map_err(|_| InvalidSecp256k1HashFormat)
+}
+
+/// Error raised when signature is not 64 bytes long (32 bytes r, 32 bytes s)
+struct InvalidSecp256k1SignatureFormat;
+
+impl From<InvalidSecp256k1SignatureFormat> for CryptoError {
+    fn from(_original: InvalidSecp256k1SignatureFormat) -> Self {
+        CryptoError::invalid_signature_format()
+    }
+}
+
+fn read_signature(data: &[u8]) -> Result<[u8; 64], InvalidSecp256k1SignatureFormat> {
+    data.try_into().map_err(|_| InvalidSecp256k1SignatureFormat)
+}
+
+/// Error raised when public key is not in one of the two supported formats:
+/// 1. Uncompressed: 65 bytes starting with 0x04
+/// 2. Compressed: 33 bytes starting with 0x02 or 0x03
+struct InvalidSecp256k1PubkeyFormat;
+
+impl From<InvalidSecp256k1PubkeyFormat> for CryptoError {
+    fn from(_original: InvalidSecp256k1PubkeyFormat) -> Self {
+        CryptoError::invalid_pubkey_format()
+    }
+}
+
+fn check_pubkey(data: &[u8]) -> Result<(), InvalidSecp256k1PubkeyFormat> {
+    let ok = match data.first() {
+        Some(0x02) | Some(0x03) => data.len() == ECDSA_COMPRESSED_PUBKEY_LEN,
+        Some(0x04) => data.len() == ECDSA_UNCOMPRESSED_PUBKEY_LEN,
+        _ => false,
+    };
+    if ok {
+        Ok(())
+    } else {
+        Err(InvalidSecp256k1PubkeyFormat)
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/packages/std/src/errors/recover_pubkey_error.rs b/packages/std/src/errors/recover_pubkey_error.rs
index 344d91722..86e0c4e13 100644
--- a/packages/std/src/errors/recover_pubkey_error.rs
+++ b/packages/std/src/errors/recover_pubkey_error.rs
@@ -7,10 +7,10 @@ use thiserror::Error;
 
 #[derive(Error, Debug)]
 pub enum RecoverPubkeyError {
-    #[error("Hash error")]
-    HashErr,
-    #[error("Signature error")]
-    SignatureErr,
+    #[error("Invalid hash format")]
+    InvalidHashFormat,
+    #[error("Invalid signature format")]
+    InvalidSignatureFormat,
     #[error("Invalid recovery parameter. Supported values: 0 and 1.")]
     InvalidRecoveryParam,
     #[error("Unknown error: {error_code}")]
@@ -34,8 +34,12 @@ impl RecoverPubkeyError {
 impl PartialEq<RecoverPubkeyError> for RecoverPubkeyError {
     fn eq(&self, rhs: &RecoverPubkeyError) -> bool {
         match self {
-            RecoverPubkeyError::HashErr => matches!(rhs, RecoverPubkeyError::HashErr),
-            RecoverPubkeyError::SignatureErr => matches!(rhs, RecoverPubkeyError::SignatureErr),
+            RecoverPubkeyError::InvalidHashFormat => {
+                matches!(rhs, RecoverPubkeyError::InvalidHashFormat)
+            }
+            RecoverPubkeyError::InvalidSignatureFormat => {
+                matches!(rhs, RecoverPubkeyError::InvalidSignatureFormat)
+            }
             RecoverPubkeyError::InvalidRecoveryParam => {
                 matches!(rhs, RecoverPubkeyError::InvalidRecoveryParam)
             }
@@ -58,10 +62,12 @@ impl PartialEq<RecoverPubkeyError> for RecoverPubkeyError {
 impl From<CryptoError> for RecoverPubkeyError {
     fn from(original: CryptoError) -> Self {
         match original {
-            CryptoError::MessageError { .. } => panic!("Conversion not supported"),
-            CryptoError::HashErr { .. } => RecoverPubkeyError::HashErr,
-            CryptoError::SignatureErr { .. } => RecoverPubkeyError::SignatureErr,
-            CryptoError::PublicKeyErr { .. } => panic!("Conversion not supported"),
+            CryptoError::MessageTooLong { .. } => panic!("Conversion not supported"),
+            CryptoError::InvalidHashFormat { .. } => RecoverPubkeyError::InvalidHashFormat,
+            CryptoError::InvalidPubkeyFormat { .. } => panic!("Conversion not supported"),
+            CryptoError::InvalidSignatureFormat { .. } => {
+                RecoverPubkeyError::InvalidSignatureFormat
+            }
             CryptoError::GenericErr { .. } => RecoverPubkeyError::unknown_err(original.code()),
             CryptoError::InvalidRecoveryParam { .. } => RecoverPubkeyError::InvalidRecoveryParam,
             CryptoError::BatchErr { .. } => panic!("Conversion not supported"),
diff --git a/packages/std/src/errors/verification_error.rs b/packages/std/src/errors/verification_error.rs
index 18f1d2802..da2fc6113 100644
--- a/packages/std/src/errors/verification_error.rs
+++ b/packages/std/src/errors/verification_error.rs
@@ -12,14 +12,14 @@ pub enum VerificationError {
     BatchErr,
     #[error("Generic error")]
     GenericErr,
-    #[error("Message error")]
-    MessageErr,
-    #[error("Hash error")]
-    HashErr,
-    #[error("Signature error")]
-    SignatureErr,
-    #[error("Public key error")]
-    PublicKeyErr,
+    #[error("Message is longer than supported")]
+    MessageTooLong,
+    #[error("Invalid hash format")]
+    InvalidHashFormat,
+    #[error("Invalid signature format")]
+    InvalidSignatureFormat,
+    #[error("Invalid public key format")]
+    InvalidPubkeyFormat,
     #[error("Invalid recovery parameter. Supported values: 0 and 1.")]
     InvalidRecoveryParam,
     #[error("Unknown error: {error_code}")]
@@ -45,10 +45,16 @@ impl PartialEq<VerificationError> for VerificationError {
         match self {
             VerificationError::BatchErr => matches!(rhs, VerificationError::BatchErr),
             VerificationError::GenericErr => matches!(rhs, VerificationError::GenericErr),
-            VerificationError::MessageErr => matches!(rhs, VerificationError::MessageErr),
-            VerificationError::HashErr => matches!(rhs, VerificationError::HashErr),
-            VerificationError::SignatureErr => matches!(rhs, VerificationError::SignatureErr),
-            VerificationError::PublicKeyErr => matches!(rhs, VerificationError::PublicKeyErr),
+            VerificationError::MessageTooLong => matches!(rhs, VerificationError::MessageTooLong),
+            VerificationError::InvalidHashFormat => {
+                matches!(rhs, VerificationError::InvalidHashFormat)
+            }
+            VerificationError::InvalidPubkeyFormat => {
+                matches!(rhs, VerificationError::InvalidPubkeyFormat)
+            }
+            VerificationError::InvalidSignatureFormat => {
+                matches!(rhs, VerificationError::InvalidSignatureFormat)
+            }
             VerificationError::InvalidRecoveryParam => {
                 matches!(rhs, VerificationError::InvalidRecoveryParam)
             }
@@ -71,10 +77,10 @@ impl PartialEq<VerificationError> for VerificationError {
 impl From<CryptoError> for VerificationError {
     fn from(original: CryptoError) -> Self {
         match original {
-            CryptoError::MessageError { .. } => VerificationError::MessageErr,
-            CryptoError::HashErr { .. } => VerificationError::HashErr,
-            CryptoError::SignatureErr { .. } => VerificationError::SignatureErr,
-            CryptoError::PublicKeyErr { .. } => VerificationError::PublicKeyErr,
+            CryptoError::MessageTooLong { .. } => VerificationError::MessageTooLong,
+            CryptoError::InvalidHashFormat { .. } => VerificationError::InvalidHashFormat,
+            CryptoError::InvalidPubkeyFormat { .. } => VerificationError::InvalidPubkeyFormat,
+            CryptoError::InvalidSignatureFormat { .. } => VerificationError::InvalidSignatureFormat,
             CryptoError::GenericErr { .. } => VerificationError::GenericErr,
             CryptoError::InvalidRecoveryParam { .. } => VerificationError::InvalidRecoveryParam,
             CryptoError::BatchErr { .. } => VerificationError::BatchErr,
diff --git a/packages/std/src/imports.rs b/packages/std/src/imports.rs
index 9ebcfe6b8..3da2b1643 100644
--- a/packages/std/src/imports.rs
+++ b/packages/std/src/imports.rs
@@ -208,10 +208,10 @@ impl Api for ExternalApi {
         match result {
             0 => Ok(true),
             1 => Ok(false),
-            2 => Err(VerificationError::MessageErr), // shouldn't happen
-            3 => Err(VerificationError::HashErr),
-            4 => Err(VerificationError::SignatureErr),
-            5 => Err(VerificationError::PublicKeyErr),
+            2 => panic!("MessageTooLong must not happen. This is a bug in the VM."),
+            3 => Err(VerificationError::InvalidHashFormat),
+            4 => Err(VerificationError::InvalidSignatureFormat),
+            5 => Err(VerificationError::InvalidPubkeyFormat),
             10 => Err(VerificationError::GenericErr),
             error_code => Err(VerificationError::unknown_err(error_code)),
         }
@@ -237,8 +237,9 @@ impl Api for ExternalApi {
                 let pubkey = unsafe { consume_region(pubkey_ptr as *mut Region) };
                 Ok(pubkey)
             }
-            3 => Err(RecoverPubkeyError::HashErr),
-            4 => Err(RecoverPubkeyError::SignatureErr),
+            2 => panic!("MessageTooLong must not happen. This is a bug in the VM."),
+            3 => Err(RecoverPubkeyError::InvalidHashFormat),
+            4 => Err(RecoverPubkeyError::InvalidSignatureFormat),
             6 => Err(RecoverPubkeyError::InvalidRecoveryParam),
             error_code => Err(RecoverPubkeyError::unknown_err(error_code)),
         }
@@ -261,10 +262,10 @@ impl Api for ExternalApi {
         match result {
             0 => Ok(true),
             1 => Ok(false),
-            2 => Err(VerificationError::MessageErr),
-            3 => Err(VerificationError::HashErr), // shouldn't happen
-            4 => Err(VerificationError::SignatureErr),
-            5 => Err(VerificationError::PublicKeyErr),
+            2 => Err(VerificationError::MessageTooLong),
+            3 => panic!("InvalidHashFormat must not happen. This is a bug in the VM."),
+            4 => Err(VerificationError::InvalidSignatureFormat),
+            5 => Err(VerificationError::InvalidPubkeyFormat),
             10 => Err(VerificationError::GenericErr),
             error_code => Err(VerificationError::unknown_err(error_code)),
         }
@@ -293,10 +294,10 @@ impl Api for ExternalApi {
         match result {
             0 => Ok(true),
             1 => Ok(false),
-            2 => Err(VerificationError::MessageErr),
-            3 => Err(VerificationError::HashErr), // shouldn't happen
-            4 => Err(VerificationError::SignatureErr),
-            5 => Err(VerificationError::PublicKeyErr),
+            2 => Err(VerificationError::MessageTooLong),
+            3 => panic!("InvalidHashFormat must not happen. This is a bug in the VM."),
+            4 => Err(VerificationError::InvalidSignatureFormat),
+            5 => Err(VerificationError::InvalidPubkeyFormat),
             10 => Err(VerificationError::GenericErr),
             error_code => Err(VerificationError::unknown_err(error_code)),
         }
diff --git a/packages/std/src/mock.rs b/packages/std/src/mock.rs
index 8dfa1ab0f..c0ed30b66 100644
--- a/packages/std/src/mock.rs
+++ b/packages/std/src/mock.rs
@@ -611,7 +611,7 @@ mod tests {
         let public_key = vec![];
 
         let res = api.secp256k1_verify(&hash, &signature, &public_key);
-        assert_eq!(res.unwrap_err(), VerificationError::PublicKeyErr);
+        assert_eq!(res.unwrap_err(), VerificationError::InvalidPubkeyFormat);
     }
 
     #[test]
@@ -677,7 +677,7 @@ mod tests {
         malformed_hash.push(0x8a);
         let result = api.secp256k1_recover_pubkey(&malformed_hash, &signature, recovery_param);
         match result.unwrap_err() {
-            RecoverPubkeyError::HashErr => {}
+            RecoverPubkeyError::InvalidHashFormat => {}
             err => panic!("Unexpected error: {:?}", err),
         }
     }
@@ -718,7 +718,7 @@ mod tests {
         let public_key = vec![];
 
         let res = api.ed25519_verify(&msg, &signature, &public_key);
-        assert_eq!(res.unwrap_err(), VerificationError::PublicKeyErr);
+        assert_eq!(res.unwrap_err(), VerificationError::InvalidPubkeyFormat);
     }
 
     // Basic "works" test.
@@ -773,7 +773,7 @@ mod tests {
         let public_keys: Vec<&[u8]> = vec![&public_key];
 
         let res = api.ed25519_batch_verify(&msgs, &signatures, &public_keys);
-        assert_eq!(res.unwrap_err(), VerificationError::PublicKeyErr);
+        assert_eq!(res.unwrap_err(), VerificationError::InvalidPubkeyFormat);
     }
 
     #[test]
diff --git a/packages/vm/src/imports.rs b/packages/vm/src/imports.rs
index 932cbf0ec..397217a89 100644
--- a/packages/vm/src/imports.rs
+++ b/packages/vm/src/imports.rs
@@ -7,7 +7,7 @@ use cosmwasm_crypto::{
 };
 use cosmwasm_crypto::{
     BATCH_MAX_LEN, ECDSA_PUBKEY_MAX_LEN, ECDSA_SIGNATURE_LEN, EDDSA_PUBKEY_LEN,
-    EDDSA_SIGNATURE_LEN, MESSAGE_HASH_MAX_LEN, MESSAGE_MAX_LEN,
+    MESSAGE_HASH_MAX_LEN, MESSAGE_MAX_LEN,
 };
 
 #[cfg(feature = "iterator")]
@@ -45,6 +45,8 @@ const MAX_LENGTH_CANONICAL_ADDRESS: usize = 32;
 /// The maximum allowed size for bech32 (https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32)
 const MAX_LENGTH_HUMAN_ADDRESS: usize = 90;
 const MAX_LENGTH_QUERY_CHAIN_REQUEST: usize = 64 * KI;
+/// Length of a serialized Ed25519  signature
+const MAX_LENGTH_ED25519_SIGNATURE: usize = 64;
 
 /// Max length for a debug message
 const MAX_LENGTH_DEBUG: usize = 2 * MI;
@@ -300,14 +302,13 @@ fn do_secp256k1_verify<A: BackendApi, S: Storage, Q: Querier>(
     process_gas_info::<A, S, Q>(env, gas_info)?;
     Ok(result.map_or_else(
         |err| match err {
-            CryptoError::MessageError { .. }
-            | CryptoError::HashErr { .. }
-            | CryptoError::SignatureErr { .. }
-            | CryptoError::PublicKeyErr { .. }
+            CryptoError::InvalidHashFormat { .. }
+            | CryptoError::InvalidPubkeyFormat { .. }
+            | CryptoError::InvalidSignatureFormat { .. }
             | CryptoError::GenericErr { .. } => err.code(),
-            CryptoError::BatchErr { .. } | CryptoError::InvalidRecoveryParam { .. } => {
-                panic!("Error must not happen for this call")
-            }
+            CryptoError::BatchErr { .. }
+            | CryptoError::InvalidRecoveryParam { .. }
+            | CryptoError::MessageTooLong { .. } => panic!("Error must not happen for this call"),
         },
         |valid| if valid { 0 } else { 1 },
     ))
@@ -335,14 +336,13 @@ fn do_secp256k1_recover_pubkey<A: BackendApi, S: Storage, Q: Querier>(
             Ok(to_low_half(pubkey_ptr))
         }
         Err(err) => match err {
-            CryptoError::MessageError { .. }
-            | CryptoError::HashErr { .. }
-            | CryptoError::SignatureErr { .. }
+            CryptoError::InvalidHashFormat { .. }
+            | CryptoError::InvalidSignatureFormat { .. }
             | CryptoError::InvalidRecoveryParam { .. }
             | CryptoError::GenericErr { .. } => Ok(to_high_half(err.code())),
-            CryptoError::BatchErr { .. } | CryptoError::PublicKeyErr { .. } => {
-                panic!("Error must not happen for this call")
-            }
+            CryptoError::BatchErr { .. }
+            | CryptoError::InvalidPubkeyFormat { .. }
+            | CryptoError::MessageTooLong { .. } => panic!("Error must not happen for this call"),
         },
     }
 }
@@ -354,7 +354,7 @@ fn do_ed25519_verify<A: BackendApi, S: Storage, Q: Querier>(
     pubkey_ptr: u32,
 ) -> VmResult<u32> {
     let message = read_region(&env.memory(), message_ptr, MESSAGE_MAX_LEN)?;
-    let signature = read_region(&env.memory(), signature_ptr, EDDSA_SIGNATURE_LEN)?;
+    let signature = read_region(&env.memory(), signature_ptr, MAX_LENGTH_ED25519_SIGNATURE)?;
     let pubkey = read_region(&env.memory(), pubkey_ptr, EDDSA_PUBKEY_LEN)?;
 
     let result = ed25519_verify(&message, &signature, &pubkey);
@@ -362,12 +362,13 @@ fn do_ed25519_verify<A: BackendApi, S: Storage, Q: Querier>(
     process_gas_info::<A, S, Q>(env, gas_info)?;
     Ok(result.map_or_else(
         |err| match err {
-            CryptoError::MessageError { .. }
-            | CryptoError::HashErr { .. }
-            | CryptoError::SignatureErr { .. }
-            | CryptoError::PublicKeyErr { .. }
+            CryptoError::MessageTooLong { .. }
+            | CryptoError::InvalidPubkeyFormat { .. }
+            | CryptoError::InvalidSignatureFormat { .. }
             | CryptoError::GenericErr { .. } => err.code(),
-            CryptoError::BatchErr { .. } | CryptoError::InvalidRecoveryParam { .. } => {
+            CryptoError::BatchErr { .. }
+            | CryptoError::InvalidHashFormat { .. }
+            | CryptoError::InvalidRecoveryParam { .. } => {
                 panic!("Error must not happen for this call")
             }
         },
@@ -389,7 +390,7 @@ fn do_ed25519_batch_verify<A: BackendApi, S: Storage, Q: Querier>(
     let signatures = read_region(
         &env.memory(),
         signatures_ptr,
-        (EDDSA_SIGNATURE_LEN + 4) * BATCH_MAX_LEN,
+        (MAX_LENGTH_ED25519_SIGNATURE + 4) * BATCH_MAX_LEN,
     )?;
     let public_keys = read_region(
         &env.memory(),
@@ -405,7 +406,19 @@ fn do_ed25519_batch_verify<A: BackendApi, S: Storage, Q: Querier>(
     let gas_info =
         GasInfo::with_cost(GAS_COST_BATCH_VERIFY_ED25519_SIGNATURE * signatures.len() as u64);
     process_gas_info::<A, S, Q>(env, gas_info)?;
-    Ok(result.map_or_else(|err| err.code(), |valid| (!valid).into()))
+    Ok(result.map_or_else(
+        |err| match err {
+            CryptoError::BatchErr { .. }
+            | CryptoError::MessageTooLong { .. }
+            | CryptoError::InvalidPubkeyFormat { .. }
+            | CryptoError::InvalidSignatureFormat { .. }
+            | CryptoError::GenericErr { .. } => err.code(),
+            CryptoError::InvalidHashFormat { .. } | CryptoError::InvalidRecoveryParam { .. } => {
+                panic!("Error must not happen for this call")
+            }
+        },
+        |valid| (!valid).into(),
+    ))
 }
 
 /// Creates a Region in the contract, writes the given data to it and returns the memory location
@@ -1181,7 +1194,7 @@ mod tests {
 
         assert_eq!(
             do_secp256k1_verify::<MA, MS, MQ>(&env, hash_ptr, sig_ptr, pubkey_ptr).unwrap(),
-            3 // mapped HashErr
+            3 // mapped InvalidHashFormat
         );
     }
 
@@ -1245,7 +1258,7 @@ mod tests {
 
         assert_eq!(
             do_secp256k1_verify::<MA, MS, MQ>(&env, hash_ptr, sig_ptr, pubkey_ptr).unwrap(),
-            4 // mapped SignatureErr
+            4 // mapped InvalidSignatureFormat
         )
     }
 
@@ -1265,7 +1278,7 @@ mod tests {
 
         assert_eq!(
             do_secp256k1_verify::<MA, MS, MQ>(&env, hash_ptr, sig_ptr, pubkey_ptr).unwrap(),
-            5 // mapped PublicKeyError
+            5 // mapped InvalidPubkeyFormat
         )
     }
 
@@ -1329,7 +1342,7 @@ mod tests {
 
         assert_eq!(
             do_secp256k1_verify::<MA, MS, MQ>(&env, hash_ptr, sig_ptr, pubkey_ptr).unwrap(),
-            5 // mapped PublicKeyErr
+            5 // mapped InvalidPubkeyFormat
         )
     }
 
@@ -1347,7 +1360,7 @@ mod tests {
 
         assert_eq!(
             do_secp256k1_verify::<MA, MS, MQ>(&env, hash_ptr, sig_ptr, pubkey_ptr).unwrap(),
-            5 // mapped PublicKeyError
+            5 // mapped InvalidPubkeyFormat
         )
     }
 
@@ -1492,7 +1505,7 @@ mod tests {
             VmError::CommunicationErr {
                 source: CommunicationError::RegionLengthTooBig { length, .. },
                 ..
-            } => assert_eq!(length, EDDSA_SIGNATURE_LEN + 1),
+            } => assert_eq!(length, MAX_LENGTH_ED25519_SIGNATURE + 1),
             e => panic!("Unexpected error: {:?}", e),
         }
     }
@@ -1513,7 +1526,7 @@ mod tests {
 
         assert_eq!(
             do_ed25519_verify::<MA, MS, MQ>(&env, msg_ptr, sig_ptr, pubkey_ptr).unwrap(),
-            4 // mapped SignatureErr
+            4 // mapped InvalidSignatureFormat
         )
     }
 
@@ -1577,7 +1590,7 @@ mod tests {
 
         assert_eq!(
             do_ed25519_verify::<MA, MS, MQ>(&env, msg_ptr, sig_ptr, pubkey_ptr).unwrap(),
-            5 // mapped PublicKeyErr
+            5 // mapped InvalidPubkeyFormat
         )
     }
 
@@ -1595,7 +1608,7 @@ mod tests {
 
         assert_eq!(
             do_ed25519_verify::<MA, MS, MQ>(&env, msg_ptr, sig_ptr, pubkey_ptr).unwrap(),
-            5 // mapped PublicKeyError
+            5 // mapped InvalidPubkeyFormat
         )
     }
 
@@ -1606,14 +1619,14 @@ mod tests {
 
         let msg = vec![0x22; MESSAGE_HASH_MAX_LEN];
         let msg_ptr = write_data(&env, &msg);
-        let sig = vec![0x22; EDDSA_SIGNATURE_LEN];
+        let sig = vec![0x22; MAX_LENGTH_ED25519_SIGNATURE];
         let sig_ptr = write_data(&env, &sig);
         let pubkey = vec![0x04; EDDSA_PUBKEY_LEN];
         let pubkey_ptr = write_data(&env, &pubkey);
 
         assert_eq!(
             do_ed25519_verify::<MA, MS, MQ>(&env, msg_ptr, sig_ptr, pubkey_ptr).unwrap(),
-            10 // mapped GenericErr
+            1 // verification failure
         )
     }
 


Test patch:
diff --git a/contracts/crypto-verify/tests/integration.rs b/contracts/crypto-verify/tests/integration.rs
index 36f1e4335..90b8c8bf7 100644
--- a/contracts/crypto-verify/tests/integration.rs
+++ b/contracts/crypto-verify/tests/integration.rs
@@ -124,7 +124,10 @@ fn cosmos_signature_verify_errors() {
         public_key: Binary(public_key),
     };
     let res = query(&mut deps, mock_env(), verify_msg);
-    assert_eq!(res.unwrap_err(), "Verification error: Public key error")
+    assert_eq!(
+        res.unwrap_err(),
+        "Verification error: Invalid public key format"
+    )
 }
 
 #[test]
@@ -304,7 +307,10 @@ fn tendermint_signature_verify_errors() {
         public_key: Binary(public_key),
     };
     let res = query(&mut deps, mock_env(), verify_msg);
-    assert_eq!(res.unwrap_err(), "Verification error: Public key error")
+    assert_eq!(
+        res.unwrap_err(),
+        "Verification error: Invalid public key format"
+    )
 }
 
 #[test]
@@ -457,7 +463,10 @@ fn tendermint_signatures_batch_verify_errors() {
         public_keys,
     };
     let res = query(&mut deps, mock_env(), verify_msg);
-    assert_eq!(res.unwrap_err(), "Verification error: Public key error")
+    assert_eq!(
+        res.unwrap_err(),
+        "Verification error: Invalid public key format"
+    )
 }
 
 #[test]