Skip to content

Instantly share code, notes, and snippets.

@Sean-Der
Created January 24, 2021 07:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Sean-Der/b469998a12facee778e9726b6f708129 to your computer and use it in GitHub Desktop.
Save Sean-Der/b469998a12facee778e9726b6f708129 to your computer and use it in GitHub Desktop.
-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