diff options
Diffstat (limited to 'src/crypto')
24 files changed, 241 insertions, 158 deletions
diff --git a/src/crypto/dsa/dsa.go b/src/crypto/dsa/dsa.go index 43826bcb55..a83359996d 100644 --- a/src/crypto/dsa/dsa.go +++ b/src/crypto/dsa/dsa.go @@ -5,6 +5,12 @@ // Package dsa implements the Digital Signature Algorithm, as defined in FIPS 186-3. // // The DSA operations in this package are not implemented using constant-time algorithms. +// +// Deprecated: DSA is a legacy algorithm, and modern alternatives such as +// Ed25519 (implemented by package crypto/ed25519) should be used instead. Keys +// with 1024-bit moduli (L1024N160 parameters) are cryptographically weak, while +// bigger keys are not widely supported. Note that FIPS 186-5 no longer approves +// DSA for signature generation. package dsa import ( diff --git a/src/crypto/hmac/hmac.go b/src/crypto/hmac/hmac.go index a6ba71c275..cdda33c2cb 100644 --- a/src/crypto/hmac/hmac.go +++ b/src/crypto/hmac/hmac.go @@ -120,6 +120,8 @@ func (h *hmac) Reset() { } // New returns a new HMAC hash using the given hash.Hash type and key. +// New functions like sha256.New from crypto/sha256 can be used as h. +// h must return a new Hash every time it is called. // Note that unlike other hash implementations in the standard library, // the returned Hash does not implement encoding.BinaryMarshaler // or encoding.BinaryUnmarshaler. @@ -127,6 +129,19 @@ func New(h func() hash.Hash, key []byte) hash.Hash { hm := new(hmac) hm.outer = h() hm.inner = h() + unique := true + func() { + defer func() { + // The comparison might panic if the underlying types are not comparable. + _ = recover() + }() + if hm.outer == hm.inner { + unique = false + } + }() + if !unique { + panic("crypto/hmac: hash generation function does not produce unique values") + } blocksize := hm.inner.BlockSize() hm.ipad = make([]byte, blocksize) hm.opad = make([]byte, blocksize) diff --git a/src/crypto/hmac/hmac_test.go b/src/crypto/hmac/hmac_test.go index 453bfb3b7f..25e67d7fe5 100644 --- a/src/crypto/hmac/hmac_test.go +++ b/src/crypto/hmac/hmac_test.go @@ -556,6 +556,17 @@ func TestHMAC(t *testing.T) { } } +func TestNonUniqueHash(t *testing.T) { + sha := sha256.New() + defer func() { + err := recover() + if err == nil { + t.Error("expected panic when calling New with a non-unique hash generation function") + } + }() + New(func() hash.Hash { return sha }, []byte("bytes")) +} + // justHash implements just the hash.Hash methods and nothing else type justHash struct { hash.Hash @@ -587,8 +598,8 @@ func BenchmarkHMACSHA256_1K(b *testing.B) { b.SetBytes(int64(len(buf))) for i := 0; i < b.N; i++ { h.Write(buf) - h.Reset() mac := h.Sum(nil) + h.Reset() buf[0] = mac[0] } } @@ -600,7 +611,18 @@ func BenchmarkHMACSHA256_32(b *testing.B) { b.SetBytes(int64(len(buf))) for i := 0; i < b.N; i++ { h.Write(buf) + mac := h.Sum(nil) h.Reset() + buf[0] = mac[0] + } +} + +func BenchmarkNewWriteSum(b *testing.B) { + buf := make([]byte, 32) + b.SetBytes(int64(len(buf))) + for i := 0; i < b.N; i++ { + h := New(sha256.New, make([]byte, 32)) + h.Write(buf) mac := h.Sum(nil) buf[0] = mac[0] } diff --git a/src/crypto/rand/eagain.go b/src/crypto/rand/eagain.go index f251ba28fc..c9499715dc 100644 --- a/src/crypto/rand/eagain.go +++ b/src/crypto/rand/eagain.go @@ -7,7 +7,7 @@ package rand import ( - "os" + "io/fs" "syscall" ) @@ -18,7 +18,7 @@ func init() { // unixIsEAGAIN reports whether err is a syscall.EAGAIN wrapped in a PathError. // See golang.org/issue/9205 func unixIsEAGAIN(err error) bool { - if pe, ok := err.(*os.PathError); ok { + if pe, ok := err.(*fs.PathError); ok { if errno, ok := pe.Err.(syscall.Errno); ok && errno == syscall.EAGAIN { return true } diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go index a5ccd19de3..fddd1147e6 100644 --- a/src/crypto/rand/rand.go +++ b/src/crypto/rand/rand.go @@ -14,7 +14,7 @@ import "io" // On Linux and FreeBSD, Reader uses getrandom(2) if available, /dev/urandom otherwise. // On OpenBSD, Reader uses getentropy(2). // On other Unix-like systems, Reader reads from /dev/urandom. -// On Windows systems, Reader uses the CryptGenRandom API. +// On Windows systems, Reader uses the RtlGenRandom API. // On Wasm, Reader uses the Web Crypto API. var Reader io.Reader @@ -23,7 +23,3 @@ var Reader io.Reader func Read(b []byte) (n int, err error) { return io.ReadFull(Reader, b) } - -func warnBlocked() { - println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel") -} diff --git a/src/crypto/rand/rand_unix.go b/src/crypto/rand/rand_unix.go index 0610f691b0..548a5e4cb9 100644 --- a/src/crypto/rand/rand_unix.go +++ b/src/crypto/rand/rand_unix.go @@ -47,6 +47,10 @@ type devReader struct { // urandom-style randomness. var altGetRandom func([]byte) (ok bool) +func warnBlocked() { + println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel") +} + func (r *devReader) Read(b []byte) (n int, err error) { if atomic.CompareAndSwapInt32(&r.used, 0, 1) { // First use of randomness. Start timer to warn about diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go index 78a4ed6d67..8b2c960906 100644 --- a/src/crypto/rand/rand_windows.go +++ b/src/crypto/rand/rand_windows.go @@ -9,48 +9,24 @@ package rand import ( "os" - "sync" - "sync/atomic" "syscall" - "time" ) -// Implemented by using Windows CryptoAPI 2.0. - func init() { Reader = &rngReader{} } -// A rngReader satisfies reads by reading from the Windows CryptGenRandom API. -type rngReader struct { - used int32 // atomic; whether this rngReader has been used - prov syscall.Handle - mu sync.Mutex -} +type rngReader struct{} func (r *rngReader) Read(b []byte) (n int, err error) { - if atomic.CompareAndSwapInt32(&r.used, 0, 1) { - // First use of randomness. Start timer to warn about - // being blocked on entropy not being available. - t := time.AfterFunc(60*time.Second, warnBlocked) - defer t.Stop() - } - r.mu.Lock() - if r.prov == 0 { - const provType = syscall.PROV_RSA_FULL - const flags = syscall.CRYPT_VERIFYCONTEXT | syscall.CRYPT_SILENT - err := syscall.CryptAcquireContext(&r.prov, nil, nil, provType, flags) - if err != nil { - r.mu.Unlock() - return 0, os.NewSyscallError("CryptAcquireContext", err) - } - } - r.mu.Unlock() + // RtlGenRandom only accepts 2**32-1 bytes at a time, so truncate. + inputLen := uint32(len(b)) - if len(b) == 0 { + if inputLen == 0 { return 0, nil } - err = syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0]) + + err = syscall.RtlGenRandom(&b[0], inputLen) if err != nil { - return 0, os.NewSyscallError("CryptGenRandom", err) + return 0, os.NewSyscallError("RtlGenRandom", err) } - return len(b), nil + return int(inputLen), nil } diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go index e4f18bf5eb..66d2c005a7 100644 --- a/src/crypto/tls/common.go +++ b/src/crypto/tls/common.go @@ -294,10 +294,26 @@ func (cs *ConnectionState) ExportKeyingMaterial(label string, context []byte, le type ClientAuthType int const ( + // NoClientCert indicates that no client certificate should be requested + // during the handshake, and if any certificates are sent they will not + // be verified. NoClientCert ClientAuthType = iota + // RequestClientCert indicates that a client certificate should be requested + // during the handshake, but does not require that the client send any + // certificates. RequestClientCert + // RequireAnyClientCert indicates that a client certificate should be requested + // during the handshake, and that at least one certificate is required to be + // sent by the client, but that certificate is not required to be valid. RequireAnyClientCert + // VerifyClientCertIfGiven indicates that a client certificate should be requested + // during the handshake, but does not require that the client sends a + // certificate. If the client does send a certificate it is required to be + // valid. VerifyClientCertIfGiven + // RequireAndVerifyClientCert indicates that a client certificate should be requested + // during the handshake, and that at least one valid certificate is required + // to be sent by the client. RequireAndVerifyClientCert ) diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go index 5dff76c988..f1d4cb926c 100644 --- a/src/crypto/tls/conn.go +++ b/src/crypto/tls/conn.go @@ -168,18 +168,18 @@ type halfConn struct { trafficSecret []byte // current TLS 1.3 traffic secret } -type permamentError struct { +type permanentError struct { err net.Error } -func (e *permamentError) Error() string { return e.err.Error() } -func (e *permamentError) Unwrap() error { return e.err } -func (e *permamentError) Timeout() bool { return e.err.Timeout() } -func (e *permamentError) Temporary() bool { return false } +func (e *permanentError) Error() string { return e.err.Error() } +func (e *permanentError) Unwrap() error { return e.err } +func (e *permanentError) Timeout() bool { return e.err.Timeout() } +func (e *permanentError) Temporary() bool { return false } func (hc *halfConn) setErrorLocked(err error) error { if e, ok := err.(net.Error); ok { - hc.err = &permamentError{err: e} + hc.err = &permanentError{err: e} } else { hc.err = err } diff --git a/src/crypto/tls/handshake_test.go b/src/crypto/tls/handshake_test.go index f55cd16ca8..605be587b5 100644 --- a/src/crypto/tls/handshake_test.go +++ b/src/crypto/tls/handshake_test.go @@ -86,7 +86,7 @@ func checkOpenSSLVersion() error { println("to update the test data.") println("") println("Configure it with:") - println("./Configure enable-weak-ssl-ciphers") + println("./Configure enable-weak-ssl-ciphers no-shared") println("and then add the apps/ directory at the front of your PATH.") println("***********************************************") @@ -403,7 +403,7 @@ func testHandshake(t *testing.T, clientConfig, serverConfig *Config) (serverStat } defer cli.Close() clientState = cli.ConnectionState() - buf, err := ioutil.ReadAll(cli) + buf, err := io.ReadAll(cli) if err != nil { t.Errorf("failed to call cli.Read: %v", err) } diff --git a/src/crypto/tls/tls_test.go b/src/crypto/tls/tls_test.go index 4ab8a430ba..9995538871 100644 --- a/src/crypto/tls/tls_test.go +++ b/src/crypto/tls/tls_test.go @@ -14,7 +14,6 @@ import ( "fmt" "internal/testenv" "io" - "io/ioutil" "math" "net" "os" @@ -594,7 +593,7 @@ func TestConnCloseWrite(t *testing.T) { } defer srv.Close() - data, err := ioutil.ReadAll(srv) + data, err := io.ReadAll(srv) if err != nil { return err } @@ -635,7 +634,7 @@ func TestConnCloseWrite(t *testing.T) { return fmt.Errorf("CloseWrite error = %v; want errShutdown", err) } - data, err := ioutil.ReadAll(conn) + data, err := io.ReadAll(conn) if err != nil { return err } @@ -698,7 +697,7 @@ func TestWarningAlertFlood(t *testing.T) { } defer srv.Close() - _, err = ioutil.ReadAll(srv) + _, err = io.ReadAll(srv) if err == nil { return errors.New("unexpected lack of error from server") } diff --git a/src/crypto/x509/internal/macos/corefoundation.go b/src/crypto/x509/internal/macos/corefoundation.go index 359694fabf..a248ee3292 100644 --- a/src/crypto/x509/internal/macos/corefoundation.go +++ b/src/crypto/x509/internal/macos/corefoundation.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin,amd64 +// +build darwin,!ios // Package macOS provides cgo-less wrappers for Core Foundation and // Security.framework, similarly to how package syscall provides access to diff --git a/src/crypto/x509/internal/macos/corefoundation.s b/src/crypto/x509/internal/macos/corefoundation.s index 8f6be47e4b..a4495d68dd 100644 --- a/src/crypto/x509/internal/macos/corefoundation.s +++ b/src/crypto/x509/internal/macos/corefoundation.s @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin,amd64 +// +build darwin,!ios #include "textflag.h" diff --git a/src/crypto/x509/internal/macos/security.go b/src/crypto/x509/internal/macos/security.go index 64fe206390..59cc19c587 100644 --- a/src/crypto/x509/internal/macos/security.go +++ b/src/crypto/x509/internal/macos/security.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin,amd64 +// +build darwin,!ios package macOS diff --git a/src/crypto/x509/internal/macos/security.s b/src/crypto/x509/internal/macos/security.s index 1630c55bab..bd446dbcbe 100644 --- a/src/crypto/x509/internal/macos/security.s +++ b/src/crypto/x509/internal/macos/security.s @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin,amd64 +// +build darwin,!ios #include "textflag.h" diff --git a/src/crypto/x509/pem_decrypt.go b/src/crypto/x509/pem_decrypt.go index 93d1e4a922..781cb3de83 100644 --- a/src/crypto/x509/pem_decrypt.go +++ b/src/crypto/x509/pem_decrypt.go @@ -95,7 +95,12 @@ func (c rfc1423Algo) deriveKey(password, salt []byte) []byte { return out } -// IsEncryptedPEMBlock returns if the PEM block is password encrypted. +// IsEncryptedPEMBlock returns whether the PEM block is password encrypted +// according to RFC 1423. +// +// Deprecated: Legacy PEM encryption as specified in RFC 1423 is insecure by +// design. Since it does not authenticate the ciphertext, it is vulnerable to +// padding oracle attacks that can let an attacker recover the plaintext. func IsEncryptedPEMBlock(b *pem.Block) bool { _, ok := b.Headers["DEK-Info"] return ok @@ -104,14 +109,18 @@ func IsEncryptedPEMBlock(b *pem.Block) bool { // IncorrectPasswordError is returned when an incorrect password is detected. var IncorrectPasswordError = errors.New("x509: decryption password incorrect") -// DecryptPEMBlock takes a password encrypted PEM block and the password used to -// encrypt it and returns a slice of decrypted DER encoded bytes. It inspects -// the DEK-Info header to determine the algorithm used for decryption. If no -// DEK-Info header is present, an error is returned. If an incorrect password -// is detected an IncorrectPasswordError is returned. Because of deficiencies -// in the encrypted-PEM format, it's not always possible to detect an incorrect -// password. In these cases no error will be returned but the decrypted DER -// bytes will be random noise. +// DecryptPEMBlock takes a PEM block encrypted according to RFC 1423 and the +// password used to encrypt it and returns a slice of decrypted DER encoded +// bytes. It inspects the DEK-Info header to determine the algorithm used for +// decryption. If no DEK-Info header is present, an error is returned. If an +// incorrect password is detected an IncorrectPasswordError is returned. Because +// of deficiencies in the format, it's not always possible to detect an +// incorrect password. In these cases no error will be returned but the +// decrypted DER bytes will be random noise. +// +// Deprecated: Legacy PEM encryption as specified in RFC 1423 is insecure by +// design. Since it does not authenticate the ciphertext, it is vulnerable to +// padding oracle attacks that can let an attacker recover the plaintext. func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) { dek, ok := b.Headers["DEK-Info"] if !ok { @@ -178,8 +187,12 @@ func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) { } // EncryptPEMBlock returns a PEM block of the specified type holding the -// given DER-encoded data encrypted with the specified algorithm and -// password. +// given DER encoded data encrypted with the specified algorithm and +// password according to RFC 1423. +// +// Deprecated: Legacy PEM encryption as specified in RFC 1423 is insecure by +// design. Since it does not authenticate the ciphertext, it is vulnerable to +// padding oracle attacks that can let an attacker recover the plaintext. func EncryptPEMBlock(rand io.Reader, blockType string, data, password []byte, alg PEMCipher) (*pem.Block, error) { ciph := cipherByKey(alg) if ciph == nil { diff --git a/src/crypto/x509/root.go b/src/crypto/x509/root.go index da5e91b91c..eccb64121f 100644 --- a/src/crypto/x509/root.go +++ b/src/crypto/x509/root.go @@ -4,7 +4,7 @@ package x509 -//go:generate go run root_darwin_ios_gen.go -version 55161.80.1 +//go:generate go run root_ios_gen.go -version 55161.80.1 import "sync" diff --git a/src/crypto/x509/root_cgo_darwin_amd64.go b/src/crypto/x509/root_cgo_darwin.go index 15c72cc0c8..15c72cc0c8 100644 --- a/src/crypto/x509/root_cgo_darwin_amd64.go +++ b/src/crypto/x509/root_cgo_darwin.go diff --git a/src/crypto/x509/root_darwin_amd64.go b/src/crypto/x509/root_darwin.go index ce88de025e..ce88de025e 100644 --- a/src/crypto/x509/root_darwin_amd64.go +++ b/src/crypto/x509/root_darwin.go diff --git a/src/crypto/x509/root_darwin_iosx.go b/src/crypto/x509/root_ios.go index 5ecc4911b3..98e747733a 100644 --- a/src/crypto/x509/root_darwin_iosx.go +++ b/src/crypto/x509/root_ios.go @@ -1,7 +1,7 @@ -// Code generated by root_darwin_ios_gen.go -version 55161.80.1; DO NOT EDIT. +// Code generated by root_ios_gen.go -version 55161.80.1; DO NOT EDIT. // Update the version in root.go and regenerate with "go generate". -// +build darwin,arm64 darwin,amd64,ios +// +build ios // +build !x509omitbundledroots package x509 diff --git a/src/crypto/x509/root_darwin_ios_gen.go b/src/crypto/x509/root_ios_gen.go index 61152b4d11..0641c073ea 100644 --- a/src/crypto/x509/root_darwin_ios_gen.go +++ b/src/crypto/x509/root_ios_gen.go @@ -4,7 +4,7 @@ // +build ignore -// Generates root_darwin_iosx.go. +// Generates root_ios.go. // // As of iOS 13, there is no API for querying the system trusted X.509 root // certificates. @@ -37,10 +37,7 @@ import ( ) func main() { - // Temporarily name the file _iosx.go, to avoid restricting it to GOOS=ios, - // as this is also used for darwin/arm64 (macOS). - // TODO: maybe use darwin/amd64 implementation on macOS arm64? - var output = flag.String("output", "root_darwin_iosx.go", "file name to write") + var output = flag.String("output", "root_ios.go", "file name to write") var version = flag.String("version", "", "security_certificates version") flag.Parse() if *version == "" { @@ -84,7 +81,7 @@ func main() { continue } - der, err := ioutil.ReadAll(tr) + der, err := io.ReadAll(tr) if err != nil { log.Fatal(err) } @@ -159,10 +156,10 @@ func main() { } } -const header = `// Code generated by root_darwin_ios_gen.go -version %s; DO NOT EDIT. +const header = `// Code generated by root_ios_gen.go -version %s; DO NOT EDIT. // Update the version in root.go and regenerate with "go generate". -// +build darwin,arm64 darwin,amd64,ios +// +build ios // +build !x509omitbundledroots package x509 diff --git a/src/crypto/x509/root_unix.go b/src/crypto/x509/root_unix.go index 2aa38751f3..ae72f025c3 100644 --- a/src/crypto/x509/root_unix.go +++ b/src/crypto/x509/root_unix.go @@ -7,6 +7,7 @@ package x509 import ( + "io/fs" "io/ioutil" "os" "path/filepath" @@ -83,7 +84,7 @@ func loadSystemRoots() (*CertPool, error) { // readUniqueDirectoryEntries is like ioutil.ReadDir but omits // symlinks that point within the directory. -func readUniqueDirectoryEntries(dir string) ([]os.FileInfo, error) { +func readUniqueDirectoryEntries(dir string) ([]fs.FileInfo, error) { fis, err := ioutil.ReadDir(dir) if err != nil { return nil, err @@ -99,8 +100,8 @@ func readUniqueDirectoryEntries(dir string) ([]os.FileInfo, error) { // isSameDirSymlink reports whether fi in dir is a symlink with a // target not containing a slash. -func isSameDirSymlink(fi os.FileInfo, dir string) bool { - if fi.Mode()&os.ModeSymlink == 0 { +func isSameDirSymlink(fi fs.FileInfo, dir string) bool { + if fi.Mode()&fs.ModeSymlink == 0 { return false } target, err := os.Readlink(filepath.Join(dir, fi.Name())) diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go index 93dca03840..b421d75973 100644 --- a/src/crypto/x509/x509.go +++ b/src/crypto/x509/x509.go @@ -159,10 +159,6 @@ type dsaAlgorithmParameters struct { P, Q, G *big.Int } -type dsaSignature struct { - R, S *big.Int -} - type validity struct { NotBefore, NotAfter time.Time } @@ -182,14 +178,15 @@ type SignatureAlgorithm int const ( UnknownSignatureAlgorithm SignatureAlgorithm = iota - MD2WithRSA - MD5WithRSA + + MD2WithRSA // Unsupported. + MD5WithRSA // Only supported for signing, not verification. SHA1WithRSA SHA256WithRSA SHA384WithRSA SHA512WithRSA - DSAWithSHA1 - DSAWithSHA256 + DSAWithSHA1 // Unsupported. + DSAWithSHA256 // Unsupported. ECDSAWithSHA1 ECDSAWithSHA256 ECDSAWithSHA384 @@ -223,7 +220,7 @@ type PublicKeyAlgorithm int const ( UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota RSA - DSA + DSA // Unsupported. ECDSA Ed25519 ) @@ -845,28 +842,6 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey } else { return rsa.VerifyPKCS1v15(pub, hashType, signed, signature) } - case *dsa.PublicKey: - if pubKeyAlgo != DSA { - return signaturePublicKeyAlgoMismatchError(pubKeyAlgo, pub) - } - dsaSig := new(dsaSignature) - if rest, err := asn1.Unmarshal(signature, dsaSig); err != nil { - return err - } else if len(rest) != 0 { - return errors.New("x509: trailing data after DSA signature") - } - if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { - return errors.New("x509: DSA signature contained zero or negative values") - } - // According to FIPS 186-3, section 4.6, the hash must be truncated if it is longer - // than the key length, but crypto/dsa doesn't do it automatically. - if maxHashLen := pub.Q.BitLen() / 8; maxHashLen < len(signed) { - signed = signed[:maxHashLen] - } - if !dsa.Verify(pub, signed, dsaSig.R, dsaSig.S) { - return errors.New("x509: DSA verification failure") - } - return case *ecdsa.PublicKey: if pubKeyAlgo != ECDSA { return signaturePublicKeyAlgoMismatchError(pubKeyAlgo, pub) @@ -2170,12 +2145,26 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv return } - return asn1.Marshal(certificate{ + signedCert, err := asn1.Marshal(certificate{ nil, c, signatureAlgorithm, asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, }) + if err != nil { + return nil, err + } + + // Check the signature to ensure the crypto.Signer behaved correctly. + // We skip this check if the signature algorithm is MD5WithRSA as we + // only support this algorithm for signing, and not verification. + if sigAlg := getSignatureAlgorithmFromAI(signatureAlgorithm); sigAlg != MD5WithRSA { + if err := checkSignature(sigAlg, c.Raw, signature, key.Public()); err != nil { + return nil, fmt.Errorf("x509: signature over certificate returned by signer is invalid: %w", err) + } + } + + return signedCert, nil } // pemCRLPrefix is the magic string that indicates that we have a PEM encoded diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go index e87294bde5..47d78cf02a 100644 --- a/src/crypto/x509/x509_test.go +++ b/src/crypto/x509/x509_test.go @@ -22,6 +22,7 @@ import ( "encoding/pem" "fmt" "internal/testenv" + "io" "math/big" "net" "net/url" @@ -988,51 +989,8 @@ func TestVerifyCertificateWithDSASignature(t *testing.T) { t.Fatalf("Failed to parse certificate: %s", err) } // test cert is self-signed - if err = cert.CheckSignatureFrom(cert); err != nil { - t.Fatalf("DSA Certificate verification failed: %s", err) - } -} - -const dsaCert1024WithSha256 = `-----BEGIN CERTIFICATE----- -MIIDKzCCAumgAwIBAgIUOXWPK4gTRZVVY7OSXTU00QEWQU8wCwYJYIZIAWUDBAMC -MEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJ -bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwIBcNMTkxMDAxMDYxODUyWhgPMzAxOTAy -MDEwNjE4NTJaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw -HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggG4MIIBLAYHKoZIzjgE -ATCCAR8CgYEAr79m/1ypU1aUbbLX1jikTyX7w2QYP+EkxNtXUiiTuxkC1KBqqxT3 -0Aht2vxFR47ODEK4B79rHO+UevhaqDaAHSH7Z/9umS0h0aS32KLDLb+LI5AneCrn -eW5YbVhfD03N7uR4kKUCKOnWj5hAk9xiE3y7oFR0bBXzqrrHJF9LMd0CFQCB6lSj -HSW0rGmNxIZsBl72u7JFLQKBgQCOFd1PGEQmddn0cdFgby5QQfjrqmoD1zNlFZEt -L0x1EbndFwelLlF1ChNh3NPNUkjwRbla07FDlONs1GMJq6w4vW11ns+pUvAZ2+RM -EVFjugip8az2ncn3UujGTVdFxnSTLBsRlMP/tFDK3ky//8zn/5ha9SKKw4v1uv6M -JuoIbwOBhQACgYEAoeKeR90nwrnoPi5MOUPBLQvuzB87slfr+3kL8vFCmgjA6MtB -7TxQKoBTOo5aVgWDp0lMIMxLd6btzBrm6r3VdRlh/cL8/PtbxkFwBa+Upe4o5NAh -ISCe2/f2leT1PxtF8xxYjz/fszeUeHsJbVMilE2cuB2SYrR5tMExiqy+QpqjUzBR -MB0GA1UdDgQWBBQDMIEL8Z3jc1d9wCxWtksUWc8RkjAfBgNVHSMEGDAWgBQDMIEL -8Z3jc1d9wCxWtksUWc8RkjAPBgNVHRMBAf8EBTADAQH/MAsGCWCGSAFlAwQDAgMv -ADAsAhQFehZgI4OyKBGpfnXvyJ0Z/0a6nAIUTO265Ane87LfJuQr3FrqvuCI354= ------END CERTIFICATE----- -` - -func TestVerifyCertificateWithDSATooLongHash(t *testing.T) { - pemBlock, _ := pem.Decode([]byte(dsaCert1024WithSha256)) - cert, err := ParseCertificate(pemBlock.Bytes) - if err != nil { - t.Fatalf("Failed to parse certificate: %s", err) - } - - // test cert is self-signed - if err = cert.CheckSignatureFrom(cert); err != nil { - t.Fatalf("DSA Certificate self-signature verification failed: %s", err) - } - - signed := []byte("A wild Gopher appears!\n") - signature, _ := hex.DecodeString("302c0214417aca7ff458f5b566e43e7b82f994953da84be50214625901e249e33f4e4838f8b5966020c286dd610e") - - // This signature is using SHA256, but only has 1024 DSA key. The hash has to be truncated - // in CheckSignature, otherwise it won't pass. - if err = cert.CheckSignature(DSAWithSHA256, signed, signature); err != nil { - t.Fatalf("DSA signature verification failed: %s", err) + if err = cert.CheckSignatureFrom(cert); err == nil { + t.Fatalf("Expected error verifying DSA certificate") } } @@ -2863,3 +2821,94 @@ func TestIA5SANEnforcement(t *testing.T) { } } } + +func BenchmarkCreateCertificate(b *testing.B) { + template := &Certificate{ + SerialNumber: big.NewInt(10), + DNSNames: []string{"example.com"}, + } + tests := []struct { + name string + gen func() crypto.Signer + }{ + { + name: "RSA 2048", + gen: func() crypto.Signer { + k, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + b.Fatalf("failed to generate test key: %s", err) + } + return k + }, + }, + { + name: "ECDSA P256", + gen: func() crypto.Signer { + k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + b.Fatalf("failed to generate test key: %s", err) + } + return k + }, + }, + } + + for _, tc := range tests { + k := tc.gen() + b.ResetTimer() + b.Run(tc.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, err := CreateCertificate(rand.Reader, template, template, k.Public(), k) + if err != nil { + b.Fatalf("failed to create certificate: %s", err) + } + } + }) + } +} + +type brokenSigner struct { + pub crypto.PublicKey +} + +func (bs *brokenSigner) Public() crypto.PublicKey { + return bs.pub +} + +func (bs *brokenSigner) Sign(_ io.Reader, _ []byte, _ crypto.SignerOpts) ([]byte, error) { + return []byte{1, 2, 3}, nil +} + +func TestCreateCertificateBrokenSigner(t *testing.T) { + template := &Certificate{ + SerialNumber: big.NewInt(10), + DNSNames: []string{"example.com"}, + } + k, err := rsa.GenerateKey(rand.Reader, 1024) + if err != nil { + t.Fatalf("failed to generate test key: %s", err) + } + expectedErr := "x509: signature over certificate returned by signer is invalid: crypto/rsa: verification error" + _, err = CreateCertificate(rand.Reader, template, template, k.Public(), &brokenSigner{k.Public()}) + if err == nil { + t.Fatal("expected CreateCertificate to fail with a broken signer") + } else if err.Error() != expectedErr { + t.Fatalf("CreateCertificate returned an unexpected error: got %q, want %q", err, expectedErr) + } +} + +func TestCreateCertificateMD5(t *testing.T) { + template := &Certificate{ + SerialNumber: big.NewInt(10), + DNSNames: []string{"example.com"}, + SignatureAlgorithm: MD5WithRSA, + } + k, err := rsa.GenerateKey(rand.Reader, 1024) + if err != nil { + t.Fatalf("failed to generate test key: %s", err) + } + _, err = CreateCertificate(rand.Reader, template, template, k.Public(), &brokenSigner{k.Public()}) + if err != nil { + t.Fatalf("CreateCertificate failed when SignatureAlgorithm = MD5WithRSA: %s", err) + } +} |