Repository: pion/dtls
Pull request #156 (2019-12-03 21:13:23)

Add CCM cipher suits for ECDHE_ECDSA, PSK
#### Feature
Add ciphers:
* TLS_ECDHE_ECDSA_WITH_AES_128_CCM
* TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8
* TLS_PSK_WITH_AES_128_CCM

#### Description
By OCF  Specification(https://openconnectivity.org/specs/OCF_Security_Specification_v2.1.0.pdf) only few ciphers are supported - mainly for certificates(TLS_ECDHE_ECDSA_WITH_AES_128_CCM, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8). This commit implements the ciphers.

codecov-io: # [Codecov](https://codecov.io/gh/pion/dtls/pull/156?src=pr&el=h1) Report
> Merging [#156](https://codecov.io/gh/pion/dtls/pull/156?src=pr&el=desc) into [master](https://codecov.io/gh/pion/dtls/commit/8748e4ac8bf7960bbf399e6f23a9bf4dd30d4e92?src=pr&el=desc) will **increase** coverage by `0.31%`.
> The diff coverage is `81.39%`.

[![Impacted file tree graph](https://codecov.io/gh/pion/dtls/pull/156/graphs/tree.svg?width=650&token=bcCB2jB88I&height=150&src=pr)](https://codecov.io/gh/pion/dtls/pull/156?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #156      +/-   ##
==========================================
+ Coverage   73.37%   73.69%   +0.31%     
==========================================
  Files          54       57       +3     
  Lines        3294     3417     +123     
==========================================
+ Hits         2417     2518     +101     
- Misses        608      622      +14     
- Partials      269      277       +8
```


| [Impacted Files](https://codecov.io/gh/pion/dtls/pull/156?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [crypto\_ccm.go](https://codecov.io/gh/pion/dtls/pull/156/diff?src=pr&el=tree#diff-Y3J5cHRvX2NjbS5nbw==) | `63.26% <100%> (+0.76%)` | :arrow_up: |
| [cipher\_suite.go](https://codecov.io/gh/pion/dtls/pull/156/diff?src=pr&el=tree#diff-Y2lwaGVyX3N1aXRlLmdv) | `97.4% <100%> (+0.3%)` | :arrow_up: |
| [cipher\_suite\_tls\_psk\_with\_aes\_128\_ccm8.go](https://codecov.io/gh/pion/dtls/pull/156/diff?src=pr&el=tree#diff-Y2lwaGVyX3N1aXRlX3Rsc19wc2tfd2l0aF9hZXNfMTI4X2NjbTguZ28=) | `78.94% <100%> (ø)` | :arrow_up: |
| [cipher\_suite\_tls\_ecdhe\_ecdsa\_with\_aes\_128\_ccm.go](https://codecov.io/gh/pion/dtls/pull/156/diff?src=pr&el=tree#diff-Y2lwaGVyX3N1aXRlX3Rsc19lY2RoZV9lY2RzYV93aXRoX2Flc18xMjhfY2NtLmdv) | `78.94% <78.94%> (ø)` | |
| [cipher\_suite\_tls\_psk\_with\_aes\_128\_ccm.go](https://codecov.io/gh/pion/dtls/pull/156/diff?src=pr&el=tree#diff-Y2lwaGVyX3N1aXRlX3Rsc19wc2tfd2l0aF9hZXNfMTI4X2NjbS5nbw==) | `78.94% <78.94%> (ø)` | |
| [cipher\_suite\_tls\_ecdhe\_ecdsa\_with\_aes\_128\_ccm8.go](https://codecov.io/gh/pion/dtls/pull/156/diff?src=pr&el=tree#diff-Y2lwaGVyX3N1aXRlX3Rsc19lY2RoZV9lY2RzYV93aXRoX2Flc18xMjhfY2NtOC5nbw==) | `78.94% <78.94%> (ø)` | |
| [client\_handlers.go](https://codecov.io/gh/pion/dtls/pull/156/diff?src=pr&el=tree#diff-Y2xpZW50X2hhbmRsZXJzLmdv) | `83.14% <0%> (+0.55%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pion/dtls/pull/156?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pion/dtls/pull/156?src=pr&el=footer). Last update [8748e4a...4a2c27f](https://codecov.io/gh/pion/dtls/pull/156?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

Sean-Der: nice, this LGTM @jkralik

Not your problem, but we should fine a way to cut down on all the duplication when defining a crypto suite. I could see a bug being found in one, and not fix in the other ones.
jkralik: @Sean-Der thx for comment. I merged them to the one struct.

Patch:
diff --git a/README.md b/README.md
index 783b8af..9f0887c 100644
--- a/README.md
+++ b/README.md
@@ -40,12 +40,15 @@ We would love contributes that fall under the 'Planned Features' and fixing any
 #### Supported ciphers
 
 ##### ECDHE
+* TLS_ECDHE_ECDSA_WITH_AES_128_CCM ([RFC 6655][rfc6655])
+* TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ([RFC 6655][rfc6655])
 * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ([RFC 5289][rfc5289])
 * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ([RFC 5289][rfc5289])
 * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ([RFC 8422][rfc8422])
 * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ([RFC 8422][rfc8422])
 
 ##### PSK
+* TLS_PSK_WITH_AES_128_CCM ([RFC 6655][rfc6655])
 * TLS_PSK_WITH_AES_128_CCM_8 ([RFC 6655][rfc6655])
 * TLS_PSK_WITH_AES_128_GCM_SHA256 ([RFC 5487][rfc5487])
 
diff --git a/cipher_suite.go b/cipher_suite.go
index e6404e4..e99ac1d 100644
--- a/cipher_suite.go
+++ b/cipher_suite.go
@@ -11,6 +11,10 @@ type CipherSuiteID uint16
 
 // Supported Cipher Suites
 const (
+	// AES-128-CCM
+	TLS_ECDHE_ECDSA_WITH_AES_128_CCM   CipherSuiteID = 0xc0ac
+	TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuiteID = 0xc0ae
+
 	// AES-128-GCM-SHA256
 	TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = 0xc02b
 	TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256   CipherSuiteID = 0xc02f
@@ -19,10 +23,36 @@ const (
 	TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CipherSuiteID = 0xc00a
 	TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA   CipherSuiteID = 0xc014
 
+	TLS_PSK_WITH_AES_128_CCM        CipherSuiteID = 0xc0a4
 	TLS_PSK_WITH_AES_128_CCM_8      CipherSuiteID = 0xc0a8
 	TLS_PSK_WITH_AES_128_GCM_SHA256 CipherSuiteID = 0x00a8
 )
 
+func (c CipherSuiteID) String() string {
+	switch c {
+	case TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
+		return "TLS_ECDHE_ECDSA_WITH_AES_128_CCM"
+	case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
+		return "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8"
+	case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+		return "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
+	case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+		return "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
+	case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+		return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"
+	case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+		return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"
+	case TLS_PSK_WITH_AES_128_CCM:
+		return "TLS_PSK_WITH_AES_128_CCM"
+	case TLS_PSK_WITH_AES_128_CCM_8:
+		return "TLS_PSK_WITH_AES_128_CCM_8"
+	case TLS_PSK_WITH_AES_128_GCM_SHA256:
+		return "TLS_PSK_WITH_AES_128_GCM_SHA256"
+	default:
+		return fmt.Sprintf("unknown(%v)", uint16(c))
+	}
+}
+
 type cipherSuite interface {
 	String() string
 	ID() CipherSuiteID
@@ -43,6 +73,10 @@ type cipherSuite interface {
 // function.
 func cipherSuiteForID(id CipherSuiteID) cipherSuite {
 	switch id {
+	case TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
+		return newCipherSuiteTLSEcdheEcdsaWithAes128Ccm()
+	case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
+		return newCipherSuiteTLSEcdheEcdsaWithAes128Ccm8()
 	case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
 		return &cipherSuiteTLSEcdheEcdsaWithAes128GcmSha256{}
 	case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
@@ -51,8 +85,10 @@ func cipherSuiteForID(id CipherSuiteID) cipherSuite {
 		return &cipherSuiteTLSEcdheEcdsaWithAes256CbcSha{}
 	case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
 		return &cipherSuiteTLSEcdheRsaWithAes256CbcSha{}
+	case TLS_PSK_WITH_AES_128_CCM:
+		return newCipherSuiteTLSPskWithAes128Ccm()
 	case TLS_PSK_WITH_AES_128_CCM_8:
-		return &cipherSuiteTLSPskWithAes128Ccm8{}
+		return newCipherSuiteTLSPskWithAes128Ccm8()
 	case TLS_PSK_WITH_AES_128_GCM_SHA256:
 		return &cipherSuiteTLSPskWithAes128GcmSha256{}
 	}
@@ -66,6 +102,8 @@ func defaultCipherSuites() []cipherSuite {
 		&cipherSuiteTLSEcdheEcdsaWithAes256CbcSha{},
 		&cipherSuiteTLSEcdheRsaWithAes128GcmSha256{},
 		&cipherSuiteTLSEcdheEcdsaWithAes128GcmSha256{},
+		newCipherSuiteTLSEcdheEcdsaWithAes128Ccm(),
+		newCipherSuiteTLSEcdheEcdsaWithAes128Ccm8(),
 	}
 }
 
diff --git a/cipher_suite_aes_128_ccm.go b/cipher_suite_aes_128_ccm.go
new file mode 100644
index 0000000..13e5a39
--- /dev/null
+++ b/cipher_suite_aes_128_ccm.go
@@ -0,0 +1,91 @@
+package dtls
+
+import (
+	"crypto/sha256"
+	"errors"
+	"hash"
+	"sync"
+)
+
+type cipherSuiteAes128Ccm struct {
+	ccm                   *cryptoCCM
+	clientCertificateType clientCertificateType
+	id                    CipherSuiteID
+	psk                   bool
+	cryptoCCMTagLen       cryptoCCMTagLen
+	sync.RWMutex
+}
+
+func newCipherSuiteAes128Ccm(clientCertificateType clientCertificateType, id CipherSuiteID, psk bool, cryptoCCMTagLen cryptoCCMTagLen) *cipherSuiteAes128Ccm {
+	return &cipherSuiteAes128Ccm{
+		clientCertificateType: clientCertificateType,
+		id:                    id,
+		psk:                   psk,
+		cryptoCCMTagLen:       cryptoCCMTagLen,
+	}
+}
+
+func (c *cipherSuiteAes128Ccm) certificateType() clientCertificateType {
+	return c.clientCertificateType
+}
+
+func (c *cipherSuiteAes128Ccm) ID() CipherSuiteID {
+	return c.id
+}
+
+func (c *cipherSuiteAes128Ccm) String() string {
+	return c.id.String()
+}
+
+func (c *cipherSuiteAes128Ccm) hashFunc() func() hash.Hash {
+	return sha256.New
+}
+
+func (c *cipherSuiteAes128Ccm) isPSK() bool {
+	return c.psk
+}
+
+func (c *cipherSuiteAes128Ccm) isInitialized() bool {
+	c.RLock()
+	defer c.RUnlock()
+	return c.ccm != nil
+}
+
+func (c *cipherSuiteAes128Ccm) init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
+	const (
+		prfMacLen = 0
+		prfKeyLen = 16
+		prfIvLen  = 4
+	)
+
+	keys, err := prfEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.hashFunc())
+	if err != nil {
+		return err
+	}
+
+	c.Lock()
+	defer c.Unlock()
+	if isClient {
+		c.ccm, err = newCryptoCCM(c.cryptoCCMTagLen, keys.clientWriteKey, keys.clientWriteIV, keys.serverWriteKey, keys.serverWriteIV)
+	} else {
+		c.ccm, err = newCryptoCCM(c.cryptoCCMTagLen, keys.serverWriteKey, keys.serverWriteIV, keys.clientWriteKey, keys.clientWriteIV)
+	}
+
+	return err
+}
+
+func (c *cipherSuiteAes128Ccm) encrypt(pkt *recordLayer, raw []byte) ([]byte, error) {
+	if !c.isInitialized() {
+		return nil, errors.New("CipherSuite has not been initialized, unable to encrypt")
+	}
+
+	return c.ccm.encrypt(pkt, raw)
+}
+
+func (c *cipherSuiteAes128Ccm) decrypt(raw []byte) ([]byte, error) {
+	if !c.isInitialized() {
+		return nil, errors.New("CipherSuite has not been initialized, unable to decrypt ")
+	}
+
+	return c.ccm.decrypt(raw)
+}
diff --git a/cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm.go b/cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm.go
new file mode 100644
index 0000000..4aa8f3b
--- /dev/null
+++ b/cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm.go
@@ -0,0 +1,5 @@
+package dtls
+
+func newCipherSuiteTLSEcdheEcdsaWithAes128Ccm() *cipherSuiteAes128Ccm {
+	return newCipherSuiteAes128Ccm(clientCertificateTypeECDSASign, TLS_ECDHE_ECDSA_WITH_AES_128_CCM, false, cryptoCCMTagLength)
+}
diff --git a/cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm8.go b/cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm8.go
new file mode 100644
index 0000000..58bbe13
--- /dev/null
+++ b/cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm8.go
@@ -0,0 +1,5 @@
+package dtls
+
+func newCipherSuiteTLSEcdheEcdsaWithAes128Ccm8() *cipherSuiteAes128Ccm {
+	return newCipherSuiteAes128Ccm(clientCertificateTypeECDSASign, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, false, cryptoCCM8TagLength)
+}
diff --git a/cipher_suite_tls_psk_with_aes_128_ccm.go b/cipher_suite_tls_psk_with_aes_128_ccm.go
new file mode 100644
index 0000000..7e72036
--- /dev/null
+++ b/cipher_suite_tls_psk_with_aes_128_ccm.go
@@ -0,0 +1,5 @@
+package dtls
+
+func newCipherSuiteTLSPskWithAes128Ccm() *cipherSuiteAes128Ccm {
+	return newCipherSuiteAes128Ccm(clientCertificateType(0), TLS_PSK_WITH_AES_128_CCM, true, cryptoCCMTagLength)
+}
diff --git a/cipher_suite_tls_psk_with_aes_128_ccm8.go b/cipher_suite_tls_psk_with_aes_128_ccm8.go
index af08bf5..a7fd07e 100644
--- a/cipher_suite_tls_psk_with_aes_128_ccm8.go
+++ b/cipher_suite_tls_psk_with_aes_128_ccm8.go
@@ -1,78 +1,5 @@
 package dtls
 
-import (
-	"crypto/sha256"
-	"errors"
-	"hash"
-	"sync"
-)
-
-type cipherSuiteTLSPskWithAes128Ccm8 struct {
-	ccm *cryptoCCM
-	sync.RWMutex
-}
-
-func (c *cipherSuiteTLSPskWithAes128Ccm8) certificateType() clientCertificateType {
-	return clientCertificateType(0)
-}
-
-func (c *cipherSuiteTLSPskWithAes128Ccm8) ID() CipherSuiteID {
-	return TLS_PSK_WITH_AES_128_CCM_8
-}
-
-func (c *cipherSuiteTLSPskWithAes128Ccm8) String() string {
-	return "TLS_PSK_WITH_AES_128_CCM_8"
-}
-
-func (c *cipherSuiteTLSPskWithAes128Ccm8) hashFunc() func() hash.Hash {
-	return sha256.New
-}
-
-func (c *cipherSuiteTLSPskWithAes128Ccm8) isPSK() bool {
-	return true
-}
-
-func (c *cipherSuiteTLSPskWithAes128Ccm8) isInitialized() bool {
-	c.RLock()
-	defer c.RUnlock()
-	return c.ccm != nil
-}
-
-func (c *cipherSuiteTLSPskWithAes128Ccm8) init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
-	const (
-		prfMacLen = 0
-		prfKeyLen = 16
-		prfIvLen  = 4
-	)
-
-	keys, err := prfEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.hashFunc())
-	if err != nil {
-		return err
-	}
-
-	c.Lock()
-	defer c.Unlock()
-	if isClient {
-		c.ccm, err = newCryptoCCM(keys.clientWriteKey, keys.clientWriteIV, keys.serverWriteKey, keys.serverWriteIV)
-	} else {
-		c.ccm, err = newCryptoCCM(keys.serverWriteKey, keys.serverWriteIV, keys.clientWriteKey, keys.clientWriteIV)
-	}
-
-	return err
-}
-
-func (c *cipherSuiteTLSPskWithAes128Ccm8) encrypt(pkt *recordLayer, raw []byte) ([]byte, error) {
-	if !c.isInitialized() {
-		return nil, errors.New("CipherSuite has not been initialized, unable to encrypt")
-	}
-
-	return c.ccm.encrypt(pkt, raw)
-}
-
-func (c *cipherSuiteTLSPskWithAes128Ccm8) decrypt(raw []byte) ([]byte, error) {
-	if !c.isInitialized() {
-		return nil, errors.New("CipherSuite has not been initialized, unable to decrypt ")
-	}
-
-	return c.ccm.decrypt(raw)
+func newCipherSuiteTLSPskWithAes128Ccm8() *cipherSuiteAes128Ccm {
+	return newCipherSuiteAes128Ccm(clientCertificateType(0), TLS_PSK_WITH_AES_128_CCM_8, true, cryptoCCM8TagLength)
 }
diff --git a/crypto_ccm.go b/crypto_ccm.go
index 57e3949..8f12210 100644
--- a/crypto_ccm.go
+++ b/crypto_ccm.go
@@ -9,23 +9,27 @@ import (
 	"github.com/pion/dtls/v2/internal/crypto/ccm"
 )
 
+type cryptoCCMTagLen int
+
 const (
-	cryptoCCMTagLength   = 8
-	cryptoCCMNonceLength = 12
+	cryptoCCM8TagLength  cryptoCCMTagLen = 8
+	cryptoCCMTagLength   cryptoCCMTagLen = 16
+	cryptoCCMNonceLength                 = 12
 )
 
 // State needed to handle encrypted input/output
 type cryptoCCM struct {
 	localCCM, remoteCCM         ccm.CCM
 	localWriteIV, remoteWriteIV []byte
+	tagLen                      cryptoCCMTagLen
 }
 
-func newCryptoCCM(localKey, localWriteIV, remoteKey, remoteWriteIV []byte) (*cryptoCCM, error) {
+func newCryptoCCM(tagLen cryptoCCMTagLen, localKey, localWriteIV, remoteKey, remoteWriteIV []byte) (*cryptoCCM, error) {
 	localBlock, err := aes.NewCipher(localKey)
 	if err != nil {
 		return nil, err
 	}
-	localCCM, err := ccm.NewCCM(localBlock, cryptoCCMTagLength, cryptoCCMNonceLength)
+	localCCM, err := ccm.NewCCM(localBlock, int(tagLen), cryptoCCMNonceLength)
 	if err != nil {
 		return nil, err
 	}
@@ -34,7 +38,7 @@ func newCryptoCCM(localKey, localWriteIV, remoteKey, remoteWriteIV []byte) (*cry
 	if err != nil {
 		return nil, err
 	}
-	remoteCCM, err := ccm.NewCCM(remoteBlock, cryptoCCMTagLength, cryptoCCMNonceLength)
+	remoteCCM, err := ccm.NewCCM(remoteBlock, int(tagLen), cryptoCCMNonceLength)
 	if err != nil {
 		return nil, err
 	}
@@ -44,6 +48,7 @@ func newCryptoCCM(localKey, localWriteIV, remoteKey, remoteWriteIV []byte) (*cry
 		localWriteIV:  localWriteIV,
 		remoteCCM:     remoteCCM,
 		remoteWriteIV: remoteWriteIV,
+		tagLen:        tagLen,
 	}, nil
 }
 
@@ -83,7 +88,7 @@ func (c *cryptoCCM) decrypt(in []byte) ([]byte, error) {
 	nonce := append(append([]byte{}, c.remoteWriteIV[:4]...), in[recordLayerHeaderSize:recordLayerHeaderSize+8]...)
 	out := in[recordLayerHeaderSize+8:]
 
-	additionalData := generateAEADAdditionalData(&h, len(out)-cryptoCCMTagLength)
+	additionalData := generateAEADAdditionalData(&h, len(out)-int(c.tagLen))
 	out, err = c.remoteCCM.Open(out[:0], nonce, out, additionalData)
 	if err != nil {
 		return nil, fmt.Errorf("decryptPacket: %v", err)
diff --git a/go.mod b/go.mod
index 9147424..e44885e 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/pion/dtls/v2
 require (
 	github.com/pion/logging v0.2.2
 	github.com/pion/transport v0.8.10
-	golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf
+	golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd
 )
 
 go 1.13
diff --git a/go.sum b/go.sum
index 1d092e0..ac9f7de 100644
--- a/go.sum
+++ b/go.sum
@@ -7,8 +7,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf h1:fnPsqIDRbCSgumaMCRpoIoF2s4qxv0xSSS0BVZUE/ss=
-golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd h1:GGJVjV8waZKRHrgwvtH66z9ZGVurTD1MT0n1Bb+q4aM=
+golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/prf.go b/prf.go
index b506a77..c83d94b 100644
--- a/prf.go
+++ b/prf.go
@@ -69,12 +69,7 @@ func prfPSKPreMasterSecret(psk []byte) []byte {
 func prfPreMasterSecret(publicKey, privateKey []byte, curve namedCurve) ([]byte, error) {
 	switch curve {
 	case namedCurveX25519:
-		var preMasterSecret, fixedWidthPrivateKey, fixedWidthPublicKey [32]byte
-		copy(fixedWidthPrivateKey[:], privateKey)
-		copy(fixedWidthPublicKey[:], publicKey)
-
-		curve25519.ScalarMult(&preMasterSecret, &fixedWidthPrivateKey, &fixedWidthPublicKey)
-		return preMasterSecret[:], nil
+		return curve25519.X25519(privateKey, publicKey)
 	case namedCurveP256:
 		return ellipticCurvePreMasterSecret(publicKey, privateKey, elliptic.P256(), elliptic.P256())
 	case namedCurveP384:


Test patch:
diff --git a/conn_test.go b/conn_test.go
index 83c97e5..8e17a2d 100644
--- a/conn_test.go
+++ b/conn_test.go
@@ -840,6 +840,20 @@ func TestCipherSuiteConfiguration(t *testing.T) {
 			WantClientError:    errors.New("alert: Alert LevelFatal: InsufficientSecurity"),
 			WantServerError:    errCipherSuiteNoIntersection,
 		},
+		{
+			Name:               "Valid CipherSuites CCM specified",
+			ClientCipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_CCM},
+			ServerCipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_CCM},
+			WantClientError:    nil,
+			WantServerError:    nil,
+		},
+		{
+			Name:               "Valid CipherSuites CCM-8 specified",
+			ClientCipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8},
+			ServerCipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8},
+			WantClientError:    nil,
+			WantServerError:    nil,
+		},
 	} {
 		ca, cb := net.Pipe()
 		type result struct {
diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go
index ecdd338..3d1bf22 100644
--- a/e2e/e2e_test.go
+++ b/e2e/e2e_test.go
@@ -222,6 +222,8 @@ func TestPionE2ESimpleED25519(t *testing.T) {
 	serverPort := randomPort(t)
 
 	for _, cipherSuite := range []dtls.CipherSuiteID{
+		dtls.TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
+		dtls.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
 		dtls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
 		dtls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
 	} {
@@ -254,6 +256,7 @@ func TestPionE2ESimplePSK(t *testing.T) {
 	serverPort := randomPort(t)
 
 	for _, cipherSuite := range []dtls.CipherSuiteID{
+		dtls.TLS_PSK_WITH_AES_128_CCM,
 		dtls.TLS_PSK_WITH_AES_128_CCM_8,
 		dtls.TLS_PSK_WITH_AES_128_GCM_SHA256,
 	} {