// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>// SPDX-License-Identifier: MIT
// Package ccm implements a CCM, Counter with CBC-MAC// as per RFC 3610.//// See https://tools.ietf.org/html/rfc3610//// This code was lifted from https://github.com/bocajim/dtls/blob/a3300364a283fcb490d28a93d7fcfa7ba437fbbe/ccm/ccm.go// and as such was not written by the Pions authors. Like Pions this// code is licensed under MIT.//// A request for including CCM into the Go standard library// can be found as issue #27484 on the https://github.com/golang/go/// repository.
package ccmimport ()// ccm represents a Counter with CBC-MAC with a specific key.type ccm struct { b cipher.Block M uint8 L uint8}const ccmBlockSize = 16// CCM is a block cipher in Counter with CBC-MAC mode.// Providing authenticated encryption with associated data via the cipher.AEAD interface.typeCCMinterface {cipher.AEAD// MaxLength returns the maxium length of plaintext in calls to Seal. // The maximum length of ciphertext in calls to Open is MaxLength()+Overhead(). // The maximum length is related to CCM's `L` parameter (15-noncesize) and // is 1<<(8*L) - 1 (but also limited by the maxium size of an int).MaxLength() int}var ( errInvalidBlockSize = errors.New("ccm: NewCCM requires 128-bit block cipher") errInvalidTagSize = errors.New("ccm: tagsize must be 4, 6, 8, 10, 12, 14, or 16") errInvalidNonceSize = errors.New("ccm: invalid nonce size"))// NewCCM returns the given 128-bit block cipher wrapped in CCM.// The tagsize must be an even integer between 4 and 16 inclusive// and is used as CCM's `M` parameter.// The noncesize must be an integer between 7 and 13 inclusive,// 15-noncesize is used as CCM's `L` parameter.func ( cipher.Block, , int) (CCM, error) {if .BlockSize() != ccmBlockSize {returnnil, errInvalidBlockSize }if < 4 || > 16 || &1 != 0 {returnnil, errInvalidTagSize } := 15 - if < 2 || > 8 {returnnil, errInvalidNonceSize } := &ccm{b: , M: uint8(), L: uint8()} //nolint:gosec // G114return , nil}func ( *ccm) () int { return15 - int(.L) }func ( *ccm) () int { returnint(.M) }func ( *ccm) () int { returnmaxlen(.L, .Overhead()) }func maxlen( uint8, int) int { := (uint64(1) << (8 * )) - 1if := uint64(math.MaxInt64) - uint64(); > 8 || > { //nolint:gosec // G114 = // The maximum lentgh on a 64bit arch }if != uint64(int()) { //nolint:gosec // G114returnmath.MaxInt32 - // We have only 32bit int's }returnint() //nolint:gosec // G114}// MaxNonceLength returns the maximum nonce length for a given plaintext length.// A return value <= 0 indicates that plaintext length is too large for// any nonce length.func ( int) int {const = 16for := 2; <= 8; ++ {ifmaxlen(uint8(), ) >= { //nolint:gosec // G115return15 - } }return0}func ( *ccm) (, []byte) {for := 0; < ccmBlockSize; ++ { [] ^= [] } .b.Encrypt(, )}func ( *ccm) (, []byte) {forlen() >= ccmBlockSize { .cbcRound(, [:ccmBlockSize]) = [ccmBlockSize:] }iflen() > 0 {var [ccmBlockSize]bytecopy([:], ) .cbcRound(, [:]) }}var errPlaintextTooLong = errors.New("ccm: plaintext too large")func ( *ccm) (, , []byte) ([]byte, error) {var [ccmBlockSize]byteiflen() > 0 { [0] |= 1 << 6 } [0] |= (.M - 2) << 2 [0] |= .L - 1iflen() != .NonceSize() {returnnil, errInvalidNonceSize }iflen() > .MaxLength() {returnnil, errPlaintextTooLong }binary.BigEndian.PutUint64([ccmBlockSize-8:], uint64(len()))copy([1:ccmBlockSize-.L], ) .b.Encrypt([:], [:])var [ccmBlockSize]byteif := uint64(len()); > 0 { //nolint:nestif// First adata block includes adata length := 2if <= 0xfeff {binary.BigEndian.PutUint16([:], uint16()) } else { [0] = 0xfe [1] = 0xffif < uint64(1<<32) { = 2 + 4binary.BigEndian.PutUint32([2:], uint32()) //nolint:gosec // G115 } else { = 2 + 8binary.BigEndian.PutUint64([2:], ) } } = copy([:], ) .cbcRound([:], [:]) .cbcData([:], [:]) }iflen() > 0 { .cbcData([:], ) }return [:.M], nil}// sliceForAppend takes a slice and a requested number of bytes. It returns a// slice with the contents of the given slice followed by that many bytes and a// second slice that aliases into it and contains only the extra bytes. If the// original slice has sufficient capacity then no allocation is performed.// From crypto/cipher/gcm.go// .func sliceForAppend( []byte, int) (, []byte) {if := len() + ; cap() >= { = [:] } else { = make([]byte, )copy(, ) } = [len():]return}// Seal encrypts and authenticates plaintext, authenticates the// additional data and appends the result to dst, returning the updated// slice. The nonce must be NonceSize() bytes long and unique for all// time, for a given key.// The plaintext must be no longer than MaxLength() bytes long.//// The plaintext and dst may alias exactly or not at all.func ( *ccm) (, , , []byte) []byte { , := .tag(, , )if != nil {// The cipher.AEAD interface doesn't allow for an error return.panic() // nolint }var , [ccmBlockSize]byte [0] = .L - 1copy([1:ccmBlockSize-.L], ) .b.Encrypt([:], [:])for := 0; < int(.M); ++ { [] ^= [] } [len()-1] |= 1 := cipher.NewCTR(.b, [:]) , := sliceForAppend(, len()+int(.M)) .XORKeyStream(, )copy([len():], )return}var ( errOpen = errors.New("ccm: message authentication failed") errCiphertextTooShort = errors.New("ccm: ciphertext too short") errCiphertextTooLong = errors.New("ccm: ciphertext too long"))func ( *ccm) (, , , []byte) ([]byte, error) {iflen() < int(.M) {returnnil, errCiphertextTooShort }iflen() > .MaxLength()+.Overhead() {returnnil, errCiphertextTooLong } := make([]byte, int(.M))copy(, [len()-int(.M):]) := [:len()-int(.M)]var , [ccmBlockSize]byte [0] = .L - 1copy([1:ccmBlockSize-.L], ) .b.Encrypt([:], [:])for := 0; < int(.M); ++ { [] ^= [] } [len()-1] |= 1 := cipher.NewCTR(.b, [:])// Cannot decrypt directly to dst since we're not supposed to // reveal the plaintext to the caller if authentication fails. := make([]byte, len()) .XORKeyStream(, ) , := .tag(, , )if != nil {returnnil, }ifsubtle.ConstantTimeCompare(, ) != 1 {returnnil, errOpen }returnappend(, ...), nil}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.