forked from cwtch.im/tapir
Merge pull request 'Reduce new allocations, propagate errors in PRNG.Next()' (#48) from perf into master
Reviewed-on: cwtch.im/tapir#48 Reviewed-by: Dan Ballard <dan@openprivacy.ca>
This commit is contained in:
commit
10980c7bee
|
@ -5,6 +5,7 @@ import (
|
||||||
"git.openprivacy.ca/cwtch.im/tapir"
|
"git.openprivacy.ca/cwtch.im/tapir"
|
||||||
"git.openprivacy.ca/cwtch.im/tapir/primitives/core"
|
"git.openprivacy.ca/cwtch.im/tapir/primitives/core"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
|
ristretto "github.com/gtank/ristretto255"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProofOfWorkApplication forces the incoming connection to do proof of work before granting a capability
|
// ProofOfWorkApplication forces the incoming connection to do proof of work before granting a capability
|
||||||
|
@ -66,9 +67,18 @@ func (powapp *ProofOfWorkApplication) solveChallenge(challenge []byte, prng core
|
||||||
var sum [32]byte
|
var sum [32]byte
|
||||||
solution := []byte{}
|
solution := []byte{}
|
||||||
solve := make([]byte, len(challenge)+32)
|
solve := make([]byte, len(challenge)+32)
|
||||||
|
// reuse our allocation
|
||||||
|
buf := make([]byte, 64)
|
||||||
|
next := new(ristretto.Scalar)
|
||||||
|
encodedSolution := make([]byte, 0, 32)
|
||||||
for !solved {
|
for !solved {
|
||||||
|
err := prng.Next(buf, next)
|
||||||
solution = prng.Next().Encode(nil)
|
if err != nil {
|
||||||
|
// this will cause the challenge to fail...
|
||||||
|
log.Errorf("error completing challenge: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
solution = next.Encode(encodedSolution)
|
||||||
|
|
||||||
copy(solve[0:], solution[:])
|
copy(solve[0:], solution[:])
|
||||||
copy(solve[len(solution):], challenge[:])
|
copy(solve[len(solution):], challenge[:])
|
||||||
|
@ -80,6 +90,8 @@ func (powapp *ProofOfWorkApplication) solveChallenge(challenge []byte, prng core
|
||||||
solved = false
|
solved = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// reuse this allocated memory next time...
|
||||||
|
encodedSolution = encodedSolution[:0]
|
||||||
}
|
}
|
||||||
log.Debugf("Validated Challenge %v: %v %v\n", challenge, solution, sum)
|
log.Debugf("Validated Challenge %v: %v %v\n", challenge, solution, sum)
|
||||||
return solution[:]
|
return solution[:]
|
||||||
|
|
|
@ -55,7 +55,10 @@ func (tokenapp *TokenApplication) Init(connection tapir.Connection) {
|
||||||
var blinded []privacypass.BlindedToken
|
var blinded []privacypass.BlindedToken
|
||||||
err := json.Unmarshal(connection.Expect(), &blinded)
|
err := json.Unmarshal(connection.Expect(), &blinded)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
batchProof := tokenapp.TokenService.SignBlindedTokenBatch(blinded, tokenapp.Transcript())
|
batchProof, err := tokenapp.TokenService.SignBlindedTokenBatch(blinded, tokenapp.Transcript())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
log.Debugf(tokenapp.Transcript().OutputTranscriptToAudit())
|
log.Debugf(tokenapp.Transcript().OutputTranscriptToAudit())
|
||||||
data, _ := json.Marshal(batchProof)
|
data, _ := json.Marshal(batchProof)
|
||||||
connection.Send(data)
|
connection.Send(data)
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -3,7 +3,7 @@ module git.openprivacy.ca/cwtch.im/tapir
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.openprivacy.ca/openprivacy/connectivity v1.8.2
|
git.openprivacy.ca/openprivacy/connectivity v1.8.3
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.3
|
git.openprivacy.ca/openprivacy/log v1.0.3
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/gtank/merlin v0.1.1
|
github.com/gtank/merlin v0.1.1
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -8,6 +8,8 @@ git.openprivacy.ca/openprivacy/connectivity v1.8.1 h1:OjWy+JTAvlrstY8PnGPBp7Ho04
|
||||||
git.openprivacy.ca/openprivacy/connectivity v1.8.1/go.mod h1:UjQiGBnWbotmBzIw59B8H6efwDadjkKzm3RPT1UaIRw=
|
git.openprivacy.ca/openprivacy/connectivity v1.8.1/go.mod h1:UjQiGBnWbotmBzIw59B8H6efwDadjkKzm3RPT1UaIRw=
|
||||||
git.openprivacy.ca/openprivacy/connectivity v1.8.2 h1:uCFnrJXsTh3ne4GcgvamoxomQ6fMishD3C2nQGpgdMY=
|
git.openprivacy.ca/openprivacy/connectivity v1.8.2 h1:uCFnrJXsTh3ne4GcgvamoxomQ6fMishD3C2nQGpgdMY=
|
||||||
git.openprivacy.ca/openprivacy/connectivity v1.8.2/go.mod h1:UjQiGBnWbotmBzIw59B8H6efwDadjkKzm3RPT1UaIRw=
|
git.openprivacy.ca/openprivacy/connectivity v1.8.2/go.mod h1:UjQiGBnWbotmBzIw59B8H6efwDadjkKzm3RPT1UaIRw=
|
||||||
|
git.openprivacy.ca/openprivacy/connectivity v1.8.3 h1:bWM8aQHqHIpobYQcLQ9OsNPoIl+H+4JFWbYGdG0nHlg=
|
||||||
|
git.openprivacy.ca/openprivacy/connectivity v1.8.3/go.mod h1:UjQiGBnWbotmBzIw59B8H6efwDadjkKzm3RPT1UaIRw=
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.2/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
git.openprivacy.ca/openprivacy/log v1.0.2/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.3 h1:E/PMm4LY+Q9s3aDpfySfEDq/vYQontlvNj/scrPaga0=
|
git.openprivacy.ca/openprivacy/log v1.0.3 h1:E/PMm4LY+Q9s3aDpfySfEDq/vYQontlvNj/scrPaga0=
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.3/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
git.openprivacy.ca/openprivacy/log v1.0.3/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||||
|
|
|
@ -2,6 +2,7 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
"github.com/gtank/merlin"
|
"github.com/gtank/merlin"
|
||||||
ristretto "github.com/gtank/ristretto255"
|
ristretto "github.com/gtank/ristretto255"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
|
@ -67,12 +68,14 @@ type PRNG struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next returns the next "random" scalar from the PRNG
|
// Next returns the next "random" scalar from the PRNG
|
||||||
func (prng *PRNG) Next() *ristretto.Scalar {
|
func (prng *PRNG) Next(buf []byte, next *ristretto.Scalar) error {
|
||||||
buf := [64]byte{}
|
n, err := io.ReadFull(prng.prng, buf)
|
||||||
io.ReadFull(prng.prng, buf[:])
|
if n != 64 || err != nil {
|
||||||
next := new(ristretto.Scalar)
|
log.Errorf("could not read prng: %v %v", n, err)
|
||||||
next.FromUniformBytes(buf[:])
|
return fmt.Errorf("error fetching complete output from prng: %v", err)
|
||||||
return next
|
}
|
||||||
|
next.FromUniformBytes(buf)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitToPRNG commits the label to the transcript and derives a PRNG from the transcript.
|
// CommitToPRNG commits the label to the transcript and derives a PRNG from the transcript.
|
||||||
|
|
|
@ -95,8 +95,14 @@ func verifyBatchProof(dleq DLEQProof, Y *ristretto.Element, blindedTokens []Blin
|
||||||
prng := transcript.CommitToPRNG("w")
|
prng := transcript.CommitToPRNG("w")
|
||||||
M := new(ristretto.Element).Zero()
|
M := new(ristretto.Element).Zero()
|
||||||
Z := new(ristretto.Element).Zero()
|
Z := new(ristretto.Element).Zero()
|
||||||
|
buf := make([]byte, 64)
|
||||||
|
c := new(ristretto.Scalar)
|
||||||
for i := range blindedTokens {
|
for i := range blindedTokens {
|
||||||
c := prng.Next()
|
err := prng.Next(buf, c)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error verifying batch proof: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
M = new(ristretto.Element).Add(new(ristretto.Element).ScalarMult(c, blindedTokens[i].P), M)
|
M = new(ristretto.Element).Add(new(ristretto.Element).ScalarMult(c, blindedTokens[i].P), M)
|
||||||
Z = new(ristretto.Element).Add(new(ristretto.Element).ScalarMult(c, signedTokens[i].Q), Z)
|
Z = new(ristretto.Element).Add(new(ristretto.Element).ScalarMult(c, signedTokens[i].Q), Z)
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,12 @@ func TestToken_ConstrainToToken(t *testing.T) {
|
||||||
// Constraint forces T = kW to be part of the batch proof
|
// Constraint forces T = kW to be part of the batch proof
|
||||||
// And because the batch proof must prove that *all* inputs share the same key and also checks the servers public key
|
// And because the batch proof must prove that *all* inputs share the same key and also checks the servers public key
|
||||||
// We get a consistency check for almost free.
|
// We get a consistency check for almost free.
|
||||||
signedTokens := server.SignBlindedTokenBatchWithConstraint([]BlindedToken{blindedToken2}, token.t, core.NewTranscript(""))
|
signedTokens, err := server.SignBlindedTokenBatchWithConstraint([]BlindedToken{blindedToken2}, token.t, core.NewTranscript(""))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error signing tokens with constraints")
|
||||||
|
}
|
||||||
|
|
||||||
transcript := core.NewTranscript("")
|
transcript := core.NewTranscript("")
|
||||||
|
|
||||||
// NOTE: For this to work token.t and token.W need to be obtain by the client from known source e.g. a public message board.
|
// NOTE: For this to work token.t and token.W need to be obtain by the client from known source e.g. a public message board.
|
||||||
|
@ -78,7 +83,10 @@ func TestGenerateBlindedTokenBatch(t *testing.T) {
|
||||||
serverTranscript := core.NewTranscript("privacyPass")
|
serverTranscript := core.NewTranscript("privacyPass")
|
||||||
|
|
||||||
tokens, blindedTokens := GenerateBlindedTokenBatch(10)
|
tokens, blindedTokens := GenerateBlindedTokenBatch(10)
|
||||||
batchProof := server.SignBlindedTokenBatch(blindedTokens, serverTranscript)
|
batchProof, err := server.SignBlindedTokenBatch(blindedTokens, serverTranscript)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error constructing signed/blinded token batch: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
verified := UnblindSignedTokenBatch(tokens, blindedTokens, batchProof.SignedTokens, server.Y, batchProof.Proof, clientTranscript)
|
verified := UnblindSignedTokenBatch(tokens, blindedTokens, batchProof.SignedTokens, server.Y, batchProof.Proof, clientTranscript)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.openprivacy.ca/cwtch.im/tapir/persistence"
|
"git.openprivacy.ca/cwtch.im/tapir/persistence"
|
||||||
"git.openprivacy.ca/cwtch.im/tapir/primitives/core"
|
"git.openprivacy.ca/cwtch.im/tapir/primitives/core"
|
||||||
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
ristretto "github.com/gtank/ristretto255"
|
ristretto "github.com/gtank/ristretto255"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -69,17 +70,23 @@ func (ts *TokenServer) SignBlindedToken(bt BlindedToken) SignedToken {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignBlindedTokenBatch signs a batch of blinded tokens under a given transcript
|
// SignBlindedTokenBatch signs a batch of blinded tokens under a given transcript
|
||||||
func (ts *TokenServer) SignBlindedTokenBatch(blindedTokens []BlindedToken, transcript *core.Transcript) SignedBatchWithProof {
|
func (ts *TokenServer) SignBlindedTokenBatch(blindedTokens []BlindedToken, transcript *core.Transcript) (*SignedBatchWithProof, error) {
|
||||||
var signedTokens []SignedToken
|
var signedTokens []SignedToken
|
||||||
for _, bt := range blindedTokens {
|
for _, bt := range blindedTokens {
|
||||||
signedTokens = append(signedTokens, ts.SignBlindedToken(bt))
|
signedTokens = append(signedTokens, ts.SignBlindedToken(bt))
|
||||||
}
|
}
|
||||||
return SignedBatchWithProof{signedTokens, ts.constructBatchProof(blindedTokens, signedTokens, transcript)}
|
|
||||||
|
proof, err := ts.constructBatchProof(blindedTokens, signedTokens, transcript)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
signedProof := SignedBatchWithProof{signedTokens, *proof}
|
||||||
|
return &signedProof, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignBlindedTokenBatchWithConstraint signs a batch of blinded tokens under a given transcript given a constraint that the tokens must be signed
|
// SignBlindedTokenBatchWithConstraint signs a batch of blinded tokens under a given transcript given a constraint that the tokens must be signed
|
||||||
// by the same public key as an existing token
|
// by the same public key as an existing token
|
||||||
func (ts *TokenServer) SignBlindedTokenBatchWithConstraint(blindedTokens []BlindedToken, constraintToken []byte, transcript *core.Transcript) SignedBatchWithProof {
|
func (ts *TokenServer) SignBlindedTokenBatchWithConstraint(blindedTokens []BlindedToken, constraintToken []byte, transcript *core.Transcript) (*SignedBatchWithProof, error) {
|
||||||
var signedTokens []SignedToken
|
var signedTokens []SignedToken
|
||||||
for _, bt := range blindedTokens {
|
for _, bt := range blindedTokens {
|
||||||
signedTokens = append(signedTokens, ts.SignBlindedToken(bt))
|
signedTokens = append(signedTokens, ts.SignBlindedToken(bt))
|
||||||
|
@ -89,11 +96,16 @@ func (ts *TokenServer) SignBlindedTokenBatchWithConstraint(blindedTokens []Blind
|
||||||
// W == kT
|
// W == kT
|
||||||
W := new(ristretto.Element).ScalarMult(ts.k, T)
|
W := new(ristretto.Element).ScalarMult(ts.k, T)
|
||||||
blindedTokens = append(blindedTokens, BlindedToken{P: T})
|
blindedTokens = append(blindedTokens, BlindedToken{P: T})
|
||||||
return SignedBatchWithProof{signedTokens, ts.constructBatchProof(blindedTokens, append(signedTokens, SignedToken{Q: W}), transcript)}
|
proof, err := ts.constructBatchProof(blindedTokens, append(signedTokens, SignedToken{Q: W}), transcript)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
signedProof := SignedBatchWithProof{signedTokens, *proof}
|
||||||
|
return &signedProof, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// constructBatchProof construct a batch proof that all the signed tokens have been signed correctly
|
// constructBatchProof construct a batch proof that all the signed tokens have been signed correctly
|
||||||
func (ts *TokenServer) constructBatchProof(blindedTokens []BlindedToken, signedTokens []SignedToken, transcript *core.Transcript) DLEQProof {
|
func (ts *TokenServer) constructBatchProof(blindedTokens []BlindedToken, signedTokens []SignedToken, transcript *core.Transcript) (*DLEQProof, error) {
|
||||||
transcript.NewProtocol(BatchProofProtocol)
|
transcript.NewProtocol(BatchProofProtocol)
|
||||||
transcript.AddToTranscript(BatchProofX, new(ristretto.Element).Base().Encode(nil))
|
transcript.AddToTranscript(BatchProofX, new(ristretto.Element).Base().Encode(nil))
|
||||||
transcript.AddToTranscript(BatchProofY, ts.Y.Encode(nil))
|
transcript.AddToTranscript(BatchProofY, ts.Y.Encode(nil))
|
||||||
|
@ -104,12 +116,19 @@ func (ts *TokenServer) constructBatchProof(blindedTokens []BlindedToken, signedT
|
||||||
M := new(ristretto.Element).Zero()
|
M := new(ristretto.Element).Zero()
|
||||||
Z := new(ristretto.Element).Zero()
|
Z := new(ristretto.Element).Zero()
|
||||||
|
|
||||||
|
buf := make([]byte, 64)
|
||||||
|
c := new(ristretto.Scalar)
|
||||||
for i := range blindedTokens {
|
for i := range blindedTokens {
|
||||||
c := prng.Next()
|
err := prng.Next(buf, c)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error constructing batch proof: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
M = new(ristretto.Element).Add(new(ristretto.Element).ScalarMult(c, blindedTokens[i].P), M)
|
M = new(ristretto.Element).Add(new(ristretto.Element).ScalarMult(c, blindedTokens[i].P), M)
|
||||||
Z = new(ristretto.Element).Add(new(ristretto.Element).ScalarMult(c, signedTokens[i].Q), Z)
|
Z = new(ristretto.Element).Add(new(ristretto.Element).ScalarMult(c, signedTokens[i].Q), Z)
|
||||||
}
|
}
|
||||||
return DiscreteLogEquivalenceProof(ts.k, new(ristretto.Element).Base(), ts.Y, M, Z, transcript)
|
proof := DiscreteLogEquivalenceProof(ts.k, new(ristretto.Element).Base(), ts.Y, M, Z, transcript)
|
||||||
|
return &proof, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpendToken returns true a SpentToken is valid and has never been spent before, false otherwise.
|
// SpendToken returns true a SpentToken is valid and has never been spent before, false otherwise.
|
||||||
|
|
16
service.go
16
service.go
|
@ -62,6 +62,7 @@ type connection struct {
|
||||||
MaxLength int
|
MaxLength int
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
service Service
|
service Service
|
||||||
|
expectBuffer []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConnection creates a new Connection
|
// NewConnection creates a new Connection
|
||||||
|
@ -74,7 +75,7 @@ func NewConnection(service Service, id *primitives.Identity, hostname string, ou
|
||||||
connection.outbound = outbound
|
connection.outbound = outbound
|
||||||
connection.MaxLength = 8192
|
connection.MaxLength = 8192
|
||||||
connection.service = service
|
connection.service = service
|
||||||
|
connection.expectBuffer = make([]byte, 8192)
|
||||||
go connection.app.Init(connection)
|
go connection.app.Init(connection)
|
||||||
return connection
|
return connection
|
||||||
}
|
}
|
||||||
|
@ -153,10 +154,9 @@ func (c *connection) closeInner() {
|
||||||
|
|
||||||
// Expect blocks and reads a single Tapir packet , from the connection.
|
// Expect blocks and reads a single Tapir packet , from the connection.
|
||||||
func (c *connection) Expect() []byte {
|
func (c *connection) Expect() []byte {
|
||||||
buffer := make([]byte, c.MaxLength)
|
|
||||||
// Multiple goroutines may invoke methods on a Conn simultaneously.
|
// Multiple goroutines may invoke methods on a Conn simultaneously.
|
||||||
// As such we don't need to mutex around closed.
|
// As such we don't need to mutex around closed.
|
||||||
n, err := io.ReadFull(c.conn, buffer)
|
n, err := io.ReadFull(c.conn, c.expectBuffer)
|
||||||
|
|
||||||
if n != c.MaxLength || err != nil {
|
if n != c.MaxLength || err != nil {
|
||||||
log.Debugf("[%v -> %v] Wire Error Reading, Read %d bytes, Error: %v", c.hostname, c.identity.Hostname(), n, err)
|
log.Debugf("[%v -> %v] Wire Error Reading, Read %d bytes, Error: %v", c.hostname, c.identity.Hostname(), n, err)
|
||||||
|
@ -167,22 +167,22 @@ func (c *connection) Expect() []byte {
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
if c.encrypted {
|
if c.encrypted {
|
||||||
var decryptNonce [24]byte
|
var decryptNonce [24]byte
|
||||||
copy(decryptNonce[:], buffer[:24])
|
copy(decryptNonce[:], c.expectBuffer[:24])
|
||||||
decrypted, ok := secretbox.Open(nil, buffer[24:], &decryptNonce, &c.key)
|
decrypted, ok := secretbox.Open(nil, c.expectBuffer[24:], &decryptNonce, &c.key)
|
||||||
if ok {
|
if ok {
|
||||||
copy(buffer, decrypted)
|
copy(c.expectBuffer, decrypted)
|
||||||
} else {
|
} else {
|
||||||
log.Errorf("[%v -> %v] Error Decrypting Message On Wire", c.hostname, c.identity.Hostname())
|
log.Errorf("[%v -> %v] Error Decrypting Message On Wire", c.hostname, c.identity.Hostname())
|
||||||
c.closeInner()
|
c.closeInner()
|
||||||
return []byte{}
|
return []byte{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
length, _ := binary.Uvarint(buffer[0:2])
|
length, _ := binary.Uvarint(c.expectBuffer[0:2])
|
||||||
if length+2 >= uint64(c.MaxLength) {
|
if length+2 >= uint64(c.MaxLength) {
|
||||||
return []byte{}
|
return []byte{}
|
||||||
}
|
}
|
||||||
//cplog.Debugf("[%v -> %v] Wire Receive: (%d) %x", c.hostname, c.ID.Hostname(), len, buffer)
|
//cplog.Debugf("[%v -> %v] Wire Receive: (%d) %x", c.hostname, c.ID.Hostname(), len, buffer)
|
||||||
return buffer[2 : length+2]
|
return c.expectBuffer[2 : length+2]
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetEncryptionKey turns on application-level encryption on the connection using the given key.
|
// SetEncryptionKey turns on application-level encryption on the connection using the given key.
|
||||||
|
|
Loading…
Reference in New Issue