// 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; }