2020-01-29 21:25:14 +00:00
|
|
|
// Package CryptMT - implementation of cryptMTv1 stream cipher
|
2020-02-07 03:02:34 +00:00
|
|
|
// (but with mtwist64 as base accum)
|
|
|
|
// https://eprint.iacr.org/2005/165.pdf
|
2020-01-29 21:25:14 +00:00
|
|
|
package cryptmt
|
|
|
|
|
|
|
|
// TODO rlm: according to go docs, stream ciphers do not implement the
|
|
|
|
// cipher.Block interface at all (thus do not support Encrypt() or
|
|
|
|
// Decrypt() .. cipher.StreamReader/StreamWriter() only call
|
|
|
|
// XORKeyStream() anyhow and for my own purposes this is all that is
|
|
|
|
// required.
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2020-02-07 03:02:34 +00:00
|
|
|
"io"
|
2020-01-29 21:25:14 +00:00
|
|
|
|
|
|
|
mtwist "blitter.com/go/mtwist"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Cipher struct {
|
2020-02-07 03:02:34 +00:00
|
|
|
r io.Reader
|
|
|
|
w io.Writer
|
2020-01-29 21:25:14 +00:00
|
|
|
accum uint64
|
|
|
|
m *mtwist.MT19937_64
|
|
|
|
}
|
|
|
|
|
2020-02-06 05:26:03 +00:00
|
|
|
func (c *Cipher) yield() (r byte) {
|
2020-01-29 21:25:14 +00:00
|
|
|
c.accum = c.accum * (c.m.Int63() | 1)
|
|
|
|
r = byte(c.accum>>56) & 0xFF
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-06 05:26:03 +00:00
|
|
|
// New creates and returns a Cipher. The key argument should be the
|
2020-01-29 21:25:14 +00:00
|
|
|
// CryptMT key, 64 bytes.
|
2020-02-07 03:02:34 +00:00
|
|
|
func New(r io.Reader, w io.Writer, key []byte) (c *Cipher) {
|
|
|
|
c = &Cipher{m: mtwist.New(), r: r, w: w}
|
2020-01-29 21:25:14 +00:00
|
|
|
c.m.SeedFullState(key)
|
|
|
|
c.accum = 1
|
|
|
|
// from paper, discard first 64 bytes of output
|
|
|
|
for idx := 0; idx < 64; idx++ {
|
2020-02-06 05:26:03 +00:00
|
|
|
_ = c.yield()
|
2020-01-29 21:25:14 +00:00
|
|
|
}
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
2020-02-07 03:02:34 +00:00
|
|
|
func (c *Cipher) Read(p []byte) (n int, err error) {
|
|
|
|
n, err = c.r.Read(p)
|
|
|
|
if err == nil {
|
|
|
|
for idx := 0; idx < n; idx++ {
|
|
|
|
p[idx] = p[idx] ^ c.yield()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cipher) Write(p []byte) (n int, err error) {
|
|
|
|
n, err = c.w.Write(p)
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
|
2020-01-29 21:25:14 +00:00
|
|
|
// XORKeyStream XORs each byte in the given slice with a byte from the
|
|
|
|
// cipher's key stream. Dst and src must overlap entirely or not at all.
|
|
|
|
//
|
|
|
|
// If len(dst) < len(src), XORKeyStream should panic. It is acceptable
|
|
|
|
// to pass a dst bigger than src, and in that case, XORKeyStream will
|
|
|
|
// only update dst[:len(src)] and will not touch the rest of dst.
|
|
|
|
//
|
|
|
|
// Multiple calls to XORKeyStream behave as if the concatenation of
|
|
|
|
// the src buffers was passed in a single run. That is, Stream
|
|
|
|
// maintains state and does not reset at each XORKeyStream call.
|
|
|
|
func (c *Cipher) XORKeyStream(dst, src []byte) {
|
|
|
|
if len(dst) < len(src) {
|
|
|
|
panic(errors.New("len(dst) < len(src)"))
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, b := range src {
|
2020-02-06 05:26:03 +00:00
|
|
|
dst[i] = b ^ c.yield()
|
2020-01-29 21:25:14 +00:00
|
|
|
}
|
|
|
|
}
|