Skip to content

Interoperability issue with PKCS#1 v1.5 signature verification #234

@cmcqueen

Description

@cmcqueen

I am trying to use this crate to verify a PKCS#1 v1.5 signature that was created with Python pycryptodome.

The following Python + pycryptodome example code successfully verifies a PKCS#1 v1.5 signature for a message:

from base64 import b64decode
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15 as pkcs1_15_sign

def main():
    public_key_pem = """-----BEGIN PUBLIC KEY-----
        MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzyaAz9Ak2ruY/9npoZ11
        S4SwZpw6Qx/4nk09xZh4nLsxEjXfXYZBzyLwLnEr040h6ZoGp4zOjAeBK+AsF2ev
        6hNLktwmF7xOXA0VtlTIXX3zl96Xax4ffeEy0uoyggGI9JvH+d+Zvb07OvAbn9js
        xEKVW2CUWHq9vHWUgB5mNf2B3iLj52NAjupiwyb7MfmnCCSWyjuJha428Vdsk4py
        iHn3k0Z1drI3kda9x21X5EfAkdsfm++FFJ5VpB7mRpGT3qg0zX9MtID8N0ZlTYCd
        7ZRlV2tPTOlOdH34tEUShESieKQBrUyxc0IT5wngxd3O6ZJ5CCLzGc4XSAeXkp8t
        9QIDAQAB
        -----END PUBLIC KEY-----"""
    signature64 = (
        "jdTNMwoSCf5bJWeCkKve5kOdKkuLtRokcItjLHEwGRcCnmmTChk6qmopKPdiGvJL"
        "w3iJfGHSh2Z71yERX35m/RVLvkbnnYQtssKFVDj49ttnDXkK/pcPLdbnWc2GPbLN"
        "v5XIpgiR2FPyp2PGbwYDoYNFnOI4ICJSBfKLjjYECCGbKKj4w5QBGzXyiCL0gV6l"
        "iS/7Wj1BCjwDb09FJF3oLGM5kpQlP3r1Ksh38cxsdUbBA/+29TOYclGEqw8VwgT9"
        "s66tTm8sylPTu/4vTFmaHBPiLNOOE9SUl4+zU2GjpqVNtVfj66f/Lm0x1lS8qaxt"
        "AY7y2KV0G5RvDkykZ7y3Yw==")
    message = b"0x59bd329d"

    # Decode base-64 signature
    signature = b64decode(signature64)

    # Calculate SHA256 hash of the message.
    message_hash = SHA256.new(message)

    # Import the public key PEM.
    public_key = RSA.importKey(public_key_pem)
    pub_verifier = pkcs1_15_sign.new(public_key)

    try:
        pub_verifier.verify(message_hash, signature)
    except Exception:
        print("Failed")
    else:
        print("Verified")

if __name__ == "__main__":
    main()

The following Rust code, using rsa v0.7.2, I think is equivalent, but fails the verification:

use base64ct::{Base64, Encoding};
use rsa::{
    pkcs1v15::Signature, pkcs1v15::VerifyingKey, pkcs8::DecodePublicKey, signature::Verifier,
};
use sha2::Sha256;

fn main() {
    let public_key_pem = "-----BEGIN PUBLIC KEY-----\n\
        MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzyaAz9Ak2ruY/9npoZ11\n\
        S4SwZpw6Qx/4nk09xZh4nLsxEjXfXYZBzyLwLnEr040h6ZoGp4zOjAeBK+AsF2ev\n\
        6hNLktwmF7xOXA0VtlTIXX3zl96Xax4ffeEy0uoyggGI9JvH+d+Zvb07OvAbn9js\n\
        xEKVW2CUWHq9vHWUgB5mNf2B3iLj52NAjupiwyb7MfmnCCSWyjuJha428Vdsk4py\n\
        iHn3k0Z1drI3kda9x21X5EfAkdsfm++FFJ5VpB7mRpGT3qg0zX9MtID8N0ZlTYCd\n\
        7ZRlV2tPTOlOdH34tEUShESieKQBrUyxc0IT5wngxd3O6ZJ5CCLzGc4XSAeXkp8t\n\
        9QIDAQAB\n\
        -----END PUBLIC KEY-----";
    let signature64 =
        "jdTNMwoSCf5bJWeCkKve5kOdKkuLtRokcItjLHEwGRcCnmmTChk6qmopKPdiGvJL\
        w3iJfGHSh2Z71yERX35m/RVLvkbnnYQtssKFVDj49ttnDXkK/pcPLdbnWc2GPbLN\
        v5XIpgiR2FPyp2PGbwYDoYNFnOI4ICJSBfKLjjYECCGbKKj4w5QBGzXyiCL0gV6l\
        iS/7Wj1BCjwDb09FJF3oLGM5kpQlP3r1Ksh38cxsdUbBA/+29TOYclGEqw8VwgT9\
        s66tTm8sylPTu/4vTFmaHBPiLNOOE9SUl4+zU2GjpqVNtVfj66f/Lm0x1lS8qaxt\
        AY7y2KV0G5RvDkykZ7y3Yw==";
    let message = "0x59bd329d";

    // Decode base-64 signature
    let signature = Base64::decode_vec(&signature64).unwrap();

    // Convert public key PEM to a VerifyingKey.
    let public_key = rsa::RsaPublicKey::from_public_key_pem(&public_key_pem).unwrap();
    let verifying_key = VerifyingKey::<Sha256>::new(public_key);

    let verify_signature: Signature = signature.into();
    let verify_result = verifying_key.verify(message.as_bytes(), &verify_signature);
    if verify_result.is_ok() {
        println!("Verified");
    } else {
        println!("Failed");
    }
}

I've verified they both produce the same value for em internally. It seems to be the Rust rsa code in pub(crate) fn verify<PK: PublicKey> in pkcs1v15.rs that is checking the em values that is deciding it fails verification.

This might be related to issue #89.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions