aboutsummaryrefslogtreecommitdiff
path: root/src/crypto/x509
diff options
context:
space:
mode:
Diffstat (limited to 'src/crypto/x509')
-rw-r--r--src/crypto/x509/internal/macos/corefoundation.go2
-rw-r--r--src/crypto/x509/internal/macos/corefoundation.s2
-rw-r--r--src/crypto/x509/internal/macos/security.go2
-rw-r--r--src/crypto/x509/internal/macos/security.s2
-rw-r--r--src/crypto/x509/pem_decrypt.go35
-rw-r--r--src/crypto/x509/root.go2
-rw-r--r--src/crypto/x509/root_cgo_darwin.go (renamed from src/crypto/x509/root_cgo_darwin_amd64.go)0
-rw-r--r--src/crypto/x509/root_darwin.go (renamed from src/crypto/x509/root_darwin_amd64.go)0
-rw-r--r--src/crypto/x509/root_ios.go (renamed from src/crypto/x509/root_darwin_iosx.go)4
-rw-r--r--src/crypto/x509/root_ios_gen.go (renamed from src/crypto/x509/root_darwin_ios_gen.go)13
-rw-r--r--src/crypto/x509/root_unix.go7
-rw-r--r--src/crypto/x509/x509.go53
-rw-r--r--src/crypto/x509/x509_test.go139
13 files changed, 155 insertions, 106 deletions
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)
+ }
+}