jedisct1 / libsodium

A modern, portable, easy to use crypto library.
https://libsodium.org
Other
12.26k stars 1.75k forks source link

Feature Request: Export Public Key in DER Format #963

Closed macjohnny closed 4 years ago

macjohnny commented 4 years ago

Context

Currently, a pair of public/private keys using the Ed25519 can be created using the

int crypto_sign_keypair(unsigned char *pk, unsigned char *sk);

function described in https://libsodium.gitbook.io/doc/public-key_cryptography/public-key_signatures and used to sign a message with e.g. the following code snippet:

#include <sodium/crypto_sign.h>

std::vector<uint8_t> public_key(crypto_sign_PUBLICKEYBYTES);
std::vector<uint8_t> private_key(crypto_sign_SECRETKEYBYTES);
crypto_sign_keypair(public_key.data(), private_key.data());

std::vector<uint8_t> message {0x11, 0x23, 0xaa};

std::vector<uint8_t> signature(crypto_sign_BYTES);
crypto_sign_detached(signature.data(), NULL, message.data(), message.size(), private_key.data());

bool signature_valid = crypto_sign_verify_detached(signature.data(), message.data(), message.size(), public_key.data()) == 0;

Feature Request

It would be useful to have a function e.g. convert_key_to_der(unsigned char *public_key, unsigned char *public_key_der) to convert the generated public_key into the DER format

This would allow to validate the signature (e.g. stored to a file signature.bin) of message (e.g. stored to a file message.bin) to be validated against the public_key (converted to DER and e.g. stored to a file public_key.der) with a command line tool such as openssl

openssl pkeyutl -in message.bin -rawin -sigfile signature.bin -inkey public_key.der -keyform der -pubin  -verify
# Signature Verified Successfully

Note: Ed25519 is only supported in openssl>=3.0.0-alpha1 (https://github.com/openssl/openssl/issues/6988#issuecomment-618297128), which can be used e.g. like this:

wget https://www.openssl.org/source/openssl-3.0.0-alpha2.tar.gz
tar -xzf openssl-3.0.0-alpha2.tar.gz
cd openssl-3.0.0-alpha2
./config
make
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)
cd ../

Proof of concept

A simple implementation to store the public_key to a valid DER format is as follows

/// header bytes of a Distinguished Encoding Rules (DER) encoded Ed25519 public key with size 32 bytes
/// The sequence can be found with the following commands:
/// # generate a new private key with the Ed25519 algorithm
/// openssl genpkey -algorithm Ed25519 -out private-key.pem
/// # extract the public key into a DER file
/// openssl pkey -in private-key.pem -outform der -out public-key.der -pubout
/// # check the actual binary content of the public key in the last BIT STRING field. Note: the first byte (number of unused bits)
/// # should be 0x00, since the key is 32 bytes long, i.e. a multiple of 8, and hence no padding bits (number of unused bits) are needed
/// openssl asn1parse -in public-key.der -inform der -dump
/// # get all bytes of the DER file except the last 32 bytes (the last 32 bytes should be the actual binary content checked above)
/// hexdump -C public-key.der
const std::vector<uint8_t> kDerHeaderSequence {0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00};

std::ofstream public_key_file("public_key.der");
public_key_file.write((char *)kDerHeaderSequence.data(), kDerHeaderSequence.size());
public_key_file.write((char *)public_key.data(), public_key.size());

std::ofstream signature_file("signature.bin");
signature_file.write((char *)signature.data(), signature.size());

std::ofstream message_file("message.bin");
message_file.write((char *)message.data(), message.size());

Implementation

The simple implementation consisting of prepending kDerHeaderSequence to the public_key allows conversion without adding a dependence on a ASN.1 serializer. Analogously, this could also be done for private_key. If you agree, I am willing to file a PR with such an implementation.

Literature

Useful concerning private key:

jedisct1 commented 4 years ago

Hi Esteban,

There may be cases where PKCS8/DER/PEM/... encoding may be necessary.

But outside of TLS, these have shown to be pretty rare.

Instead of trying to address one particular case, it would be better done as a distinct, standalone project, that properly handles different keys and signature encodings.

macjohnny commented 4 years ago

@jedisct1 how about extending the docs?

macjohnny commented 4 years ago

just now I discovered your project https://jedisct1.github.io/minisign/, which allows to verify the signature in the command line, thanks! Note: this requires implementing the format required by minisign when storing the key, which is different from binary-only or DER.