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