-
Notifications
You must be signed in to change notification settings - Fork 295
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This introduces a new package and module containing code for supporting peer-to-peer transaction mixing derived from CoinShuffle++. The mixing package contains the function and type primitives that will be used by mixing clients to perform the slot reservation and XOR DC-nets.
- Loading branch information
Showing
15 changed files
with
1,077 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
// Copyright (c) 2019-2024 The Decred developers | ||
// Use of this source code is governed by an ISC | ||
// license that can be found in the LICENSE file. | ||
|
||
package mixing | ||
|
||
import ( | ||
"encoding/binary" | ||
"math/big" | ||
|
||
"github.com/decred/dcrd/crypto/blake256" | ||
"github.com/decred/dcrd/wire" | ||
) | ||
|
||
// SRMixPads creates a vector of exponential DC-net pads from a vector of | ||
// shared secrets with each participating peer in the DC-net. | ||
func SRMixPads(kp [][]byte, my uint32) []*big.Int { | ||
h := blake256.New() | ||
scratch := make([]byte, 8) | ||
|
||
pads := make([]*big.Int, len(kp)) | ||
partialPad := new(big.Int) | ||
for j := uint32(0); j < uint32(len(kp)); j++ { | ||
pads[j] = new(big.Int) | ||
binary.LittleEndian.PutUint64(scratch, uint64(j)+1) | ||
for i := uint32(0); i < uint32(len(kp)); i++ { | ||
if my == i { | ||
continue | ||
} | ||
h.Reset() | ||
h.Write(kp[i]) | ||
h.Write(scratch) | ||
digest := h.Sum(nil) | ||
partialPad.SetBytes(digest) | ||
if my > i { | ||
pads[j].Add(pads[j], partialPad) | ||
} else { | ||
pads[j].Sub(pads[j], partialPad) | ||
} | ||
} | ||
pads[j].Mod(pads[j], F) | ||
} | ||
return pads | ||
} | ||
|
||
// SRMix creates the padded {m**1, m**2, ..., m**n} message exponentials | ||
// vector. Message must be bounded by the field prime and must be unique to | ||
// every exponential SR run in a mix session to ensure anonymity. | ||
func SRMix(m *big.Int, pads []*big.Int) []*big.Int { | ||
mix := make([]*big.Int, len(pads)) | ||
exp := new(big.Int) | ||
for i := int64(0); i < int64(len(mix)); i++ { | ||
mexp := new(big.Int).Exp(m, exp.SetInt64(i+1), nil) | ||
mix[i] = mexp.Add(mexp, pads[i]) | ||
mix[i].Mod(mix[i], F) | ||
} | ||
return mix | ||
} | ||
|
||
// IntVectorsFromBytes creates a 2-dimensional *big.Int slice from their absolute | ||
// values as bytes. | ||
func IntVectorsFromBytes(vs [][][]byte) [][]*big.Int { | ||
ints := make([][]*big.Int, len(vs)) | ||
for i := range vs { | ||
ints[i] = make([]*big.Int, len(vs[i])) | ||
for j := range vs[i] { | ||
ints[i][j] = new(big.Int).SetBytes(vs[i][j]) | ||
} | ||
} | ||
return ints | ||
} | ||
|
||
// IntVectorsToBytes creates a 2-dimensional slice of big.Int absolute values as | ||
// bytes. | ||
func IntVectorsToBytes(ints [][]*big.Int) [][][]byte { | ||
bytes := make([][][]byte, len(ints)) | ||
for i := range ints { | ||
bytes[i] = make([][]byte, len(ints[i])) | ||
for j := range ints[i] { | ||
bytes[i][j] = ints[i][j].Bytes() | ||
} | ||
} | ||
return bytes | ||
} | ||
|
||
// AddVectors sums each vector element over F, returning a new vector. When | ||
// peers are honest (DC-mix pads sum to zero) this creates the unpadded vector | ||
// of message power sums. | ||
func AddVectors(vs ...[]*big.Int) []*big.Int { | ||
sums := make([]*big.Int, len(vs)) | ||
for i := range sums { | ||
sums[i] = new(big.Int) | ||
for j := range vs { | ||
sums[i].Add(sums[i], vs[j][i]) | ||
} | ||
sums[i].Mod(sums[i], F) | ||
} | ||
return sums | ||
} | ||
|
||
// Coefficients calculates a{0}..a{n} for the polynomial: | ||
// | ||
// g(x) = a{0} + a{1}x + a{2}x**2 + ... + a{n-1}x**(n-1) + a{n}x**n (mod F) | ||
// | ||
// where | ||
// | ||
// a{n} = -1 | ||
// a{n-1} = -(1/1) * a{n}*S{0} | ||
// a{n-2} = -(1/2) * (a{n-1}*S{0} + a{n}*S{1}) | ||
// a{n-3} = -(1/3) * (a{n-2}*S{0} + a{n-1}*S{1} + a{n}*S{2}) | ||
// ... | ||
// | ||
// The roots of this polynomial are the set of recovered messages. | ||
// | ||
// Note that the returned slice of coefficients is one element larger than the | ||
// slice of partial sums. | ||
func Coefficients(S []*big.Int) []*big.Int { | ||
n := len(S) + 1 | ||
a := make([]*big.Int, n) | ||
a[len(a)-1] = big.NewInt(-1) | ||
a[len(a)-1].Add(a[len(a)-1], F) // a{n} = -1 (mod F) = F - 1 | ||
scratch := new(big.Int) | ||
for i := 0; i < len(a)-1; i++ { | ||
a[n-2-i] = new(big.Int) | ||
for j := 0; j <= i; j++ { | ||
a[n-2-i].Add(a[n-2-i], scratch.Mul(a[n-1-i+j], S[j])) | ||
} | ||
xinv := scratch.ModInverse(scratch.SetInt64(int64(i)+1), F) | ||
xinv.Neg(xinv) | ||
a[n-2-i].Mul(a[n-2-i], xinv) | ||
a[n-2-i].Mod(a[n-2-i], F) | ||
} | ||
return a | ||
} | ||
|
||
// IsRoot checks that the message m is a root of the polynomial with | ||
// coefficients a (mod F) without solving for every root. | ||
func IsRoot(m *big.Int, a []*big.Int) bool { | ||
sum := new(big.Int) | ||
scratch := new(big.Int) | ||
for i := range a { | ||
scratch.Exp(m, scratch.SetInt64(int64(i)), F) | ||
scratch.Mul(scratch, a[i]) | ||
sum.Add(sum, scratch) | ||
} | ||
sum.Mod(sum, F) | ||
return sum.Sign() == 0 | ||
} | ||
|
||
// DCMixPads creates the vector of DC-net pads from shared secrets with each mix | ||
// participant. | ||
func DCMixPads(kp []wire.MixVect, my uint32) Vec { | ||
pads := make(Vec, len(kp)) | ||
for i := range kp { | ||
if uint32(i) == my { | ||
continue | ||
} | ||
pads.Xor(pads, Vec(kp[i])) | ||
} | ||
return pads | ||
} | ||
|
||
// DCMix creates the DC-net vector of message m xor'd into m's reserved | ||
// anonymous slot position of the pads DC-net pads. Panics if len(m) is not the | ||
// vector's message size. | ||
func DCMix(pads Vec, m []byte, slot uint32) Vec { | ||
if len(m) != Msize { | ||
panic("m is not len Msize") | ||
} | ||
|
||
dcmix := make(Vec, len(pads)) | ||
copy(dcmix, pads) | ||
slotm := dcmix[slot][:] | ||
for i := range m { | ||
slotm[i] ^= m[i] | ||
} | ||
return dcmix | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Copyright (c) 2024 The Decred developers | ||
// Use of this source code is governed by an ISC | ||
// license that can be found in the LICENSE file. | ||
|
||
package mixing | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
) | ||
|
||
var ( | ||
errInvalidPROrder = errors.New("invalid pair request order") | ||
|
||
errInvalidSessionID = errors.New("invalid session ID") | ||
) | ||
|
||
// DecapsulateError identifies the unmixed peer position of a peer who | ||
// submitted an undecryptable ciphertext. | ||
type DecapsulateError struct { | ||
SubmittingIndex uint32 | ||
} | ||
|
||
// Error satisifies the error interface. | ||
func (e *DecapsulateError) Error() string { | ||
return fmt.Sprintf("decapsulate failure of ciphertext by peer %d", | ||
e.SubmittingIndex) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright (c) 2023-2024 The Decred developers | ||
// Use of this source code is governed by an ISC | ||
// license that can be found in the LICENSE file. | ||
|
||
package mixing | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/decred/dcrd/chaincfg/v3" | ||
) | ||
|
||
// MaxExpiry returns the maximum allowed expiry for a new pair request message | ||
// created with a blockchain tip at tipHeight. | ||
func MaxExpiry(tipHeight uint32, params *chaincfg.Params) uint32 { | ||
target := params.TargetTimePerBlock | ||
return tipHeight + uint32(60*time.Minute/target) + 1 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package mixing | ||
|
||
import ( | ||
"math/big" | ||
) | ||
|
||
// FieldPrime is the field prime 2**127 - 1. | ||
var F *big.Int | ||
|
||
func init() { | ||
F, _ = new(big.Int).SetString("7fffffffffffffffffffffffffffffff", 16) | ||
} | ||
|
||
// InField returns whether x is bounded by the field F. | ||
func InField(x *big.Int) bool { | ||
return x.Sign() != -1 && x.Cmp(F) == -1 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package mixing | ||
|
||
const ( | ||
// PRFlagCanSolveRoots describes a bit in the pair request flags field | ||
// indicating support for solving and publishing factored slot | ||
// reservation polynomials. | ||
PRFlagCanSolveRoots byte = 1 << iota | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
module github.com/decred/dcrd/mixing | ||
|
||
go 1.17 | ||
|
||
require ( | ||
github.com/companyzero/sntrup4591761 v0.0.0-20220309191932-9e0f3af2f07a | ||
github.com/decred/dcrd/chaincfg/chainhash v1.0.4 | ||
github.com/decred/dcrd/chaincfg/v3 v3.2.0 | ||
github.com/decred/dcrd/crypto/blake256 v1.0.1 | ||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 | ||
github.com/decred/dcrd/wire v1.6.0 | ||
golang.org/x/crypto v0.7.0 | ||
) | ||
|
||
require ( | ||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect | ||
golang.org/x/sys v0.6.0 // indirect | ||
lukechampine.com/blake3 v1.2.1 // indirect | ||
) | ||
|
||
replace github.com/decred/dcrd/wire => ../wire |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
github.com/companyzero/sntrup4591761 v0.0.0-20220309191932-9e0f3af2f07a h1:clYxJ3Os0EQUKDDVU8M0oipllX0EkuFNBfhVQuIfyF0= | ||
github.com/companyzero/sntrup4591761 v0.0.0-20220309191932-9e0f3af2f07a/go.mod h1:z/9Ck1EDixEbBbZ2KH2qNHekEmDLTOZ+FyoIPWWSVOI= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/decred/dcrd/chaincfg/chainhash v1.0.4 h1:zRCv6tdncLfLTKYqu7hrXvs7hW+8FO/NvwoFvGsrluU= | ||
github.com/decred/dcrd/chaincfg/chainhash v1.0.4/go.mod h1:hA86XxlBWwHivMvxzXTSD0ZCG/LoYsFdWnCekkTMCqY= | ||
github.com/decred/dcrd/chaincfg/v3 v3.2.0 h1:6WxA92AGBkycEuWvxtZMvA76FbzbkDRoK8OGbsR2muk= | ||
github.com/decred/dcrd/chaincfg/v3 v3.2.0/go.mod h1:2rHW1TKyFmwZTVBLoU/Cmf0oxcpBjUEegbSlBfrsriI= | ||
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= | ||
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= | ||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= | ||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= | ||
github.com/decred/dcrd/wire v1.6.0 h1:YOGwPHk4nzGr6OIwUGb8crJYWDiVLpuMxfDBCCF7s/o= | ||
github.com/decred/dcrd/wire v1.6.0/go.mod h1:XQ8Xv/pN/3xaDcb7sH8FBLS9cdgVctT7HpBKKGsIACk= | ||
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= | ||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= | ||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= | ||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | ||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= | ||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= | ||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= | ||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= | ||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= | ||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= | ||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= | ||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= | ||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | ||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= | ||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= | ||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | ||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= | ||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= | ||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= | ||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= | ||
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// Copyright (c) 2023 The Decred developers | ||
// Use of this source code is governed by an ISC | ||
// license that can be found in the LICENSE file. | ||
|
||
package chacha20prng | ||
|
||
import ( | ||
"encoding/binary" | ||
"strconv" | ||
|
||
"golang.org/x/crypto/chacha20" | ||
) | ||
|
||
// SeedSize is the required length of seeds for New. | ||
const SeedSize = 32 | ||
|
||
// Reader is a ChaCha20 PRNG for a DC-net run. It implements io.Reader. | ||
type Reader struct { | ||
cipher *chacha20.Cipher | ||
} | ||
|
||
// New creates a ChaCha20 PRNG seeded by a 32-byte key and a run iteration. The | ||
// returned reader is not safe for concurrent access. This will panic if the | ||
// length of seed is not SeedSize bytes. | ||
func New(seed []byte, run uint32) *Reader { | ||
if l := len(seed); l != SeedSize { | ||
panic("chacha20prng: bad seed length " + strconv.Itoa(l)) | ||
} | ||
|
||
nonce := make([]byte, chacha20.NonceSize) | ||
binary.LittleEndian.PutUint32(nonce[:4], run) | ||
|
||
cipher, _ := chacha20.NewUnauthenticatedCipher(seed, nonce) | ||
return &Reader{cipher: cipher} | ||
} | ||
|
||
// Read implements io.Reader. | ||
func (r *Reader) Read(b []byte) (int, error) { | ||
// Zero the source such that the destination is written with just the | ||
// keystream. Destination and source are allowed to overlap (exactly). | ||
for i := range b { | ||
b[i] = 0 | ||
} | ||
r.cipher.XORKeyStream(b, b) | ||
return len(b), nil | ||
} | ||
|
||
// Next returns the next n bytes from the reader. | ||
func (r *Reader) Next(n int) []byte { | ||
b := make([]byte, n) | ||
r.cipher.XORKeyStream(b, b) | ||
return b | ||
} |
Oops, something went wrong.