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.
This commit is contained in:
parasssh 2018-09-13 15:07:17 -07:00 committed by Jeff Wendling
parent 3b86b42896
commit 8ea58d1789
7 changed files with 288 additions and 26 deletions

View File

@ -21,3 +21,4 @@ Stephen Gallagher <sgallagh@redhat.com>
Viacheslav Biriukov <v.v.biriukov@gmail.com>
Zack Owens <zowens2009@gmail.com>
Ramesh Rayaprolu <rarayapr@cisco.com>
Paras Shah <parasssh@gmail.com>

79
key.go
View File

@ -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,9 +114,34 @@ 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 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")
}
@ -129,12 +158,35 @@ func (key *pKey) SignPKCS1v15(method Method, data []byte) ([]byte, error) {
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 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")
}
@ -149,6 +201,7 @@ func (key *pKey) VerifyPKCS1v15(method Method, data, sig []byte) error {
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
}

View File

@ -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)
}
}

4
nid.go
View File

@ -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
)

53
shim.c
View File

@ -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

5
shim.h
View File

@ -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);

View File

@ -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-----
`)
)