summaryrefslogtreecommitdiff
path: root/libgo/go/crypto/block/xor.go
blob: 9d8b1722400ded5d2980153a848298585cd1d48d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// 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
}