From 98cd2050e649cc8f5f498e4c0f27e0676b725766 Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Wed, 28 Feb 2018 12:32:30 -0500 Subject: [PATCH 1/2] Add x448 as a DH group --- crypto.go | 35 +++++++++++++++++++++++++++++++++++ crypto_test.go | 15 ++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/crypto.go b/crypto.go index ef7397d..2241795 100644 --- a/crypto.go +++ b/crypto.go @@ -17,6 +17,8 @@ import ( "math/big" "time" + "git.schwanenlied.me/yawning/x448.git" + //"github.com/twstrike/ed448" "golang.org/x/crypto/curve25519" // Blank includes to ensure hash support @@ -148,6 +150,8 @@ func keyExchangeSizeFromNamedGroup(group NamedGroup) (size int) { switch group { case X25519: size = 32 + case X448: + size = 56 case P256: size = 65 case P384: @@ -257,6 +261,22 @@ func newKeyShare(group NamedGroup) (pub []byte, priv []byte, err error) { pub = public[:] return + case X448: + var private, public [56]byte + _, err = prng.Read(private[:]) + if err != nil { + return + } + + rv := x448.ScalarBaseMult(&public, &private) + if rv != 0 { + return nil, nil, fmt.Errorf("tls.newkeyshare: X448 failed") + } + + priv = private[:] + pub = public[:] + return + default: return nil, nil, fmt.Errorf("tls.newkeyshare: Unsupported group %v", group) } @@ -308,6 +328,21 @@ func keyAgreement(group NamedGroup, pub []byte, priv []byte) ([]byte, error) { return ret[:], nil + case X448: + if len(pub) != keyExchangeSizeFromNamedGroup(group) { + return nil, fmt.Errorf("tls.keyagreement: Wrong public key size") + } + + var private, public, ret [56]byte + copy(private[:], priv) + copy(public[:], pub) + rv := x448.ScalarMult(&ret, &private, &public) + if rv != 0 { + return nil, fmt.Errorf("tls.keyagreement: Error in X448") + } + + return ret[:], nil + default: return nil, fmt.Errorf("tls.keyagreement: Unsupported group %v", group) } diff --git a/crypto_test.go b/crypto_test.go index 52c92d6..d434424 100644 --- a/crypto_test.go +++ b/crypto_test.go @@ -17,7 +17,7 @@ import ( var ( ecGroups = []NamedGroup{P256, P384, P521} - nonECGroups = []NamedGroup{FFDHE2048, FFDHE3072, FFDHE4096, FFDHE6144, FFDHE8192, X25519} + nonECGroups = []NamedGroup{FFDHE2048, FFDHE3072, FFDHE4096, FFDHE6144, FFDHE8192, X25519, X448} dhGroups = append(ecGroups, nonECGroups...) shortKeyPubHex = "04e9f6076620ddf6a24e4398162057eccd3077892f046b412" + @@ -92,6 +92,15 @@ func TestNewKeyShare(t *testing.T) { assertError(t, err, "Generated an X25519 key with no entropy") prng = originalPRNG + // Test failure case for an X448 key generation failure + originalPRNG = prng + prng = bytes.NewReader(nil) + _, _, err = newKeyShare(X448) + assertError(t, err, "Generated an X448 key with no entropy") + prng = originalPRNG + + // TODO(rlb@ipv.sx): Test failure for X448 when the zero key is derived + // Test failure case for an unknown group _, _, err = newKeyShare(NamedGroup(0)) assertError(t, err, "Generated a key for an unsupported group") @@ -135,6 +144,10 @@ func TestKeyAgreement(t *testing.T) { _, err = keyAgreement(X25519, shortKeyPub[:5], shortKeyPriv) assertError(t, err, "Performed key agreement with a truncated public key") + // Test failure for a too-short X448 public key + _, err = keyAgreement(X448, shortKeyPub[:5], shortKeyPriv) + assertError(t, err, "Performed key agreement with a truncated public key") + // Test failure case for an unknown group _, err = keyAgreement(NamedGroup(0), shortKeyPub, shortKeyPriv) assertError(t, err, "Performed key agreement with an unsupported group") From 177f52d0f862fcf6b14dc1d731374c87d94399df Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Wed, 28 Feb 2018 12:54:27 -0500 Subject: [PATCH 2/2] Add support for Ed25519 --- crypto.go | 29 ++++++++++++++++++++++++++++- crypto_test.go | 26 ++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/crypto.go b/crypto.go index 2241795..1d75295 100644 --- a/crypto.go +++ b/crypto.go @@ -18,8 +18,8 @@ import ( "time" "git.schwanenlied.me/yawning/x448.git" - //"github.com/twstrike/ed448" "golang.org/x/crypto/curve25519" + "golang.org/x/crypto/ed25519" // Blank includes to ensure hash support _ "crypto/sha1" @@ -46,6 +46,7 @@ const ( signatureAlgorithmRSA_PKCS1 signatureAlgorithmRSA_PSS signatureAlgorithmECDSA + signatureAlgorithmEd25519 ) var ( @@ -73,6 +74,7 @@ var ( RSA_PSS_SHA256: signatureAlgorithmRSA_PSS, RSA_PSS_SHA384: signatureAlgorithmRSA_PSS, RSA_PSS_SHA512: signatureAlgorithmRSA_PSS, + Ed25519: signatureAlgorithmEd25519, } curveMap = map[SignatureScheme]NamedGroup{ @@ -116,6 +118,8 @@ var ( ECDSA_P256_SHA256: x509.ECDSAWithSHA256, ECDSA_P384_SHA384: x509.ECDSAWithSHA384, ECDSA_P521_SHA512: x509.ECDSAWithSHA512, + // XXX(rlb@ipv.sx): Ed25519 not supported by crypto/x509 + // XXX(rlb@ipv.sx): Ed448 not supported by crypto/x509 } defaultRSAKeySize = 2048 @@ -195,6 +199,8 @@ func schemeValidForKey(alg SignatureScheme, key crypto.Signer) bool { return sigType == signatureAlgorithmRSA_PKCS1 || sigType == signatureAlgorithmRSA_PSS case *ecdsa.PrivateKey: return sigType == signatureAlgorithmECDSA + case *ed25519.PrivateKey: + return sigType == signatureAlgorithmEd25519 default: return false } @@ -361,6 +367,9 @@ func newSigningKey(sig SignatureScheme) (crypto.Signer, error) { return ecdsa.GenerateKey(elliptic.P384(), prng) case ECDSA_P521_SHA512: return ecdsa.GenerateKey(elliptic.P521(), prng) + case Ed25519: + _, priv, err := ed25519.GenerateKey(prng) + return priv, err default: return nil, fmt.Errorf("tls.newsigningkey: Unsupported signature algorithm [%04x]", sig) } @@ -413,6 +422,13 @@ func sign(alg SignatureScheme, privateKey crypto.Signer, sigInput []byte) ([]byt h := hash.New() h.Write(sigInput) realInput = h.Sum(nil) + case ed25519.PrivateKey: + if sigType != signatureAlgorithmEd25519 { + return nil, fmt.Errorf("tls.crypto.sign: Unsupported algorithm for ECDSA key") + } + + realInput = sigInput + opts = crypto.Hash(0) default: return nil, fmt.Errorf("tls.crypto.sign: Unsupported private key type") } @@ -480,6 +496,17 @@ func verify(alg SignatureScheme, publicKey crypto.PublicKey, sigInput []byte, si return fmt.Errorf("tls.verify: ECDSA verification failure") } return nil + + case ed25519.PublicKey: + if sigType != signatureAlgorithmEd25519 { + return fmt.Errorf("tls.verify: Unsupported algorithm for ECDSA key") + } + + if !ed25519.Verify(pub, sigInput, sig) { + return fmt.Errorf("tls.verify: Ed25519 verification failure") + } + return nil + default: return fmt.Errorf("tls.verify: Unsupported key type") } diff --git a/crypto_test.go b/crypto_test.go index d434424..d34aa6a 100644 --- a/crypto_test.go +++ b/crypto_test.go @@ -13,6 +13,8 @@ import ( "io" "math/big" "testing" + + "golang.org/x/crypto/ed25519" ) var ( @@ -162,7 +164,7 @@ func TestNewSigningKey(t *testing.T) { // Test ECDSA success (P-256) privECDSA, err := newSigningKey(ECDSA_P256_SHA256) - assertNotError(t, err, "failed to generate RSA private key") + assertNotError(t, err, "failed to generate ECDSA private key") _, ok = privECDSA.(*ecdsa.PrivateKey) assert(t, ok, "New ECDSA key was not actually an ECDSA key") pub := privECDSA.(*ecdsa.PrivateKey).Public().(*ecdsa.PublicKey) @@ -170,7 +172,7 @@ func TestNewSigningKey(t *testing.T) { // Test ECDSA success (P-384) privECDSA, err = newSigningKey(ECDSA_P384_SHA384) - assertNotError(t, err, "failed to generate RSA private key") + assertNotError(t, err, "failed to generate ECDSA private key") _, ok = privECDSA.(*ecdsa.PrivateKey) assert(t, ok, "New ECDSA key was not actually an ECDSA key") pub = privECDSA.(*ecdsa.PrivateKey).Public().(*ecdsa.PublicKey) @@ -178,14 +180,20 @@ func TestNewSigningKey(t *testing.T) { // Test ECDSA success (P-521) privECDSA, err = newSigningKey(ECDSA_P521_SHA512) - assertNotError(t, err, "failed to generate RSA private key") + assertNotError(t, err, "failed to generate ECDSA private key") _, ok = privECDSA.(*ecdsa.PrivateKey) assert(t, ok, "New ECDSA key was not actually an ECDSA key") pub = privECDSA.(*ecdsa.PrivateKey).Public().(*ecdsa.PublicKey) assertEquals(t, P521, namedGroupFromECDSAKey(pub)) + // Test Ed25519 success + privEd25519, err := newSigningKey(Ed25519) + assertNotError(t, err, "failed to generate Ed25519 private key") + _, ok = privEd25519.(ed25519.PrivateKey) + assert(t, ok, "New Ed25519 key was not actually an Ed25519 key") + // Test unsupported algorithm - _, err = newSigningKey(Ed25519) + _, err = newSigningKey(Ed448 + 1) assertError(t, err, "Created a private key for an unsupported algorithm") } @@ -221,6 +229,8 @@ func TestSignVerify(t *testing.T) { assertNotError(t, err, "failed to generate RSA private key") privECDSA, err := newSigningKey(ECDSA_P256_SHA256) assertNotError(t, err, "failed to generate ECDSA private key") + privEd25519, err := newSigningKey(Ed25519) + assertNotError(t, err, "failed to generate Ed25519 private key") // Test successful signing with PKCS#1 when it is allowed originalAllowPKCS1 := allowPKCS1 @@ -248,6 +258,10 @@ func TestSignVerify(t *testing.T) { sigECDSA, err := sign(ECDSA_P256_SHA256, privECDSA, data) assertNotError(t, err, "Failed to generate ECDSA signature") + // Test successful signing with Ed25519 + sigEd25519, err := sign(Ed25519, privEd25519, data) + assertNotError(t, err, "Failed to generate Ed25519 signature") + // Test signature failure on use of SHA-1 _, err = sign(RSA_PKCS1_SHA1, privRSA, data) assertError(t, err, "Allowed a SHA-1 signature") @@ -290,6 +304,10 @@ func TestSignVerify(t *testing.T) { err = verify(ECDSA_P256_SHA256, privECDSA.Public(), data, sigECDSA) assertNotError(t, err, "Failed to verify a valid ECDSA signature") + // Test successful verification with Ed25519 + err = verify(Ed25519, privEd25519.Public(), data, sigEd25519) + assertNotError(t, err, "Failed to verify a valid Ed25519 signature") + // Test that SHA-1 is forbidden err = verify(RSA_PKCS1_SHA1, privECDSA.Public(), data, sigECDSA) assertError(t, err, "Allowed verification of a SHA-1 signature")