Skip to content

Commit 3b5d798

Browse files
committed
Disable insecure hash algorithms by default
We add a new option on the configuratio which toggles the use of hash algorithms that are deemed insecure. We default to disallowing insecure hash algorithms, MD2 (which we don't support at all), MD5 and SHA-1. When parsing signature algorithms we check whether it's being passed an insecure hash algorithm, and whether we are configured to allow it or not. If we don't allow it, we omit adding it, to ensure we never advertise this capability.
1 parent 24f778e commit 3b5d798

6 files changed

Lines changed: 78 additions & 19 deletions

config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ type Config struct {
5757
// This should be used only for testing.
5858
InsecureSkipVerify bool
5959

60+
// InsecureHashes allows the use of hashing algorithms that are known
61+
// to be vulnerable.
62+
InsecureHashes bool
63+
6064
// VerifyPeerCertificate, if not nil, is called after normal
6165
// certificate verification by either a client or server. It
6266
// receives the certificate provided by the peer and also a flag

conn.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient
8383
return nil, err
8484
}
8585

86-
signatureSchemes, err := parseSignatureSchemes(config.SignatureSchemes)
86+
signatureSchemes, err := parseSignatureSchemes(config.SignatureSchemes, config.InsecureHashes)
8787
if err != nil {
8888
return nil, err
8989
}

hash_algorithm.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type hashAlgorithm uint16
1414

1515
// Supported hash hash algorithms
1616
const (
17-
// hashAlgorithmMD2 hashAlgorithm = 0 // Blacklisted
17+
hashAlgorithmMD2 hashAlgorithm = 0 // Blacklisted
1818
hashAlgorithmMD5 hashAlgorithm = 1 // Blacklisted
1919
hashAlgorithmSHA1 hashAlgorithm = 2 // Blacklisted
2020
hashAlgorithmSHA224 hashAlgorithm = 3
@@ -27,6 +27,8 @@ const (
2727
// String makes hashAlgorithm printable
2828
func (h hashAlgorithm) String() string {
2929
switch h {
30+
case hashAlgorithmMD2:
31+
return "md2"
3032
case hashAlgorithmMD5:
3133
return "md5" // [RFC3279]
3234
case hashAlgorithmSHA1:
@@ -42,7 +44,7 @@ func (h hashAlgorithm) String() string {
4244
case hashAlgorithmEd25519:
4345
return "null"
4446
default:
45-
return "unknown hash algorithm"
47+
return "unknown or unsupported hash algorithm"
4648
}
4749
}
4850

@@ -71,6 +73,15 @@ func (h hashAlgorithm) digest(b []byte) []byte {
7173
}
7274
}
7375

76+
func (h hashAlgorithm) insecure() bool {
77+
switch h {
78+
case hashAlgorithmMD2, hashAlgorithmMD5, hashAlgorithmSHA1:
79+
return true
80+
default:
81+
return false
82+
}
83+
}
84+
7485
func (h hashAlgorithm) cryptoHash() crypto.Hash {
7586
switch h {
7687
case hashAlgorithmMD5:
@@ -88,7 +99,7 @@ func (h hashAlgorithm) cryptoHash() crypto.Hash {
8899
case hashAlgorithmEd25519:
89100
return crypto.Hash(0)
90101
default:
91-
return 0
102+
return crypto.Hash(0)
92103
}
93104
}
94105

signature_hash_algorithm.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func (s *signatureHashAlgorithm) isCompatible(privateKey crypto.PrivateKey) bool
5353

5454
// parseSignatureSchemes translates []tls.SignatureScheme to []signatureHashAlgorithm.
5555
// It returns default signature scheme list if no SignatureScheme is passed.
56-
func parseSignatureSchemes(sigs []tls.SignatureScheme) ([]signatureHashAlgorithm, error) {
56+
func parseSignatureSchemes(sigs []tls.SignatureScheme, insecureHashes bool) ([]signatureHashAlgorithm, error) {
5757
if len(sigs) == 0 {
5858
return defaultSignatureSchemes(), nil
5959
}
@@ -71,10 +71,18 @@ func parseSignatureSchemes(sigs []tls.SignatureScheme) ([]signatureHashAlgorithm
7171
xerrors.Errorf("SignatureScheme %04x: %w", ss, errInvalidHashAlgorithm),
7272
}
7373
}
74+
if h.insecure() && !insecureHashes {
75+
continue
76+
}
7477
out = append(out, signatureHashAlgorithm{
7578
hash: h,
7679
signature: sig,
7780
})
7881
}
82+
83+
if len(out) == 0 {
84+
return nil, errNoAvailableSignatureSchemes
85+
}
86+
7987
return out, nil
8088
}

signature_hash_algorithm_go113_test.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import (
1212

1313
func TestParseSignatureSchemes_Ed25519(t *testing.T) {
1414
cases := map[string]struct {
15-
input []tls.SignatureScheme
16-
expected []signatureHashAlgorithm
17-
err error
15+
input []tls.SignatureScheme
16+
expected []signatureHashAlgorithm
17+
err error
18+
insecureHashes bool
1819
}{
1920
"Translate": {
2021
input: []tls.SignatureScheme{
@@ -23,14 +24,15 @@ func TestParseSignatureSchemes_Ed25519(t *testing.T) {
2324
expected: []signatureHashAlgorithm{
2425
{hashAlgorithmEd25519, signatureAlgorithmEd25519},
2526
},
26-
err: nil,
27+
err: nil,
28+
insecureHashes: false,
2729
},
2830
}
2931

3032
for name, testCase := range cases {
3133
testCase := testCase
3234
t.Run(name, func(t *testing.T) {
33-
output, err := parseSignatureSchemes(testCase.input)
35+
output, err := parseSignatureSchemes(testCase.input, testCase.insecureHashes)
3436
if testCase.err != nil && !xerrors.Is(err, testCase.err) {
3537
t.Fatalf("Expected error: %v, got: %v", testCase.err, err)
3638
}

signature_hash_algorithm_test.go

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ import (
1010

1111
func TestParseSignatureSchemes(t *testing.T) {
1212
cases := map[string]struct {
13-
input []tls.SignatureScheme
14-
expected []signatureHashAlgorithm
15-
err error
13+
input []tls.SignatureScheme
14+
expected []signatureHashAlgorithm
15+
err error
16+
insecureHashes bool
1617
}{
1718
"Translate": {
1819
input: []tls.SignatureScheme{
@@ -31,30 +32,63 @@ func TestParseSignatureSchemes(t *testing.T) {
3132
{hashAlgorithmSHA384, signatureAlgorithmRSA},
3233
{hashAlgorithmSHA512, signatureAlgorithmRSA},
3334
},
34-
err: nil,
35+
insecureHashes: false,
36+
err: nil,
3537
},
3638
"InvalidSignatureAlgorithm": {
3739
input: []tls.SignatureScheme{
3840
tls.ECDSAWithP256AndSHA256, // Valid
3941
0x04FF, // Invalid: unknown signature with SHA-256
4042
},
41-
expected: nil,
42-
err: errInvalidSignatureAlgorithm,
43+
expected: nil,
44+
insecureHashes: false,
45+
err: errInvalidSignatureAlgorithm,
4346
},
4447
"InvalidHashAlgorithm": {
4548
input: []tls.SignatureScheme{
4649
tls.ECDSAWithP256AndSHA256, // Valid
4750
0x0003, // Invalid: ECDSA with MD2
4851
},
49-
expected: nil,
50-
err: errInvalidHashAlgorithm,
52+
expected: nil,
53+
insecureHashes: false,
54+
err: errInvalidHashAlgorithm,
55+
},
56+
"InsecureHashAlgorithmDenied": {
57+
input: []tls.SignatureScheme{
58+
tls.ECDSAWithP256AndSHA256, // Valid
59+
tls.ECDSAWithSHA1, // Insecure
60+
},
61+
expected: []signatureHashAlgorithm{
62+
{hashAlgorithmSHA256, signatureAlgorithmECDSA},
63+
},
64+
insecureHashes: false,
65+
err: nil,
66+
},
67+
"InsecureHashAlgorithmAllowed": {
68+
input: []tls.SignatureScheme{
69+
tls.ECDSAWithP256AndSHA256, // Valid
70+
tls.ECDSAWithSHA1, // Insecure
71+
},
72+
expected: []signatureHashAlgorithm{
73+
{hashAlgorithmSHA256, signatureAlgorithmECDSA},
74+
{hashAlgorithmSHA1, signatureAlgorithmECDSA},
75+
},
76+
insecureHashes: true,
77+
err: nil,
78+
},
79+
"OnlyInsecureHashAlgorithm": {
80+
input: []tls.SignatureScheme{
81+
tls.ECDSAWithSHA1, // Insecure
82+
},
83+
insecureHashes: false,
84+
err: errNoAvailableSignatureSchemes,
5185
},
5286
}
5387

5488
for name, testCase := range cases {
5589
testCase := testCase
5690
t.Run(name, func(t *testing.T) {
57-
output, err := parseSignatureSchemes(testCase.input)
91+
output, err := parseSignatureSchemes(testCase.input, testCase.insecureHashes)
5892
if testCase.err != nil && !xerrors.Is(err, testCase.err) {
5993
t.Fatalf("Expected error: %v, got: %v", testCase.err, err)
6094
}

0 commit comments

Comments
 (0)