Commit 8ea58d17 authored by parasssh's avatar parasssh Committed by Jeff Wendling

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.
parent 3b86b428
...@@ -21,3 +21,4 @@ Stephen Gallagher <sgallagh@redhat.com> ...@@ -21,3 +21,4 @@ Stephen Gallagher <sgallagh@redhat.com>
Viacheslav Biriukov <v.v.biriukov@gmail.com> Viacheslav Biriukov <v.v.biriukov@gmail.com>
Zack Owens <zowens2009@gmail.com> Zack Owens <zowens2009@gmail.com>
Ramesh Rayaprolu <rarayapr@cisco.com> Ramesh Rayaprolu <rarayapr@cisco.com>
Paras Shah <parasssh@gmail.com>
...@@ -50,6 +50,10 @@ const ( ...@@ -50,6 +50,10 @@ const (
KeyTypeCMAC = NID_cmac KeyTypeCMAC = NID_cmac
KeyTypeTLS1PRF = NID_tls1_prf KeyTypeTLS1PRF = NID_tls1_prf
KeyTypeHKDF = NID_hkdf KeyTypeHKDF = NID_hkdf
KeyTypeX25519 = NID_X25519
KeyTypeX448 = NID_X448
KeyTypeED25519 = NID_ED25519
KeyTypeED448 = NID_ED448
) )
type PublicKey interface { type PublicKey interface {
...@@ -110,45 +114,94 @@ func (key *pKey) BaseType() NID { ...@@ -110,45 +114,94 @@ func (key *pKey) BaseType() NID {
} }
func (key *pKey) SignPKCS1v15(method Method, data []byte) ([]byte, error) { func (key *pKey) SignPKCS1v15(method Method, data []byte) ([]byte, error) {
ctx := C.X_EVP_MD_CTX_new() ctx := C.X_EVP_MD_CTX_new()
defer C.X_EVP_MD_CTX_free(ctx) defer C.X_EVP_MD_CTX_free(ctx)
if 1 != C.X_EVP_SignInit(ctx, method) { if key.KeyType() == KeyTypeED25519 {
return nil, errors.New("signpkcs1v15: failed to init signature") // do ED specific one-shot sign
}
if len(data) > 0 { if method != nil || len(data) == 0 {
if 1 != C.X_EVP_SignUpdate( return nil, errors.New("signpkcs1v15: 0-length data or non-null digest")
ctx, unsafe.Pointer(&data[0]), C.uint(len(data))) {
return nil, errors.New("signpkcs1v15: failed to update signature")
} }
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 { func (key *pKey) VerifyPKCS1v15(method Method, data, sig []byte) error {
ctx := C.X_EVP_MD_CTX_new() ctx := C.X_EVP_MD_CTX_new()
defer C.X_EVP_MD_CTX_free(ctx) defer C.X_EVP_MD_CTX_free(ctx)
if 1 != C.X_EVP_VerifyInit(ctx, method) { if key.KeyType() == KeyTypeED25519 {
return errors.New("verifypkcs1v15: failed to init verify") // do ED specific one-shot sign
}
if len(data) > 0 { if method != nil || len(data) == 0 || len(sig) == 0 {
if 1 != C.X_EVP_VerifyUpdate( return errors.New("verifypkcs1v15: 0-length data or sig or non-null digest")
ctx, unsafe.Pointer(&data[0]), C.uint(len(data))) {
return errors.New("verifypkcs1v15: failed to update verify")
} }
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, func (key *pKey) MarshalPKCS1PrivateKeyPEM() (pem_block []byte,
...@@ -420,3 +473,29 @@ func GenerateECKey(curve EllipticCurve) (PrivateKey, error) { ...@@ -420,3 +473,29 @@ func GenerateECKey(curve EllipticCurve) (PrivateKey, error) {
}) })
return p, nil 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
}
...@@ -174,6 +174,21 @@ func TestGenerateEC(t *testing.T) { ...@@ -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) { func TestSign(t *testing.T) {
key, _ := GenerateRSAKey(1024) key, _ := GenerateRSAKey(1024)
data := []byte("the quick brown fox jumps over the lazy dog") data := []byte("the quick brown fox jumps over the lazy dog")
...@@ -237,6 +252,30 @@ func TestSignEC(t *testing.T) { ...@@ -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) { func TestMarshalEC(t *testing.T) {
key, err := LoadPrivateKeyFromPEM(prime256v1KeyBytes) key, err := LoadPrivateKeyFromPEM(prime256v1KeyBytes)
if err != nil { if err != nil {
...@@ -353,3 +392,70 @@ func TestMarshalEC(t *testing.T) { ...@@ -353,3 +392,70 @@ func TestMarshalEC(t *testing.T) {
t.Fatal("invalid public key der bytes") 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)
}
}
...@@ -203,4 +203,8 @@ const ( ...@@ -203,4 +203,8 @@ const (
NID_dhpublicnumber NID = 920 NID_dhpublicnumber NID = 920
NID_tls1_prf NID = 1021 NID_tls1_prf NID = 1021
NID_hkdf NID = 1036 NID_hkdf NID = 1036
NID_X25519 NID = 1034
NID_X448 NID = 1035
NID_ED25519 NID = 1087
NID_ED448 NID = 1088
) )
...@@ -38,6 +38,59 @@ static int go_write_bio_puts(BIO *b, const char *str) { ...@@ -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)); 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 * v1.1.X and later implementation
......
...@@ -102,6 +102,7 @@ extern BIO *X_BIO_new_write_bio(); ...@@ -102,6 +102,7 @@ extern BIO *X_BIO_new_write_bio();
extern BIO *X_BIO_new_read_bio(); extern BIO *X_BIO_new_read_bio();
/* EVP methods */ /* EVP methods */
extern int X_EVP_PKEY_ED25519;
extern const EVP_MD *X_EVP_get_digestbyname(const char *name); extern const EVP_MD *X_EVP_get_digestbyname(const char *name);
extern EVP_MD_CTX *X_EVP_MD_CTX_new(); extern EVP_MD_CTX *X_EVP_MD_CTX_new();
extern void X_EVP_MD_CTX_free(EVP_MD_CTX *ctx); 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); ...@@ -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_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_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_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 EVP_PKEY *X_EVP_PKEY_new(void);
extern void X_EVP_PKEY_free(EVP_PKEY *pkey); extern void X_EVP_PKEY_free(EVP_PKEY *pkey);
extern int X_EVP_PKEY_size(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, ...@@ -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_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_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_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_block_size(EVP_CIPHER *c);
extern int X_EVP_CIPHER_key_length(EVP_CIPHER *c); extern int X_EVP_CIPHER_key_length(EVP_CIPHER *c);
extern int X_EVP_CIPHER_iv_length(EVP_CIPHER *c); extern int X_EVP_CIPHER_iv_length(EVP_CIPHER *c);
......
...@@ -103,6 +103,20 @@ FhlGM1wzvusyGrm26Vrbqm4wDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNJ ...@@ -103,6 +103,20 @@ FhlGM1wzvusyGrm26Vrbqm4wDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNJ
ADBGAiEA6PWNjm4B6zs3Wcha9qyDdfo1ILhHfk9rZEAGrnfyc2UCIQD1IDVJUkI4 ADBGAiEA6PWNjm4B6zs3Wcha9qyDdfo1ILhHfk9rZEAGrnfyc2UCIQD1IDVJUkI4
J/QVoOtP5DOdRPs/3XFy0Bk0qH+Uj5D7LQ== J/QVoOtP5DOdRPs/3XFy0Bk0qH+Uj5D7LQ==
-----END CERTIFICATE----- -----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-----
`) `)
) )
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment