Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failure to Verify EdDSA Signature from DER Public Key, Succeeds in PEM #398

Open
naftulikay opened this issue Jul 19, 2024 · 2 comments
Open

Comments

@naftulikay
Copy link

I'm generating ed25519 keys using ed25519-dalek and the rand crates, and while I can sign and verify using public key PEM encoding, verification fails when using public key DER encoding.

Cargo.toml:

# ...
[dependencies]
ed25519-dalek = { version = "2", features = ["pkcs8", "pem", "rand_core"] }
jsonwebtoken = "9"
rand = "0.8"

Here is a test case which generates a keypair, converts it into jsonwebtoken types, and successfully signs and verifies a signature:

tests/test_alpha.rs:

use ed25519::pkcs8::{EncodePrivateKey, EncodePublicKey};
use ed25519::pkcs8::spki::der::pem::LineEnding;
use ed25519_dalek::SigningKey as Ed25519SigningKey;
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey};

/// Tests that we can generate an ed25519 keypair via [ed25519_dalek], import it into
/// [jsonwebtoken], generate a signature, and verify that signature.
#[test]
fn test_ed25519_roundtrip() {
    use jsonwebtoken::crypto::{sign, verify};

    // first, generate an ed25519 private key using the thread rng
    let dalek_private = Ed25519SigningKey::generate(&mut rand::thread_rng());
    let dalek_public = dalek_private.verifying_key();

    // next, in order for it to work with jsonwebtoken, we convert it into pkcs8
    let (public_pem, private_der) = (
        // FIXME this line succeeds when using pem, fails when using der
        dalek_public.to_public_key_pem(LineEnding::LF).expect("unable to convert public to der"),
        dalek_private.to_pkcs8_der().expect("unable to convert private to der")
    );

    // create the jsonwebtoken key structure
    let (encoding_key, decoding_key) = (
        EncodingKey::from_ed_der(private_der.as_bytes()),
        // FIXME this line succeeds when using pem, fails when using der
        DecodingKey::from_ed_pem(public_pem.as_bytes()).expect("unable to parse public pem"),
    );

    // create a signature
    let message = "Hello World";
    let signature = sign(message.as_bytes(), &encoding_key, Algorithm::EdDSA).unwrap();

    let valid = verify(signature.as_str(), message.as_bytes(), &decoding_key, Algorithm::EdDSA)
        .unwrap();

    assert!(valid, "failed to verify signature");
}

The above code does pass the test, but note that I'm converting my public ed25519 key to PEM format, and creating a DecodingKey using DecodingKey::from_ed_pem:

let (public_pem, private_der) = (
    // FIXME this line succeeds when using pem, fails when using der
    dalek_public.to_public_key_pem(LineEnding::LF).expect("unable to convert public to der"),
    dalek_private.to_pkcs8_der().expect("unable to convert private to der")
);

let (encoding_key, decoding_key) = (
    EncodingKey::from_ed_der(private_der.as_bytes()),
    DecodingKey::from_ed_pem(public_pem.as_bytes()).expect("unable to parse public pem"),
);

My private key converts just fine when using DER encoding, but the public key seems to be the problem. If I change the above code to use DER for the public key, the test fails:

let (public_der, private_der) = (
    // FIXME this line succeeds when using pem, fails when using der
    dalek_public.to_public_key_der().expect("unable to convert public to der"),
    dalek_private.to_pkcs8_der().expect("unable to convert private to der")
);

let (encoding_key, decoding_key) = (
    EncodingKey::from_ed_der(private_der.as_bytes()),
    DecodingKey::from_ed_der(public_der.as_bytes()),
);

Just changing the DecodingKey from PEM format to DER format causes the verification to fail.

Am I doing something wrong here when trying to work with DER format? Is it possible that the DER format emitted by ed25519-dalek is incompatible with this library, and if so, what can I do to determine where the incompatibility is coming from?

@naftulikay
Copy link
Author

I don't think this is a problem within ed25519-dalek, as I have verified things externally using the openssl CLI. I generated a key-pair, wrote the PEM format to public.pem, the DER format to public.der, and then ran the following to convert the PEM to DER:

openssl pkey -in public.pem -pubin -outform der -out public.openssl.der

I then compared the contents, and they are the same:

$ sha256sum public.der public.openssl.der
a17fcf0a2f50e2d495e4f90ce263410edc183add6c62699a2facbccf60410f74  public.der
a17fcf0a2f50e2d495e4f90ce263410edc183add6c62699a2facbccf60410f74  public.openssl.der

Therefore it seems that there is a problem within the DecodingKey::from_ed_der method.

@naftulikay
Copy link
Author

I also just attempted to use the private key DER bytes to create a DecodingKey:

let private_der = dalek_private.to_pkcs8_der().unwrap();
let decoding_key = DecodingKey::from_ed_der(private_der.as_bytes());

This also fails verification. It seems the only thing that I can do is use PEM. I may try some of the other options as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant