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

x25519_dalek::PublicKey::from generates a different public key compared to OpenSSL when using the same private key #90

Closed
alwitt opened this issue Jul 15, 2022 · 3 comments

Comments

@alwitt
Copy link

alwitt commented Jul 15, 2022

I am testing this crate (v1.2.0) using a ED25519 key pair generated by OpenSSL 1.1.1o FIPS 3 May 2022, and checking for correct DH shared secret generation against a x25519_dalek::EphemeralSecret based key pair.

The issue I am seeing is that starting from the private key generated by OpenSSL, the pubic key generated by PublicKey::from for that private key is different from what OpenSSL generated. Am I using the APIs correctly? What is the correct way to use existing key pairs?

// Parse existing private key
let alice_priv_key = pkcs8::KeypairBytes::from_pkcs8_pem(PRIV_KEY).unwrap();
// Parse existing public key
let alice_pub_key = pkcs8::PublicKeyBytes::from_public_key_pem(PUB_KEY).unwrap();

// Define Alice's ed25519 pair
let alice_secret = StaticSecret::from(alice_priv_key.secret_key);
let alice_public = PublicKey::from(&alice_secret);

let parsed_existing_public = PublicKey::from(alice_pub_key.to_bytes());
// This should not fail
assert_eq!(alice_public.to_bytes()[..], parsed_existing_public.to_bytes()[..]);

The complete test code

#[cfg(test)]
mod tests {
    fn init() {
        let _ = env_logger::builder().is_test(true).try_init();
    }

    /* Creating the ED25519 key pairs
    $ openssl genpkey -algorithm ED25519 > private.pem
    $ openssl pkey -outform PEM -pubout -in private.pem > public.pem
    */

    static PUB_KEY: &str = "-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEADOuQ5lqOanpHOtLV2gqqcYuYkJrdpNueHJ7M9ejY3M0=
-----END PUBLIC KEY-----";

    static PRIV_KEY: &str = "-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEINLDN5YuEgo6Z5G+ww0kTv33KyQrPN1O+vdeet74+cVm
-----END PRIVATE KEY-----";

    mod test_ed25519_dh {
        use ed25519::pkcs8;
        use ed25519::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePublicKey};
        use rand_core::OsRng;
        use x25519_dalek::StaticSecret;
        use x25519_dalek::{EphemeralSecret, PublicKey};

        #[test]
        fn test_ed25519_dh() {
            super::init();

            // Parse existing private key
            let mut alice_priv_key = pkcs8::KeypairBytes::from_pkcs8_pem(super::PRIV_KEY).unwrap();
            // Parse existing public key
            let alice_pub_key = pkcs8::PublicKeyBytes::from_public_key_pem(super::PUB_KEY).unwrap();

            // Define Alice's ed25519 pair
            let alice_secret = StaticSecret::from(alice_priv_key.secret_key);
            let alice_public = PublicKey::from(&alice_secret);

            // Dump the generated public key to stdout
            alice_priv_key.public_key = Some(alice_public.to_bytes());
            let alice_public_other_form = pkcs8::PublicKeyBytes::try_from(&alice_priv_key).unwrap();
            log::debug!(
                "Alice generated public key as PEM:\n{}",
                alice_public_other_form
                    .to_public_key_pem(base64ct::LineEnding::LF)
                    .unwrap()
            );
            /* This is not what OpenSSL generated, shown in PUB_KEY
               -----BEGIN PUBLIC KEY-----
               MCowBQYDK2VwAyEAUSd2eTv0CdgCpnccB3re+kxjo9miMQV9Di9SFHc/PQ4=
               -----END PUBLIC KEY-----
            */

            let parsed_existing_public = PublicKey::from(alice_pub_key.to_bytes());
            // This should not fail
            assert_eq!(
                alice_public.to_bytes()[..],
                parsed_existing_public.to_bytes()[..]
            );

            // Create Bob's ed25519 pair
            let bob_secret = EphemeralSecret::new(OsRng);
            let bob_public = PublicKey::from(&bob_secret);

            // Define the shared secrets
            let alice_shared_secret = alice_secret.diffie_hellman(&bob_public);
            let bob_shared_secret = bob_secret.diffie_hellman(&alice_public);

            assert_eq!(
                alice_shared_secret.as_bytes()[..],
                bob_shared_secret.as_bytes()[..]
            );
        }
    }
}
@shampoofactory
Copy link

I'm no cryptographer, but should we not be testing with X25519 key pairs as opposed to Ed25519?

$ openssl genpkey -algorithm X25519 -out prv.pem
$ openssl pkey -in prv.pem -pubout -out pub.pem

Quote: 'The public key representations are related but not the same. They cannot be used interchangeably without additional processing.'
Source: https://crypto.stackexchange.com/questions/76156/public-key-generation-for-ed25519-vs-x25519
Author: Frank Denis

@tarcieri
Copy link
Contributor

tarcieri commented Feb 8, 2023

Yeah, it seems like the code example in the OP is using ed25519::pkcs8, which is for the Edwards form of Curve25519.

X25519 uses the Montgomery form. It's possible to convert from the Edwards form to the Montgomery form, but they are distinct.

@tarcieri tarcieri closed this as completed Feb 8, 2023
@alwitt
Copy link
Author

alwitt commented Feb 9, 2023

@tarcieri thank you for clarifying. I am glad to see there are activities in this repo again.

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

3 participants