blake2x.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // Copyright 2017 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. package blake2b
  5. import (
  6. "encoding/binary"
  7. "errors"
  8. "io"
  9. )
  10. // XOF defines the interface to hash functions that
  11. // support arbitrary-length output.
  12. //
  13. // New callers should prefer the standard library [hash.XOF].
  14. type XOF interface {
  15. // Write absorbs more data into the hash's state. It panics if called
  16. // after Read.
  17. io.Writer
  18. // Read reads more output from the hash. It returns io.EOF if the limit
  19. // has been reached.
  20. io.Reader
  21. // Clone returns a copy of the XOF in its current state.
  22. Clone() XOF
  23. // Reset resets the XOF to its initial state.
  24. Reset()
  25. }
  26. // OutputLengthUnknown can be used as the size argument to NewXOF to indicate
  27. // the length of the output is not known in advance.
  28. const OutputLengthUnknown = 0
  29. // magicUnknownOutputLength is a magic value for the output size that indicates
  30. // an unknown number of output bytes.
  31. const magicUnknownOutputLength = (1 << 32) - 1
  32. // maxOutputLength is the absolute maximum number of bytes to produce when the
  33. // number of output bytes is unknown.
  34. const maxOutputLength = (1 << 32) * 64
  35. // NewXOF creates a new variable-output-length hash. The hash either produce a
  36. // known number of bytes (1 <= size < 2**32-1), or an unknown number of bytes
  37. // (size == OutputLengthUnknown). In the latter case, an absolute limit of
  38. // 256GiB applies.
  39. //
  40. // A non-nil key turns the hash into a MAC. The key must between
  41. // zero and 32 bytes long.
  42. //
  43. // The result can be safely interface-upgraded to [hash.XOF].
  44. func NewXOF(size uint32, key []byte) (XOF, error) {
  45. if len(key) > Size {
  46. return nil, errKeySize
  47. }
  48. if size == magicUnknownOutputLength {
  49. // 2^32-1 indicates an unknown number of bytes and thus isn't a
  50. // valid length.
  51. return nil, errors.New("blake2b: XOF length too large")
  52. }
  53. if size == OutputLengthUnknown {
  54. size = magicUnknownOutputLength
  55. }
  56. x := &xof{
  57. d: digest{
  58. size: Size,
  59. keyLen: len(key),
  60. },
  61. length: size,
  62. }
  63. copy(x.d.key[:], key)
  64. x.Reset()
  65. return x, nil
  66. }
  67. type xof struct {
  68. d digest
  69. length uint32
  70. remaining uint64
  71. cfg, root, block [Size]byte
  72. offset int
  73. nodeOffset uint32
  74. readMode bool
  75. }
  76. func (x *xof) Write(p []byte) (n int, err error) {
  77. if x.readMode {
  78. panic("blake2b: write to XOF after read")
  79. }
  80. return x.d.Write(p)
  81. }
  82. func (x *xof) Clone() XOF {
  83. clone := *x
  84. return &clone
  85. }
  86. func (x *xof) BlockSize() int {
  87. return x.d.BlockSize()
  88. }
  89. func (x *xof) Reset() {
  90. x.cfg[0] = byte(Size)
  91. binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length
  92. binary.LittleEndian.PutUint32(x.cfg[12:], x.length) // XOF length
  93. x.cfg[17] = byte(Size) // inner hash size
  94. x.d.Reset()
  95. x.d.h[1] ^= uint64(x.length) << 32
  96. x.remaining = uint64(x.length)
  97. if x.remaining == magicUnknownOutputLength {
  98. x.remaining = maxOutputLength
  99. }
  100. x.offset, x.nodeOffset = 0, 0
  101. x.readMode = false
  102. }
  103. func (x *xof) Read(p []byte) (n int, err error) {
  104. if !x.readMode {
  105. x.d.finalize(&x.root)
  106. x.readMode = true
  107. }
  108. if x.remaining == 0 {
  109. return 0, io.EOF
  110. }
  111. n = len(p)
  112. if uint64(n) > x.remaining {
  113. n = int(x.remaining)
  114. p = p[:n]
  115. }
  116. if x.offset > 0 {
  117. blockRemaining := Size - x.offset
  118. if n < blockRemaining {
  119. x.offset += copy(p, x.block[x.offset:])
  120. x.remaining -= uint64(n)
  121. return
  122. }
  123. copy(p, x.block[x.offset:])
  124. p = p[blockRemaining:]
  125. x.offset = 0
  126. x.remaining -= uint64(blockRemaining)
  127. }
  128. for len(p) >= Size {
  129. binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset)
  130. x.nodeOffset++
  131. x.d.initConfig(&x.cfg)
  132. x.d.Write(x.root[:])
  133. x.d.finalize(&x.block)
  134. copy(p, x.block[:])
  135. p = p[Size:]
  136. x.remaining -= uint64(Size)
  137. }
  138. if todo := len(p); todo > 0 {
  139. if x.remaining < uint64(Size) {
  140. x.cfg[0] = byte(x.remaining)
  141. }
  142. binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset)
  143. x.nodeOffset++
  144. x.d.initConfig(&x.cfg)
  145. x.d.Write(x.root[:])
  146. x.d.finalize(&x.block)
  147. x.offset = copy(p, x.block[:todo])
  148. x.remaining -= uint64(todo)
  149. }
  150. return
  151. }
  152. func (d *digest) initConfig(cfg *[Size]byte) {
  153. d.offset, d.c[0], d.c[1] = 0, 0, 0
  154. for i := range d.h {
  155. d.h[i] = iv[i] ^ binary.LittleEndian.Uint64(cfg[i*8:])
  156. }
  157. }