forkato da cwtch.im/tapir
Updates given Erinns Comments
This commit is contained in:
parent
ff7a32722d
commit
a19204caf4
|
@ -27,7 +27,7 @@ pipeline:
|
|||
commands:
|
||||
- ./tor -f ./torrc
|
||||
- sleep 15
|
||||
- go test -v cwtch.im/tapir/testing
|
||||
- go test -race -v cwtch.im/tapir/testing
|
||||
notify-email:
|
||||
image: drillster/drone-email
|
||||
host: build.openprivacy.ca
|
||||
|
|
|
@ -6,3 +6,4 @@ coverage.out
|
|||
/applications/tor/
|
||||
*.db
|
||||
/applications/tokenboard/tor/
|
||||
fuzzing/
|
||||
|
|
|
@ -63,7 +63,7 @@ func (ea *AuthApp) Init(connection tapir.Connection) {
|
|||
challengeRemote, _ := json.Marshal(remoteAuthMessage)
|
||||
challengeLocal, _ := json.Marshal(authMessage)
|
||||
|
||||
// Define canonical labels so both sides of the
|
||||
// Define canonical labels so both sides of the connection can generate the same key
|
||||
var outboundAuthMessage []byte
|
||||
var outboundHostname string
|
||||
var inboundAuthMessage []byte
|
||||
|
|
|
@ -18,16 +18,16 @@ type TokenApplication struct {
|
|||
const HasTokensCapability = tapir.Capability("HasTokensCapability")
|
||||
|
||||
// NewInstance should always return a new instantiation of the application.
|
||||
func (powapp *TokenApplication) NewInstance() tapir.Application {
|
||||
func (tokenapp *TokenApplication) NewInstance() tapir.Application {
|
||||
app := new(TokenApplication)
|
||||
app.TokenService = powapp.TokenService
|
||||
app.TokenService = tokenapp.TokenService
|
||||
return app
|
||||
}
|
||||
|
||||
// Init is run when the connection is first started.
|
||||
func (powapp *TokenApplication) Init(connection tapir.Connection) {
|
||||
powapp.Transcript().NewProtocol("token-app")
|
||||
log.Debugf(powapp.Transcript().OutputTranscriptToAudit())
|
||||
func (tokenapp *TokenApplication) Init(connection tapir.Connection) {
|
||||
tokenapp.Transcript().NewProtocol("token-app")
|
||||
log.Debugf(tokenapp.Transcript().OutputTranscriptToAudit())
|
||||
if connection.IsOutbound() {
|
||||
tokens, blinded := privacypass.GenerateBlindedTokenBatch(10)
|
||||
data, _ := json.Marshal(blinded)
|
||||
|
@ -35,21 +35,24 @@ func (powapp *TokenApplication) Init(connection tapir.Connection) {
|
|||
var signedBatch privacypass.SignedBatchWithProof
|
||||
err := json.Unmarshal(connection.Expect(), &signedBatch)
|
||||
if err == nil {
|
||||
verified := privacypass.UnblindSignedTokenBatch(tokens, blinded, signedBatch.SignedTokens, powapp.TokenService.Y, signedBatch.Proof, powapp.Transcript())
|
||||
verified := privacypass.UnblindSignedTokenBatch(tokens, blinded, signedBatch.SignedTokens, tokenapp.TokenService.Y, signedBatch.Proof, tokenapp.Transcript())
|
||||
if verified {
|
||||
log.Debugf("Successfully obtained signed tokens")
|
||||
powapp.Tokens = tokens
|
||||
tokenapp.Tokens = tokens
|
||||
connection.SetCapability(HasTokensCapability)
|
||||
return
|
||||
}
|
||||
// This will close the connection by default and no tokens will be available.
|
||||
// This usecase can be checked by the existing WaitForCapabilityOrClose() function using the HasTokensCapability
|
||||
// If the connection closes without the HasTokensCapability then the error can be handled by whatever client needs it
|
||||
log.Debugf("Failed to verify signed token batch")
|
||||
}
|
||||
} else {
|
||||
var blinded []privacypass.BlindedToken
|
||||
err := json.Unmarshal(connection.Expect(), &blinded)
|
||||
if err == nil {
|
||||
batchProof := powapp.TokenService.SignBlindedTokenBatch(blinded, powapp.Transcript())
|
||||
log.Debugf(powapp.Transcript().OutputTranscriptToAudit())
|
||||
batchProof := tokenapp.TokenService.SignBlindedTokenBatch(blinded, tokenapp.Transcript())
|
||||
log.Debugf(tokenapp.Transcript().OutputTranscriptToAudit())
|
||||
data, _ := json.Marshal(batchProof)
|
||||
connection.Send(data)
|
||||
return
|
||||
|
|
|
@ -60,7 +60,6 @@ func (ta *Client) Listen() {
|
|||
|
||||
var message Message
|
||||
json.Unmarshal(data, &message)
|
||||
log.Debugf("Received a Message: %v", message)
|
||||
switch message.MessageType {
|
||||
case postResultMessage:
|
||||
log.Debugf("Post result: %x", message.PostResult.Proof)
|
||||
|
@ -101,7 +100,7 @@ func (ta *Client) PurchaseTokens() {
|
|||
|
||||
// Post sends a Post Request to the server
|
||||
func (ta *Client) Post(message auditable.Message) bool {
|
||||
token, err := ta.paymentHandler.NextToken(message)
|
||||
token, err := ta.paymentHandler.NextToken(message, ta.connection.Hostname())
|
||||
if err == nil {
|
||||
data, _ := json.Marshal(Message{MessageType: postRequestMessage, PostRequest: postRequest{Token: token, Message: message}})
|
||||
ta.connection.Send(data)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package tokenboard
|
||||
|
||||
// NOTE: This is a sketch implementation, Not suitable for production use. The real auditable store is still being designed.
|
||||
|
||||
import (
|
||||
"cwtch.im/tapir"
|
||||
"cwtch.im/tapir/applications"
|
||||
|
@ -55,7 +57,7 @@ func (ta *Server) Listen() {
|
|||
|
||||
var message Message
|
||||
json.Unmarshal(data, &message)
|
||||
log.Debugf("Received a Message: %v", message)
|
||||
|
||||
switch message.MessageType {
|
||||
case postRequestMessage:
|
||||
postrequest := message.PostRequest
|
||||
|
@ -77,7 +79,7 @@ func (ta *Server) Listen() {
|
|||
}
|
||||
|
||||
func (ta *Server) postMessageRequest(token privacypass.SpentToken, message auditable.Message) {
|
||||
if err := ta.TokenService.SpendToken(token, message); err == nil {
|
||||
if err := ta.TokenService.SpendToken(token, append(message, ta.connection.ID().Hostname()...)); err == nil {
|
||||
log.Debugf("Token is valid")
|
||||
signedproof := ta.AuditableStore.Add(message)
|
||||
data, _ := json.Marshal(Message{MessageType: postResultMessage, PostResult: postResult{true, signedproof}})
|
||||
|
|
|
@ -47,23 +47,24 @@ func (fph *FreePaymentHandler) MakePayment() {
|
|||
ChainApplication(new(applications.ProofOfWorkApplication), applications.SuccessfulProofOfWorkCapability).
|
||||
ChainApplication(tokenApplication, applications.HasTokensCapability)
|
||||
client.Connect(fph.ServerHostname, powTokenApp)
|
||||
conn,err := client.WaitForCapabilityOrClose(fph.ServerHostname, applications.HasTokensCapability)
|
||||
conn, err := client.WaitForCapabilityOrClose(fph.ServerHostname, applications.HasTokensCapability)
|
||||
if err == nil {
|
||||
powtapp, _ := conn.App().(*applications.TokenApplication)
|
||||
fph.tokens = append(fph.tokens, powtapp.Tokens...)
|
||||
log.Debugf("Transcript: %v", powtapp.Transcript().OutputTranscriptToAudit())
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
log.Debugf("Error making payment: %v", err)
|
||||
}
|
||||
|
||||
func (fph *FreePaymentHandler) NextToken(data []byte) (privacypass.SpentToken, error) {
|
||||
func (fph *FreePaymentHandler) NextToken(data []byte, hostname string) (privacypass.SpentToken, error) {
|
||||
if len(fph.tokens) == 0 {
|
||||
return privacypass.SpentToken{}, errors.New("No more tokens")
|
||||
}
|
||||
token := fph.tokens[0]
|
||||
fph.tokens = fph.tokens[1:]
|
||||
return token.SpendToken(data), nil
|
||||
return token.SpendToken(append(data, hostname...)), nil
|
||||
}
|
||||
|
||||
func TestTokenBoardApp(t *testing.T) {
|
||||
|
@ -96,7 +97,7 @@ func TestTokenBoardApp(t *testing.T) {
|
|||
sg := new(sync.WaitGroup)
|
||||
sg.Add(1)
|
||||
go func() {
|
||||
service.Listen(NewTokenBoardServer(&tokenService, serverAuditableStore))
|
||||
service.Listen(NewTokenBoardServer(tokenService, serverAuditableStore))
|
||||
sg.Done()
|
||||
}()
|
||||
|
||||
|
@ -108,7 +109,7 @@ func TestTokenBoardApp(t *testing.T) {
|
|||
sg.Add(1)
|
||||
go func() {
|
||||
tokenApplication := new(applications.TokenApplication)
|
||||
tokenApplication.TokenService = &tokenService
|
||||
tokenApplication.TokenService = tokenService
|
||||
powTokenApp := new(applications.ApplicationChain).
|
||||
ChainApplication(new(applications.ProofOfWorkApplication), applications.SuccessfulProofOfWorkCapability).
|
||||
ChainApplication(tokenApplication, applications.HasTokensCapability)
|
||||
|
@ -121,7 +122,7 @@ func TestTokenBoardApp(t *testing.T) {
|
|||
var client tapir.Service
|
||||
client = new(tor.BaseOnionService)
|
||||
client.Init(acn, sk, &id)
|
||||
client.Connect(sid.Hostname(), NewTokenBoardClient(clientAuditableStore, Handler{Store: clientAuditableStore}, &FreePaymentHandler{ACN: acn, TokenService: &tokenService, ServerHostname: spowid.Hostname()}))
|
||||
client.Connect(sid.Hostname(), NewTokenBoardClient(clientAuditableStore, Handler{Store: clientAuditableStore}, &FreePaymentHandler{ACN: acn, TokenService: tokenService, ServerHostname: spowid.Hostname()}))
|
||||
client.WaitForCapabilityOrClose(sid.Hostname(), applications.AuthCapability)
|
||||
conn, _ := client.GetConnection(sid.Hostname())
|
||||
tba, _ := conn.App().(*Client)
|
||||
|
|
|
@ -54,7 +54,6 @@ func (s *BaseOnionService) WaitForCapabilityOrClose(cid string, name tapir.Capab
|
|||
func (s *BaseOnionService) GetConnection(hostname string) (tapir.Connection, error) {
|
||||
var conn tapir.Connection
|
||||
s.connections.Range(func(key, value interface{}) bool {
|
||||
log.Debugf("Checking %v", key)
|
||||
connection := value.(tapir.Connection)
|
||||
if connection.Hostname() == hostname {
|
||||
if !connection.IsClosed() {
|
||||
|
|
|
@ -53,6 +53,7 @@ func (bp *BoltPersistence) Check(bucket string, name string) (bool, error) {
|
|||
val = b.Get([]byte(name))
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if val != nil {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package auditable
|
||||
|
||||
// WARNING NOTE: This is a sketch implementation, Not suitable for production use. The real auditable store is still being designed.
|
||||
|
||||
import (
|
||||
"cwtch.im/tapir/persistence"
|
||||
"cwtch.im/tapir/primitives"
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
package primitives
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"math"
|
||||
"math/big"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// BloomFilter implements a bloom filter
|
||||
type BloomFilter struct {
|
||||
B []bool
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// Init constructs a bloom filter of size m
|
||||
func (bf *BloomFilter) Init(m int64) {
|
||||
bf.B = make([]bool, m)
|
||||
|
||||
}
|
||||
|
||||
// Hash transforms a message to a set of bit flips
|
||||
func (bf *BloomFilter) Hash(msg []byte) []int {
|
||||
|
||||
// Not the fastest hash function ever, but cryptographic security is more important than speed.
|
||||
hash1 := sha256.Sum256(append([]byte("h1"), msg...))
|
||||
hash2 := sha256.Sum256(append([]byte("h2"), msg...))
|
||||
hash3 := sha256.Sum256(append([]byte("h3"), msg...))
|
||||
hash4 := sha256.Sum256(append([]byte("h4"), msg...))
|
||||
|
||||
m := int64(len(bf.B))
|
||||
// Number of bytes needed to pick a position from [0,m)
|
||||
B := int(math.Ceil(math.Log2(float64(m)) / 8.0))
|
||||
|
||||
p1 := big.NewInt(0).SetBytes(hash1[:B]).Int64()
|
||||
p2 := big.NewInt(0).SetBytes(hash2[:B]).Int64()
|
||||
p3 := big.NewInt(0).SetBytes(hash3[:B]).Int64()
|
||||
p4 := big.NewInt(0).SetBytes(hash4[:B]).Int64()
|
||||
|
||||
return []int{int(p1), int(p2), int(p3), int(p4)}
|
||||
}
|
||||
|
||||
// Insert updates the BloomFilter (suitable for concurrent use)
|
||||
func (bf *BloomFilter) Insert(msg []byte) {
|
||||
pos := bf.Hash(msg)
|
||||
bf.lock.Lock()
|
||||
defer bf.lock.Unlock()
|
||||
bf.B[pos[0]] = true
|
||||
bf.B[pos[1]] = true
|
||||
bf.B[pos[2]] = true
|
||||
bf.B[pos[3]] = true
|
||||
}
|
||||
|
||||
// Check returns true if the messages might be in the BloomFilter
|
||||
// (No false positives, possible false negatives due to the probabilistic nature of the filter)
|
||||
func (bf *BloomFilter) Check(msg []byte) bool {
|
||||
pos := bf.Hash(msg)
|
||||
if bf.B[pos[0]] && bf.B[pos[1]] && bf.B[pos[2]] && bf.B[pos[3]] {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package primitives
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBloomFilter_Insert(t *testing.T) {
|
||||
bf := new(BloomFilter)
|
||||
bf.Init(256)
|
||||
|
||||
fp := 0
|
||||
for i := 0; i < 256; i++ {
|
||||
input := []byte("test" + strconv.Itoa(256+i))
|
||||
if bf.Check(input) {
|
||||
t.Log("False Positive!")
|
||||
fp++
|
||||
}
|
||||
bf.Insert(input)
|
||||
}
|
||||
|
||||
t.Logf("Num false positives %v %v%%", fp, (float64(fp)/256.0)*100)
|
||||
|
||||
}
|
|
@ -1,186 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
ristretto "github.com/gtank/ristretto255"
|
||||
)
|
||||
|
||||
// ScalarVector explicit type checking
|
||||
type ScalarVector []*ristretto.Scalar
|
||||
|
||||
// PointVector explicit type checking
|
||||
type PointVector []*ristretto.Element
|
||||
|
||||
// GeneratorVector explicit type checking
|
||||
type GeneratorVector []*ristretto.Element
|
||||
|
||||
// CopyVector safely copies a vector
|
||||
func CopyVector(G GeneratorVector) GeneratorVector {
|
||||
H := make(GeneratorVector, len(G))
|
||||
for i, g := range G {
|
||||
H[i] = new(ristretto.Element).Add(new(ristretto.Element).Zero(), g)
|
||||
}
|
||||
return H
|
||||
}
|
||||
|
||||
// InnerProduct takes the inner product of a and b i.e. <a,b>
|
||||
func InnerProduct(a, b ScalarVector) *ristretto.Scalar {
|
||||
if len(a) != len(b) {
|
||||
panic(fmt.Sprintf("len(a) = %v ; len(b) = %v;", len(a), len(b)))
|
||||
}
|
||||
|
||||
result := new(ristretto.Scalar).Zero()
|
||||
for i, ai := range a {
|
||||
result.Add(result, new(ristretto.Scalar).Multiply(ai, b[i]))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// MultiExp takes in a vector of scalars = {a,b,c...} and a vector of generator = {A,B,C...} and outputs
|
||||
// {aA,bB,cC}
|
||||
func MultiExp(a ScalarVector, G GeneratorVector) *ristretto.Element {
|
||||
if len(a) > len(G) {
|
||||
panic(fmt.Sprintf("len(a) = %v ; len(b) = %v;", len(a), len(G)))
|
||||
}
|
||||
result := new(ristretto.Element).Zero()
|
||||
for i, ai := range a {
|
||||
aG := new(ristretto.Element).ScalarMult(ai, G[i])
|
||||
result = new(ristretto.Element).Add(result, aG)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// SafeAppend is defined for a vector of Scalars
|
||||
func (a ScalarVector) SafeAppend(b *ristretto.Scalar) ScalarVector {
|
||||
list := make(ScalarVector, len(a)+1)
|
||||
for i := 0; i < len(a); i++ {
|
||||
list[i] = a[i]
|
||||
}
|
||||
list[len(a)] = b
|
||||
return list
|
||||
}
|
||||
|
||||
// Join is defined for a vector of Scalars
|
||||
func (a ScalarVector) Join(b ScalarVector) ScalarVector {
|
||||
list := make(ScalarVector, len(a)+len(b))
|
||||
for i := 0; i < len(a); i++ {
|
||||
list[i] = a[i]
|
||||
}
|
||||
for i := len(a); i < len(a)+len(b); i++ {
|
||||
list[i] = b[i-len(a)]
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// SafeAppend as defined for a vector of Generators
|
||||
func (a GeneratorVector) SafeAppend(b *ristretto.Element) GeneratorVector {
|
||||
list := make(GeneratorVector, len(a)+1)
|
||||
for i := 0; i < len(a); i++ {
|
||||
list[i] = a[i]
|
||||
}
|
||||
list[len(a)] = b
|
||||
return list
|
||||
}
|
||||
|
||||
// Join as defined for a vector of Generators
|
||||
func (a GeneratorVector) Join(b GeneratorVector) GeneratorVector {
|
||||
list := make(GeneratorVector, len(a)+len(b))
|
||||
for i := 0; i < len(a); i++ {
|
||||
list[i] = a[i]
|
||||
}
|
||||
for i := len(a); i < len(a)+len(b); i++ {
|
||||
list[i] = b[i-len(a)]
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// VectorAddScalar takes in a vector v = {a,b,c..} and a scalar s and outputs {a+s,b+s,c+s....}
|
||||
func VectorAddScalar(vector ScalarVector, scalar *ristretto.Scalar) ScalarVector {
|
||||
result := make(ScalarVector, len(vector))
|
||||
for i := range vector {
|
||||
result[i] = new(ristretto.Scalar)
|
||||
result[i].Add(vector[i], scalar)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// VectorNegate takes in a vector v = {a,b,c..} and a scalar s and outputs {-a,-b,-c}
|
||||
func VectorNegate(vector ScalarVector) ScalarVector {
|
||||
result := make(ScalarVector, len(vector))
|
||||
for i := range vector {
|
||||
result[i] = new(ristretto.Scalar).Negate(vector[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// VectorMulScalar takes in a vector v = {a,b,c..} and a scalar s and outputs {as,bs,cs....}
|
||||
func VectorMulScalar(vector ScalarVector, scalar *ristretto.Scalar) ScalarVector {
|
||||
result := make(ScalarVector, len(vector))
|
||||
for i := range vector {
|
||||
result[i] = new(ristretto.Scalar)
|
||||
result[i].Multiply(vector[i], scalar)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// EntrywiseSum takes the entry wise sum of two vectors
|
||||
func EntrywiseSum(vector ScalarVector, vector2 ScalarVector) ScalarVector {
|
||||
result := make(ScalarVector, len(vector))
|
||||
for i, v := range vector {
|
||||
result[i] = new(ristretto.Scalar)
|
||||
result[i].Add(v, vector2[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// EntrywiseSub takes the entry wise subtraction of two vectors
|
||||
func EntrywiseSub(vector ScalarVector, vector2 ScalarVector) ScalarVector {
|
||||
result := make(ScalarVector, len(vector))
|
||||
for i, v := range vector {
|
||||
result[i] = new(ristretto.Scalar)
|
||||
result[i].Subtract(v, vector2[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// EntryWiseProduct takes the entry wise product of two vectors
|
||||
func EntryWiseProduct(vector ScalarVector, vector2 ScalarVector) ScalarVector {
|
||||
result := make(ScalarVector, len(vector))
|
||||
for i, v := range vector {
|
||||
result[i] = new(ristretto.Scalar)
|
||||
result[i].Multiply(v, vector2[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// One returns a ristretto scalar == 1
|
||||
func One() *ristretto.Scalar {
|
||||
one := new(ristretto.Scalar)
|
||||
one.Decode([]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
|
||||
return one
|
||||
}
|
||||
|
||||
// IdentityVector is a convenience function to generate a vector v = {1,1,1...1}
|
||||
func IdentityVector(n int) ScalarVector {
|
||||
result := make(ScalarVector, n)
|
||||
one := new(ristretto.Scalar)
|
||||
one.Decode([]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
|
||||
for i := 0; i < n; i++ {
|
||||
result[i] = one
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// PowerVector creates a vector v = {1,x,x^2,x^3..x^n}
|
||||
func PowerVector(x *ristretto.Scalar, n int) ScalarVector {
|
||||
result := make(ScalarVector, n)
|
||||
one := new(ristretto.Scalar)
|
||||
one.Decode([]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
|
||||
result[0] = one
|
||||
result[1] = x
|
||||
for i := 2; i < n; i++ {
|
||||
result[i] = new(ristretto.Scalar)
|
||||
result[i].Multiply(result[i-1], x)
|
||||
}
|
||||
return result
|
||||
}
|
|
@ -14,7 +14,7 @@ type DLEQProof struct {
|
|||
}
|
||||
|
||||
// DiscreteLogEquivalenceProof constructs a valid DLEQProof for the given parameters and transcript
|
||||
// Given P = kX, Q = kP, Y=kX
|
||||
// Given Y = kX & Q = kP
|
||||
// Peggy: t := choose randomly from Zq
|
||||
// A := tX
|
||||
// B := tP
|
||||
|
@ -43,12 +43,12 @@ func DiscreteLogEquivalenceProof(k *ristretto.Scalar, X *ristretto.Element, Y *r
|
|||
}
|
||||
|
||||
// VerifyDiscreteLogEquivalenceProof verifies the DLEQ for the given parameters and transcript
|
||||
// Given P = kX, Q = kP, Y=kX, and Proof = (c,s)
|
||||
// Given Y = kX & Q = kP and Proof = (c,s)
|
||||
// Vicky: X' := sX
|
||||
// Y' := cY
|
||||
// P' := sP
|
||||
// Q' := cQ
|
||||
// A' = X'+Y' == sX + cY ?= sX + ckX == (s+ck)X == tX == A
|
||||
// A' = X'+Y' == sX + cY ?= sG + ckG == (s+ck)X == tX == A
|
||||
// B' = P'+Q' == sP + cQ ?= sP + ckP == (s+ck)P == tP == B
|
||||
// c' := H(transcript(X,Y,P,Q,A',B'))
|
||||
// Tests c ?= c
|
||||
|
|
|
@ -38,7 +38,8 @@ type SpentToken struct {
|
|||
// TokenPaymentHandler defines an interface with external payment processors
|
||||
type TokenPaymentHandler interface {
|
||||
MakePayment()
|
||||
NextToken(data []byte) (SpentToken, error)
|
||||
// Next Token
|
||||
NextToken(data []byte, hostname string) (SpentToken, error)
|
||||
}
|
||||
|
||||
// GenBlindedToken initializes the Token
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"cwtch.im/tapir/persistence"
|
||||
"cwtch.im/tapir/primitives/core"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"github.com/gtank/ristretto255"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -31,11 +33,40 @@ func TestToken_SpendToken(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestToken_ConstrainToToken(t *testing.T) {
|
||||
server := NewTokenServer()
|
||||
|
||||
token := new(Token)
|
||||
blindedToken := token.GenBlindedToken()
|
||||
|
||||
signedToken := server.SignBlindedToken(blindedToken)
|
||||
token.unblindSignedToken(signedToken)
|
||||
|
||||
spentToken := token.SpendToken([]byte("Hello"))
|
||||
|
||||
if server.SpendToken(spentToken, []byte("Hello World")) == nil {
|
||||
t.Errorf("Token Should be InValid")
|
||||
}
|
||||
|
||||
token2 := new(Token)
|
||||
blindedToken2 := token2.GenBlindedToken()
|
||||
Ht := sha3.Sum512(token.t)
|
||||
T := new(ristretto255.Element).FromUniformBytes(Ht[:])
|
||||
// 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
|
||||
// We get a consistency check for almost free.
|
||||
signedTokens := server.SignBlindedTokenBatchWithConstraint([]BlindedToken{blindedToken2}, *token, core.NewTranscript(""))
|
||||
transcript := core.NewTranscript("")
|
||||
t.Logf("Result of constaint proof %v", UnblindSignedTokenBatch([]*Token{token2}, []BlindedToken{blindedToken2, {P: T}}, append(signedTokens.SignedTokens, SignedToken{token.W}), server.Y, signedTokens.Proof, transcript))
|
||||
t.Log(transcript.OutputTranscriptToAudit())
|
||||
}
|
||||
|
||||
func TestGenerateBlindedTokenBatch(t *testing.T) {
|
||||
log.SetLevel(log.LevelDebug)
|
||||
db := new(persistence.BoltPersistence)
|
||||
db.Open("tokens.db")
|
||||
server := NewTokenServer()
|
||||
defer db.Close()
|
||||
server := NewTokenServerFromStore(db)
|
||||
|
||||
clientTranscript := core.NewTranscript("privacyPass")
|
||||
serverTranscript := core.NewTranscript("privacyPass")
|
||||
|
@ -65,5 +96,4 @@ func TestGenerateBlindedTokenBatch(t *testing.T) {
|
|||
if verified {
|
||||
t.Errorf("Something went wrong, the proof passed with wrong transcript: %s", wrongTranscript.OutputTranscriptToAudit())
|
||||
}
|
||||
db.Close()
|
||||
}
|
||||
|
|
|
@ -23,29 +23,48 @@ type TokenServer struct {
|
|||
|
||||
// SignedBatchWithProof encapsulates a signed batch of blinded tokens with a batch proof for verification
|
||||
type SignedBatchWithProof struct {
|
||||
SignedTokens []SignedToken
|
||||
Proof DLEQProof
|
||||
SignedTokens []SignedToken `json:"st"`
|
||||
Proof DLEQProof `json:"dp"`
|
||||
}
|
||||
|
||||
const tokenBucket = "tokens"
|
||||
const keyBucket = "keys"
|
||||
|
||||
// NewTokenServer generates a new TokenServer (used mostly for testing with ephemeral instances)
|
||||
func NewTokenServer() TokenServer {
|
||||
func NewTokenServer() *TokenServer {
|
||||
k := new(ristretto.Scalar)
|
||||
b := make([]byte, 64)
|
||||
rand.Read(b)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
// unable to generate secure random numbers
|
||||
panic("unable to generate secure random numbers")
|
||||
}
|
||||
k.FromUniformBytes(b)
|
||||
return TokenServer{k, new(ristretto.Element).ScalarBaseMult(k), make(map[string]bool), nil, sync.Mutex{}}
|
||||
return &TokenServer{k, new(ristretto.Element).ScalarBaseMult(k), make(map[string]bool), nil, sync.Mutex{}}
|
||||
}
|
||||
|
||||
// NewTokenServerFromStore generates a new TokenServer backed by a persistence service.
|
||||
func NewTokenServerFromStore(persistenceService persistence.Service) TokenServer {
|
||||
k := new(ristretto.Scalar)
|
||||
b := make([]byte, 64)
|
||||
rand.Read(b)
|
||||
k.FromUniformBytes(b)
|
||||
func NewTokenServerFromStore(persistenceService persistence.Service) *TokenServer {
|
||||
tokenServer := NewTokenServer()
|
||||
persistenceService.Setup([]string{tokenBucket})
|
||||
return TokenServer{k, new(ristretto.Element).ScalarBaseMult(k), make(map[string]bool), persistenceService, sync.Mutex{}}
|
||||
persistenceService.Setup([]string{keyBucket})
|
||||
exists, err := persistenceService.Check(keyBucket, "k")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// if we don't have a stored k then save the one we have generated
|
||||
// otherwise use the k we have stored
|
||||
if !exists {
|
||||
persistenceService.Persist(keyBucket, "k", tokenServer.k)
|
||||
} else {
|
||||
persistenceService.Load(keyBucket, "k", tokenServer.k)
|
||||
|
||||
// recalculate public key from stored k
|
||||
tokenServer.Y = new(ristretto.Element).ScalarBaseMult(tokenServer.k)
|
||||
}
|
||||
|
||||
tokenServer.persistanceService = persistenceService
|
||||
return tokenServer
|
||||
}
|
||||
|
||||
// SignBlindedToken calculates kP for the given BlindedToken P
|
||||
|
@ -63,6 +82,20 @@ func (ts *TokenServer) SignBlindedTokenBatch(blindedTokens []BlindedToken, trans
|
|||
return SignedBatchWithProof{signedTokens, ts.constructBatchProof(blindedTokens, signedTokens, transcript)}
|
||||
}
|
||||
|
||||
// SignBlindedTokenBatchWithConstraint signs a batch of blinded tokens under a given transcript given a contraint that the tokens must be signed
|
||||
// by the same public key as an existing token
|
||||
func (ts *TokenServer) SignBlindedTokenBatchWithConstraint(blindedTokens []BlindedToken, token Token, transcript *core.Transcript) SignedBatchWithProof {
|
||||
var signedTokens []SignedToken
|
||||
for _, bt := range blindedTokens {
|
||||
signedTokens = append(signedTokens, ts.SignBlindedToken(bt))
|
||||
}
|
||||
Ht := sha3.Sum512(token.t)
|
||||
T := new(ristretto.Element).FromUniformBytes(Ht[:])
|
||||
// W == kT
|
||||
blindedTokens = append(blindedTokens, BlindedToken{P: T})
|
||||
return SignedBatchWithProof{signedTokens, ts.constructBatchProof(blindedTokens, append(signedTokens, SignedToken{Q: token.W}), transcript)}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
transcript.NewProtocol(BatchProofProtocol)
|
||||
|
@ -102,8 +135,8 @@ func (ts *TokenServer) SpendToken(token SpentToken, data []byte) error {
|
|||
W := new(ristretto.Element).ScalarMult(ts.k, T)
|
||||
key := sha3.Sum256(append(token.T, W.Encode(nil)...))
|
||||
mac := hmac.New(sha3.New512, key[:])
|
||||
K := mac.Sum(data)
|
||||
result := hmac.Equal(token.MAC, K)
|
||||
computedMAC := mac.Sum(data)
|
||||
result := hmac.Equal(token.MAC, computedMAC)
|
||||
if result == true {
|
||||
if ts.persistanceService == nil {
|
||||
ts.seen[hex.EncodeToString(token.T)] = true
|
||||
|
|
20
service.go
20
service.go
|
@ -51,6 +51,7 @@ type connection struct {
|
|||
outbound bool
|
||||
closed bool
|
||||
MaxLength int
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// NewConnection creates a new Connection
|
||||
|
@ -73,17 +74,23 @@ func (c *connection) ID() *primitives.Identity {
|
|||
|
||||
// App returns the overarching application using this Connection.
|
||||
func (c *connection) App() Application {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
return c.app
|
||||
}
|
||||
|
||||
// App returns the overarching application using this Connection.
|
||||
func (c *connection) SetApp(application Application) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
c.app = application
|
||||
}
|
||||
|
||||
// Hostname returns the hostname of the connection (if the connection has not been authorized it will return the
|
||||
// temporary hostname identifier)
|
||||
func (c *connection) Hostname() string {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
return c.hostname
|
||||
}
|
||||
|
||||
|
@ -95,11 +102,15 @@ func (c *connection) IsOutbound() bool {
|
|||
|
||||
// IsClosed returns true if the connection is closed (connections cannot be reopened)
|
||||
func (c *connection) IsClosed() bool {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
return c.closed
|
||||
}
|
||||
|
||||
// SetHostname sets the hostname on the connection
|
||||
func (c *connection) SetHostname(hostname string) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
log.Debugf("[%v -- %v] Asserting Remote Hostname: %v", c.identity.Hostname(), c.hostname, hostname)
|
||||
c.hostname = hostname
|
||||
}
|
||||
|
@ -118,6 +129,8 @@ func (c *connection) HasCapability(name Capability) bool {
|
|||
|
||||
// Close forcibly closes the connection
|
||||
func (c *connection) Close() {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
c.closed = true
|
||||
c.conn.Close()
|
||||
}
|
||||
|
@ -133,7 +146,8 @@ func (c *connection) Expect() []byte {
|
|||
c.closed = true
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if c.encrypted {
|
||||
var decryptNonce [24]byte
|
||||
copy(decryptNonce[:], buffer[:24])
|
||||
|
@ -157,6 +171,8 @@ func (c *connection) Expect() []byte {
|
|||
|
||||
// SetEncryptionKey turns on application-level encryption on the connection using the given key.
|
||||
func (c *connection) SetEncryptionKey(key [32]byte) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
c.key = key
|
||||
c.encrypted = true
|
||||
}
|
||||
|
@ -168,6 +184,8 @@ func (c *connection) Send(message []byte) {
|
|||
binary.PutUvarint(buffer[0:2], uint64(len(message)))
|
||||
copy(buffer[2:], message)
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if c.encrypted {
|
||||
var nonce [24]byte
|
||||
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
set -e
|
||||
pwd
|
||||
go test ${1} -coverprofile=applications.cover.out -v ./applications
|
||||
go test ${1} -coverprofile=applications.tokenboard.cover.out -v ./applications/tokenboard
|
||||
go test ${1} -coverprofile=primitives.cover.out -v ./primitives
|
||||
go test ${1} -coverprofile=primitives.auditable.cover.out -v ./primitives/auditable
|
||||
go test ${1} -coverprofile=primitives.core.cover.out -v ./primitives/core
|
||||
go test ${1} -coverprofile=primitives.privacypass.cover.out -v ./primitives/privacypass
|
||||
go test -race ${1} -coverprofile=applications.cover.out -v ./applications
|
||||
go test -race ${1} -coverprofile=applications.tokenboard.cover.out -v ./applications/tokenboard
|
||||
go test -race ${1} -coverprofile=primitives.cover.out -v ./primitives
|
||||
go test -race ${1} -coverprofile=primitives.auditable.cover.out -v ./primitives/auditable
|
||||
go test -race ${1} -coverprofile=primitives.core.cover.out -v ./primitives/core
|
||||
go test -race ${1} -coverprofile=primitives.privacypass.cover.out -v ./primitives/privacypass
|
||||
go test -bench "BenchmarkAuditableStore" -benchtime 1000x primitives/auditable/*.go
|
||||
echo "mode: set" > coverage.out && cat *.cover.out | grep -v mode: | sort -r | \
|
||||
awk '{if($1 != last) {print $0;last=$1}}' >> coverage.out
|
||||
|
|
Caricamento…
Fai riferimento in un nuovo problema