Browse Source

Updates given Erinns Comments

transcript
Sarah Jamie Lewis 1 year ago
parent
commit
a19204caf4
19 changed files with 138 additions and 320 deletions
  1. +1
    -1
      .drone.yml
  2. +1
    -0
      .gitignore
  3. +1
    -1
      applications/auth.go
  4. +12
    -9
      applications/token_app.go
  5. +1
    -2
      applications/tokenboard/client.go
  6. +4
    -2
      applications/tokenboard/server.go
  7. +7
    -6
      applications/tokenboard/tokenboard_integration_test.go
  8. +0
    -1
      networks/tor/BaseOnionService.go
  9. +1
    -0
      persistence/bolt_persistence.go
  10. +2
    -0
      primitives/auditable/auditablestore.go
  11. +0
    -62
      primitives/bloom.go
  12. +0
    -24
      primitives/bloom_test.go
  13. +0
    -186
      primitives/core/operations.go
  14. +3
    -3
      primitives/privacypass/dlogeq.go
  15. +2
    -1
      primitives/privacypass/token.go
  16. +32
    -2
      primitives/privacypass/token_test.go
  17. +46
    -13
      primitives/privacypass/tokenserver.go
  18. +19
    -1
      service.go
  19. +6
    -6
      testing/tests.sh

+ 1
- 1
.drone.yml View File

@@ -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


+ 1
- 0
.gitignore View File

@@ -6,3 +6,4 @@ coverage.out
/applications/tor/
*.db
/applications/tokenboard/tor/
fuzzing/

+ 1
- 1
applications/auth.go View File

@@ -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


+ 12
- 9
applications/token_app.go View File

@@ -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


+ 1
- 2
applications/tokenboard/client.go View File

@@ -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)


+ 4
- 2
applications/tokenboard/server.go View File

@@ -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}})


+ 7
- 6
applications/tokenboard/tokenboard_integration_test.go View File

@@ -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)


+ 0
- 1
networks/tor/BaseOnionService.go View File

@@ -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() {


+ 1
- 0
persistence/bolt_persistence.go View File

@@ -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 {


+ 2
- 0
primitives/auditable/auditablestore.go View File

@@ -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"


+ 0
- 62
primitives/bloom.go View File

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

+ 0
- 24
primitives/bloom_test.go View File

@@ -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)

}

+ 0
- 186
primitives/core/operations.go View File

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

+ 3
- 3
primitives/privacypass/dlogeq.go View File

@@ -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


+ 2
- 1
primitives/privacypass/token.go View File

@@ -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


+ 32
- 2
primitives/privacypass/token_test.go View File

@@ -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()
}

+ 46
- 13
primitives/privacypass/tokenserver.go View File

@@ -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


+ 19
- 1
service.go View File

@@ -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 {


+ 6
- 6
testing/tests.sh View File

@@ -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


Loading…
Cancel
Save