Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate DC test vectors and add other signature schemes #35

Merged
merged 8 commits into from
Feb 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,18 @@ validatepcap: $(VALIDATEPCAP_SRCS)
go get ./cmd/validatepcap/...
go build -o ${VALIDATEPCAP} ./cmd/validatepcap/...

# TODO(claucece): replace this makefile creation with golden files created by golang itself
algorithms := 0x0807 0x0403 0x0503 0x0603
r = $(shell awk -v min=1 -v max=3 'BEGIN{srand(); print int(min+rand()*(max-min+1))}')
alg = $(word $(call r), $(algorithms))

.PHONY: testinputs
testinputs: util
mkdir -p ${TESTDATA_DIR}
${UTIL} -make-root -out ${TESTDATA_DIR}/root.crt -key-out ${TESTDATA_DIR}/root.key -host root.com
${UTIL} -make-intermediate -cert-in ${TESTDATA_DIR}/root.crt -key-in ${TESTDATA_DIR}/root.key -out ${TESTDATA_DIR}/example.crt -key-out ${TESTDATA_DIR}/example.key -host example.com
${UTIL} -make-intermediate -cert-in ${TESTDATA_DIR}/root.crt -key-in ${TESTDATA_DIR}/root.key -out ${TESTDATA_DIR}/client-facing.crt -key-out ${TESTDATA_DIR}/client-facing.key -host client-facing.com
${UTIL} -make-dc -cert-in ${TESTDATA_DIR}/example.crt -key-in ${TESTDATA_DIR}/example.key -out ${TESTDATA_DIR}/dc.txt
${UTIL} -make-dc -cert-in ${TESTDATA_DIR}/example.crt -key-in ${TESTDATA_DIR}/example.key -alg $(call alg) -out ${TESTDATA_DIR}/dc.txt
${UTIL} -make-ech -out ${TESTDATA_DIR}/ech_configs -key-out ${TESTDATA_DIR}/ech_key -host client-facing.com
${UTIL} -make-ech -out ${TESTDATA_DIR}/ech_configs_invalid -key-out /dev/null -host client-facing.com

Expand Down
1 change: 1 addition & 0 deletions cmd/util/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ type Config struct {
// implementations.
Bugs CertificateBugs

// SignatureAlgorithm defines the signature algorithm for certificates or delegated credentials
SignatureAlgorithm signatureAlgorithm
}

Expand Down
18 changes: 9 additions & 9 deletions cmd/util/make.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ package main
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rand"
"crypto/sha1"
"crypto/x509"
Expand All @@ -35,10 +36,9 @@ func makeRootCertificate(config *Config, outPath string, outKeyPath string) {
signer, err := getSigner(&config.Bugs, config.rand(), config.SignatureAlgorithm)
fatalIfErr(err, "failed to get Signer")

priv, err := signer.GenerateKey()
priv, pub, err := signer.GenerateKey()
fatalIfErr(err, "failed to generate private key")

pub := priv.Public()
spkiASN1, err := x509.MarshalPKIXPublicKey(pub)
fatalIfErr(err, "failed to encode public key")

Expand Down Expand Up @@ -100,11 +100,9 @@ func makeIntermediateCertificate(config *Config, inCertPath string, inKeyPath st
signer, err := getSigner(&config.Bugs, config.rand(), config.SignatureAlgorithm)
fatalIfErr(err, "failed to get Signer")

priv, err := signer.GenerateKey()
priv, pub, err := signer.GenerateKey()
fatalIfErr(err, "failed to generate private key")

pub := priv.Public()

serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(config.rand(), serialNumberLimit)
fatalIfErr(err, "failed to generate serial number")
Expand Down Expand Up @@ -184,6 +182,7 @@ func makeIntermediateCertificate(config *Config, inCertPath string, inKeyPath st
}

// makeDelegatedCredential is based on code found in https://boringssl.googlesource.com/boringssl/+/refs/heads/master/ssl/test/runner/
// It generates a delegated credential for the party that asks for it
func makeDelegatedCredential(config *Config, parentSignConfig *Config, inCertPath string, inKeyPath string, outPath string) {
lifetimeSecs := int64(config.ValidFor.Seconds())
var dc []byte
Expand All @@ -194,13 +193,12 @@ func makeDelegatedCredential(config *Config, parentSignConfig *Config, inCertPat
signer, err := getSigner(&config.Bugs, config.rand(), config.SignatureAlgorithm)
fatalIfErr(err, "failed to get Signer")

priv, err := signer.GenerateKey()
priv, pub, err := signer.GenerateKey()
fatalIfErr(err, "failed to generate private key")

privPKCS8, err := x509.MarshalPKCS8PrivateKey(priv)
fatalIfErr(err, "failed to marshal ECDSA Key")
fatalIfErr(err, "failed to marshal private Key")

pub := priv.Public()
pubBytes, err := x509.MarshalPKIXPublicKey(pub)
fatalIfErr(err, "failed to marshal public Key")

Expand Down Expand Up @@ -238,6 +236,8 @@ func makeDelegatedCredential(config *Config, parentSignConfig *Config, inCertPat
} else {
log.Fatalf("ERROR: public key unsupported\n")
}
case ed25519.PublicKey:
parentSigAlg = signatureEd25519
default:
log.Fatalf("ERROR: public key unsupported\n")
}
Expand Down Expand Up @@ -268,7 +268,7 @@ func makeDelegatedCredential(config *Config, parentSignConfig *Config, inCertPat
dcSerialized := fmt.Sprintf("%x,%x", dc, privPKCS8)
err = ioutil.WriteFile(outPath, []byte(dcSerialized), 0644)
fatalIfErr(err, "failed to save DC")
log.Printf("\nThe generated DC (format: DC, privkey) is at \"%s\" \n\n", outPath)
log.Printf("\nThe generated DC (format: DC, privkey) using algorithm %x is at \"%s\" \n\n", config.SignatureAlgorithm, outPath)
}

// makeECHKey generates an ECH config and corresponding key, writing the key to
Expand Down
124 changes: 72 additions & 52 deletions cmd/util/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,92 +12,112 @@ package main
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
_ "crypto/sha256"
_ "crypto/sha512"
"encoding/asn1"
"fmt"
"io"
"math/big"
)

type ECDSASigner struct {
// Signer represents an structure holding the signing information
type Signer struct {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be fine for now, but I'm guessing once we handle RSA we should probably have a separate struct for that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could also be that we have a bool for each case: isRSA, is ed25519 and curve param for eddsa.

bugs *CertificateBugs
curve elliptic.Curve
hash crypto.Hash
priv *ecdsa.PrivateKey
priv crypto.PrivateKey
rand io.Reader
ecdsa bool
}

func maybeCorruptECDSAValue(n *big.Int, typeOfCorruption BadValue, limit *big.Int) *big.Int {
func maybeCorruptECDSASignature(typeOfCorruption BadValue, signature []byte) []byte {
switch typeOfCorruption {
case BadValueNone:
return n
case BadValueNegative:
return new(big.Int).Neg(n)
return signature
case BadValueZero:
return big.NewInt(0)
case BadValueLimit:
return limit
signature = nil
return signature
case BadValueLarge:
bad := new(big.Int).Set(limit)
return bad.Lsh(bad, 20)
signature = append(signature, 0)
return signature
default:
panic("unknown BadValue type")
panic("unknown corruption type")
}
}

func (e *ECDSASigner) Public() *ecdsa.PublicKey {
return &e.priv.PublicKey
}

// GenerateKey generates a public and private key pair.
func (e *ECDSASigner) GenerateKey() (*ecdsa.PrivateKey, error) {
priv, err := ecdsa.GenerateKey(e.curve, e.rand)
e.priv = priv
return priv, err
}

func (e *ECDSASigner) SignWithKey(key crypto.PrivateKey, msg []byte) ([]byte, error) {
ecdsaKey, _ := key.(*ecdsa.PrivateKey)

h := e.hash.New()
h.Write(msg)
digest := h.Sum(nil)

r, s, err := ecdsa.Sign(e.rand, ecdsaKey, digest)
fatalIfErr(err, "failed to sign ECDHE parameters")
// TODO(claucece): as this is used beyond DCs, it needs to support all the other algos.
func (e *Signer) GenerateKey() (crypto.PrivateKey, crypto.PublicKey, error) {
var privK crypto.PrivateKey
var pubK crypto.PublicKey
var err error

if e.ecdsa == true {
privK, err = ecdsa.GenerateKey(e.curve, e.rand)
if err != nil {
return nil, nil, err
}
pubK = privK.(*ecdsa.PrivateKey).Public()
} else {
pubK, privK, err = ed25519.GenerateKey(e.rand)
if err != nil {
return nil, nil, err
}
}

order := ecdsaKey.Curve.Params().N
r = maybeCorruptECDSAValue(r, e.bugs.BadECDSAR, order)
s = maybeCorruptECDSAValue(s, e.bugs.BadECDSAS, order)
return asn1.Marshal(ECDSASignature{r, s})
e.priv = privK
return privK, pubK, err
}

func (e *ECDSASigner) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
h := e.hash.New()
h.Write(msg)
digest := h.Sum(nil)
var directSigning crypto.Hash = 0

// SignWithKey sings a message with the appropriate key. It is only used by
// delegated credentials, and only supports algorithms allowed for them.
func (e *Signer) SignWithKey(key crypto.PrivateKey, msg []byte) ([]byte, error) {
var digest []byte
if e.hash != directSigning {
h := e.hash.New()
h.Write(msg)
digest = h.Sum(nil)
}

r, s, err := ecdsa.Sign(e.rand, e.priv, digest)
fatalIfErr(err, "failed to sign ECDHE parameters")
var sig []byte
var err error
switch sk := key.(type) {
case *ecdsa.PrivateKey:
opts := crypto.SignerOpts(e.hash)
sig, err = sk.Sign(e.rand, digest, opts)
if err != nil {
fatalIfErr(err, "failed to sign parameters")
return nil, err
}
case ed25519.PrivateKey:
opts := crypto.SignerOpts(e.hash)
sig, err = sk.Sign(e.rand, msg, opts)
if err != nil {
fatalIfErr(err, "failed to sign parameters")
return nil, err
}
default:
return nil, fmt.Errorf("tls: unsupported key type")
}

order := e.priv.Curve.Params().N
r = maybeCorruptECDSAValue(r, e.bugs.BadECDSAR, order)
s = maybeCorruptECDSAValue(s, e.bugs.BadECDSAS, order)
return asn1.Marshal(ECDSASignature{r, s})
return sig, nil
}

func getSigner(bugs *CertificateBugs, rand io.Reader, sigAlg signatureAlgorithm) (*ECDSASigner, error) {
// TODO(claucece): as this is used beyond DCs, it needs to support all the other algos.
func getSigner(bugs *CertificateBugs, rand io.Reader, sigAlg signatureAlgorithm) (*Signer, error) {
switch sigAlg {
case signatureECDSAWithSHA1:
return &ECDSASigner{bugs, nil, crypto.SHA1, nil, rand}, nil
return &Signer{bugs, nil, crypto.SHA1, nil, rand, true}, nil
case signatureECDSAWithP256AndSHA256:
return &ECDSASigner{bugs, elliptic.P256(), crypto.SHA256, nil, rand}, nil
return &Signer{bugs, elliptic.P256(), crypto.SHA256, nil, rand, true}, nil
case signatureECDSAWithP384AndSHA384:
return &ECDSASigner{bugs, elliptic.P384(), crypto.SHA384, nil, rand}, nil
return &Signer{bugs, elliptic.P384(), crypto.SHA384, nil, rand, true}, nil
case signatureECDSAWithP521AndSHA512:
return &ECDSASigner{bugs, elliptic.P521(), crypto.SHA512, nil, rand}, nil
return &Signer{bugs, elliptic.P521(), crypto.SHA512, nil, rand, true}, nil
case signatureEd25519:
return &Signer{bugs, nil, directSigning, nil, rand, false}, nil
}

return nil, fmt.Errorf("unsupported signature algorithm %04x", sigAlg)
Expand Down
12 changes: 7 additions & 5 deletions cmd/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ const usage = `Usage:

$ util -make-root -out root.crt -key-out root.key -host root.com
$ util -make-intermediate -cert-in parent.crt -key-in parent.key -out child.crt -key-out child.key -host example.com
$ util -make-dc -cert-in leaf.crt -key-in leaf.key -out dc.txt
$ util -make-dc -cert-in leaf.crt -key-in leaf.key -alg algorithm -out dc.txt
$ util -make-ech-key -cert-in client-facing.crt -out ech_configs -key-out ech_key
$ util -make-ech-key -cert-in client_facing.crt -out ech_configs -key-out ech_key

Note: This is a barebones CLI intended for basic usage/debugging.
`
Expand All @@ -37,6 +38,7 @@ func main() {
outPath = flag.String("out", "", "")
outKeyPath = flag.String("key-out", "", "")
hostName = flag.String("host", "", "")
algorithm = flag.Uint("alg", 0, "")
)
flag.Parse()
if *help {
Expand All @@ -46,11 +48,11 @@ func main() {
if *makeRootCert && (*outPath == "" || *outKeyPath == "" || *hostName == "") {
log.Fatalln("ERROR: -make-root requires -out and -key-out -host")
}
if *makeIntermediateCert && (*inCertPath == "" || *outKeyPath == "" || *inKeyPath == "" || *outKeyPath == "" || *hostName == "") {
if *makeIntermediateCert && (*inCertPath == "" || *outKeyPath == "" || *inKeyPath == "" || *outPath == "" || *hostName == "") {
log.Fatalln("ERROR: -make-intermediate requires -cert-in, -key-in, -out, -key-out, -host")
}
if *makeDC && (*inCertPath == "" || *outPath == "" || *inKeyPath == "") {
log.Fatalln("ERROR: -make-dc requires -cert-in, -key-in, -out")
if *makeDC && (*inCertPath == "" || *outPath == "" || *inKeyPath == "" || *algorithm == 0) {
log.Fatalln("ERROR: -make-dc requires -cert-in, -key-in, -alg, -out")
}
if *makeECH && (*hostName == "") {
log.Fatalln("ERROR: -make-ech requires -host")
Expand Down Expand Up @@ -85,7 +87,7 @@ func main() {
makeDelegatedCredential(
&Config{
ValidFor: 24 * time.Hour,
SignatureAlgorithm: signatureECDSAWithP521AndSHA512,
SignatureAlgorithm: signatureAlgorithm(*algorithm),
},
&Config{},
*inCertPath,
Expand Down