Created
January 24, 2021 07:06
-
-
Save Sean-Der/b469998a12facee778e9726b6f708129 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-type cipherSuite interface { | |
+type CipherSuite interface { | |
String() string | |
ID() CipherSuiteID | |
- certificateType() clientCertificateType | |
- hashFunc() func() hash.Hash | |
- isPSK() bool | |
- isInitialized() bool | |
+ CertificateType() ClientCertificateType | |
+ HashFunc() func() hash.Hash | |
+ IsPSK() bool | |
+ IsAnon() bool | |
+ IsInitialized() bool | |
// Generate the internal encryption state | |
- init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error | |
+ Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error | |
- encrypt(pkt *recordLayer, raw []byte) ([]byte, error) | |
- decrypt(in []byte) ([]byte, error) | |
+ Encrypt(pkt *RecordLayer, raw []byte) ([]byte, error) | |
+ Decrypt(in []byte) ([]byte, error) | |
} | |
// CipherSuiteName provides the same functionality as tls.CipherSuiteName | |
diff --git a/cipher_suite_tls_ecdh_anon_with_aes_128_cbc_sha256.go b/cipher_suite_tls_ecdh_anon_with_aes_128_cbc_sha256.go | |
new file mode 100644 | |
index 0000000..5378b70 | |
--- /dev/null | |
+++ b/cipher_suite_tls_ecdh_anon_with_aes_128_cbc_sha256.go | |
@@ -0,0 +1,96 @@ | |
// Dial connects to the given network address and establishes a DTLS connection on top. | |
// Connection handshake will timeout using ConnectContextMaker in the Config. | |
// If you want to specify the timeout duration, use DialWithContext() instead. | |
@@ -264,7 +292,24 @@ func ServerWithContext(ctx context.Context, conn net.Conn, config *Config) (*Con | |
case config == nil: | |
return nil, errNoConfigProvided | |
case config.PSK == nil && len(config.Certificates) == 0: | |
- return nil, errServerMustHaveCertificate | |
+ var hasAnon bool | |
+ for _, id := range config.CipherSuites { | |
+ if id == TLS_ECDH_ANON_WITH_AES_128_CBC_SHA256 { | |
+ hasAnon = true | |
+ break | |
+ } | |
+ } | |
+ if !hasAnon && config.CipherSuitesFactory != nil { | |
+ for _, c := range config.CipherSuitesFactory() { | |
+ if c.IsAnon() { | |
+ hasAnon = true | |
+ break | |
+ } | |
+ } | |
+ } | |
+ if !hasAnon { | |
+ return nil, errServerMustHaveCertificate | |
+ } | |
} | |
return createConn(ctx, conn, config, false, nil) | |
return additionalData[:] | |
diff --git a/crypto_cbc.go b/crypto_cbc.go | |
index 6a4e7f4..e7b76c1 100644 | |
--- a/crypto_cbc.go | |
+++ b/crypto_cbc.go | |
@@ -4,9 +4,9 @@ import ( //nolint:gci | |
"crypto/aes" | |
"crypto/cipher" | |
"crypto/hmac" | |
- "crypto/rand" | |
- "crypto/sha1" //nolint:gosec | |
+ "crypto/rand" //nolint:gosec | |
"encoding/binary" | |
+ "hash" | |
) | |
// block ciphers using cipher block chaining. | |
@@ -16,15 +16,13 @@ type cbcMode interface { | |
} | |
// State needed to handle encrypted input/output | |
-type cryptoCBC struct { | |
+type CryptoCBC struct { | |
writeCBC, readCBC cbcMode | |
writeMac, readMac []byte | |
+ hash func() hash.Hash | |
} | |
-// Currently hardcoded to be SHA1 only | |
-var cryptoCBCMacFunc = sha1.New //nolint:gochecknoglobals | |
- | |
-func newCryptoCBC(localKey, localWriteIV, localMac, remoteKey, remoteWriteIV, remoteMac []byte) (*cryptoCBC, error) { | |
+func NewCryptoCBC(hash func() hash.Hash, localKey, localWriteIV, localMac, remoteKey, remoteWriteIV, remoteMac []byte) (*CryptoCBC, error) { | |
writeBlock, err := aes.NewCipher(localKey) | |
if err != nil { | |
return nil, err | |
@@ -35,24 +33,25 @@ func newCryptoCBC(localKey, localWriteIV, localMac, remoteKey, remoteWriteIV, re | |
return nil, err | |
} | |
- return &cryptoCBC{ | |
+ return &CryptoCBC{ | |
writeCBC: cipher.NewCBCEncrypter(writeBlock, localWriteIV).(cbcMode), | |
writeMac: localMac, | |
readCBC: cipher.NewCBCDecrypter(readBlock, remoteWriteIV).(cbcMode), | |
readMac: remoteMac, | |
+ hash: hash, | |
}, nil | |
} | |
-func (c *cryptoCBC) encrypt(pkt *recordLayer, raw []byte) ([]byte, error) { | |
+func (c *CryptoCBC) Encrypt(pkt *RecordLayer, raw []byte) ([]byte, error) { | |
payload := raw[recordLayerHeaderSize:] | |
raw = raw[:recordLayerHeaderSize] | |
blockSize := c.writeCBC.BlockSize() | |
// Generate + Append MAC | |
- h := pkt.recordLayerHeader | |
+ h := pkt.RecordLayerHeader | |
- MAC, err := prfMac(h.epoch, h.sequenceNumber, h.contentType, h.protocolVersion, payload, c.writeMac) | |
+ MAC, err := prfMac(c.hash, h.Epoch, h.SequenceNumber, h.ContentType, h.ProtocolVersion, payload, c.writeMac) | |
if err != nil { | |
return nil, err | |
} | |
-func (c *cryptoCBC) decrypt(in []byte) ([]byte, error) { | |
+func (c *CryptoCBC) Decrypt(in []byte) ([]byte, error) { | |
body := in[recordLayerHeaderSize:] | |
blockSize := c.readCBC.BlockSize() | |
- mac := cryptoCBCMacFunc() | |
+ mac := c.hash() | |
- var h recordLayerHeader | |
+ var h RecordLayerHeader | |
err := h.Unmarshal(in) | |
switch { | |
case err != nil: | |
return nil, err | |
- case h.contentType == contentTypeChangeCipherSpec: | |
+ case h.ContentType == ContentTypeChangeCipherSpec: | |
// Nothing to encrypt with ChangeCipherSpec | |
return in, nil | |
case len(body)%blockSize != 0 || len(body) < blockSize+max(mac.Size()+1, blockSize): | |
@@ -122,7 +121,7 @@ func (c *cryptoCBC) decrypt(in []byte) ([]byte, error) { | |
dataEnd := len(body) - macSize - paddingLen | |
expectedMAC := body[dataEnd : dataEnd+macSize] | |
- actualMAC, err := prfMac(h.epoch, h.sequenceNumber, h.contentType, h.protocolVersion, body[:dataEnd], c.readMac) | |
+ actualMAC, err := prfMac(c.hash, h.Epoch, h.SequenceNumber, h.ContentType, h.ProtocolVersion, body[:dataEnd], c.readMac) | |
// Compute Local MAC and compare | |
if paddingGood != 255 || err != nil || !hmac.Equal(actualMAC, expectedMAC) { | |
diff --git a/flight3handler.go b/flight3handler.go | |
index d5b7aeb..b46e96b 100644 | |
--- a/flight3handler.go | |
+++ b/flight3handler.go | |
@@ -33,7 +33,7 @@ func flight3Parse(ctx context.Context, c flightConn, state *State, cache *handsh | |
} else { | |
seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence, | |
handshakeCachePullRule{handshakeTypeServerHello, cfg.initialEpoch, false, false}, | |
- handshakeCachePullRule{handshakeTypeCertificate, cfg.initialEpoch, false, false}, | |
+ handshakeCachePullRule{handshakeTypeCertificate, cfg.initialEpoch, false, true}, | |
handshakeCachePullRule{handshakeTypeServerKeyExchange, cfg.initialEpoch, false, false}, | |
handshakeCachePullRule{handshakeTypeCertificateRequest, cfg.initialEpoch, false, true}, | |
handshakeCachePullRule{handshakeTypeServerHelloDone, cfg.initialEpoch, false, false}, | |
diff --git a/flight4handler.go b/flight4handler.go | |
index e23cce4..6c1b926 100644 | |
--- a/flight4handler.go | |
+++ b/flight4handler.go | |
@@ -135,6 +135,10 @@ func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handsh | |
return 0, &alert{alertLevelFatal, alertInternalError}, nil | |
} | |
+ if state.CipherSuite != nil && state.CipherSuite.IsAnon() { | |
+ return flight6, nil, nil | |
+ } | |
+ | |
switch cfg.clientAuth { | |
case RequireAnyClientCert: | |
if state.PeerCertificates == nil { | |
@@ -201,18 +205,34 @@ func flight4Generate(c flightConn, state *State, cache *handshakeCache, cfg *han | |
}, | |
}) | |
- if cfg.localPSKCallback == nil { | |
+ switch { | |
+ case state.CipherSuite != nil && state.CipherSuite.IsAnon(): | |
+ pkts = append(pkts, &packet{ | |
+ record: &RecordLayer{ | |
+ RecordLayerHeader: RecordLayerHeader{ | |
+ ProtocolVersion: ProtocolVersion1_2, | |
+ }, | |
+ Content: &handshake{ | |
+ handshakeMessage: &handshakeMessageServerKeyExchange{ | |
+ ellipticCurveType: ellipticCurveTypeNamedCurve, | |
+ namedCurve: state.namedCurve, | |
+ publicKey: state.localKeypair.publicKey, | |
+ }, | |
+ }, | |
+ }, | |
+ }) | |
+ case cfg.localPSKCallback == nil: | |
certificate, err := cfg.getCertificate(cfg.serverName) | |
if err != nil { | |
return nil, &alert{alertLevelFatal, alertHandshakeFailure}, err | |
} | |
pkts = append(pkts, &packet{ | |
- record: &recordLayer{ | |
- recordLayerHeader: recordLayerHeader{ | |
- protocolVersion: protocolVersion1_2, | |
+ record: &RecordLayer{ | |
+ RecordLayerHeader: RecordLayerHeader{ | |
+ ProtocolVersion: ProtocolVersion1_2, | |
}, | |
- content: &handshake{ | |
+ Content: &handshake{ | |
handshakeMessage: &handshakeMessageCertificate{ | |
certificate: certificate.Certificate, | |
}, | |
diff --git a/flight5handler.go b/flight5handler.go | |
index bb7c31b..96b820d 100644 | |
--- a/flight5handler.go | |
+++ b/flight5handler.go | |
@@ -260,23 +260,23 @@ func initalizeCipherSuite(state *State, cache *handshakeCache, cfg *handshakeCon | |
if state.extendedMasterSecret { | |
var sessionHash []byte | |
- sessionHash, err = cache.sessionHash(state.cipherSuite.hashFunc(), cfg.initialEpoch, sendingPlainText) | |
+ sessionHash, err = cache.sessionHash(state.CipherSuite.HashFunc(), cfg.initialEpoch, sendingPlainText) | |
if err != nil { | |
return &alert{alertLevelFatal, alertInternalError}, err | |
} | |
- state.masterSecret, err = prfExtendedMasterSecret(state.preMasterSecret, sessionHash, state.cipherSuite.hashFunc()) | |
+ state.masterSecret, err = prfExtendedMasterSecret(state.preMasterSecret, sessionHash, state.CipherSuite.HashFunc()) | |
if err != nil { | |
return &alert{alertLevelFatal, alertIllegalParameter}, err | |
} | |
} else { | |
- state.masterSecret, err = prfMasterSecret(state.preMasterSecret, clientRandom[:], serverRandom[:], state.cipherSuite.hashFunc()) | |
+ state.masterSecret, err = prfMasterSecret(state.preMasterSecret, clientRandom[:], serverRandom[:], state.CipherSuite.HashFunc()) | |
if err != nil { | |
return &alert{alertLevelFatal, alertInternalError}, err | |
} | |
} | |
- if cfg.localPSKCallback == nil { | |
+ if cfg.localPSKCallback == nil && !(state.CipherSuite != nil && state.CipherSuite.IsAnon()) { | |
// Verify that the pair of hash algorithm and signiture is listed. | |
var validSignatureScheme bool | |
for _, ss := range cfg.localSignatureSchemes { | |
diff --git a/handshake_message_server_key_exchange.go b/handshake_message_server_key_exchange.go | |
index f9a1170..988c4bc 100644 | |
--- a/handshake_message_server_key_exchange.go | |
+++ b/handshake_message_server_key_exchange.go | |
@@ -33,6 +33,9 @@ func (h *handshakeMessageServerKeyExchange) Marshal() ([]byte, error) { | |
out = append(out, byte(len(h.publicKey))) | |
out = append(out, h.publicKey...) | |
+ if h.hashAlgorithm == hashAlgorithmNone && h.signatureAlgorithm == signatureAlgorithmAnonymous && len(h.signature) == 0 { | |
+ return out, nil | |
+ } | |
out = append(out, []byte{byte(h.hashAlgorithm), byte(h.signatureAlgorithm), 0x00, 0x00}...) | |
binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(h.signature))) | |
@@ -75,9 +78,13 @@ func (h *handshakeMessageServerKeyExchange) Unmarshal(data []byte) error { | |
return errBufferTooSmall | |
} | |
h.publicKey = append([]byte{}, data[4:offset]...) | |
- if len(data) <= offset { | |
+ if len(data) < offset { | |
return errBufferTooSmall | |
} | |
+ if len(data) == offset { | |
+ // Anon connection doesn't contains hashAlgorithm, signatureAlgorithm, signature | |
+ return nil | |
+ } | |
h.hashAlgorithm = hashAlgorithm(data[offset]) | |
if _, ok := hashAlgorithms()[h.hashAlgorithm]; !ok { | |
return errInvalidHashAlgorithm | |
diff --git a/handshake_test.go b/handshake_test.go | |
diff --git a/hash_algorithm.go b/hash_algorithm.go | |
index b659b36..573e1e9 100644 | |
--- a/hash_algorithm.go | |
+++ b/hash_algorithm.go | |
@@ -14,7 +14,7 @@ type hashAlgorithm uint16 | |
// Supported hash hash algorithms | |
const ( | |
- hashAlgorithmMD2 hashAlgorithm = 0 // Blacklisted | |
+ hashAlgorithmNone hashAlgorithm = 0 // Blacklisted | |
hashAlgorithmMD5 hashAlgorithm = 1 // Blacklisted | |
hashAlgorithmSHA1 hashAlgorithm = 2 // Blacklisted | |
hashAlgorithmSHA224 hashAlgorithm = 3 | |
@@ -27,8 +27,8 @@ const ( | |
// String makes hashAlgorithm printable | |
func (h hashAlgorithm) String() string { | |
switch h { | |
- case hashAlgorithmMD2: | |
- return "md2" | |
+ case hashAlgorithmNone: | |
+ return "none" | |
case hashAlgorithmMD5: | |
return "md5" // [RFC3279] | |
case hashAlgorithmSHA1: | |
@@ -50,6 +50,8 @@ func (h hashAlgorithm) String() string { | |
func (h hashAlgorithm) digest(b []byte) []byte { | |
switch h { | |
+ case hashAlgorithmNone: | |
+ return nil | |
case hashAlgorithmMD5: | |
hash := md5.Sum(b) // #nosec | |
return hash[:] | |
@@ -75,7 +77,7 @@ func (h hashAlgorithm) digest(b []byte) []byte { | |
func (h hashAlgorithm) insecure() bool { | |
switch h { | |
- case hashAlgorithmMD2, hashAlgorithmMD5, hashAlgorithmSHA1: | |
+ case hashAlgorithmNone, hashAlgorithmMD5, hashAlgorithmSHA1: | |
return true | |
default: | |
return false | |
@@ -84,6 +86,8 @@ func (h hashAlgorithm) insecure() bool { | |
func (h hashAlgorithm) cryptoHash() crypto.Hash { | |
switch h { | |
+ case hashAlgorithmNone: | |
+ return crypto.Hash(0) | |
case hashAlgorithmMD5: | |
return crypto.MD5 | |
case hashAlgorithmSHA1: | |
@@ -105,6 +109,7 @@ func (h hashAlgorithm) cryptoHash() crypto.Hash { | |
func hashAlgorithms() map[hashAlgorithm]struct{} { | |
return map[hashAlgorithm]struct{}{ | |
+ hashAlgorithmNone: {}, | |
hashAlgorithmMD5: {}, | |
hashAlgorithmSHA1: {}, | |
hashAlgorithmSHA224: {}, | |
diff --git a/hash_algorithm_test.go b/hash_algorithm_test.go | |
index bc55b2e..fb88221 100644 | |
--- a/hash_algorithm_test.go | |
+++ b/hash_algorithm_test.go | |
@@ -8,7 +8,7 @@ import ( | |
func TestHashAlgorithm_StringRoundtrip(t *testing.T) { | |
for algo := range hashAlgorithms() { | |
- if algo == hashAlgorithmEd25519 { | |
+ if algo == hashAlgorithmEd25519 || algo == hashAlgorithmNone { | |
// Ed25519 does hashing and signing in one time. | |
continue | |
} | |
diff --git a/prf.go b/prf.go | |
index 2d649a1..bf9c0ae 100644 | |
--- a/prf.go | |
+++ b/prf.go | |
@@ -2,8 +2,7 @@ package dtls | |
import ( //nolint:gci | |
"crypto/elliptic" | |
- "crypto/hmac" | |
- "crypto/sha1" //nolint:gosec | |
+ "crypto/hmac" //nolint:gosec | |
"encoding/binary" | |
"fmt" | |
"hash" | |
@@ -207,17 +206,17 @@ func prfVerifyDataServer(masterSecret, handshakeBodies []byte, h hashFunc) ([]by | |
return prfVerifyData(masterSecret, handshakeBodies, prfVerifyDataServerLabel, h) | |
} | |
-// compute the MAC using HMAC-SHA1 | |
-func prfMac(epoch uint16, sequenceNumber uint64, contentType contentType, protocolVersion protocolVersion, payload []byte, key []byte) ([]byte, error) { | |
- h := hmac.New(sha1.New, key) | |
+// compute the MAC using hash SHA1/SHA256 | |
+func prfMac(hash func() hash.Hash, epoch uint16, sequenceNumber uint64, contentType ContentType, protocolVersion ProtocolVersion, payload []byte, key []byte) ([]byte, error) { | |
+ h := hmac.New(hash, key) | |
msg := make([]byte, 13) | |
binary.BigEndian.PutUint16(msg, epoch) | |
putBigEndianUint48(msg[2:], sequenceNumber) | |
msg[8] = byte(contentType) | |
- msg[9] = protocolVersion.major | |
- msg[10] = protocolVersion.minor | |
+ msg[9] = protocolVersion.Major | |
+ msg[10] = protocolVersion.Minor | |
binary.BigEndian.PutUint16(msg[11:], uint16(len(payload))) | |
if _, err := h.Write(msg); err != nil { | |
diff --git a/signature_algorithm.go b/signature_algorithm.go | |
index 65191de..ab69768 100644 | |
--- a/signature_algorithm.go | |
+++ b/signature_algorithm.go | |
@@ -4,15 +4,17 @@ package dtls | |
type signatureAlgorithm uint16 | |
const ( | |
- signatureAlgorithmRSA signatureAlgorithm = 1 | |
- signatureAlgorithmECDSA signatureAlgorithm = 3 | |
- signatureAlgorithmEd25519 signatureAlgorithm = 7 | |
+ signatureAlgorithmAnonymous signatureAlgorithm = 0 | |
+ signatureAlgorithmRSA signatureAlgorithm = 1 | |
+ signatureAlgorithmECDSA signatureAlgorithm = 3 | |
+ signatureAlgorithmEd25519 signatureAlgorithm = 7 | |
) | |
func signatureAlgorithms() map[signatureAlgorithm]bool { | |
return map[signatureAlgorithm]bool{ | |
- signatureAlgorithmRSA: true, | |
- signatureAlgorithmECDSA: true, | |
- signatureAlgorithmEd25519: true, | |
+ signatureAlgorithmAnonymous: true, | |
+ signatureAlgorithmRSA: true, | |
+ signatureAlgorithmECDSA: true, | |
+ signatureAlgorithmEd25519: true, | |
} | |
} | |
diff --git a/signature_hash_algorithm.go b/signature_hash_algorithm.go | |
index 97e53ef..903ca20 100644 | |
--- a/signature_hash_algorithm.go | |
+++ b/signature_hash_algorithm.go | |
@@ -66,7 +66,7 @@ func parseSignatureSchemes(sigs []tls.SignatureScheme, insecureHashes bool) ([]s | |
} | |
} | |
h := hashAlgorithm(ss >> 8) | |
- if _, ok := hashAlgorithms()[h]; !ok { | |
+ if _, ok := hashAlgorithms()[h]; !ok || (ok && h == hashAlgorithmNone) { | |
return nil, &FatalError{ | |
xerrors.Errorf("SignatureScheme %04x: %w", ss, errInvalidHashAlgorithm), | |
} | |
diff --git a/signature_hash_algorithm_test.go b/signature_hash_algorithm_test.go | |
index 7b9bd71..150d266 100644 | |
--- a/signature_hash_algorithm_test.go | |
+++ b/signature_hash_algorithm_test.go | |
@@ -47,7 +47,7 @@ func TestParseSignatureSchemes(t *testing.T) { | |
"InvalidHashAlgorithm": { | |
input: []tls.SignatureScheme{ | |
tls.ECDSAWithP256AndSHA256, // Valid | |
- 0x0003, // Invalid: ECDSA with MD2 | |
+ 0x0003, // Invalid: ECDSA with None | |
}, | |
expected: nil, | |
insecureHashes: false, |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment