You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

127 lines
3.3 KiB

  1. // Copyright 2016 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build go1.7,amd64,!gccgo,!appengine
  5. package chacha20poly1305
  6. import "encoding/binary"
  7. //go:noescape
  8. func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool
  9. //go:noescape
  10. func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte)
  11. // cpuid is implemented in chacha20poly1305_amd64.s.
  12. func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
  13. // xgetbv with ecx = 0 is implemented in chacha20poly1305_amd64.s.
  14. func xgetbv() (eax, edx uint32)
  15. var (
  16. useASM bool
  17. useAVX2 bool
  18. )
  19. func init() {
  20. detectCPUFeatures()
  21. }
  22. // detectCPUFeatures is used to detect if cpu instructions
  23. // used by the functions implemented in assembler in
  24. // chacha20poly1305_amd64.s are supported.
  25. func detectCPUFeatures() {
  26. maxID, _, _, _ := cpuid(0, 0)
  27. if maxID < 1 {
  28. return
  29. }
  30. _, _, ecx1, _ := cpuid(1, 0)
  31. haveSSSE3 := isSet(9, ecx1)
  32. useASM = haveSSSE3
  33. haveOSXSAVE := isSet(27, ecx1)
  34. osSupportsAVX := false
  35. // For XGETBV, OSXSAVE bit is required and sufficient.
  36. if haveOSXSAVE {
  37. eax, _ := xgetbv()
  38. // Check if XMM and YMM registers have OS support.
  39. osSupportsAVX = isSet(1, eax) && isSet(2, eax)
  40. }
  41. haveAVX := isSet(28, ecx1) && osSupportsAVX
  42. if maxID < 7 {
  43. return
  44. }
  45. _, ebx7, _, _ := cpuid(7, 0)
  46. haveAVX2 := isSet(5, ebx7) && haveAVX
  47. haveBMI2 := isSet(8, ebx7)
  48. useAVX2 = haveAVX2 && haveBMI2
  49. }
  50. // isSet checks if bit at bitpos is set in value.
  51. func isSet(bitpos uint, value uint32) bool {
  52. return value&(1<<bitpos) != 0
  53. }
  54. // setupState writes a ChaCha20 input matrix to state. See
  55. // https://tools.ietf.org/html/rfc7539#section-2.3.
  56. func setupState(state *[16]uint32, key *[32]byte, nonce []byte) {
  57. state[0] = 0x61707865
  58. state[1] = 0x3320646e
  59. state[2] = 0x79622d32
  60. state[3] = 0x6b206574
  61. state[4] = binary.LittleEndian.Uint32(key[:4])
  62. state[5] = binary.LittleEndian.Uint32(key[4:8])
  63. state[6] = binary.LittleEndian.Uint32(key[8:12])
  64. state[7] = binary.LittleEndian.Uint32(key[12:16])
  65. state[8] = binary.LittleEndian.Uint32(key[16:20])
  66. state[9] = binary.LittleEndian.Uint32(key[20:24])
  67. state[10] = binary.LittleEndian.Uint32(key[24:28])
  68. state[11] = binary.LittleEndian.Uint32(key[28:32])
  69. state[12] = 0
  70. state[13] = binary.LittleEndian.Uint32(nonce[:4])
  71. state[14] = binary.LittleEndian.Uint32(nonce[4:8])
  72. state[15] = binary.LittleEndian.Uint32(nonce[8:12])
  73. }
  74. func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
  75. if !useASM {
  76. return c.sealGeneric(dst, nonce, plaintext, additionalData)
  77. }
  78. var state [16]uint32
  79. setupState(&state, &c.key, nonce)
  80. ret, out := sliceForAppend(dst, len(plaintext)+16)
  81. chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
  82. return ret
  83. }
  84. func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
  85. if !useASM {
  86. return c.openGeneric(dst, nonce, ciphertext, additionalData)
  87. }
  88. var state [16]uint32
  89. setupState(&state, &c.key, nonce)
  90. ciphertext = ciphertext[:len(ciphertext)-16]
  91. ret, out := sliceForAppend(dst, len(ciphertext))
  92. if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
  93. for i := range out {
  94. out[i] = 0
  95. }
  96. return nil, errOpen
  97. }
  98. return ret, nil
  99. }