diff --git a/crypto.go b/crypto.go index ef7397d..1d75295 100644 --- a/crypto.go +++ b/crypto.go @@ -17,7 +17,9 @@ import ( "math/big" "time" + "git.schwanenlied.me/yawning/x448.git" "golang.org/x/crypto/curve25519" + "golang.org/x/crypto/ed25519" // Blank includes to ensure hash support _ "crypto/sha1" @@ -44,6 +46,7 @@ const ( signatureAlgorithmRSA_PKCS1 signatureAlgorithmRSA_PSS signatureAlgorithmECDSA + signatureAlgorithmEd25519 ) var ( @@ -71,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{ @@ -114,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 @@ -148,6 +154,8 @@ func keyExchangeSizeFromNamedGroup(group NamedGroup) (size int) { switch group { case X25519: size = 32 + case X448: + size = 56 case P256: size = 65 case P384: @@ -191,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 } @@ -257,6 +267,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 +334,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) } @@ -326,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) } @@ -378,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") } @@ -445,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 52c92d6..d34aa6a 100644 --- a/crypto_test.go +++ b/crypto_test.go @@ -13,11 +13,13 @@ import ( "io" "math/big" "testing" + + "golang.org/x/crypto/ed25519" ) 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 +94,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 +146,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") @@ -149,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) @@ -157,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) @@ -165,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") } @@ -208,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 @@ -235,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") @@ -277,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")