// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Encrypt/decrypt data by xor with a pseudo-random data stream.
package block
import (
"io";
"os";
)
// A dataStream is an interface to an unending stream of data,
// used by XorReader and XorWriter to model a pseudo-random generator.
// Calls to Next() return sequential blocks of data from the stream.
// Each call must return at least one byte: there is no EOF.
type dataStream interface {
Next() []byte;
}
type xorReader struct {
r io.Reader;
rand dataStream; // pseudo-random
buf []byte; // data available from last call to rand
}
func newXorReader(rand dataStream, r io.Reader) io.Reader {
x := new(xorReader);
x.r = r;
x.rand = rand;
return x;
}
func (x *xorReader) Read(p []byte) (n int, err os.Error) {
n, err = x.r.Read(p);
// xor input with stream.
bp := 0;
buf := x.buf;
for i := 0; i < n; i++ {
if bp >= len(buf) {
buf = x.rand.Next();
bp = 0;
}
p[i] ^= buf[bp];
bp++;
}
x.buf = buf[bp:];
return n, err;
}
type xorWriter struct {
w io.Writer;
rand dataStream; // pseudo-random
buf []byte; // last buffer returned by rand
extra []byte; // extra random data (use before buf)
work []byte; // work space
}
func newXorWriter(rand dataStream, w io.Writer) io.Writer {
x := new(xorWriter);
x.w = w;
x.rand = rand;
x.work = make([]byte, 4096);
return x;
}
func (x *xorWriter) Write(p []byte) (n int, err os.Error) {
for len(p) > 0 {
// Determine next chunk of random data
// and xor with p into x.work.
var chunk []byte;
m := len(p);
if nn := len(x.extra); nn > 0 {
// extra points into work, so edit directly
if m > nn {
m = nn
}
for i := 0; i < m; i++ {
x.extra[i] ^= p[i]
}
chunk = x.extra[0:m];
} else {
// xor p ^ buf into work, refreshing buf as needed
if nn := len(x.work); m > nn {
m = nn
}
bp := 0;
buf := x.buf;
for i := 0; i < m; i++ {
if bp >= len(buf) {
buf = x.rand.Next();
bp = 0;
}
x.work[i] = buf[bp] ^ p[i];
bp++;
}
x.buf = buf[bp:];
chunk = x.work[0:m];
}
// Write chunk.
var nn int;
nn, err = x.w.Write(chunk);
if nn != len(chunk) && err == nil {
err = io.ErrShortWrite
}
if nn < len(chunk) {
// Reconstruct the random bits from the unwritten
// data and save them for next time.
for i := nn; i < m; i++ {
chunk[i] ^= p[i]
}
x.extra = chunk[nn:];
}
n += nn;
if err != nil {
return
}
p = p[m:];
}
return;
}
|