Compare commits

..

8 Commits

30 changed files with 1602 additions and 284 deletions

View File

@ -4,10 +4,6 @@ workspace:
pipeline:
fetch:
when:
repo: cwtch.im/tapir
branch: master
event: [ push, pull_request ]
image: golang
commands:
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/tor
@ -17,33 +13,21 @@ pipeline:
- go mod vendor
- go get -u golang.org/x/lint/golint
quality:
when:
repo: cwtch.im/tapir
branch: master
event: [ push, pull_request ]
image: golang
commands:
- go list ./... | xargs go vet
- go list ./... | xargs golint -set_exit_status
units-tests:
when:
repo: cwtch.im/tapir
branch: master
event: [ push, pull_request ]
image: golang
commands:
- export PATH=$PATH:/go/src/cwtch.im/tapir
- sh testing/tests.sh
integ-test:
when:
repo: cwtch.im/tapir
branch: master
event: [ push, pull_request ]
image: golang
commands:
- ./tor -f ./torrc
- sleep 15
- go test -race -v cwtch.im/tapir/testing
- go test -v cwtch.im/tapir/testing
notify-email:
image: drillster/drone-email
host: build.openprivacy.ca
@ -51,15 +35,10 @@ pipeline:
skip_verify: true
from: drone@openprivacy.ca
when:
repo: cwtch.im/tapir
branch: master
event: [ push, pull_request ]
status: [ failure ]
notify-gogs:
image: openpriv/drone-gogs
when:
repo: cwtch.im/tapir
branch: master
event: pull_request
status: [ success, changed, failure ]
secrets: [gogs_account_token]

2
.gitignore vendored
View File

@ -5,5 +5,3 @@ coverage.out
/testing/tor/
/applications/tor/
*.db
/applications/tokenboard/tor/
fuzzing/

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 connection can generate the same key
// Define canonical labels so both sides of the
var outboundAuthMessage []byte
var outboundHostname string
var inboundAuthMessage []byte

View File

@ -68,7 +68,7 @@ func (powapp *ProofOfWorkApplication) solveChallenge(challenge []byte, prng core
solve := make([]byte, len(challenge)+32)
for !solved {
solution = prng.Next().Encode(nil)
solution = prng.Next().Bytes()
copy(solve[0:], solution[:])
copy(solve[len(solution):], challenge[:])

View File

@ -16,49 +16,41 @@ type TokenApplication struct {
// HasTokensCapability is granted once the client has obtained signed tokens
const HasTokensCapability = tapir.Capability("HasTokensCapability")
const numTokens = 10
// NewInstance should always return a new instantiation of the application.
func (tokenapp *TokenApplication) NewInstance() tapir.Application {
func (powapp *TokenApplication) NewInstance() tapir.Application {
app := new(TokenApplication)
app.TokenService = tokenapp.TokenService
app.TokenService = powapp.TokenService
return app
}
// Init is run when the connection is first started.
func (tokenapp *TokenApplication) Init(connection tapir.Connection) {
tokenapp.Transcript().NewProtocol("token-app")
log.Debugf(tokenapp.Transcript().OutputTranscriptToAudit())
func (powapp *TokenApplication) Init(connection tapir.Connection) {
powapp.Transcript().NewProtocol("token-app")
if connection.IsOutbound() {
tokens, blinded := privacypass.GenerateBlindedTokenBatch(numTokens)
tokens, blinded := privacypass.GenerateBlindedTokenBatch(10)
data, _ := json.Marshal(blinded)
connection.Send(data)
var signedBatch privacypass.SignedBatchWithProof
err := json.Unmarshal(connection.Expect(), &signedBatch)
if err == nil {
verified := privacypass.UnblindSignedTokenBatch(tokens, blinded, signedBatch.SignedTokens, tokenapp.TokenService.Y, signedBatch.Proof, tokenapp.Transcript())
verified := privacypass.UnblindSignedTokenBatch(tokens, blinded, signedBatch.SignedTokens, powapp.TokenService.Y, signedBatch.Proof, powapp.Transcript())
if verified {
log.Debugf("Successfully obtained signed tokens")
tokenapp.Tokens = tokens
powapp.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")
}
return
}
// We are the server
var blinded []privacypass.BlindedToken
err := json.Unmarshal(connection.Expect(), &blinded)
if err == nil {
batchProof := tokenapp.TokenService.SignBlindedTokenBatch(blinded, tokenapp.Transcript())
log.Debugf(tokenapp.Transcript().OutputTranscriptToAudit())
data, _ := json.Marshal(batchProof)
connection.Send(data)
return
} else {
var blinded []privacypass.BlindedToken
err := json.Unmarshal(connection.Expect(), &blinded)
if err == nil {
batchProof := powapp.TokenService.SignBlindedTokenBatch(blinded, powapp.Transcript())
data, _ := json.Marshal(batchProof)
connection.Send(data)
return
}
}
}

View File

@ -60,6 +60,7 @@ 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)
@ -100,7 +101,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, ta.connection.Hostname())
token, err := ta.paymentHandler.NextToken(message)
if err == nil {
data, _ := json.Marshal(Message{MessageType: postRequestMessage, PostRequest: postRequest{Token: token, Message: message}})
ta.connection.Send(data)

View File

@ -1,7 +1,5 @@
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"
@ -57,7 +55,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
@ -79,7 +77,7 @@ func (ta *Server) Listen() {
}
func (ta *Server) postMessageRequest(token privacypass.SpentToken, message auditable.Message) {
if err := ta.TokenService.SpendToken(token, append(message, ta.connection.ID().Hostname()...)); err == nil {
if err := ta.TokenService.SpendToken(token, message); err == nil {
log.Debugf("Token is valid")
signedproof := ta.AuditableStore.Add(message)
data, _ := json.Marshal(Message{MessageType: postResultMessage, PostResult: postResult{true, signedproof}})

View File

@ -47,24 +47,21 @@ 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)
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)
client.WaitForCapabilityOrClose(fph.ServerHostname, applications.HasTokensCapability)
conn, _ := client.GetConnection(fph.ServerHostname)
powtapp, _ := conn.App().(*applications.TokenApplication)
fph.tokens = append(fph.tokens, powtapp.Tokens...)
log.Debugf("Transcript: %v", powtapp.Transcript().OutputTranscriptToAudit())
conn.Close()
}
func (fph *FreePaymentHandler) NextToken(data []byte, hostname string) (privacypass.SpentToken, error) {
func (fph *FreePaymentHandler) NextToken(data []byte) (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(append(data, hostname...)), nil
return token.SpendToken(data), nil
}
func TestTokenBoardApp(t *testing.T) {
@ -97,7 +94,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()
}()
@ -109,7 +106,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)
@ -117,12 +114,12 @@ func TestTokenBoardApp(t *testing.T) {
sg.Done()
}()
time.Sleep(time.Second * 60) // wait for server to initialize
time.Sleep(time.Second * 30) // wait for server to initialize
id, sk := primitives.InitializeEphemeralIdentity()
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)

6
go.mod
View File

@ -2,11 +2,5 @@ module cwtch.im/tapir
require (
git.openprivacy.ca/openprivacy/libricochet-go v1.0.4
github.com/gtank/merlin v0.1.1
github.com/gtank/ristretto255 v0.1.2
go.etcd.io/bbolt v1.3.3
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f
golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect
)
go 1.13

11
go.sum
View File

@ -8,19 +8,11 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc=
github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o=
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0=
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
golang.org/x/crypto v0.0.0-20190128193316-c7b33c32a30b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo=
@ -28,9 +20,8 @@ golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -54,6 +54,7 @@ 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() {

View File

@ -53,7 +53,6 @@ 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 {

View File

@ -1,7 +1,5 @@
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"

62
primitives/bloom.go Normal file
View File

@ -0,0 +1,62 @@
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
}

24
primitives/bloom_test.go Normal file
View File

@ -0,0 +1,24 @@
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)
}

View File

@ -0,0 +1,118 @@
package bulletproofs
import (
"crypto/sha512"
"cwtch.im/tapir/primitives/core"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
ristretto "github.com/gtank/ristretto255"
"testing"
)
func TestConstraintSystem(t *testing.T) {
log.SetLevel(log.LevelDebug)
cs := NewConstrainSystem(Setup(2, core.NewTranscript("")))
abytes := sha512.Sum512([]byte("a"))
a := new(ristretto.Scalar)
a.FromUniformBytes(abytes[:])
bbytes := sha512.Sum512([]byte("b"))
b := new(ristretto.Scalar)
b.FromUniformBytes(bbytes[:])
xbytes := sha512.Sum512([]byte("b"))
x := new(ristretto.Scalar)
x.FromUniformBytes(xbytes[:])
ybytes := sha512.Sum512([]byte("a"))
y := new(ristretto.Scalar)
y.FromUniformBytes(ybytes[:])
prng := core.NewTranscript("private").CommitToPRNG("private")
V1, a_lc := cs.Commit(a, prng.Next())
V2, b_lc := cs.Commit(b, prng.Next())
V3, x_lc := cs.Commit(x, prng.Next())
V4, y_lc := cs.Commit(y, prng.Next())
vcs := NewConstrainSystem(Setup(2, core.NewTranscript("")))
va_lc := vcs.VerifierCommit(V1)
vb_lc := vcs.VerifierCommit(V2)
vx_lc := vcs.VerifierCommit(V3)
vy_lc := vcs.VerifierCommit(V4)
_, _, in := cs.Multiply(a_lc, b_lc)
_, _, out := cs.Multiply(x_lc, y_lc)
cs.Constrain(in.Sub(out))
_, _, vin := vcs.Multiply(va_lc, vb_lc)
_, _, vout := vcs.Multiply(vx_lc, vy_lc)
vcs.Constrain(vin.Sub(vout))
wL, wR, wO, wV := cs.flatten(core.One())
lhs := new(ristretto.Scalar)
lhs.Add(lhs, core.InnerProduct(cs.aL, wL))
lhs.Add(lhs, core.InnerProduct(cs.aR, wR))
rhs := new(ristretto.Scalar)
rhs.Add(rhs, core.InnerProduct(cs.aO, wO))
rrhs := new(ristretto.Scalar)
rrhs.Add(rrhs, core.InnerProduct(cs.v, wV))
t.Logf("lhs: %v\n", lhs)
t.Logf("rhs: %v\n", rhs)
t.Logf("rrhs: %v\n", rrhs)
t.Logf("\nwL: %v\nwR: %v\nwO: %v\nwC: %v", wL, wR, wO, wV)
proof := cs.Prove(cs.params, core.NewTranscript(""))
t.Logf("Proof Result: %v", vcs.Verify(proof, cs.params, core.NewTranscript("")))
}
func TestConstraintSystemMix(t *testing.T) {
log.SetLevel(log.LevelDebug)
cs := NewConstrainSystem(Setup(2, core.NewTranscript("")))
one := core.One()
two := new(ristretto.Scalar).Add(one, one)
three := new(ristretto.Scalar).Add(two, one)
four := new(ristretto.Scalar).Add(two, two)
prng := core.NewTranscript("private").CommitToPRNG("private")
V1, a_lc := cs.Commit(three, prng.Next())
V2, b_lc := cs.Commit(three, prng.Next())
V3, x_lc := cs.Commit(two, prng.Next())
V4, y_lc := cs.Commit(four, prng.Next())
// todo make this an actual verifier!
cs.VerifierCommit(V1)
cs.VerifierCommit(V2)
cs.VerifierCommit(V3)
cs.VerifierCommit(V4)
_, _, out := cs.Multiply(a_lc.Sub(x_lc).Add(b_lc.Sub(y_lc)), x_lc.Add(y_lc).Sub(a_lc.Add(b_lc)))
_, _, out2 := cs.Multiply(a_lc.Sub(x_lc).Add(b_lc.Sub(y_lc)), x_lc.Add(y_lc).Sub(a_lc.Add(b_lc)))
cs.Constrain(out.Add(out2))
wL, wR, wO, wV := cs.flatten(core.One())
lhs := new(ristretto.Scalar)
lhs.Add(lhs, core.InnerProduct(cs.aL, wL))
lhs.Add(lhs, core.InnerProduct(cs.aR, wR))
rhs := new(ristretto.Scalar)
rhs.Add(rhs, core.InnerProduct(cs.aO, wO))
rrhs := new(ristretto.Scalar)
rrhs.Add(rrhs, core.InnerProduct(cs.v, wV))
t.Logf("lhs: %v\n", lhs)
t.Logf("rhs: %v\n", rhs)
t.Logf("rrhs: %v\n", rrhs)
t.Logf("\nwL: %v\nwR: %v\nwO: %v\nwC: %v", wL, wR, wO, wV)
proof := cs.Prove(cs.params, core.NewTranscript(""))
t.Logf("Proof Result: %v", cs.Verify(proof, cs.params, core.NewTranscript("")))
}

View File

@ -0,0 +1,437 @@
package bulletproofs
import (
"crypto/rand"
"cwtch.im/tapir/primitives/core"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
ristretto "github.com/gtank/ristretto255"
)
type Variable struct {
Enum string
Index int
}
type Term struct {
Variable
Coefficient *ristretto.Scalar
}
// LinearCombination represent a vector of Terms
// We use the same structure as dalek-bulletproofs and reference the variable by an index into the canonical
// term in the constraint system.
type LinearCombination struct {
Terms []Term
}
// Adding together linear combinations lc+olc = lc[:]+olc[:]
// (a * -1) + (b * -1) = (a*-1 + b * -1)
func (lc *LinearCombination) Add(olc *LinearCombination) *LinearCombination {
terms := lc.Terms
for _, term := range olc.Terms {
terms = append(terms, term)
}
return &LinearCombination{terms}
}
// Subtracting a linear combination olc from lc results in the terms of lc being extended
// with those of lc, but with the coefficients in old being negated.
// (a * -1 - b * -1) = (a*-1 + b * 1)
func (lc *LinearCombination) Sub(olc *LinearCombination) *LinearCombination {
terms := lc.Terms
for _, term := range olc.Terms {
term.Coefficient = new(ristretto.Scalar).Negate(term.Coefficient)
terms = append(terms, term)
}
return &LinearCombination{terms}
}
// ConstraintSystem allow constructing constraint systems over committed parameters, generating a proof of that constraint
// and allows a verifier to check that the constraint holds.
type ConstraintSystem struct {
aL core.ScalarVector
aR core.ScalarVector
aO core.ScalarVector
v core.ScalarVector
vBlinding core.ScalarVector
constraints []*LinearCombination
V core.GeneratorVector // todo clarify this
params CommitmentsParams
}
func NewConstrainSystem(params CommitmentsParams) ConstraintSystem {
cs := ConstraintSystem{}
cs.params = params
return cs
}
// Multiply constructs a multiplication gate within our constraint system.
// It takes in two linear combinations a and b, evaluates the result c, and outputs three linear combinations
// one for the left input wire (a), one of the right input wire b, and one for the output wire (c)
// a and b are constrained to (a*-1) and (b*-1)
func (cs *ConstraintSystem) Multiply(a, b *LinearCombination) (*LinearCombination, *LinearCombination, *LinearCombination) {
l := cs.eval(a)
r := cs.eval(b)
c := new(ristretto.Scalar).Multiply(l, r)
log.Debugf("%v & %v == %v", l, r, c)
a.Terms = append(a.Terms, Term{Variable{"left", len(cs.aL)}, new(ristretto.Scalar).Negate(core.One())})
b.Terms = append(b.Terms, Term{Variable{"right", len(cs.aR)}, new(ristretto.Scalar).Negate(core.One())})
o := &LinearCombination{[]Term{{Variable{"output", len(cs.aO)}, core.One()}}}
cs.aL = append(cs.aL, l)
cs.aR = append(cs.aR, r)
cs.aO = append(cs.aO, c)
cs.Constrain(a)
cs.Constrain(b)
return a, b, o
}
// eval evaluates a linear combination lc, by looking up each term in the relevant vector in the constrain system
// and multiplying it by it's coefficient.
func (cs *ConstraintSystem) eval(lc *LinearCombination) *ristretto.Scalar {
result := new(ristretto.Scalar)
for _, term := range lc.Terms {
switch term.Enum {
case "left":
result.Add(result, new(ristretto.Scalar).Multiply(term.Coefficient, cs.aL[term.Index]))
case "right":
result.Add(result, new(ristretto.Scalar).Multiply(term.Coefficient, cs.aR[term.Index]))
case "output":
result.Add(result, new(ristretto.Scalar).Multiply(term.Coefficient, cs.aO[term.Index]))
case "committed":
if len(cs.V) > 0 {
result.Add(result, new(ristretto.Scalar).Multiply(term.Coefficient, core.One()))
} else {
result.Add(result, new(ristretto.Scalar).Multiply(term.Coefficient, cs.v[term.Index]))
}
case "one":
result.Add(result, term.Coefficient)
default:
panic("")
}
}
return result
}
func (cs *ConstraintSystem) Commit(v *ristretto.Scalar, vBlind *ristretto.Scalar) (*ristretto.Element, *LinearCombination) {
i := len(cs.v)
cs.v = append(cs.v, v)
cs.vBlinding = append(cs.vBlinding, vBlind)
V := core.MultiExp(core.ScalarVector{v, vBlind}, core.GeneratorVector{cs.params.g, cs.params.h})
return V, &LinearCombination{[]Term{{Variable{"committed", i}, core.One()}}}
}
func (cs *ConstraintSystem) VerifierCommit(V *ristretto.Element) *LinearCombination {
i := len(cs.V)
cs.V = append(cs.V, V)
return &LinearCombination{[]Term{{Variable{"committed", i}, core.One()}}}
}
// Constrain adds the given linear combination to the constraints vector
// when constraints are flattened, each constraint will be evaluated and determine the
// weights to add.
func (cs *ConstraintSystem) Constrain(c *LinearCombination) {
cs.constraints = append(cs.constraints, c)
}
// flatten constructs a set of 4 vectors of weights wL, wR, wO and wV representing the left inputs (L), right inputs (R), outputs (O) and
// committed values (C) such that
// <aL,wL> + <aR,wR> + <aO, wO> = <v,WL>
func (cs *ConstraintSystem) flatten(z *ristretto.Scalar) (wL core.ScalarVector, wR core.ScalarVector, wO core.ScalarVector, wV core.ScalarVector) {
wL = make(core.ScalarVector, len(cs.aL))
wR = make(core.ScalarVector, len(cs.aL))
wO = make(core.ScalarVector, len(cs.aL))
var m int
if len(cs.V) > 0 {
m = len(cs.V)
wV = make(core.ScalarVector, len(cs.V))
} else {
m = len(cs.v)
wV = make(core.ScalarVector, len(cs.v))
}
for i := 0; i < len(cs.aL); i++ {
wL[i] = new(ristretto.Scalar)
wR[i] = new(ristretto.Scalar)
wO[i] = new(ristretto.Scalar)
}
for i := 0; i < m; i++ {
wV[i] = new(ristretto.Scalar)
}
expZ := new(ristretto.Scalar).Add(z, new(ristretto.Scalar).Zero())
for _, constraint := range cs.constraints {
for _, term := range constraint.Terms {
log.Debugf("term: %v", term)
switch term.Enum {
case "left":
wL[term.Index].Add(wL[term.Index], new(ristretto.Scalar).Multiply(expZ, term.Coefficient))
case "right":
wR[term.Index].Add(wR[term.Index], new(ristretto.Scalar).Multiply(expZ, term.Coefficient))
case "output":
wO[term.Index].Add(wO[term.Index], new(ristretto.Scalar).Multiply(expZ, term.Coefficient))
case "committed":
wV[term.Index].Subtract(wV[term.Index], new(ristretto.Scalar).Multiply(expZ, term.Coefficient))
default:
panic("")
}
}
expZ = expZ.Multiply(expZ, z)
}
return
}
type ConstraintProof struct {
Ai *ristretto.Element
Ao *ristretto.Element
S *ristretto.Element
T1 *ristretto.Element
T3 *ristretto.Element
T4 *ristretto.Element
T5 *ristretto.Element
T6 *ristretto.Element
Micro *ristretto.Scalar
TX *ristretto.Scalar
TX_Blinding *ristretto.Scalar
IPP InnerProductProof
}
// Prove the constraint
func (cs *ConstraintSystem) Prove(c CommitmentsParams, transcript *core.Transcript) ConstraintProof {
n := len(cs.aL)
log.Debugf("n = %v\n", n)
// Generate a prng to from this transcript and some external randomness
// We use this to generate the rest of our private scalars
// TODO: move to transcript
private := make([]byte, 64)
rand.Read(private)
prngTranscript := core.NewTranscript("private-transcript")
prngTranscript.AddToTranscript("randomness", private)
prng := prngTranscript.CommitToPRNG(transcript.OutputTranscriptToAudit())
alpha := prng.Next()
beta := prng.Next()
rho := prng.Next()
Ai := core.MultiExp(append(cs.aL.Join(cs.aR), alpha), append(c.G.Join(c.H), c.h))
Ao := core.MultiExp(cs.aO.SafeAppend(beta), c.G.SafeAppend(c.h))
Sl := make(core.ScalarVector, c.max)
Sr := make(core.ScalarVector, c.max)
for i := 0; i < c.max; i++ {
Sl[i] = prng.Next()
Sr[i] = prng.Next()
}
S := core.MultiExp(append(Sl.Join(Sr), rho), c.G.Join(c.H).SafeAppend(c.h))
transcript.AddToTranscript("Ai", []byte(Ai.String()))
transcript.AddToTranscript("So", []byte(Ao.String()))
transcript.AddToTranscript("S", []byte(S.String()))
y := transcript.CommitToTranscriptScalar("y")
yinv := new(ristretto.Scalar).Invert(y)
z := transcript.CommitToTranscriptScalar("z")
log.Debugf("Calculating y^%v", n)
powY := core.PowerVector(y, n)
powYInv := core.PowerVector(yinv, n)
wL, wR, wO, wV := cs.flatten(z)
lhs := make([]core.ScalarVector, 4)
//lhs[0] = 0
lhs[1] = core.EntrywiseSum(cs.aL, core.EntryWiseProduct(powYInv, wR))
lhs[2] = cs.aO
lhs[3] = Sl
rhs := make([]core.ScalarVector, 4)
rhs[0] = core.EntrywiseSub(wO, powY)
rhs[1] = core.EntrywiseSum(core.EntryWiseProduct(powY, cs.aR), wL)
// r2 = 0
rhs[3] = core.EntryWiseProduct(powY, Sr)
t1 := core.InnerProduct(lhs[1], rhs[0])
t2 := new(ristretto.Scalar).Add(core.InnerProduct(lhs[1], rhs[1]), core.InnerProduct(lhs[2], rhs[0]))
t3 := new(ristretto.Scalar).Add(core.InnerProduct(lhs[2], rhs[1]), core.InnerProduct(lhs[3], rhs[0]))
t4 := new(ristretto.Scalar).Add(core.InnerProduct(lhs[1], rhs[3]), core.InnerProduct(lhs[3], rhs[1]))
t5 := core.InnerProduct(lhs[2], rhs[3])
t6 := core.InnerProduct(lhs[3], rhs[3])
//t2 := core.InnerProduct(cs.aL,core.EntryWiseProduct(cs.aR, powY))
//t2.Subtract(t2, core.InnerProduct(cs.aO, powY))
//t2.Add(t2, )
//
tau1 := prng.Next()
tau3 := prng.Next()
tau4 := prng.Next()
tau5 := prng.Next()
tau6 := prng.Next()
T1 := core.MultiExp(core.ScalarVector{t1, tau1}, core.GeneratorVector{c.g, c.h})
T3 := core.MultiExp(core.ScalarVector{t3, tau3}, core.GeneratorVector{c.g, c.h})
T4 := core.MultiExp(core.ScalarVector{t4, tau4}, core.GeneratorVector{c.g, c.h})
T5 := core.MultiExp(core.ScalarVector{t5, tau5}, core.GeneratorVector{c.g, c.h})
T6 := core.MultiExp(core.ScalarVector{t6, tau6}, core.GeneratorVector{c.g, c.h})
transcript.AddToTranscript("T1", []byte(T1.String()))
transcript.AddToTranscript("T3", []byte(T3.String()))
transcript.AddToTranscript("T4", []byte(T4.String()))
transcript.AddToTranscript("T5", []byte(T5.String()))
transcript.AddToTranscript("T6", []byte(T6.String()))
//u := transcript.CommitToTranscriptScalar("u")
x := transcript.CommitToTranscriptScalar("x")
log.Debugf("x: %v", x)
// T(X) = t0 + t1x + t2x
TX := new(ristretto.Scalar).Zero()
TX.Add(TX, new(ristretto.Scalar).Multiply(t1, x))
x2 := new(ristretto.Scalar).Multiply(x, x)
TX.Add(TX, new(ristretto.Scalar).Multiply(t2, x2))
x3 := new(ristretto.Scalar).Multiply(x2, x)
TX.Add(TX, new(ristretto.Scalar).Multiply(t3, x3))
x4 := new(ristretto.Scalar).Multiply(x3, x)
TX.Add(TX, new(ristretto.Scalar).Multiply(t4, x4))
x5 := new(ristretto.Scalar).Multiply(x4, x)
TX.Add(TX, new(ristretto.Scalar).Multiply(t5, x5))
x6 := new(ristretto.Scalar).Multiply(x5, x)
TX.Add(TX, new(ristretto.Scalar).Multiply(t6, x6))
// evaluate l(X)
// lx0 = 0
lx1 := core.VectorMulScalar(lhs[1], x)
lx2 := core.VectorMulScalar(lhs[2], x2)
lx3 := core.VectorMulScalar(lhs[3], new(ristretto.Scalar).Multiply(x2, x))
lx := core.EntrywiseSum(core.EntrywiseSum(lx1, lx2), lx3)
// evaluate r(X)
rx1 := core.VectorMulScalar(rhs[1], x)
// rx2 := core.VectorMulScalar(rhs[2],x2 ) rhs[2] == 0
rx3 := core.VectorMulScalar(rhs[3], new(ristretto.Scalar).Multiply(x2, x))
rx := core.EntrywiseSum(core.EntrywiseSum(rx1, rhs[0]), rx3)
// calculate the inner product (t̂)
iplr := core.InnerProduct(lx, rx)
// T(x) ?= <l(X),r(X)>
log.Debugf("T(X) = %v", TX)
log.Debugf("ipp = %v", iplr)
log.Debugf("equal: %v", TX.Equal(iplr) == 1)
tau_blind := new(ristretto.Scalar).Multiply(tau1, x)
tau_blind.Add(tau_blind, new(ristretto.Scalar).Multiply(tau3, x3))
tau_blind.Add(tau_blind, new(ristretto.Scalar).Multiply(tau4, x4))
tau_blind.Add(tau_blind, new(ristretto.Scalar).Multiply(tau5, x5))
tau_blind.Add(tau_blind, new(ristretto.Scalar).Multiply(tau6, x6))
//delta := core.InnerProduct(core.EntryWiseProduct(powYInv,wR), wL)
tau_blind.Add(tau_blind, new(ristretto.Scalar).Multiply(core.InnerProduct(wV, cs.vBlinding), x2))
micro := new(ristretto.Scalar).Multiply(alpha, x)
micro.Add(micro, new(ristretto.Scalar).Multiply(beta, x2))
micro.Add(micro, new(ristretto.Scalar).Multiply(rho, x3))
// generate h'
H_ := make(core.GeneratorVector, c.max)
H_[0] = c.H[0]
for i := 1; i < c.max; i++ {
H_[i] = new(ristretto.Element).ScalarMult(new(ristretto.Scalar).Invert(powY[i]), c.H[i])
}
P := core.MultiExp(lx.Join(rx), c.G.Join(H_))
log.Debugf("P: %v", P)
uP := new(ristretto.Element).Add(P, new(ristretto.Element).ScalarMult(iplr, c.u))
log.Debugf("uP: %v", uP)
ipp := ProveInnerProduct(lx, rx, c.u, new(ristretto.Element).Add(new(ristretto.Element).Zero(), uP), core.CopyVector(c.G), core.CopyVector(H_), transcript)
return ConstraintProof{Ai, Ao, S, T1, T3, T4, T5, T6, micro, TX, tau_blind, ipp}
}
// Verify the constraint
func (cs *ConstraintSystem) Verify(proof ConstraintProof, c CommitmentsParams, transcript *core.Transcript) bool {
log.Debugf("Verifying: %v\n", proof)
n := len(cs.aL)
transcript.AddToTranscript("Ai", []byte(proof.Ai.String()))
transcript.AddToTranscript("So", []byte(proof.Ao.String()))
transcript.AddToTranscript("S", []byte(proof.S.String()))
y := transcript.CommitToTranscriptScalar("y")
yinv := new(ristretto.Scalar).Invert(y)
z := transcript.CommitToTranscriptScalar("z")
log.Debugf("Calculating y^%v", n)
powY := core.PowerVector(y, n)
powYInv := core.PowerVector(yinv, n)
wL, wR, wO, wV := cs.flatten(z)
// generate h'
H_ := make(core.GeneratorVector, c.max)
H_[0] = c.H[0]
for i := 1; i < c.max; i++ {
H_[i] = new(ristretto.Element).ScalarMult(powYInv[i], c.H[i])
}
Wl := core.MultiExp(wL, H_)
Wr := core.MultiExp(core.EntryWiseProduct(powYInv, wR), c.G)
Wo := core.MultiExp(wO, H_)
transcript.AddToTranscript("T1", []byte(proof.T1.String()))
transcript.AddToTranscript("T3", []byte(proof.T3.String()))
transcript.AddToTranscript("T4", []byte(proof.T4.String()))
transcript.AddToTranscript("T5", []byte(proof.T5.String()))
transcript.AddToTranscript("T6", []byte(proof.T6.String()))
x := transcript.CommitToTranscriptScalar("x")
log.Debugf("x: %v", x)
x2 := new(ristretto.Scalar).Multiply(x, x)
x3 := new(ristretto.Scalar).Multiply(x2, x)
x4 := new(ristretto.Scalar).Multiply(x3, x)
x5 := new(ristretto.Scalar).Multiply(x4, x)
x6 := new(ristretto.Scalar).Multiply(x5, x)
HY := core.MultiExp(core.VectorNegate(powY), H_)
P := core.MultiExp(core.ScalarVector{x, x2, x3, x, x, core.One()}, core.GeneratorVector{proof.Ai, proof.Ao, proof.S, Wl, Wr, Wo})
P.Add(P, HY)
P_ := P.Subtract(P, new(ristretto.Element).ScalarMult(proof.Micro, c.h))
uP := new(ristretto.Element).Add(P_, new(ristretto.Element).ScalarMult(proof.TX, c.u))
log.Debugf("P: %v", P_)
log.Debugf("uP: %v", uP)
lhs := core.MultiExp(core.ScalarVector{proof.TX, proof.TX_Blinding}, core.GeneratorVector{c.g, c.h})
delta := core.InnerProduct(core.EntryWiseProduct(powYInv, wR), wL)
rhs := core.MultiExp(core.VectorMulScalar(wV, x2).Join(core.ScalarVector{x, x3, x4, x5, x6, new(ristretto.Scalar).Multiply(x2, delta)}),
cs.V.Join(core.GeneratorVector{proof.T1, proof.T3, proof.T4, proof.T5, proof.T6, c.g}))
log.Debugf("lhs: %v", lhs)
log.Debugf("rhs: %v", rhs)
//rhs := core.MultiExp(,cs.)
return lhs.Equal(rhs) == 1 && Verify(proof.IPP, n, c.u, new(ristretto.Element).Add(new(ristretto.Element).Zero(), uP), core.CopyVector(c.G), core.CopyVector(H_), transcript)
}

View File

@ -0,0 +1,4 @@
package bulletproofs
type Generator struct {
}

View File

@ -0,0 +1,119 @@
package bulletproofs
import (
"cwtch.im/tapir/primitives/core"
"encoding/binary"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
ristretto "github.com/gtank/ristretto255"
)
// InnerProductProof encapsulates an inner product proof
type InnerProductProof struct {
L core.PointVector
R core.PointVector
A *ristretto.Scalar
B *ristretto.Scalar
}
// ProveInnerProduct generates a proof for <a,b>, the inner product of a and b
func ProveInnerProduct(a, b core.ScalarVector, u *ristretto.Element, P *ristretto.Element, G, H core.GeneratorVector, transcript *core.Transcript) InnerProductProof {
n := len(a)
transcript.AddToTranscript("dom-sep", []byte("ipp v1"))
nb := make([]byte, 8)
binary.LittleEndian.PutUint64(nb, uint64(n))
transcript.AddToTranscript("n", nb)
Lvec := core.PointVector{}
Rvec := core.PointVector{}
for n != 1 {
np := n / 2
aL, aR := a[:np], a[np:]
bL, bR := b[:np], b[np:]
GL, GR := G[:np], G[np:]
HL, HR := H[:np], H[np:]
cL := core.InnerProduct(aL, bR)
cR := core.InnerProduct(aR, bL)
L := core.MultiExp(append(aL.Join(bR), cL), append(GR.Join(HL), u))
R := core.MultiExp(append(aR.Join(bL), cR), append(GL.Join(HR), u))
transcript.AddElementToTranscript("L", L)
Lvec = append(Lvec, L)
transcript.AddElementToTranscript("R", R)
Rvec = append(Rvec, R)
u := transcript.CommitToTranscriptScalar("u")
uinv := new(ristretto.Scalar)
uinv.Invert(u)
for i := 0; i < len(aL); i++ {
aL_ := new(ristretto.Scalar).Multiply(aL[i], u)
aL[i] = new(ristretto.Scalar).Add(aL_, new(ristretto.Scalar).Multiply(aR[i], uinv))
bL_ := new(ristretto.Scalar).Multiply(bL[i], uinv)
bL[i] = new(ristretto.Scalar).Add(bL_, new(ristretto.Scalar).Multiply(bR[i], u))
GL[i] = core.MultiExp(core.ScalarVector{uinv, u}, core.GeneratorVector{GL[i], GR[i]})
HL[i] = core.MultiExp(core.ScalarVector{u, uinv}, core.GeneratorVector{HL[i], HR[i]})
}
x2 := new(ristretto.Scalar).Multiply(u, u)
P_ := new(ristretto.Element).ScalarMult(x2, L)
P_.Add(P_, P)
P_.Add(P_, new(ristretto.Element).ScalarMult(new(ristretto.Scalar).Invert(x2), R))
P = P_
//ranscript.AddToTranscript("P'", []byte(P.String()))
a = aL
b = bL
G = GL
H = HL
n = np
}
return InnerProductProof{Lvec, Rvec, a[0], b[0]}
}
// Verify checks the given inner product proof
func Verify(proof InnerProductProof, n int, u, P *ristretto.Element, G, H core.GeneratorVector, transcript *core.Transcript) bool {
np := n / 2
transcript.AddToTranscript("dom-sep", []byte("ipp v1"))
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, uint64(n))
transcript.AddToTranscript("n", b)
for i := range proof.L {
GL, GR := G[:np], G[np:]
HL, HR := H[:np], H[np:]
transcript.AddElementToTranscript("L", proof.L[i])
transcript.AddElementToTranscript("R", proof.R[i])
x := transcript.CommitToTranscriptScalar("u")
log.Debugf("L: %x\n", proof.L[i].Encode([]byte{}))
log.Debugf("R %x\n", proof.R[i].Encode([]byte{}))
log.Debugf("u: %x\n", x.Encode([]byte{}))
xinv := new(ristretto.Scalar)
xinv.Invert(x)
for j := 0; j < np; j++ {
GL[j] = core.MultiExp(core.ScalarVector{xinv, x}, core.GeneratorVector{GL[j], GR[j]})
HL[j] = core.MultiExp(core.ScalarVector{x, xinv}, core.GeneratorVector{HL[j], HR[j]})
}
x2 := new(ristretto.Scalar).Multiply(x, x)
P_ := new(ristretto.Element).ScalarMult(x2, proof.L[i])
P_.Add(P_, P)
P_.Add(P_, new(ristretto.Element).ScalarMult(new(ristretto.Scalar).Invert(x2), proof.R[i]))
P = P_
G = GL
H = HL
np = np / 2
}
c := new(ristretto.Scalar)
c.Multiply(proof.A, proof.B)
P_ := core.MultiExp(core.ScalarVector{proof.A, proof.B, c}, core.GeneratorVector{G[0], H[0], u})
log.Debugf("P:%v\nP':%v\n", P, P_)
return P.Equal(P_) == 1
}

View File

@ -0,0 +1,70 @@
package bulletproofs
import (
"cwtch.im/tapir/primitives/core"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
ristretto "github.com/gtank/ristretto255"
"testing"
)
func assert(t *testing.T, expected *ristretto.Scalar, actual *ristretto.Scalar) {
if expected.Equal(actual) == 1 {
t.Logf("inner_product matched: %v", actual)
} else {
t.Fatalf("c should be %v instead: %v", expected, actual)
}
}
func Test_inner_product(t *testing.T) {
one := core.IdentityVector(1)[0]
zero := new(ristretto.Scalar)
zero.Zero()
a := core.ScalarVector{one, zero, one, zero}
b := core.ScalarVector{zero, one, zero, one}
c := core.InnerProduct(a, b)
assert(t, zero, c)
a = core.ScalarVector{one, one, one, zero}
b = core.ScalarVector{one, one, zero, one}
c = core.InnerProduct(a, b)
check := new(ristretto.Scalar)
check.Add(one, one)
assert(t, check, c)
}
func TestProveInnerProduct(t *testing.T) {
log.SetLevel(log.LevelDebug)
one := core.IdentityVector(1)[0]
zero := new(ristretto.Scalar)
zero.Zero()
a := core.ScalarVector{one, zero, one, one}
b := core.ScalarVector{zero, one, one, one}
proverTranscript := core.NewTranscript("test_innerproductproof")
verifierTranscript := core.NewTranscript("test_innerproductproof")
G := proverTranscript.CommitToGenerators("G", 4)
H := proverTranscript.CommitToGenerators("H", 4)
u := proverTranscript.CommitToGenerator("u")
verifierTranscript.CommitToGenerators("G", 4)
verifierTranscript.CommitToGenerators("H", 4)
verifierTranscript.CommitToGenerator("u")
c := core.InnerProduct(a, b)
P_ := core.MultiExp(append(a.Join(b), c), append(core.GeneratorVector(G).Join(core.GeneratorVector(H)), u))
proof := ProveInnerProduct(a, b, u, new(ristretto.Element).Add(new(ristretto.Element).Zero(), P_), core.CopyVector(G), core.CopyVector(H), proverTranscript)
if Verify(proof, 4, u, P_, core.CopyVector(G), core.CopyVector(H), verifierTranscript) {
t.Logf("Inner Product Proof Passed!")
} else {
t.Logf("%v\n\n%v\n", proverTranscript.OutputTranscriptToAudit(), verifierTranscript.OutputTranscriptToAudit())
t.Fatalf("Inner Product Proof Failed!")
}
}

View File

@ -0,0 +1,305 @@
package bulletproofs
import (
"crypto/rand"
"cwtch.im/tapir/primitives/core"
"encoding/binary"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
ristretto "github.com/gtank/ristretto255"
"math/big"
)
// RangeProof encapsulates a proof that V = [0, max) where max is defined by the Setup function
type RangeProof struct {
A *ristretto.Element
S *ristretto.Element
T1 *ristretto.Element
T2 *ristretto.Element
TauX *ristretto.Scalar
InnerProduct *ristretto.Scalar
Mu *ristretto.Scalar
IPP InnerProductProof
V []*ristretto.Element
}
// CommitmentsParams encapsulates the commitment parameters for a given range proof
type CommitmentsParams struct {
G core.GeneratorVector
H core.GeneratorVector
u *ristretto.Element
g *ristretto.Element
h *ristretto.Element
max int
}
// Setup generates multiple independent commitment parameters from the underlying transcript
func Setup(max int, transcript *core.Transcript) (c CommitmentsParams) {
c.G = transcript.CommitToGenerators("G", int(max))
c.H = transcript.CommitToGenerators("H", int(max))
c.u = transcript.CommitToGenerator("u")
c.g = transcript.CommitToGenerator("g")
c.h = transcript.CommitToGenerator("h")
c.max = max
return
}
func Rand(seed string) *ristretto.Scalar {
t := core.NewTranscript(seed)
return t.CommitToTranscriptScalar("seed")
}
// GenerateRangeProof creates a valid rangeproof that value is within [0,max) under the given transcript
// It returns the rangeproof and a random scalar "gamma" that can be used to open V, the commitment to v vGgH
func GenerateRangeProof(value int32, c CommitmentsParams, transcript *core.Transcript) (RangeProof, *ristretto.Scalar) {
one := core.IdentityVector(1)[0]
two := new(ristretto.Scalar)
two.Add(one, one)
two_n := core.PowerVector(two, c.max)
transcript.AddToTranscript("dom-sep", []byte("rangeproof v1"))
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, uint64(c.max))
transcript.AddToTranscript("n", b)
b = make([]byte, 8)
binary.LittleEndian.PutUint64(b, 1)
transcript.AddToTranscript("m", b)
// Generate a prng to from this transcript and some external randomness
// We use this to generate the rest of our private scalars
// TODO: move to transcript
private := make([]byte, 64)
rand.Read(private)
prngTranscript := core.NewTranscript("private-transcript")
prngTranscript.AddToTranscript("randomness", private)
prng := prngTranscript.CommitToPRNG(transcript.OutputTranscriptToAudit())
gamma := prng.Next()
aL := valueToVector(value, c.max)
aR := core.VectorAddScalar(aL, new(ristretto.Scalar).Negate(one))
alpha := prng.Next()
vs := new(ristretto.Scalar)
b = make([]byte, 32)
copy(b, big.NewInt(int64(value)).Bytes())
vs.Decode(b)
V := core.MultiExp(core.ScalarVector{gamma, vs}, core.GeneratorVector{c.h, c.g})
transcript.AddElementToTranscript("V", V)
A := core.MultiExp(append(aL.Join(aR), alpha), append(c.G.Join(c.H), c.h))
log.Debugf("vs: %v", vs)
Sl := make(core.ScalarVector, c.max)
Sr := make(core.ScalarVector, c.max)
for i := 0; i < c.max; i++ {
Sl[i] = prng.Next()
Sr[i] = prng.Next()
}
p := prng.Next()
S := core.MultiExp(append(Sl.Join(Sr), p), append(c.G.Join(c.H), c.h))
transcript.AddElementToTranscript("A", A)
transcript.AddElementToTranscript("S", S)
y := transcript.CommitToTranscriptScalar("y")
z := transcript.CommitToTranscriptScalar("z")
y_n := core.PowerVector(y, c.max)
z2 := new(ristretto.Scalar).Multiply(z, z)
l0 := core.VectorAddScalar(aL, new(ristretto.Scalar).Negate(z))
//l1 == Sr
r0 := core.EntrywiseSum(core.EntryWiseProduct(y_n, core.VectorAddScalar(aR, z)), core.VectorMulScalar(two_n, z2))
r1 := core.EntryWiseProduct(Sr, y_n)
t0 := new(ristretto.Scalar).Add(new(ristretto.Scalar).Multiply(z2, vs), delta(y_n, z, c.max))
t1 := new(ristretto.Scalar)
t1.Add(core.InnerProduct(Sl, r0), core.InnerProduct(l0, r1))
t2 := core.InnerProduct(Sl, r1)
tau1 := prng.Next()
tau2 := prng.Next()
T1 := core.MultiExp(core.ScalarVector{t1, tau1}, core.GeneratorVector{c.g, c.h})
T2 := core.MultiExp(core.ScalarVector{t2, tau2}, core.GeneratorVector{c.g, c.h})
transcript.AddElementToTranscript("T_1", T1)
transcript.AddElementToTranscript("T_2", T2)
x := transcript.CommitToTranscriptScalar("x")
// T(X) = t0 + t1x + t2x
TX := new(ristretto.Scalar)
TX.Add(new(ristretto.Scalar).Zero(), t0)
TX.Add(TX, new(ristretto.Scalar).Multiply(t1, x))
TX.Add(TX, new(ristretto.Scalar).Multiply(t2, new(ristretto.Scalar).Multiply(x, x)))
l := core.EntrywiseSum(core.VectorAddScalar(aL, new(ristretto.Scalar).Negate(z)), core.VectorMulScalar(Sl, x))
_r := core.EntrywiseSum(core.VectorAddScalar(aR, z), core.VectorMulScalar(Sr, x))
r := core.EntrywiseSum(core.EntryWiseProduct(y_n, _r), core.VectorMulScalar(two_n, z2))
iplr := core.InnerProduct(l, r)
log.Debugf("T(X) = %v", TX)
log.Debugf("ipp = %v", iplr)
log.Debugf("equal: %v", TX.Equal(iplr) == 1)
// generate h'
H_ := make(core.GeneratorVector, c.max)
H_[0] = c.H[0]
for i := 1; i < c.max; i++ {
H_[i] = new(ristretto.Element).ScalarMult(new(ristretto.Scalar).Invert(y_n[i]), c.H[i])
}
P := core.MultiExp(l.Join(r), c.G.Join(H_))
log.Debugf("P: %v", P)
mu := new(ristretto.Scalar)
mu.Add(alpha, new(ristretto.Scalar).Multiply(p, x))
taux := new(ristretto.Scalar)
taux.Multiply(tau2, new(ristretto.Scalar).Multiply(x, x))
taux.Add(taux, new(ristretto.Scalar).Multiply(tau1, x))
taux.Add(taux, new(ristretto.Scalar).Multiply(z2, gamma))
transcript.AddToTranscript("t_x", iplr.Encode([]byte{}))
transcript.AddToTranscript("t_x_blinding", taux.Encode([]byte{}))
transcript.AddToTranscript("e_blinding", mu.Encode([]byte{}))
w := transcript.CommitToTranscriptScalar("w")
U := new(ristretto.Element).ScalarMult(w, c.g)
uP := new(ristretto.Element).Add(P, new(ristretto.Element).ScalarMult(iplr, U))
log.Debugf("uP: %v", uP)
ipp := ProveInnerProduct(l, r, U, new(ristretto.Element).Add(new(ristretto.Element).Zero(), uP), core.CopyVector(c.G), core.CopyVector(H_), transcript)
return RangeProof{A, S, T1, T2, taux, iplr, mu, ipp, []*ristretto.Element{V}}, gamma
}
// VerifyRangeProof returns true if the given proof passes all the checks for a given set of commitment parameters
// and the given transcript
func VerifyRangeProof(proof RangeProof, c CommitmentsParams, transcript *core.Transcript) bool {
one := core.IdentityVector(1)[0]
two := new(ristretto.Scalar)
two.Add(one, one)
two_n := core.PowerVector(two, c.max)
transcript.AddToTranscript("dom-sep", []byte("rangeproof v1"))
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, uint64(c.max))
transcript.AddToTranscript("n", b)
b = make([]byte, 8)
binary.LittleEndian.PutUint64(b, 1)
transcript.AddToTranscript("m", b)
for _, v := range proof.V {
transcript.AddElementToTranscript("V", v)
}
transcript.AddElementToTranscript("A", proof.A)
transcript.AddElementToTranscript("S", proof.S)
y := transcript.CommitToTranscriptScalar("y")
z := transcript.CommitToTranscriptScalar("z")
transcript.AddElementToTranscript("T_1", proof.T1)
transcript.AddElementToTranscript("T_2", proof.T2)
x := transcript.CommitToTranscriptScalar("x")
transcript.AddToTranscript("t_x", proof.InnerProduct.Encode([]byte{}))
transcript.AddToTranscript("t_x_blinding", proof.TauX.Encode([]byte{}))
transcript.AddToTranscript("e_blinding", proof.Mu.Encode([]byte{}))
log.Debugf("mu: %x", proof.Mu.Encode([]byte{}))
log.Debugf("x: %x", x.Encode([]byte{}))
y_n := core.PowerVector(y, c.max)
// generate h'
H_ := make(core.GeneratorVector, c.max)
H_[0] = c.H[0]
for i := 1; i < c.max; i++ {
H_[i] = new(ristretto.Element).ScalarMult(new(ristretto.Scalar).Invert(y_n[i]), c.H[i])
}
// check t = t(x) = t0 + t1.x + t1.x^2
lhs := core.MultiExp(core.ScalarVector{proof.InnerProduct, proof.TauX}, core.GeneratorVector{c.g, c.h})
z2 := new(ristretto.Scalar)
z2.Multiply(z, z)
x2 := new(ristretto.Scalar)
x2.Multiply(x, x)
rhs := core.MultiExp(core.ScalarVector{z2, delta(y_n, z, c.max), x, x2}, core.GeneratorVector(proof.V).Join(core.GeneratorVector{c.g, proof.T1, proof.T2}))
log.Debugf("lhs: %v", lhs)
log.Debugf("rhs: %v", rhs)
log.Debugf("equal: %v", lhs.Equal(rhs))
if lhs.Equal(rhs) == 1 {
// compute P
negz := new(ristretto.Scalar).Negate(z)
negzG := new(ristretto.Element).Zero()
for _, gen := range c.G {
negzG = negzG.Add(negzG, new(ristretto.Element).ScalarMult(negz, gen))
}
mul := core.EntrywiseSum(core.VectorMulScalar(y_n, z), core.VectorMulScalar(two_n, new(ristretto.Scalar).Multiply(z, z)))
Pr := new(ristretto.Element).Add(new(ristretto.Element).Zero(), proof.A)
Pr = Pr.Add(Pr, new(ristretto.Element).ScalarMult(x, proof.S))
Pr = Pr.Add(Pr, negzG)
Pr = Pr.Add(Pr, core.MultiExp(mul, H_))
Pl := new(ristretto.Element).Subtract(Pr, new(ristretto.Element).ScalarMult(proof.Mu, c.h))
// check inner product
w := transcript.CommitToTranscriptScalar("w")
U := new(ristretto.Element).ScalarMult(w, c.g)
uP := new(ristretto.Element).Add(Pl, new(ristretto.Element).ScalarMult(proof.InnerProduct, U))
log.Debugf("P: %v", Pl)
log.Debugf("uP: %v", uP)
return Verify(proof.IPP, c.max, U, new(ristretto.Element).Add(new(ristretto.Element).Zero(), uP), core.CopyVector(c.G), core.CopyVector(H_), transcript)
}
return false
}
func delta(y_n core.ScalarVector, z *ristretto.Scalar, max int) *ristretto.Scalar {
one := core.IdentityVector(1)[0]
// (z-z^2)
z2 := new(ristretto.Scalar).Multiply(z, z)
result := new(ristretto.Scalar)
result.Subtract(z, z2)
// (z-z^2) * <1^n,y^n>
result.Multiply(result, core.InnerProduct(core.IdentityVector(max), y_n))
two := new(ristretto.Scalar)
two.Add(one, one)
two_n := core.PowerVector(two, max)
// (z-z^2) * <1^n,y^n> - z^3 *<1n,2n>
z3 := new(ristretto.Scalar).Multiply(z2, z)
result.Subtract(result, new(ristretto.Scalar).Multiply(z3, core.InnerProduct(core.IdentityVector(max), two_n)))
return result
}
func valueToVector(value int32, max int) core.ScalarVector {
one := core.IdentityVector(1)[0]
zero := new(ristretto.Scalar)
zero.Zero()
result := core.ScalarVector{}
for len(result) != max {
v := value & 0x0001
if v == 1 {
result = append(result, one)
} else {
result = append(result, zero)
}
value = value >> 1
}
return result
}

View File

@ -0,0 +1,150 @@
package bulletproofs
import (
"cwtch.im/tapir/primitives/core"
"encoding/hex"
"encoding/json"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
"github.com/gtank/ristretto255"
"golang.org/x/crypto/sha3"
"testing"
)
func TestProove(t *testing.T) {
log.SetLevel(log.LevelDebug)
proverTranscript := core.NewTranscript("rangeproof")
verifierTranscript := core.NewTranscript("rangeproof")
rangeproof, _ := GenerateRangeProof(10, Setup(32, proverTranscript), proverTranscript)
if VerifyRangeProof(rangeproof, Setup(32, verifierTranscript), verifierTranscript) == true {
t.Logf("Range Proof Passed!")
t.Logf("%v\n\n%v\n", proverTranscript.OutputTranscriptToAudit(), verifierTranscript.OutputTranscriptToAudit())
jsonProof, _ := json.Marshal(rangeproof)
t.Logf("RangeProof: %s", jsonProof)
} else {
t.Logf("%v\n\n%v\n", proverTranscript.OutputTranscriptToAudit(), verifierTranscript.OutputTranscriptToAudit())
t.Fatalf("Failed to Verify Range Proof")
}
}
func byteToPoint(in []byte) *ristretto255.Element {
element := ristretto255.NewElement()
element.Decode(in)
return element
}
func byteToScalar(in []byte) *ristretto255.Scalar {
scalar := ristretto255.NewScalar()
scalar.Decode(in[0:32])
return scalar
}
func decodeInnerProduct(in []byte) *InnerProductProof {
num_elements := len(in) / 32
lg_n := (num_elements - 2) / 2
lvec := make(core.PointVector, lg_n)
rvec := make(core.PointVector, lg_n)
for i := 0; i < lg_n; i++ {
pos := 2 * i * 32
lvec[i] = byteToPoint(in[pos : pos+32])
rvec[i] = byteToPoint(in[pos+32 : pos+64])
}
pos := 2 * lg_n * 32
a := byteToScalar(in[pos : pos+32])
b := byteToScalar(in[pos+32 : pos+64])
return &InnerProductProof{lvec, rvec, a, b}
}
func TestDalek(t *testing.T) {
log.SetLevel(log.LevelDebug)
t.Logf("Testing dalek-cryptography bulletproofs test vector...")
rp, _ := hex.DecodeString("46b6ea8b6a9710c41c2622d4b353dbcf5f89afe8ed66c469f192bec19dc71d23c0442827f97fc9085a89caa87d294b0a21e7b8957732ec4951f6bf7d3aa2c66e7af3b7b956c7dcb3bed1223575a217a30642b603b6bf1d4138ed95e3458c524510b42c8d82958f40b447a84242b1ba1eeea54013f80bad643048eeb0b17c292a057cb6ae1c42338837c05eaa6336a17d60fa141204e015a1df15b28c1318c709d7eb35569cde89c0bf37eace54880a151498b38da54c6d739564f46f01b73601e518355ea06c9ef58a45fcb3baadbd1ac54e0838c471a6b91845f123d569fa0c46ef94471b7b826230e8576146beec08ac3e6683998815c576581f4c0e493433480f95f6495210636eaa2e32b577e1c363e35e522db85b18a56d57eb626f9e2b50578e0d7ee7b74b328e158b366bb9d117db725820a2fec3b1508212d75823345a801c0b602bfa05919d7e3bb8e71944587072badc363f334b08ba90d13e077ad24b82bacd51fc668d2b880daabd3b87e6bdc9584af66523026a30aadfc359283891bb65cca502f47421ffeee1fb5a5237bfa965b66a8b8ca5d6954f4f8222244c6a5340dc81e8d781d092cae2a763f185dd0b89965b1dd2506807b5d3e5a305fd9a68e60b91389dcffae6f85538713aa7ed272b8174e2f0b9730ebb6c464d06")
t.Logf("Deserializing dalek-cryptography bulletproofs test vector...%x", rp)
A := byteToPoint(rp[0:32])
S := byteToPoint(rp[32:64])
T1 := byteToPoint(rp[64:96])
T2 := byteToPoint(rp[96:128])
TX := byteToScalar(rp[128:160])
TX_blinding := byteToScalar(rp[160:192])
micro := byteToScalar(rp[192 : 192+32])
ipp := decodeInnerProduct(rp[192+32:])
vbytes := []string{
"90b0c2fe57934dff9f5396e135e7d72b82b3c5393e1843178918eb2cf28a5f3c",
"74256a3e2a7fe948210c4095195ae4db3e3498c6c5fddc2afb226c0f1e97e468",
"7e348def6d03dc7bcbe7e03736ca2898e2efa9f6ff8ae4ed1cb5252ec1744075",
"861859f5d4c14f5d6d7ad88dcf43c9a98064a7d8702ffc9bad9eba2ed766702a",
"4c09b1260c833fefe25b1c3d3becc80979beca5e864d57fcb410bb15c7ba5c14",
"08cf26bfdf2e6b731536f5e48b4c0ac7b5fc846d36aaa3fe0d28f07c207f0814",
"a6e2d1c2770333c9a8a5ac10d9eb28e8609d5954428261335b2fd6ff0e0e8d69",
"30beef3b58fd2c18dde771d5c77e32f8dc01361e284aef517bce54a5c74c4665",
}
V := make([]*ristretto255.Element, len(vbytes))
for i, v := range vbytes {
V[i] = ristretto255.NewElement()
vdec, _ := hex.DecodeString(v)
V[i].Decode(vdec)
}
rangeProof := RangeProof{A, S, T1, T2, TX_blinding, TX, micro, *ipp, V[0:1]}
json, _ := json.Marshal(rangeProof)
t.Logf("RangeProof: %s", json)
t.Logf("Deserialized Range Proof: %s", json)
t.Logf("Generating dalek-cryptography pedersen generators....")
params := CommitmentsParams{}
params.g = ristretto255.NewElement().Base()
params.h = ristretto255.NewElement()
h := sha3.Sum512(params.g.Encode([]byte{}))
params.h = ristretto255.NewElement().FromUniformBytes(h[:])
params.max = 8
params.G = make(core.GeneratorVector, params.max)
params.H = make(core.GeneratorVector, params.max)
labelG := []byte{'G', 0, 0, 0, 0}
shake := sha3.NewShake256()
shake.Write([]byte("GeneratorsChain"))
shake.Write(labelG[:])
labelH := []byte{'H', 0, 0, 0, 0}
shakeH := sha3.NewShake256()
shakeH.Write([]byte("GeneratorsChain"))
shakeH.Write(labelH[:])
t.Logf("Generating dalek-cryptography BP generators....")
for i := 0; i < 8; i++ {
b := make([]byte, 64)
shake.Read(b)
params.G[i] = ristretto255.NewElement()
params.G[i].FromUniformBytes(b)
//t.Logf("G: %x", params.G[i].Encode([]byte{}))
bH := make([]byte, 64)
shakeH.Read(bH)
params.H[i] = ristretto255.NewElement()
params.H[i].FromUniformBytes(bH)
// t.Logf("H: %x", params.H[i].Encode([]byte{}))
}
//t.Logf("parmas: %v", params)
verifierTranscript := core.NewTranscript("Deserialize-And-Verify Test")
t.Logf("Verification Result: %v", VerifyRangeProof(rangeProof, params, verifierTranscript))
t.Logf("Transcript: %s\n", verifierTranscript.OutputTranscriptToAudit())
}

View File

@ -0,0 +1,186 @@
package core
import (
"fmt"
ristretto "github.com/gtank/ristretto255"
)
// ScalarVector explicit type checking
type ScalarVector []*ristretto.Scalar
// ElementVector 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
}
// Join 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
}
// Join 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
}
// EntrywiseSubtract takes the entry wise sum 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
}

View File

@ -15,6 +15,8 @@ import (
// - be able to produce a human-readable transcript for auditing.
//
// The design of this API was inspired by Merlin: https://docs.rs/crate/merlin/
//
// At some point we might want to extend this to be compatible with Merlin transcripts, built on STROBE
type Transcript struct {
merlinTranscript *merlin.Transcript
transcript string
@ -56,8 +58,8 @@ func (t *Transcript) NewProtocol(label string) {
// CommitToTranscript generates a challenge based on the current transcript, it also commits the challenge to the transcript.
func (t *Transcript) CommitToTranscript(label string) []byte {
//t.AddToTranscript("commit", []byte(label))
b := t.merlinTranscript.ExtractBytes([]byte(label), 64)
t.transcript = fmt.Sprintf("%v\nextract %v: %v", t.transcript, label, b)
return b
}

View File

@ -1,9 +1,8 @@
package privacypass
import (
"crypto/rand"
"cwtch.im/tapir/primitives/core"
ristretto "github.com/gtank/ristretto255"
"github.com/bwesterb/go-ristretto"
)
// DLEQProof encapsulates a Chaum-Pedersen DLEQ Proof
@ -14,7 +13,7 @@ type DLEQProof struct {
}
// DiscreteLogEquivalenceProof constructs a valid DLEQProof for the given parameters and transcript
// Given Y = kX & Q = kP
// Given P = kX, Q = kP, Y=kX
// Peggy: t := choose randomly from Zq
// A := tX
// B := tP
@ -22,52 +21,49 @@ type DLEQProof struct {
// s := (t + ck) mod q
//
// Sends c,s to Vicky
func DiscreteLogEquivalenceProof(k *ristretto.Scalar, X *ristretto.Element, Y *ristretto.Element, P *ristretto.Element, Q *ristretto.Element, transcript *core.Transcript) DLEQProof {
private := make([]byte, 64)
rand.Read(private)
t := new(ristretto.Scalar)
t.FromUniformBytes(private)
A := new(ristretto.Element).ScalarMult(t, X)
B := new(ristretto.Element).ScalarMult(t, P)
func DiscreteLogEquivalenceProof(k *ristretto.Scalar, X *ristretto.Point, Y *ristretto.Point, P *ristretto.Point, Q *ristretto.Point, transcript *core.Transcript) DLEQProof {
t := new(ristretto.Scalar).Rand()
A := new(ristretto.Point).ScalarMult(X, t)
B := new(ristretto.Point).ScalarMult(P, t)
transcript.AddToTranscript(DLEQX, X.Encode(nil))
transcript.AddToTranscript(DLEQY, Y.Encode(nil))
transcript.AddToTranscript(DLEQP, P.Encode(nil))
transcript.AddToTranscript(DLEQQ, Q.Encode(nil))
transcript.AddToTranscript(DLEQA, A.Encode(nil))
transcript.AddToTranscript(DLEQB, B.Encode(nil))
transcript.AddToTranscript(DLEQX, X.Bytes())
transcript.AddToTranscript(DLEQY, Y.Bytes())
transcript.AddToTranscript(DLEQP, P.Bytes())
transcript.AddToTranscript(DLEQQ, Q.Bytes())
transcript.AddToTranscript(DLEQA, A.Bytes())
transcript.AddToTranscript(DLEQB, B.Bytes())
c := transcript.CommitToTranscriptScalar("c")
s := new(ristretto.Scalar).Subtract(t, new(ristretto.Scalar).Multiply(c, k))
s := new(ristretto.Scalar).Sub(t, new(ristretto.Scalar).Mul(c, k))
return DLEQProof{c, s}
}
// VerifyDiscreteLogEquivalenceProof verifies the DLEQ for the given parameters and transcript
// Given Y = kX & Q = kP and Proof = (c,s)
// Given P = kX, Q = kP, Y=kX, and Proof = (c,s)
// Vicky: X' := sX
// Y' := cY
// P' := sP
// Q' := cQ
// A' = X'+Y' == sX + cY ?= sG + ckG == (s+ck)X == tX == A
// A' = X'+Y' == sX + cY ?= sX + ckX == (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
func VerifyDiscreteLogEquivalenceProof(dleq DLEQProof, X *ristretto.Element, Y *ristretto.Element, P *ristretto.Element, Q *ristretto.Element, transcript *core.Transcript) bool {
func VerifyDiscreteLogEquivalenceProof(dleq DLEQProof, X *ristretto.Point, Y *ristretto.Point, P *ristretto.Point, Q *ristretto.Point, transcript *core.Transcript) bool {
Xs := new(ristretto.Element).ScalarMult(dleq.S, X)
Yc := new(ristretto.Element).ScalarMult(dleq.C, Y)
Ps := new(ristretto.Element).ScalarMult(dleq.S, P)
Qc := new(ristretto.Element).ScalarMult(dleq.C, Q)
Xs := new(ristretto.Point).ScalarMult(X, dleq.S)
Yc := new(ristretto.Point).ScalarMult(Y, dleq.C)
Ps := new(ristretto.Point).ScalarMult(P, dleq.S)
Qc := new(ristretto.Point).ScalarMult(Q, dleq.C)
A := new(ristretto.Element).Add(Xs, Yc)
B := new(ristretto.Element).Add(Ps, Qc)
A := new(ristretto.Point).Add(Xs, Yc)
B := new(ristretto.Point).Add(Ps, Qc)
transcript.AddToTranscript(DLEQX, X.Encode(nil))
transcript.AddToTranscript(DLEQY, Y.Encode(nil))
transcript.AddToTranscript(DLEQP, P.Encode(nil))
transcript.AddToTranscript(DLEQQ, Q.Encode(nil))
transcript.AddToTranscript(DLEQA, A.Encode(nil))
transcript.AddToTranscript(DLEQB, B.Encode(nil))
transcript.AddToTranscript(DLEQX, X.Bytes())
transcript.AddToTranscript(DLEQY, Y.Bytes())
transcript.AddToTranscript(DLEQP, P.Bytes())
transcript.AddToTranscript(DLEQQ, Q.Bytes())
transcript.AddToTranscript(DLEQA, A.Bytes())
transcript.AddToTranscript(DLEQB, B.Bytes())
return transcript.CommitToTranscriptScalar("c").Equal(dleq.C) == 1
return transcript.CommitToTranscriptScalar("c").Equals(dleq.C)
}

View File

@ -6,8 +6,7 @@ import (
"cwtch.im/tapir/primitives/core"
"fmt"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
ristretto "github.com/gtank/ristretto255"
"github.com/bwesterb/go-ristretto"
"golang.org/x/crypto/sha3"
)
@ -16,22 +15,17 @@ import (
type Token struct {
t []byte
r *ristretto.Scalar
W *ristretto.Element
}
// GetT returns the underlying bytes for token for use in constraint proofs.
func (t Token) GetT() []byte {
return t.t
W *ristretto.Point
}
// BlindedToken encapsulates a Blinded Token
type BlindedToken struct {
P *ristretto.Element
P *ristretto.Point
}
// SignedToken encapsulates a Signed (Blinded) Token
type SignedToken struct {
Q *ristretto.Element
Q *ristretto.Point
}
// SpentToken encapsulates the parameters needed to spend a Token
@ -43,8 +37,7 @@ type SpentToken struct {
// TokenPaymentHandler defines an interface with external payment processors
type TokenPaymentHandler interface {
MakePayment()
// Next Token
NextToken(data []byte, hostname string) (SpentToken, error)
NextToken(data []byte) (SpentToken, error)
}
// GenBlindedToken initializes the Token
@ -52,25 +45,23 @@ type TokenPaymentHandler interface {
func (t *Token) GenBlindedToken() BlindedToken {
t.t = make([]byte, 32)
rand.Read(t.t)
t.r = new(ristretto.Scalar)
b := make([]byte, 64)
rand.Read(b)
t.r.FromUniformBytes(b)
t.r = new(ristretto.Scalar).Rand()
Ht := sha3.Sum512(t.t)
T := new(ristretto.Element).FromUniformBytes(Ht[:])
P := new(ristretto.Element).ScalarMult(t.r, T)
Ht := sha3.Sum256(t.t)
log.Debugf("token: %x", Ht)
T := new(ristretto.Point).SetElligator(&Ht)
P := new(ristretto.Point).ScalarMult(T, t.r)
return BlindedToken{P}
}
// unblindSignedToken unblinds a token that has been signed by a server
func (t *Token) unblindSignedToken(token SignedToken) {
t.W = new(ristretto.Element).ScalarMult(new(ristretto.Scalar).Invert(t.r), token.Q)
t.W = new(ristretto.Point).ScalarMult(token.Q, new(ristretto.Scalar).Inverse(t.r))
}
// SpendToken binds the token with data and then redeems the token
func (t *Token) SpendToken(data []byte) SpentToken {
key := sha3.Sum256(append(t.t, t.W.Encode(nil)...))
key := sha3.Sum256(append(t.t, t.W.Bytes()...))
mac := hmac.New(sha3.New512, key[:])
return SpentToken{t.t, mac.Sum(data)}
}
@ -85,29 +76,28 @@ func GenerateBlindedTokenBatch(num int) (tokens []*Token, blindedTokens []Blinde
}
// verifyBatchProof verifies a given batch proof (see also UnblindSignedTokenBatch)
func verifyBatchProof(dleq DLEQProof, Y *ristretto.Element, blindedTokens []BlindedToken, signedTokens []SignedToken, transcript *core.Transcript) bool {
func verifyBatchProof(dleq DLEQProof, Y *ristretto.Point, blindedTokens []BlindedToken, signedTokens []SignedToken, transcript *core.Transcript) bool {
transcript.NewProtocol(BatchProofProtocol)
transcript.AddToTranscript(BatchProofX, new(ristretto.Element).Base().Encode(nil))
transcript.AddToTranscript(BatchProofY, Y.Encode(nil))
transcript.AddToTranscript(BatchProofX, new(ristretto.Point).SetBase().Bytes())
transcript.AddToTranscript(BatchProofY, Y.Bytes())
transcript.AddToTranscript(BatchProofPVector, []byte(fmt.Sprintf("%v", blindedTokens)))
transcript.AddToTranscript(BatchProofQVector, []byte(fmt.Sprintf("%v", signedTokens)))
prng := transcript.CommitToPRNG("w")
M := new(ristretto.Element).Zero()
Z := new(ristretto.Element).Zero()
M := new(ristretto.Point).SetZero()
Z := new(ristretto.Point).SetZero()
for i := range blindedTokens {
c := prng.Next()
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)
M = new(ristretto.Point).Add(new(ristretto.Point).ScalarMult(blindedTokens[i].P, c), M)
Z = new(ristretto.Point).Add(new(ristretto.Point).ScalarMult(signedTokens[i].Q, c), Z)
}
return VerifyDiscreteLogEquivalenceProof(dleq, new(ristretto.Element).Base(), Y, M, Z, transcript)
return VerifyDiscreteLogEquivalenceProof(dleq, new(ristretto.Point).SetBase(), Y, M, Z, transcript)
}
// UnblindSignedTokenBatch taking in a set of tokens, their blinded & signed counterparts, a server public key (Y), a DLEQ proof and a transcript
// verifies that the signing procedure has taken place correctly and unblinds the tokens.
func UnblindSignedTokenBatch(tokens []*Token, blindedTokens []BlindedToken, signedTokens []SignedToken, Y *ristretto.Element, proof DLEQProof, transcript *core.Transcript) bool {
func UnblindSignedTokenBatch(tokens []*Token, blindedTokens []BlindedToken, signedTokens []SignedToken, Y *ristretto.Point, proof DLEQProof, transcript *core.Transcript) bool {
verified := verifyBatchProof(proof, Y, blindedTokens, signedTokens, transcript)
if !verified {
log.Debugf("Failed to unblind tokens: %v", transcript.OutputTranscriptToAudit())
return false
}
for i, t := range tokens {

View File

@ -4,8 +4,6 @@ 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"
)
@ -33,41 +31,10 @@ 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.t, 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.
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")
defer db.Close()
server := NewTokenServerFromStore(db)
clientTranscript := core.NewTranscript("privacyPass")
@ -98,4 +65,5 @@ func TestGenerateBlindedTokenBatch(t *testing.T) {
if verified {
t.Errorf("Something went wrong, the proof passed with wrong transcript: %s", wrongTranscript.OutputTranscriptToAudit())
}
db.Close()
}

View File

@ -2,12 +2,11 @@ package privacypass
import (
"crypto/hmac"
"crypto/rand"
"cwtch.im/tapir/persistence"
"cwtch.im/tapir/primitives/core"
"encoding/hex"
"fmt"
ristretto "github.com/gtank/ristretto255"
"github.com/bwesterb/go-ristretto"
"golang.org/x/crypto/sha3"
"sync"
)
@ -15,7 +14,7 @@ import (
// TokenServer implements a token server.
type TokenServer struct {
k *ristretto.Scalar
Y *ristretto.Element
Y *ristretto.Point
seen map[string]bool
persistanceService persistence.Service
mutex sync.Mutex
@ -23,53 +22,28 @@ type TokenServer struct {
// SignedBatchWithProof encapsulates a signed batch of blinded tokens with a batch proof for verification
type SignedBatchWithProof struct {
SignedTokens []SignedToken `json:"st"`
Proof DLEQProof `json:"dp"`
SignedTokens []SignedToken
Proof DLEQProof
}
const tokenBucket = "tokens"
const keyBucket = "keys"
// NewTokenServer generates a new TokenServer (used mostly for testing with ephemeral instances)
func NewTokenServer() *TokenServer {
k := new(ristretto.Scalar)
b := make([]byte, 64)
_, 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{}}
func NewTokenServer() TokenServer {
k := new(ristretto.Scalar).Rand()
return TokenServer{k, new(ristretto.Point).ScalarMultBase(k), make(map[string]bool), nil, sync.Mutex{}}
}
// NewTokenServerFromStore generates a new TokenServer backed by a persistence service.
func NewTokenServerFromStore(persistenceService persistence.Service) *TokenServer {
tokenServer := NewTokenServer()
func NewTokenServerFromStore(persistenceService persistence.Service) TokenServer {
k := new(ristretto.Scalar).Rand()
persistenceService.Setup([]string{tokenBucket})
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
return TokenServer{k, new(ristretto.Point).ScalarMultBase(k), make(map[string]bool), persistenceService, sync.Mutex{}}
}
// SignBlindedToken calculates kP for the given BlindedToken P
func (ts *TokenServer) SignBlindedToken(bt BlindedToken) SignedToken {
Q := new(ristretto.Element).ScalarMult(ts.k, bt.P)
Q := new(ristretto.Point).ScalarMult(bt.P, ts.k)
return SignedToken{Q}
}
@ -82,39 +56,24 @@ 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 constraint that the tokens must be signed
// by the same public key as an existing token
func (ts *TokenServer) SignBlindedTokenBatchWithConstraint(blindedTokens []BlindedToken, constraintToken []byte, transcript *core.Transcript) SignedBatchWithProof {
var signedTokens []SignedToken
for _, bt := range blindedTokens {
signedTokens = append(signedTokens, ts.SignBlindedToken(bt))
}
Ht := sha3.Sum512(constraintToken)
T := new(ristretto.Element).FromUniformBytes(Ht[:])
// W == kT
W := new(ristretto.Element).ScalarMult(ts.k, T)
blindedTokens = append(blindedTokens, BlindedToken{P: T})
return SignedBatchWithProof{signedTokens, ts.constructBatchProof(blindedTokens, append(signedTokens, SignedToken{Q: 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)
transcript.AddToTranscript(BatchProofX, new(ristretto.Element).Base().Encode(nil))
transcript.AddToTranscript(BatchProofY, ts.Y.Encode(nil))
transcript.AddToTranscript(BatchProofX, new(ristretto.Point).SetBase().Bytes())
transcript.AddToTranscript(BatchProofY, ts.Y.Bytes())
transcript.AddToTranscript(BatchProofPVector, []byte(fmt.Sprintf("%v", blindedTokens)))
transcript.AddToTranscript(BatchProofQVector, []byte(fmt.Sprintf("%v", signedTokens)))
prng := transcript.CommitToPRNG("w")
M := new(ristretto.Element).Zero()
Z := new(ristretto.Element).Zero()
M := new(ristretto.Point).SetZero()
Z := new(ristretto.Point).SetZero()
for i := range blindedTokens {
c := prng.Next()
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)
M = new(ristretto.Point).Add(new(ristretto.Point).ScalarMult(blindedTokens[i].P, c), M)
Z = new(ristretto.Point).Add(new(ristretto.Point).ScalarMult(signedTokens[i].Q, c), Z)
}
return DiscreteLogEquivalenceProof(ts.k, new(ristretto.Element).Base(), ts.Y, M, Z, transcript)
return DiscreteLogEquivalenceProof(ts.k, new(ristretto.Point).SetBase(), ts.Y, M, Z, transcript)
}
// SpendToken returns true a SpentToken is valid and has never been spent before, false otherwise.
@ -131,13 +90,13 @@ func (ts *TokenServer) SpendToken(token SpentToken, data []byte) error {
return fmt.Errorf("token: %v has already been spent", token)
}
}
Ht := sha3.Sum512(token.T)
T := new(ristretto.Element).FromUniformBytes(Ht[:])
W := new(ristretto.Element).ScalarMult(ts.k, T)
key := sha3.Sum256(append(token.T, W.Encode(nil)...))
Ht := sha3.Sum256(token.T)
T := new(ristretto.Point).SetElligator(&Ht)
W := new(ristretto.Point).ScalarMult(T, ts.k)
key := sha3.Sum256(append(token.T, W.Bytes()...))
mac := hmac.New(sha3.New512, key[:])
computedMAC := mac.Sum(data)
result := hmac.Equal(token.MAC, computedMAC)
K := mac.Sum(data)
result := hmac.Equal(token.MAC, K)
if result == true {
if ts.persistanceService == nil {
ts.seen[hex.EncodeToString(token.T)] = true

View File

@ -9,6 +9,7 @@ import (
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/nacl/secretbox"
"io"
"net"
"sync"
)
@ -42,7 +43,7 @@ type Connection interface {
// Connection defines a Tapir Connection
type connection struct {
hostname string
conn io.ReadWriteCloser
conn net.Conn
capabilities sync.Map
encrypted bool
key [32]byte
@ -51,11 +52,10 @@ type connection struct {
outbound bool
closed bool
MaxLength int
lock sync.Mutex
}
// NewConnection creates a new Connection
func NewConnection(id *primitives.Identity, hostname string, outbound bool, conn io.ReadWriteCloser, app Application) Connection {
func NewConnection(id *primitives.Identity, hostname string, outbound bool, conn net.Conn, app Application) Connection {
connection := new(connection)
connection.hostname = hostname
connection.conn = conn
@ -74,23 +74,17 @@ 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
}
@ -102,15 +96,11 @@ 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
}
@ -129,8 +119,6 @@ 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()
}
@ -146,8 +134,7 @@ 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])
@ -161,18 +148,13 @@ func (c *connection) Expect() []byte {
return []byte{}
}
}
length, _ := binary.Uvarint(buffer[0:2])
if length+2 >= uint64(c.MaxLength) {
return []byte{}
}
len, _ := binary.Uvarint(buffer[0:2])
//cplog.Debugf("[%v -> %v] Wire Receive: (%d) %x", c.hostname, c.ID.Hostname(), len, buffer)
return buffer[2 : length+2]
return buffer[2 : len+2]
}
// 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
}
@ -184,8 +166,6 @@ 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 {

View File

@ -2,12 +2,12 @@
set -e
pwd
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 ${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 -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