From 8ea58d1789dbf6e6595bea9a7e49730be91cd8d1 Mon Sep 17 00:00:00 2001 From: parasssh <42328883+parasssh@users.noreply.github.com> Date: Thu, 13 Sep 2018 15:07:17 -0700 Subject: [PATCH] Ed25519 key support (#103) * ed gen, sign, verify works. tested25519 fails * Ignore some unit tests in ED since it relies on GoLang lib to support Ed keys * Cleanup sign and verify operations * Cleanup and prettify * cleanups * Conditional X_EVP_PKEY_ED25519 and X_EVP_Digest{Sign|Verify}[Init] based on openssl version. --- AUTHORS | 1 + key.go | 131 +++++++++++++++++++++++++++++++++++++++++----------- key_test.go | 106 ++++++++++++++++++++++++++++++++++++++++++ nid.go | 4 ++ shim.c | 53 +++++++++++++++++++++ shim.h | 5 ++ ssl_test.go | 14 ++++++ 7 files changed, 288 insertions(+), 26 deletions(-) diff --git a/AUTHORS b/AUTHORS index 11824c4..a048c1e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -21,3 +21,4 @@ Stephen Gallagher Viacheslav Biriukov Zack Owens Ramesh Rayaprolu +Paras Shah diff --git a/key.go b/key.go index c7b4665..91c7e7e 100644 --- a/key.go +++ b/key.go @@ -50,6 +50,10 @@ const ( KeyTypeCMAC = NID_cmac KeyTypeTLS1PRF = NID_tls1_prf KeyTypeHKDF = NID_hkdf + KeyTypeX25519 = NID_X25519 + KeyTypeX448 = NID_X448 + KeyTypeED25519 = NID_ED25519 + KeyTypeED448 = NID_ED448 ) type PublicKey interface { @@ -110,45 +114,94 @@ func (key *pKey) BaseType() NID { } func (key *pKey) SignPKCS1v15(method Method, data []byte) ([]byte, error) { + ctx := C.X_EVP_MD_CTX_new() defer C.X_EVP_MD_CTX_free(ctx) - if 1 != C.X_EVP_SignInit(ctx, method) { - return nil, errors.New("signpkcs1v15: failed to init signature") - } - if len(data) > 0 { - if 1 != C.X_EVP_SignUpdate( - ctx, unsafe.Pointer(&data[0]), C.uint(len(data))) { - return nil, errors.New("signpkcs1v15: failed to update signature") + if key.KeyType() == KeyTypeED25519 { + // do ED specific one-shot sign + + if method != nil || len(data) == 0 { + return nil, errors.New("signpkcs1v15: 0-length data or non-null digest") } + + if 1 != C.X_EVP_DigestSignInit(ctx, nil, nil, nil, key.key) { + return nil, errors.New("signpkcs1v15: failed to init signature") + } + + // evp signatures are 64 bytes + sig := make([]byte, 64, 64) + var sigblen C.ulong = 64 + if 1 != C.X_EVP_DigestSign(ctx, + ((*C.uchar)(unsafe.Pointer(&sig[0]))), + &sigblen, + (*C.uchar)(unsafe.Pointer(&data[0])), + C.ulong(len(data))) { + return nil, errors.New("signpkcs1v15: failed to do one-shot signature") + } + + return sig[:sigblen], nil + } else { + if 1 != C.X_EVP_SignInit(ctx, method) { + return nil, errors.New("signpkcs1v15: failed to init signature") + } + if len(data) > 0 { + if 1 != C.X_EVP_SignUpdate( + ctx, unsafe.Pointer(&data[0]), C.uint(len(data))) { + return nil, errors.New("signpkcs1v15: failed to update signature") + } + } + sig := make([]byte, C.X_EVP_PKEY_size(key.key)) + var sigblen C.uint + if 1 != C.X_EVP_SignFinal(ctx, + ((*C.uchar)(unsafe.Pointer(&sig[0]))), &sigblen, key.key) { + return nil, errors.New("signpkcs1v15: failed to finalize signature") + } + return sig[:sigblen], nil } - sig := make([]byte, C.X_EVP_PKEY_size(key.key)) - var sigblen C.uint - if 1 != C.X_EVP_SignFinal(ctx, - ((*C.uchar)(unsafe.Pointer(&sig[0]))), &sigblen, key.key) { - return nil, errors.New("signpkcs1v15: failed to finalize signature") - } - return sig[:sigblen], nil } func (key *pKey) VerifyPKCS1v15(method Method, data, sig []byte) error { ctx := C.X_EVP_MD_CTX_new() defer C.X_EVP_MD_CTX_free(ctx) - if 1 != C.X_EVP_VerifyInit(ctx, method) { - return errors.New("verifypkcs1v15: failed to init verify") - } - if len(data) > 0 { - if 1 != C.X_EVP_VerifyUpdate( - ctx, unsafe.Pointer(&data[0]), C.uint(len(data))) { - return errors.New("verifypkcs1v15: failed to update verify") + if key.KeyType() == KeyTypeED25519 { + // do ED specific one-shot sign + + if method != nil || len(data) == 0 || len(sig) == 0 { + return errors.New("verifypkcs1v15: 0-length data or sig or non-null digest") } + + if 1 != C.X_EVP_DigestVerifyInit(ctx, nil, nil, nil, key.key) { + return errors.New("verifypkcs1v15: failed to init verify") + } + + if 1 != C.X_EVP_DigestVerify(ctx, + ((*C.uchar)(unsafe.Pointer(&sig[0]))), + C.ulong(len(sig)), + (*C.uchar)(unsafe.Pointer(&data[0])), + C.ulong(len(data))) { + return errors.New("verifypkcs1v15: failed to do one-shot verify") + } + + return nil + + } else { + if 1 != C.X_EVP_VerifyInit(ctx, method) { + return errors.New("verifypkcs1v15: failed to init verify") + } + if len(data) > 0 { + if 1 != C.X_EVP_VerifyUpdate( + ctx, unsafe.Pointer(&data[0]), C.uint(len(data))) { + return errors.New("verifypkcs1v15: failed to update verify") + } + } + if 1 != C.X_EVP_VerifyFinal(ctx, + ((*C.uchar)(unsafe.Pointer(&sig[0]))), C.uint(len(sig)), key.key) { + return errors.New("verifypkcs1v15: failed to finalize verify") + } + return nil } - if 1 != C.X_EVP_VerifyFinal(ctx, - ((*C.uchar)(unsafe.Pointer(&sig[0]))), C.uint(len(sig)), key.key) { - return errors.New("verifypkcs1v15: failed to finalize verify") - } - return nil } func (key *pKey) MarshalPKCS1PrivateKeyPEM() (pem_block []byte, @@ -420,3 +473,29 @@ func GenerateECKey(curve EllipticCurve) (PrivateKey, error) { }) return p, nil } + +// GenerateED25519Key generates a Ed25519 key +func GenerateED25519Key() (PrivateKey, error) { + + // Key context + keyCtx := C.EVP_PKEY_CTX_new_id(C.X_EVP_PKEY_ED25519, nil) + if keyCtx == nil { + return nil, errors.New("failed creating EC parameter generation context") + } + defer C.EVP_PKEY_CTX_free(keyCtx) + + // Generate the key + var privKey *C.EVP_PKEY + if int(C.EVP_PKEY_keygen_init(keyCtx)) != 1 { + return nil, errors.New("failed initializing ED25519 key generation context") + } + if int(C.EVP_PKEY_keygen(keyCtx, &privKey)) != 1 { + return nil, errors.New("failed generating ED25519 private key") + } + + p := &pKey{key: privKey} + runtime.SetFinalizer(p, func(p *pKey) { + C.X_EVP_PKEY_free(p.key) + }) + return p, nil +} diff --git a/key_test.go b/key_test.go index 3f064df..2d9cd16 100644 --- a/key_test.go +++ b/key_test.go @@ -174,6 +174,21 @@ func TestGenerateEC(t *testing.T) { } } +func TestGenerateEd25519(t *testing.T) { + key, err := GenerateED25519Key() + if err != nil { + t.Fatal(err) + } + _, err = key.MarshalPKIXPublicKeyPEM() + if err != nil { + t.Fatal(err) + } + _, err = key.MarshalPKCS1PrivateKeyPEM() + if err != nil { + t.Fatal(err) + } +} + func TestSign(t *testing.T) { key, _ := GenerateRSAKey(1024) data := []byte("the quick brown fox jumps over the lazy dog") @@ -237,6 +252,30 @@ func TestSignEC(t *testing.T) { }) } +func TestSignED25519(t *testing.T) { + t.Parallel() + + key, err := GenerateED25519Key() + if err != nil { + t.Fatal(err) + } + data := []byte("the quick brown fox jumps over the lazy dog") + + t.Run("new", func(t *testing.T) { + t.Parallel() + sig, err := key.SignPKCS1v15(nil, data) + if err != nil { + t.Fatal(err) + } + + err = key.VerifyPKCS1v15(nil, data, sig) + if err != nil { + t.Fatal(err) + } + + }) +} + func TestMarshalEC(t *testing.T) { key, err := LoadPrivateKeyFromPEM(prime256v1KeyBytes) if err != nil { @@ -353,3 +392,70 @@ func TestMarshalEC(t *testing.T) { t.Fatal("invalid public key der bytes") } } + +func TestMarshalEd25519(t *testing.T) { + key, err := LoadPrivateKeyFromPEM(ed25519KeyBytes) + if err != nil { + t.Fatal(err) + } + cert, err := LoadCertificateFromPEM(ed25519CertBytes) + if err != nil { + t.Fatal(err) + } + + privateBlock, _ := pem_pkg.Decode(ed25519KeyBytes) + key, err = LoadPrivateKeyFromDER(privateBlock.Bytes) + if err != nil { + t.Fatal(err) + } + + pem, err := cert.MarshalPEM() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(pem, ed25519CertBytes) { + ioutil.WriteFile("generated", pem, 0644) + ioutil.WriteFile("hardcoded", ed25519CertBytes, 0644) + t.Fatal("invalid cert pem bytes") + } + + pem, err = key.MarshalPKCS1PrivateKeyPEM() + if err != nil { + t.Fatal(err) + } + + der, err := key.MarshalPKCS1PrivateKeyDER() + if err != nil { + t.Fatal(err) + } + + der, err = key.MarshalPKIXPublicKeyDER() + if err != nil { + t.Fatal(err) + } + + pem, err = key.MarshalPKIXPublicKeyPEM() + if err != nil { + t.Fatal(err) + } + + loaded_pubkey_from_pem, err := LoadPublicKeyFromPEM(pem) + if err != nil { + t.Fatal(err) + } + + loaded_pubkey_from_der, err := LoadPublicKeyFromDER(der) + if err != nil { + t.Fatal(err) + } + + _, err = loaded_pubkey_from_pem.MarshalPKIXPublicKeyDER() + if err != nil { + t.Fatal(err) + } + + _, err = loaded_pubkey_from_der.MarshalPKIXPublicKeyDER() + if err != nil { + t.Fatal(err) + } +} diff --git a/nid.go b/nid.go index 6766b84..936a52e 100644 --- a/nid.go +++ b/nid.go @@ -203,4 +203,8 @@ const ( NID_dhpublicnumber NID = 920 NID_tls1_prf NID = 1021 NID_hkdf NID = 1036 + NID_X25519 NID = 1034 + NID_X448 NID = 1035 + NID_ED25519 NID = 1087 + NID_ED448 NID = 1088 ) diff --git a/shim.c b/shim.c index 755eb02..3052878 100644 --- a/shim.c +++ b/shim.c @@ -38,6 +38,59 @@ static int go_write_bio_puts(BIO *b, const char *str) { return go_write_bio_write(b, (char*)str, (int)strlen(str)); } +/* + ************************************************ + * v1.1.1 and later implementation + ************************************************ + */ +#if OPENSSL_VERSION_NUMBER >= 0x1010100fL +int X_EVP_PKEY_ED25519 = EVP_PKEY_ED25519; + +int X_EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, + const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey){ + return EVP_DigestSignInit(ctx, pctx, type, e, pkey); +} + +int X_EVP_DigestSign(EVP_MD_CTX *ctx, unsigned char *sigret, + size_t *siglen, const unsigned char *tbs, size_t tbslen) { + return EVP_DigestSign(ctx, sigret, siglen, tbs, tbslen); +} + + +int X_EVP_DigestVerifyInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, + const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey){ + return EVP_DigestVerifyInit(ctx, pctx, type, e, pkey); +} + +int X_EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret, + size_t siglen, const unsigned char *tbs, size_t tbslen){ + return EVP_DigestVerify(ctx, sigret, siglen, tbs, tbslen); +} +#else +int X_EVP_PKEY_ED25519 = EVP_PKEY_NONE; + +int X_EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, + const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey){ + return 0; +} + +int X_EVP_DigestSign(EVP_MD_CTX *ctx, unsigned char *sigret, + size_t *siglen, const unsigned char *tbs, size_t tbslen) { + return 0; +} + + +int X_EVP_DigestVerifyInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, + const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey){ + return 0; +} + +int X_EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret, + size_t siglen, const unsigned char *tbs, size_t tbslen){ + return 0; +} +#endif + /* ************************************************ * v1.1.X and later implementation diff --git a/shim.h b/shim.h index 13490ca..1015625 100644 --- a/shim.h +++ b/shim.h @@ -102,6 +102,7 @@ extern BIO *X_BIO_new_write_bio(); extern BIO *X_BIO_new_read_bio(); /* EVP methods */ +extern int X_EVP_PKEY_ED25519; extern const EVP_MD *X_EVP_get_digestbyname(const char *name); extern EVP_MD_CTX *X_EVP_MD_CTX_new(); extern void X_EVP_MD_CTX_free(EVP_MD_CTX *ctx); @@ -122,6 +123,8 @@ extern int X_EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, size_t cnt); extern int X_EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s); extern int X_EVP_SignInit(EVP_MD_CTX *ctx, const EVP_MD *type); extern int X_EVP_SignUpdate(EVP_MD_CTX *ctx, const void *d, unsigned int cnt); +extern int X_EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey); +extern int X_EVP_DigestSign(EVP_MD_CTX *ctx, unsigned char *sigret, size_t *siglen, const unsigned char *tbs, size_t tbslen); extern EVP_PKEY *X_EVP_PKEY_new(void); extern void X_EVP_PKEY_free(EVP_PKEY *pkey); extern int X_EVP_PKEY_size(EVP_PKEY *pkey); @@ -132,6 +135,8 @@ extern int X_EVP_SignFinal(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s, extern int X_EVP_VerifyInit(EVP_MD_CTX *ctx, const EVP_MD *type); extern int X_EVP_VerifyUpdate(EVP_MD_CTX *ctx, const void *d, unsigned int cnt); extern int X_EVP_VerifyFinal(EVP_MD_CTX *ctx, const unsigned char *sigbuf, unsigned int siglen, EVP_PKEY *pkey); +extern int X_EVP_DigestVerifyInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey); +extern int X_EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret, size_t siglen, const unsigned char *tbs, size_t tbslen); extern int X_EVP_CIPHER_block_size(EVP_CIPHER *c); extern int X_EVP_CIPHER_key_length(EVP_CIPHER *c); extern int X_EVP_CIPHER_iv_length(EVP_CIPHER *c); diff --git a/ssl_test.go b/ssl_test.go index a771c59..a0bd9d5 100644 --- a/ssl_test.go +++ b/ssl_test.go @@ -103,6 +103,20 @@ FhlGM1wzvusyGrm26Vrbqm4wDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNJ ADBGAiEA6PWNjm4B6zs3Wcha9qyDdfo1ILhHfk9rZEAGrnfyc2UCIQD1IDVJUkI4 J/QVoOtP5DOdRPs/3XFy0Bk0qH+Uj5D7LQ== -----END CERTIFICATE----- +`) + ed25519CertBytes = []byte(`-----BEGIN CERTIFICATE----- +MIIBIzCB1gIUd0UUPX+qHrSKSVN9V/A3F1Eeti4wBQYDK2VwMDYxCzAJBgNVBAYT +AnVzMQ0wCwYDVQQKDARDU0NPMRgwFgYDVQQDDA9lZDI1NTE5X3Jvb3RfY2EwHhcN +MTgwODE3MDMzNzQ4WhcNMjgwODE0MDMzNzQ4WjAzMQswCQYDVQQGEwJ1czENMAsG +A1UECgwEQ1NDTzEVMBMGA1UEAwwMZWQyNTUxOV9sZWFmMCowBQYDK2VwAyEAKZZJ +zzlBcpjdbvzV0BRoaSiJKxbU6GnFeAELA0cHWR0wBQYDK2VwA0EAbfUJ7L7v3GDq +Gv7R90wQ/OKAc+o0q9eOrD6KRYDBhvlnMKqTMRVucnHXfrd5Rhmf4yHTvFTOhwmO +t/hpmISAAA== +-----END CERTIFICATE----- +`) + ed25519KeyBytes = []byte(`-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIL3QVwyuusKuLgZwZn356UHk9u1REGHbNTLtFMPKNQSb +-----END PRIVATE KEY----- `) )