| // 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:len(buf)]; |
| 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:len(buf)]; |
| 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:len(chunk)]; |
| } |
| n += nn; |
| if err != nil { |
| return; |
| } |
| p = p[m:len(p)]; |
| } |
| return; |
| } |
| |