ccm.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
  2. // SPDX-License-Identifier: MIT
  3. package ciphersuite
  4. import (
  5. "crypto/aes"
  6. "encoding/binary"
  7. "fmt"
  8. "github.com/pion/dtls/v2/pkg/crypto/ccm"
  9. "github.com/pion/dtls/v2/pkg/protocol"
  10. "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
  11. )
  12. // CCMTagLen is the length of Authentication Tag
  13. type CCMTagLen int
  14. // CCM Enums
  15. const (
  16. CCMTagLength8 CCMTagLen = 8
  17. CCMTagLength CCMTagLen = 16
  18. ccmNonceLength = 12
  19. )
  20. // CCM Provides an API to Encrypt/Decrypt DTLS 1.2 Packets
  21. type CCM struct {
  22. localCCM, remoteCCM ccm.CCM
  23. localWriteIV, remoteWriteIV []byte
  24. tagLen CCMTagLen
  25. }
  26. // NewCCM creates a DTLS GCM Cipher
  27. func NewCCM(tagLen CCMTagLen, localKey, localWriteIV, remoteKey, remoteWriteIV []byte) (*CCM, error) {
  28. localBlock, err := aes.NewCipher(localKey)
  29. if err != nil {
  30. return nil, err
  31. }
  32. localCCM, err := ccm.NewCCM(localBlock, int(tagLen), ccmNonceLength)
  33. if err != nil {
  34. return nil, err
  35. }
  36. remoteBlock, err := aes.NewCipher(remoteKey)
  37. if err != nil {
  38. return nil, err
  39. }
  40. remoteCCM, err := ccm.NewCCM(remoteBlock, int(tagLen), ccmNonceLength)
  41. if err != nil {
  42. return nil, err
  43. }
  44. return &CCM{
  45. localCCM: localCCM,
  46. localWriteIV: localWriteIV,
  47. remoteCCM: remoteCCM,
  48. remoteWriteIV: remoteWriteIV,
  49. tagLen: tagLen,
  50. }, nil
  51. }
  52. // Encrypt encrypt a DTLS RecordLayer message
  53. func (c *CCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
  54. payload := raw[recordlayer.HeaderSize:]
  55. raw = raw[:recordlayer.HeaderSize]
  56. // [Psiphon]
  57. // See comment in generateAEADNonce.
  58. nonce := generateAEADNonce(c.localWriteIV, &pkt.Header)
  59. additionalData := generateAEADAdditionalData(&pkt.Header, len(payload))
  60. encryptedPayload := c.localCCM.Seal(nil, nonce, payload, additionalData)
  61. encryptedPayload = append(nonce[4:], encryptedPayload...)
  62. raw = append(raw, encryptedPayload...)
  63. // Update recordLayer size to include explicit nonce
  64. binary.BigEndian.PutUint16(raw[recordlayer.HeaderSize-2:], uint16(len(raw)-recordlayer.HeaderSize))
  65. return raw, nil
  66. }
  67. // Decrypt decrypts a DTLS RecordLayer message
  68. func (c *CCM) Decrypt(in []byte) ([]byte, error) {
  69. var h recordlayer.Header
  70. err := h.Unmarshal(in)
  71. switch {
  72. case err != nil:
  73. return nil, err
  74. case h.ContentType == protocol.ContentTypeChangeCipherSpec:
  75. // Nothing to encrypt with ChangeCipherSpec
  76. return in, nil
  77. case len(in) <= (8 + recordlayer.HeaderSize):
  78. return nil, errNotEnoughRoomForNonce
  79. }
  80. nonce := append(append([]byte{}, c.remoteWriteIV[:4]...), in[recordlayer.HeaderSize:recordlayer.HeaderSize+8]...)
  81. out := in[recordlayer.HeaderSize+8:]
  82. additionalData := generateAEADAdditionalData(&h, len(out)-int(c.tagLen))
  83. out, err = c.remoteCCM.Open(out[:0], nonce, out, additionalData)
  84. if err != nil {
  85. return nil, fmt.Errorf("%w: %v", errDecryptPacket, err) //nolint:errorlint
  86. }
  87. return append(in[:recordlayer.HeaderSize], out...), nil
  88. }