123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- // 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.
- // +build go1.7,amd64,!gccgo,!appengine
- package chacha20poly1305
- import "encoding/binary"
- //go:noescape
- func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool
- //go:noescape
- func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte)
- // cpuid is implemented in chacha20poly1305_amd64.s.
- func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
- // xgetbv with ecx = 0 is implemented in chacha20poly1305_amd64.s.
- func xgetbv() (eax, edx uint32)
- var (
- useASM bool
- useAVX2 bool
- )
- func init() {
- detectCPUFeatures()
- }
- // detectCPUFeatures is used to detect if cpu instructions
- // used by the functions implemented in assembler in
- // chacha20poly1305_amd64.s are supported.
- func detectCPUFeatures() {
- maxID, _, _, _ := cpuid(0, 0)
- if maxID < 1 {
- return
- }
- _, _, ecx1, _ := cpuid(1, 0)
- haveSSSE3 := isSet(9, ecx1)
- useASM = haveSSSE3
- haveOSXSAVE := isSet(27, ecx1)
- osSupportsAVX := false
- // For XGETBV, OSXSAVE bit is required and sufficient.
- if haveOSXSAVE {
- eax, _ := xgetbv()
- // Check if XMM and YMM registers have OS support.
- osSupportsAVX = isSet(1, eax) && isSet(2, eax)
- }
- haveAVX := isSet(28, ecx1) && osSupportsAVX
- if maxID < 7 {
- return
- }
- _, ebx7, _, _ := cpuid(7, 0)
- haveAVX2 := isSet(5, ebx7) && haveAVX
- haveBMI2 := isSet(8, ebx7)
- useAVX2 = haveAVX2 && haveBMI2
- }
- // isSet checks if bit at bitpos is set in value.
- func isSet(bitpos uint, value uint32) bool {
- return value&(1<<bitpos) != 0
- }
- // setupState writes a ChaCha20 input matrix to state. See
- // https://tools.ietf.org/html/rfc7539#section-2.3.
- func setupState(state *[16]uint32, key *[32]byte, nonce []byte) {
- state[0] = 0x61707865
- state[1] = 0x3320646e
- state[2] = 0x79622d32
- state[3] = 0x6b206574
- state[4] = binary.LittleEndian.Uint32(key[:4])
- state[5] = binary.LittleEndian.Uint32(key[4:8])
- state[6] = binary.LittleEndian.Uint32(key[8:12])
- state[7] = binary.LittleEndian.Uint32(key[12:16])
- state[8] = binary.LittleEndian.Uint32(key[16:20])
- state[9] = binary.LittleEndian.Uint32(key[20:24])
- state[10] = binary.LittleEndian.Uint32(key[24:28])
- state[11] = binary.LittleEndian.Uint32(key[28:32])
- state[12] = 0
- state[13] = binary.LittleEndian.Uint32(nonce[:4])
- state[14] = binary.LittleEndian.Uint32(nonce[4:8])
- state[15] = binary.LittleEndian.Uint32(nonce[8:12])
- }
- func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
- if !useASM {
- return c.sealGeneric(dst, nonce, plaintext, additionalData)
- }
- var state [16]uint32
- setupState(&state, &c.key, nonce)
- ret, out := sliceForAppend(dst, len(plaintext)+16)
- chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
- return ret
- }
- func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
- if !useASM {
- return c.openGeneric(dst, nonce, ciphertext, additionalData)
- }
- var state [16]uint32
- setupState(&state, &c.key, nonce)
- ciphertext = ciphertext[:len(ciphertext)-16]
- ret, out := sliceForAppend(dst, len(ciphertext))
- if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
- for i := range out {
- out[i] = 0
- }
- return nil, errOpen
- }
- return ret, nil
- }
|