Go Home Page
The Go Programming Language

Source file src/pkg/crypto/block/xor.go

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