Skip to content

Changes to SHAKE in preparation for Dilithium support #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions dh/sidh/sike.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"io"

"github.com/cloudflare/circl/dh/sidh/internal/common"
"github.com/cloudflare/circl/dh/sidh/internal/shake"
"github.com/cloudflare/circl/internal/shake"
)

// SIKE KEM interface
Expand All @@ -16,7 +16,7 @@ type KEM struct {
msg []byte
secretBytes []byte
params *common.SidhParams
shake *shake.Shake
shake shake.Shake
}

// NewSike434 instantiates SIKE/p434 KEM
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
91 changes: 42 additions & 49 deletions dh/sidh/internal/shake/sha3.go → internal/shake/sha3.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ const (
maxRate = 168
)

type state struct {
type Shake struct {
// Generic sponge components.
a [25]uint64 // main state of the hash
buf []byte // points into storage
rate int // the number of bytes of state to use
a [25]uint64 // main state of the hash
bufo, bufe int
rate int // the number of bytes of state to use

// dsbyte contains the "domain separation" bits and the first bit of
// the padding. Sections 6.1 and 6.2 of [1] separate the outputs of the
Expand All @@ -49,107 +49,101 @@ type state struct {
}

// BlockSize returns the rate of sponge underlying this hash function.
func (d *state) BlockSize() int { return d.rate }
func (d *Shake) BlockSize() int { return d.rate }

// Size returns the output size of the hash function in bytes.
func (d *state) Size() int { return d.outputLen }
func (d *Shake) Size() int { return d.outputLen }

// Reset clears the internal state by zeroing the sponge state and
// the byte buffer, and setting Sponge.state to absorbing.
func (d *state) Reset() {
func (d *Shake) Reset() {
// Zero the permutation's state.
for i := range d.a {
d.a[i] = 0
}
d.state = spongeAbsorbing
d.buf = d.storage[:0]
d.bufo = 0
d.bufe = 0
}

func (d *state) clone(ret *state) {
// shallow copy
*ret = *d
// deep copy for a buf
if ret.state == spongeAbsorbing {
ret.buf = ret.storage[:len(d.buf)]
} else {
ret.buf = ret.storage[d.rate-len(d.buf) : d.rate]
}
func (d *Shake) Clone() (ret Shake) {
return *d
}

// permute applies the KeccakF-1600 permutation. It handles
// any input-output buffering.
func (d *state) permute() {
func (d *Shake) permute() {
switch d.state {
case spongeAbsorbing:
// If we're absorbing, we need to xor the input into the state
// before applying the permutation.
xorIn(d, d.buf)
d.buf = d.storage[:0]
xorIn(d, d.storage[d.bufo:d.bufe])
d.bufe = 0
d.bufo = 0
keccakF1600(&d.a)
case spongeSqueezing:
// If we're squeezing, we need to apply the permutatin before
// copying more output.
keccakF1600(&d.a)
d.buf = d.storage[:d.rate]
copyOut(d, d.buf)
d.bufo = 0
d.bufe = d.rate
copyOut(d, d.storage[:d.rate])
}
}

// pads appends the domain separation bits in dsbyte, applies
// the multi-bitrate 10..1 padding rule, and permutes the state.
func (d *state) padAndPermute(dsbyte byte) {
if d.buf == nil {
d.buf = d.storage[:0]
}
func (d *Shake) padAndPermute(dsbyte byte) {
// Pad with this instance's domain-separator bits. We know that there's
// at least one byte of space in d.buf because, if it were full,
// permute would have been called to empty it. dsbyte also contains the
// first one bit for the padding. See the comment in the state struct.
d.buf = append(d.buf, dsbyte)
zerosStart := len(d.buf)
d.buf = d.storage[:d.rate]
d.storage[d.bufe] = dsbyte
d.bufe++
zerosStart := d.bufe - d.bufo
d.bufo = 0
d.bufe = d.rate
for i := zerosStart; i < d.rate; i++ {
d.buf[i] = 0
d.storage[i] = 0
}
// This adds the final one bit for the padding. Because of the way that
// bits are numbered from the LSB upwards, the final bit is the MSB of
// the last byte.
d.buf[d.rate-1] ^= 0x80
d.storage[d.rate-1] ^= 0x80
// Apply the permutation
d.permute()
d.state = spongeSqueezing
d.buf = d.storage[:d.rate]
copyOut(d, d.buf)
d.bufo = 0
d.bufe = d.rate
copyOut(d, d.storage[:d.rate])
}

// Write absorbs more data into the hash's state. It produces an error
// if more data is written to the ShakeHash after writing
func (d *state) Write(p []byte) (int, error) {
func (d *Shake) Write(p []byte) (int, error) {
if d.state != spongeAbsorbing {
panic("shake: write to sponge after read")
}
if d.buf == nil {
d.buf = d.storage[:0]
}
written := len(p)

for len(p) > 0 {
if len(d.buf) == 0 && len(p) >= d.rate {
if d.bufe == d.bufo && len(p) >= d.rate {
// The fast path; absorb a full "rate" bytes of input and apply the permutation.
xorIn(d, p[:d.rate])
p = p[d.rate:]
keccakF1600(&d.a)
} else {
// The slow path; buffer the input until we can fill the sponge, and then xor it in.
todo := d.rate - len(d.buf)
todo := d.rate - (d.bufe - d.bufo)
if todo > len(p) {
todo = len(p)
}
d.buf = append(d.buf, p[:todo]...)
copy(d.storage[d.bufe:], p[:todo])
d.bufe += todo
p = p[todo:]

// If the sponge is full, apply the permutation.
if len(d.buf) == d.rate {
if d.bufe-d.bufo == d.rate {
d.permute()
}
}
Expand All @@ -159,7 +153,7 @@ func (d *state) Write(p []byte) (int, error) {
}

// Read squeezes an arbitrary number of bytes from the sponge.
func (d *state) Read(out []byte) (n int, err error) {
func (d *Shake) Read(out []byte) (n int, err error) {
// If we're still absorbing, pad and apply the permutation.
if d.state == spongeAbsorbing {
d.padAndPermute(d.dsbyte)
Expand All @@ -169,12 +163,12 @@ func (d *state) Read(out []byte) (n int, err error) {

// Now, do the squeezing.
for len(out) > 0 {
n := copy(out, d.buf)
d.buf = d.buf[n:]
n := copy(out, d.storage[d.bufo:d.bufe])
d.bufo += n
out = out[n:]

// Apply the permutation if we've squeezed the sponge dry.
if len(d.buf) == 0 {
if d.bufe-d.bufo == 0 {
d.permute()
}
}
Expand All @@ -184,16 +178,15 @@ func (d *state) Read(out []byte) (n int, err error) {

// Sum applies padding to the hash state and then squeezes out the desired
// number of output bytes.
func (d *state) Sum(in []byte) []byte {
func (d *Shake) Sum(in []byte) []byte {
// Make a copy of the original hash so that caller can keep writing
// and summing.
var dup state
d.clone(&dup)
dup := d.Clone()
hash := make([]byte, dup.outputLen)
dup.Read(hash)
return append(in, hash...)
}

// Only use this function if you require compatibility with an existing cryptosystem
// that uses non-standard padding. All other users should use New256 instead.
func NewLegacyKeccak256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x01} }
func NewLegacyKeccak256() hash.Hash { return &Shake{rate: 136, outputLen: 32, dsbyte: 0x01} }
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ func BenchmarkPermutationFunction(b *testing.B) {

// benchmarkShake is specialized to the Shake instances, which don't
// require a copy on reading output.
func benchmarkShake(b *testing.B, h *Shake, size, num int) {
func benchmarkShake(b *testing.B, h Shake, size, num int) {
b.StopTimer()
h.Reset()
data := sequentialBytes(size)
Expand Down
32 changes: 14 additions & 18 deletions dh/sidh/internal/shake/shake.go → internal/shake/shake.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,28 @@ package shake
//
// [1] https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf

// cSHAKE specific context
type Shake struct {
state // SHA-3 state context and Read/Write operations
}

// Consts for configuring initial SHA-3 state
const (
dsbyteShake = 0x1f
rate256 = 136
rate128 = 168
)

// Reset resets the hash to initial state.
func (c *Shake) Reset() {
c.state.Reset()
// NewShake256 creates a new SHAKE256 variable-output-length Shake.
// Its generic security strength is 256 bits against all attacks if
// at least 64 bytes of its output are used.
func NewShake256() Shake {
return Shake{rate: rate256, dsbyte: dsbyteShake}
}

// Clone returns copy of a cSHAKE context within its current state.
func (c *Shake) Clone() Shake {
var ret Shake
c.clone(&ret.state)
return ret
// NewShake128 creates a new SHAKE128 variable-output-length Shake.
// Its generic security strength is 128 bits against all attacks if
// at least 32 bytes of its output are used.
func NewShake128() Shake {
return Shake{rate: rate128, dsbyte: dsbyteShake}
}

// NewShake256 creates a new SHAKE256 variable-output-length Shake.
// Its generic security strength is 256 bits against all attacks if
// at least 64 bytes of its output are used.
func NewShake256() *Shake {
return &Shake{state{rate: rate256, dsbyte: dsbyteShake}}
func (c *Shake) Init128() {
c.rate = rate128
c.dsbyte = dsbyteShake
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import "encoding/binary"
// xorInGeneric xors the bytes in buf into the state; it
// makes no non-portable assumptions about memory layout
// or alignment.
func xorIn(d *state, buf []byte) {
func xorIn(d *Shake, buf []byte) {
n := len(buf) / 8

for i := 0; i < n; i++ {
Expand All @@ -22,7 +22,7 @@ func xorIn(d *state, buf []byte) {
}

// copyOutGeneric copies ulint64s to a byte buffer.
func copyOut(d *state, b []byte) {
func copyOut(d *Shake, b []byte) {
for i := 0; len(b) >= 8; i++ {
binary.LittleEndian.PutUint64(b, d.a[i])
b = b[8:]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ package shake

import "unsafe"

func xorIn(d *state, buf []byte) {
func xorIn(d *Shake, buf []byte) {
bw := (*[maxRate / 8]uint64)(unsafe.Pointer(&buf[0]))
n := len(buf)
if n >= 72 {
Expand Down Expand Up @@ -45,7 +45,7 @@ func xorIn(d *state, buf []byte) {
}
}

func copyOut(d *state, buf []byte) {
func copyOut(d *Shake, buf []byte) {
ab := (*[maxRate]uint8)(unsafe.Pointer(&d.a[0]))
copy(buf, ab[:])
}