commit da3a531e53ecbcbc8f4105daea74ee6987104ab7 Author: Russ Magee Date: Tue Oct 23 23:25:31 2018 -0700 Initial commit diff --git a/cryptmt.go b/cryptmt.go new file mode 100644 index 0000000..d0c0d4d --- /dev/null +++ b/cryptmt.go @@ -0,0 +1,57 @@ +// CryptMT +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. + +import ( + "errors" + + mtwist "blitter.com/go/mtwist" +) + +type Cipher struct { + accum uint64 + m *mtwist.MT19937_64 +} + +func (c *Cipher) yield8() (r byte) { + c.accum = c.accum * (c.m.Int63() | 1) + r = byte(c.accum>>56) & 0xFF + return +} + +// NewCipher creates and returns a Cipher. The key argument should be the +// CryptMT key, 64 bytes. +func NewCipher(key []byte) (c *Cipher) { + c = &Cipher{m: mtwist.New()} + c.m.SeedFullState(key) + c.accum = 1 + // from paper, discard first 64 bytes of output + for idx := 0; idx < 64; idx++ { + _ = c.yield8() + } + return c +} + +// 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 { + dst[i] = b ^ c.yield8() + } +}