| // 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 encryption algorithm |
| // as specified in RFC 8439. |
| package chacha20 |
| |
| import ( |
| "crypto/cipher" |
| "encoding/binary" |
| "math/bits" |
| |
| "golang.org/x/crypto/internal/subtle" |
| ) |
| |
| // Cipher is a stateful instance of ChaCha20 using a particular key |
| // and nonce. A *Cipher implements the cipher.Stream interface. |
| type Cipher struct { |
| // The ChaCha20 state is 16 words: 4 constant, 8 of key, 1 of counter |
| // (incremented after each block), and 3 of nonce. |
| key [8]uint32 |
| counter uint32 |
| nonce [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. |
| buf [bufSize]byte |
| len int |
| |
| // The counter-independent results of the first round are cached after they |
| // are computed the first time. |
| precompDone bool |
| p1, p5, p9, p13 uint32 |
| p2, p6, p10, p14 uint32 |
| p3, p7, p11, p15 uint32 |
| } |
| |
| var _ cipher.Stream = (*Cipher)(nil) |
| |
| // New creates a new ChaCha20 stream cipher with the given key and nonce. |
| // The initial counter value is set to 0. |
| func New(key [8]uint32, nonce [3]uint32) *Cipher { |
| return &Cipher{key: key, nonce: nonce} |
| } |
| |
| // The constant first 4 words of the ChaCha20 state. |
| const ( |
| j0 uint32 = 0x61707865 // expa |
| j1 uint32 = 0x3320646e // nd 3 |
| j2 uint32 = 0x79622d32 // 2-by |
| j3 uint32 = 0x6b206574 // te k |
| ) |
| |
| const blockSize = 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 quarterRound(a, b, c, d uint32) (uint32, uint32, uint32, uint32) { |
| a += b |
| d ^= a |
| d = bits.RotateLeft32(d, 16) |
| c += d |
| b ^= c |
| b = bits.RotateLeft32(b, 12) |
| a += b |
| d ^= a |
| d = bits.RotateLeft32(d, 8) |
| c += d |
| b ^= c |
| b = bits.RotateLeft32(b, 7) |
| return a, b, c, d |
| } |
| |
| // 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 (s *Cipher) XORKeyStream(dst, src []byte) { |
| if len(src) == 0 { |
| return |
| } |
| if len(dst) < len(src) { |
| panic("chacha20: output smaller than input") |
| } |
| dst = dst[:len(src)] |
| if subtle.InexactOverlap(dst, src) { |
| panic("chacha20: invalid buffer overlap") |
| } |
| |
| // First, drain any remaining key stream from a previous XORKeyStream. |
| if s.len != 0 { |
| keyStream := s.buf[bufSize-s.len:] |
| if len(src) < len(keyStream) { |
| keyStream = keyStream[:len(src)] |
| } |
| _ = src[len(keyStream)-1] // bounds check elimination hint |
| for i, b := range keyStream { |
| dst[i] = src[i] ^ b |
| } |
| s.len -= len(keyStream) |
| src = src[len(keyStream):] |
| dst = dst[len(keyStream):] |
| } |
| |
| const blocksPerBuf = bufSize / blockSize |
| numBufs := (uint64(len(src)) + bufSize - 1) / bufSize |
| if uint64(s.counter)+numBufs*blocksPerBuf >= 1<<32 { |
| panic("chacha20: counter overflow") |
| } |
| |
| // 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. |
| |
| rem := len(src) % bufSize |
| full := len(src) - rem |
| |
| if full > 0 { |
| s.xorKeyStreamBlocks(dst[:full], src[:full]) |
| } |
| |
| // If we have a partial (multi-)block, pad it for xorKeyStreamBlocks, and |
| // keep the leftover keystream for the next XORKeyStream invocation. |
| if rem > 0 { |
| s.buf = [bufSize]byte{} |
| copy(s.buf[:], src[full:]) |
| s.xorKeyStreamBlocks(s.buf[:], s.buf[:]) |
| s.len = bufSize - copy(dst[full:], s.buf[:]) |
| } |
| } |
| |
| func (s *Cipher) xorKeyStreamBlocksGeneric(dst, src []byte) { |
| if len(dst) != len(src) || len(dst)%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=nonce |
| var ( |
| c0, c1, c2, c3 = j0, j1, j2, j3 |
| c4, c5, c6, c7 = s.key[0], s.key[1], s.key[2], s.key[3] |
| c8, c9, c10, c11 = s.key[4], s.key[5], s.key[6], s.key[7] |
| _, c13, c14, c15 = s.counter, s.nonce[0], s.nonce[1], s.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 !s.precompDone { |
| s.p1, s.p5, s.p9, s.p13 = quarterRound(c1, c5, c9, c13) |
| s.p2, s.p6, s.p10, s.p14 = quarterRound(c2, c6, c10, c14) |
| s.p3, s.p7, s.p11, s.p15 = quarterRound(c3, c7, c11, c15) |
| s.precompDone = true |
| } |
| |
| for i := 0; i < len(src); i += blockSize { |
| // The remainder of the first column round. |
| fcr0, fcr4, fcr8, fcr12 := quarterRound(c0, c4, c8, s.counter) |
| |
| // The second diagonal round. |
| x0, x5, x10, x15 := quarterRound(fcr0, s.p5, s.p10, s.p15) |
| x1, x6, x11, x12 := quarterRound(s.p1, s.p6, s.p11, fcr12) |
| x2, x7, x8, x13 := quarterRound(s.p2, s.p7, fcr8, s.p13) |
| x3, x4, x9, x14 := quarterRound(s.p3, fcr4, s.p9, s.p14) |
| |
| // The remaining 18 rounds. |
| for i := 0; i < 9; i++ { |
| // Column round. |
| x0, x4, x8, x12 = quarterRound(x0, x4, x8, x12) |
| x1, x5, x9, x13 = quarterRound(x1, x5, x9, x13) |
| x2, x6, x10, x14 = quarterRound(x2, x6, x10, x14) |
| x3, x7, x11, x15 = quarterRound(x3, x7, x11, x15) |
| |
| // Diagonal round. |
| x0, x5, x10, x15 = quarterRound(x0, x5, x10, x15) |
| x1, x6, x11, x12 = quarterRound(x1, x6, x11, x12) |
| x2, x7, x8, x13 = quarterRound(x2, x7, x8, x13) |
| x3, x4, x9, x14 = quarterRound(x3, x4, x9, x14) |
| } |
| |
| // Finally, add back the initial state to generate the key stream. |
| x0 += c0 |
| x1 += c1 |
| x2 += c2 |
| x3 += c3 |
| x4 += c4 |
| x5 += c5 |
| x6 += c6 |
| x7 += c7 |
| x8 += c8 |
| x9 += c9 |
| x10 += c10 |
| x11 += c11 |
| x12 += s.counter |
| x13 += c13 |
| x14 += c14 |
| x15 += c15 |
| |
| s.counter += 1 |
| if s.counter == 0 { |
| panic("chacha20: internal error: counter overflow") |
| } |
| |
| in, out := src[i:], dst[i:] |
| in, out = in[:blockSize], out[:blockSize] // bounds check elimination hint |
| |
| // XOR the key stream with the source and write out the result. |
| xor(out[0:], in[0:], x0) |
| xor(out[4:], in[4:], x1) |
| xor(out[8:], in[8:], x2) |
| xor(out[12:], in[12:], x3) |
| xor(out[16:], in[16:], x4) |
| xor(out[20:], in[20:], x5) |
| xor(out[24:], in[24:], x6) |
| xor(out[28:], in[28:], x7) |
| xor(out[32:], in[32:], x8) |
| xor(out[36:], in[36:], x9) |
| xor(out[40:], in[40:], x10) |
| xor(out[44:], in[44:], x11) |
| xor(out[48:], in[48:], x12) |
| xor(out[52:], in[52:], x13) |
| xor(out[56:], in[56:], x14) |
| xor(out[60:], in[60:], x15) |
| } |
| } |
| |
| // Advance discards bytes in the key stream until the next 64 byte block |
| // boundary is reached. If the key stream is already at a block boundary no |
| // bytes will be discarded. |
| func (s *Cipher) Advance() { |
| s.len -= s.len % blockSize |
| } |
| |
| // XORKeyStream crypts bytes from in to out using the given key and counters. |
| // In and out must overlap entirely or not at all. Counter contains the raw |
| // ChaCha20 counter bytes (i.e. block counter followed by nonce). |
| func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) { |
| s := Cipher{ |
| key: [8]uint32{ |
| binary.LittleEndian.Uint32(key[0:4]), |
| binary.LittleEndian.Uint32(key[4:8]), |
| binary.LittleEndian.Uint32(key[8:12]), |
| binary.LittleEndian.Uint32(key[12:16]), |
| binary.LittleEndian.Uint32(key[16:20]), |
| binary.LittleEndian.Uint32(key[20:24]), |
| binary.LittleEndian.Uint32(key[24:28]), |
| binary.LittleEndian.Uint32(key[28:32]), |
| }, |
| nonce: [3]uint32{ |
| binary.LittleEndian.Uint32(counter[4:8]), |
| binary.LittleEndian.Uint32(counter[8:12]), |
| binary.LittleEndian.Uint32(counter[12:16]), |
| }, |
| counter: binary.LittleEndian.Uint32(counter[0:4]), |
| } |
| s.XORKeyStream(out, in) |
| } |
| |
| // HChaCha20 uses the ChaCha20 core to generate a derived key from a key and a |
| // nonce. It should only be used as part of the XChaCha20 construction. |
| func HChaCha20(key *[8]uint32, nonce *[4]uint32) [8]uint32 { |
| x0, x1, x2, x3 := j0, j1, j2, j3 |
| x4, x5, x6, x7 := key[0], key[1], key[2], key[3] |
| x8, x9, x10, x11 := key[4], key[5], key[6], key[7] |
| x12, x13, x14, x15 := nonce[0], nonce[1], nonce[2], nonce[3] |
| |
| for i := 0; i < 10; i++ { |
| // Diagonal round. |
| x0, x4, x8, x12 = quarterRound(x0, x4, x8, x12) |
| x1, x5, x9, x13 = quarterRound(x1, x5, x9, x13) |
| x2, x6, x10, x14 = quarterRound(x2, x6, x10, x14) |
| x3, x7, x11, x15 = quarterRound(x3, x7, x11, x15) |
| |
| // Column round. |
| x0, x5, x10, x15 = quarterRound(x0, x5, x10, x15) |
| x1, x6, x11, x12 = quarterRound(x1, x6, x11, x12) |
| x2, x7, x8, x13 = quarterRound(x2, x7, x8, x13) |
| x3, x4, x9, x14 = quarterRound(x3, x4, x9, x14) |
| } |
| |
| var out [8]uint32 |
| out[0], out[1], out[2], out[3] = x0, x1, x2, x3 |
| out[4], out[5], out[6], out[7] = x12, x13, x14, x15 |
| return out |
| } |