gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

blake2x.go (4120B)


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