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.

178 lines
4.0 KiB

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