// Copyright 2016 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.
// Package chacha20 implements the ChaCha20 and XChaCha20 encryption algorithms// as specified in RFC 8439 and draft-irtf-cfrg-xchacha-01.
package chacha20import ()const (// KeySize is the size of the key used by this cipher, in bytes.KeySize = 32// NonceSize is the size of the nonce used with the standard variant of this // cipher, in bytes. // // Note that this is too short to be safely generated at random if the same // key is reused more than 2³² times.NonceSize = 12// NonceSizeX is the size of the nonce used with the XChaCha20 variant of // this cipher, in bytes.NonceSizeX = 24)// Cipher is a stateful instance of ChaCha20 or XChaCha20 using a particular key// and nonce. A *Cipher implements the cipher.Stream interface.typeCipherstruct {// The ChaCha20 state is 16 words: 4 constant, 8 of key, 1 of counter // (incremented after each block), and 3 of nonce.key [8]uint32counteruint32nonce [3]uint32// The last len bytes of buf are leftover key stream bytes from the previous // XORKeyStream invocation. The size of buf depends on how many blocks are // computed at a time by xorKeyStreamBlocks.buf [bufSize]bytelenint// overflow is set when the counter overflowed, no more blocks can be // generated, and the next XORKeyStream call should panic.overflowbool// The counter-independent results of the first round are cached after they // are computed the first time.precompDoneboolp1, p5, p9, p13uint32p2, p6, p10, p14uint32p3, p7, p11, p15uint32}var _ cipher.Stream = (*Cipher)(nil)// NewUnauthenticatedCipher creates a new ChaCha20 stream cipher with the given// 32 bytes key and a 12 or 24 bytes nonce. If a nonce of 24 bytes is provided,// the XChaCha20 construction will be used. It returns an error if key or nonce// have any other length.//// Note that ChaCha20, like all stream ciphers, is not authenticated and allows// attackers to silently tamper with the plaintext. For this reason, it is more// appropriate as a building block than as a standalone encryption mechanism.// Instead, consider using package golang.org/x/crypto/chacha20poly1305.func (, []byte) (*Cipher, error) {// This function is split into a wrapper so that the Cipher allocation will // be inlined, and depending on how the caller uses the return value, won't // escape to the heap. := &Cipher{}returnnewUnauthenticatedCipher(, , )}func ( *Cipher, , []byte) (*Cipher, error) {iflen() != KeySize {returnnil, errors.New("chacha20: wrong key size") }iflen() == NonceSizeX {// XChaCha20 uses the ChaCha20 core to mix 16 bytes of the nonce into a // derived key, allowing it to operate on a nonce of 24 bytes. See // draft-irtf-cfrg-xchacha-01, Section 2.3. , _ = HChaCha20(, [0:16]) := make([]byte, NonceSize)copy([4:12], [16:24]) = } elseiflen() != NonceSize {returnnil, errors.New("chacha20: wrong nonce size") } , = [:KeySize], [:NonceSize] // bounds check elimination hint .key = [8]uint32{binary.LittleEndian.Uint32([0:4]),binary.LittleEndian.Uint32([4:8]),binary.LittleEndian.Uint32([8:12]),binary.LittleEndian.Uint32([12:16]),binary.LittleEndian.Uint32([16:20]),binary.LittleEndian.Uint32([20:24]),binary.LittleEndian.Uint32([24:28]),binary.LittleEndian.Uint32([28:32]), } .nonce = [3]uint32{binary.LittleEndian.Uint32([0:4]),binary.LittleEndian.Uint32([4:8]),binary.LittleEndian.Uint32([8:12]), }return , nil}// The constant first 4 words of the ChaCha20 state.const (j0uint32 = 0x61707865// expaj1uint32 = 0x3320646e// nd 3j2uint32 = 0x79622d32// 2-byj3uint32 = 0x6b206574// te k)constblockSize = 64// quarterRound is the core of ChaCha20. It shuffles the bits of 4 state words.// It's executed 4 times for each of the 20 ChaCha20 rounds, operating on all 16// words each round, in columnar or diagonal groups of 4 at a time.func (, , , uint32) (uint32, uint32, uint32, uint32) { += ^= = bits.RotateLeft32(, 16) += ^= = bits.RotateLeft32(, 12) += ^= = bits.RotateLeft32(, 8) += ^= = bits.RotateLeft32(, 7)return , , , }// SetCounter sets the Cipher counter. The next invocation of XORKeyStream will// behave as if (64 * counter) bytes had been encrypted so far.//// To prevent accidental counter reuse, SetCounter panics if counter is less// than the current value.//// Note that the execution time of XORKeyStream is not independent of the// counter value.func ( *Cipher) ( uint32) {// Internally, s may buffer multiple blocks, which complicates this // implementation slightly. When checking whether the counter has rolled // back, we must use both s.counter and s.len to determine how many blocks // we have already output. := .counter - uint32(.len)/blockSizeif .overflow || < {panic("chacha20: SetCounter attempted to rollback counter") }// In the general case, we set the new counter value and reset s.len to 0, // causing the next call to XORKeyStream to refill the buffer. However, if // we're advancing within the existing buffer, we can save work by simply // setting s.len.if < .counter { .len = int(.counter-) * blockSize } else { .counter = .len = 0 }}// XORKeyStream XORs each byte in the given slice with a byte from the// cipher's key stream. Dst and src must overlap entirely or not at all.//// If len(dst) < len(src), XORKeyStream will panic. It is acceptable// to pass a dst bigger than src, and in that case, XORKeyStream will// only update dst[:len(src)] and will not touch the rest of dst.//// Multiple calls to XORKeyStream behave as if the concatenation of// the src buffers was passed in a single run. That is, Cipher// maintains state and does not reset at each XORKeyStream call.func ( *Cipher) (, []byte) {iflen() == 0 {return }iflen() < len() {panic("chacha20: output smaller than input") } = [:len()]ifalias.InexactOverlap(, ) {panic("chacha20: invalid buffer overlap") }// First, drain any remaining key stream from a previous XORKeyStream.if .len != 0 { := .buf[bufSize-.len:]iflen() < len() { = [:len()] } _ = [len()-1] // bounds check elimination hintfor , := range { [] = [] ^ } .len -= len() , = [len():], [len():] }iflen() == 0 {return }// If we'd need to let the counter overflow and keep generating output, // panic immediately. If instead we'd only reach the last block, remember // not to generate any more output after the buffer is drained. := (uint64(len()) + blockSize - 1) / blockSizeif .overflow || uint64(.counter)+ > 1<<32 {panic("chacha20: counter overflow") } elseifuint64(.counter)+ == 1<<32 { .overflow = true }// xorKeyStreamBlocks implementations expect input lengths that are a // multiple of bufSize. Platform-specific ones process multiple blocks at a // time, so have bufSizes that are a multiple of blockSize. := len() - len()%bufSizeif > 0 { .xorKeyStreamBlocks([:], [:]) } , = [:], [:]// If using a multi-block xorKeyStreamBlocks would overflow, use the generic // one that does one block at a time.const = bufSize / blockSizeifuint64(.counter)+ > 1<<32 { .buf = [bufSize]byte{} := (len() + blockSize - 1) / blockSize := .buf[bufSize-*blockSize:]copy(, ) .xorKeyStreamBlocksGeneric(, ) .len = len() - copy(, )return }// If we have a partial (multi-)block, pad it for xorKeyStreamBlocks, and // keep the leftover keystream for the next XORKeyStream invocation.iflen() > 0 { .buf = [bufSize]byte{}copy(.buf[:], ) .xorKeyStreamBlocks(.buf[:], .buf[:]) .len = bufSize - copy(, .buf[:]) }}func ( *Cipher) (, []byte) {iflen() != len() || len()%blockSize != 0 {panic("chacha20: internal error: wrong dst and/or src length") }// To generate each block of key stream, the initial cipher state // (represented below) is passed through 20 rounds of shuffling, // alternatively applying quarterRounds by columns (like 1, 5, 9, 13) // or by diagonals (like 1, 6, 11, 12). // // 0:cccccccc 1:cccccccc 2:cccccccc 3:cccccccc // 4:kkkkkkkk 5:kkkkkkkk 6:kkkkkkkk 7:kkkkkkkk // 8:kkkkkkkk 9:kkkkkkkk 10:kkkkkkkk 11:kkkkkkkk // 12:bbbbbbbb 13:nnnnnnnn 14:nnnnnnnn 15:nnnnnnnn // // c=constant k=key b=blockcount n=noncevar ( , , , = j0, j1, j2, j3 , , , = .key[0], .key[1], .key[2], .key[3] , , , = .key[4], .key[5], .key[6], .key[7] , , , = .counter, .nonce[0], .nonce[1], .nonce[2] )// Three quarters of the first round don't depend on the counter, so we can // calculate them here, and reuse them for multiple blocks in the loop, and // for future XORKeyStream invocations.if !.precompDone { .p1, .p5, .p9, .p13 = quarterRound(, , , ) .p2, .p6, .p10, .p14 = quarterRound(, , , ) .p3, .p7, .p11, .p15 = quarterRound(, , , ) .precompDone = true }// A condition of len(src) > 0 would be sufficient, but this also // acts as a bounds check elimination hint.forlen() >= 64 && len() >= 64 {// The remainder of the first column round. , , , := quarterRound(, , , .counter)// The second diagonal round. , , , := quarterRound(, .p5, .p10, .p15) , , , := quarterRound(.p1, .p6, .p11, ) , , , := quarterRound(.p2, .p7, , .p13) , , , := quarterRound(.p3, , .p9, .p14)// The remaining 18 rounds.for := 0; < 9; ++ {// Column round. , , , = quarterRound(, , , ) , , , = quarterRound(, , , ) , , , = quarterRound(, , , ) , , , = quarterRound(, , , )// Diagonal round. , , , = quarterRound(, , , ) , , , = quarterRound(, , , ) , , , = quarterRound(, , , ) , , , = quarterRound(, , , ) }// Add back the initial state to generate the key stream, then // XOR the key stream with the source and write out the result.addXor([0:4], [0:4], , )addXor([4:8], [4:8], , )addXor([8:12], [8:12], , )addXor([12:16], [12:16], , )addXor([16:20], [16:20], , )addXor([20:24], [20:24], , )addXor([24:28], [24:28], , )addXor([28:32], [28:32], , )addXor([32:36], [32:36], , )addXor([36:40], [36:40], , )addXor([40:44], [40:44], , )addXor([44:48], [44:48], , )addXor([48:52], [48:52], , .counter)addXor([52:56], [52:56], , )addXor([56:60], [56:60], , )addXor([60:64], [60:64], , ) .counter += 1 , = [blockSize:], [blockSize:] }}// HChaCha20 uses the ChaCha20 core to generate a derived key from a 32 bytes// key and a 16 bytes nonce. It returns an error if key or nonce have any other// length. It is used as part of the XChaCha20 construction.func (, []byte) ([]byte, error) {// This function is split into a wrapper so that the slice allocation will // be inlined, and depending on how the caller uses the return value, won't // escape to the heap. := make([]byte, 32)returnhChaCha20(, , )}func (, , []byte) ([]byte, error) {iflen() != KeySize {returnnil, errors.New("chacha20: wrong HChaCha20 key size") }iflen() != 16 {returnnil, errors.New("chacha20: wrong HChaCha20 nonce size") } , , , := j0, j1, j2, j3 := binary.LittleEndian.Uint32([0:4]) := binary.LittleEndian.Uint32([4:8]) := binary.LittleEndian.Uint32([8:12]) := binary.LittleEndian.Uint32([12:16]) := binary.LittleEndian.Uint32([16:20]) := binary.LittleEndian.Uint32([20:24]) := binary.LittleEndian.Uint32([24:28]) := binary.LittleEndian.Uint32([28:32]) := binary.LittleEndian.Uint32([0:4]) := binary.LittleEndian.Uint32([4:8]) := binary.LittleEndian.Uint32([8:12]) := binary.LittleEndian.Uint32([12:16])for := 0; < 10; ++ {// Diagonal round. , , , = quarterRound(, , , ) , , , = quarterRound(, , , ) , , , = quarterRound(, , , ) , , , = quarterRound(, , , )// Column round. , , , = quarterRound(, , , ) , , , = quarterRound(, , , ) , , , = quarterRound(, , , ) , , , = quarterRound(, , , ) } _ = [31] // bounds check elimination hintbinary.LittleEndian.PutUint32([0:4], )binary.LittleEndian.PutUint32([4:8], )binary.LittleEndian.PutUint32([8:12], )binary.LittleEndian.PutUint32([12:16], )binary.LittleEndian.PutUint32([16:20], )binary.LittleEndian.PutUint32([20:24], )binary.LittleEndian.PutUint32([24:28], )binary.LittleEndian.PutUint32([28:32], )return , nil}
The pages are generated with Goldsv0.6.7. (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 @Go100and1 (reachable from the left QR code) to get the latest news of Golds.