Support NIST curve P256 (#769)

* Support NIST curve P256

This change adds support for NIST curve P256. When you use `nebula-cert ca`
or `nebula-cert keygen`, you can specify `-curve P256` to enable it. The
curve to use is based on the curve defined in your CA certificate.

Internally, we use ECDSA P256 to sign certificates, and ECDH P256 to do
Noise handshakes. P256 is not supported natively in Noise Protocol, so
we define `DHP256` in the `noiseutil` package to implement support for
it.

You cannot have a mixed network of Curve25519 and P256 certificates,
since the Noise protocol will only attempt to parse using the Curve
defined in the host's certificate.

* verify the curves match in VerifyPrivateKey

This would have failed anyways once we tried to actually use the bytes
in the private key, but its better to detect the issue up front with
a better error message.

* add cert.Curve argument to Sign method

* fix mismerge

* use crypto/ecdh

This is the preferred method for doing ECDH functions now, and also has
a boringcrypto specific codepath.

* remove other ecdh uses of crypto/elliptic

use crypto/ecdh instead
This commit is contained in:
Wade Simmons
2023-05-04 17:50:23 -04:00
committed by GitHub
parent 702e1c59bd
commit e0185c4b01
21 changed files with 857 additions and 165 deletions

View File

@@ -1,6 +1,8 @@
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"flag"
"fmt"
@@ -31,6 +33,8 @@ type caFlags struct {
argonIterations *uint
argonParallelism *uint
encryption *bool
curve *string
}
func newCaFlags() *caFlags {
@@ -48,6 +52,7 @@ func newCaFlags() *caFlags {
cf.argonParallelism = cf.set.Uint("argon-parallelism", 4, "Optional: Argon2 parallelism parameter used for encrypted private key passphrase")
cf.argonIterations = cf.set.Uint("argon-iterations", 1, "Optional: Argon2 iterations parameter used for encrypted private key passphrase")
cf.encryption = cf.set.Bool("encrypt", false, "Optional: prompt for passphrase and write out-key in an encrypted format")
cf.curve = cf.set.String("curve", "25519", "EdDSA/ECDSA Curve (25519, P256)")
return &cf
}
@@ -160,9 +165,25 @@ func ca(args []string, out io.Writer, errOut io.Writer, pr PasswordReader) error
}
}
pub, rawPriv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return fmt.Errorf("error while generating ed25519 keys: %s", err)
var curve cert.Curve
var pub, rawPriv []byte
switch *cf.curve {
case "25519", "X25519", "Curve25519", "CURVE25519":
curve = cert.Curve_CURVE25519
pub, rawPriv, err = ed25519.GenerateKey(rand.Reader)
if err != nil {
return fmt.Errorf("error while generating ed25519 keys: %s", err)
}
case "P256":
var key *ecdsa.PrivateKey
curve = cert.Curve_P256
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return fmt.Errorf("error while generating ecdsa keys: %s", err)
}
// ref: https://github.com/golang/go/blob/go1.19/src/crypto/x509/sec1.go#L60
rawPriv = key.D.FillBytes(make([]byte, 32))
pub = elliptic.Marshal(elliptic.P256(), key.X, key.Y)
}
nc := cert.NebulaCertificate{
@@ -175,6 +196,7 @@ func ca(args []string, out io.Writer, errOut io.Writer, pr PasswordReader) error
NotAfter: time.Now().Add(*cf.duration),
PublicKey: pub,
IsCA: true,
Curve: curve,
},
}
@@ -186,20 +208,20 @@ func ca(args []string, out io.Writer, errOut io.Writer, pr PasswordReader) error
return fmt.Errorf("refusing to overwrite existing CA cert: %s", *cf.outCertPath)
}
err = nc.Sign(rawPriv)
err = nc.Sign(curve, rawPriv)
if err != nil {
return fmt.Errorf("error while signing: %s", err)
}
if *cf.encryption {
b, err := cert.EncryptAndMarshalEd25519PrivateKey(rawPriv, passphrase, kdfParams)
b, err := cert.EncryptAndMarshalSigningPrivateKey(curve, rawPriv, passphrase, kdfParams)
if err != nil {
return fmt.Errorf("error while encrypting out-key: %s", err)
}
err = ioutil.WriteFile(*cf.outKeyPath, b, 0600)
} else {
err = ioutil.WriteFile(*cf.outKeyPath, cert.MarshalEd25519PrivateKey(rawPriv), 0600)
err = ioutil.WriteFile(*cf.outKeyPath, cert.MarshalSigningPrivateKey(curve, rawPriv), 0600)
}
if err != nil {