forked from cwtch.im/tapir
Compare commits
13 Commits
bulletproo
...
master
Author | SHA1 | Date |
---|---|---|
Dan Ballard | a3262d3478 | |
Dan Ballard | 65697b1efb | |
Dan Ballard | 883a5424c6 | |
Sarah Jamie Lewis | b2975e7224 | |
erinn | 89ba421cd9 | |
Sarah Jamie Lewis | c5e3837a5d | |
Sarah Jamie Lewis | bcaeb969e4 | |
Sarah Jamie Lewis | dcf0635f5f | |
Sarah Jamie Lewis | a0e58b8736 | |
Sarah Jamie Lewis | a19204caf4 | |
Sarah Jamie Lewis | ff7a32722d | |
Sarah Jamie Lewis | 345d11f506 | |
Sarah Jamie Lewis | 5b64c2d708 |
23
.drone.yml
23
.drone.yml
|
@ -4,6 +4,10 @@ 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
|
||||
|
@ -13,21 +17,33 @@ 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 -v cwtch.im/tapir/testing
|
||||
- go test -race -v cwtch.im/tapir/testing
|
||||
notify-email:
|
||||
image: drillster/drone-email
|
||||
host: build.openprivacy.ca
|
||||
|
@ -35,10 +51,15 @@ 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]
|
||||
|
|
|
@ -5,3 +5,5 @@ coverage.out
|
|||
/testing/tor/
|
||||
/applications/tor/
|
||||
*.db
|
||||
/applications/tokenboard/tor/
|
||||
fuzzing/
|
||||
|
|
|
@ -63,7 +63,7 @@ func (ea *AuthApp) Init(connection tapir.Connection) {
|
|||
challengeRemote, _ := json.Marshal(remoteAuthMessage)
|
||||
challengeLocal, _ := json.Marshal(authMessage)
|
||||
|
||||
// Define canonical labels so both sides of the
|
||||
// Define canonical labels so both sides of the connection can generate the same key
|
||||
var outboundAuthMessage []byte
|
||||
var outboundHostname string
|
||||
var inboundAuthMessage []byte
|
||||
|
|
|
@ -68,7 +68,7 @@ func (powapp *ProofOfWorkApplication) solveChallenge(challenge []byte, prng core
|
|||
solve := make([]byte, len(challenge)+32)
|
||||
for !solved {
|
||||
|
||||
solution = prng.Next().Bytes()
|
||||
solution = prng.Next().Encode(nil)
|
||||
|
||||
copy(solve[0:], solution[:])
|
||||
copy(solve[len(solution):], challenge[:])
|
||||
|
|
|
@ -16,41 +16,49 @@ 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 (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")
|
||||
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)
|
||||
tokens, blinded := privacypass.GenerateBlindedTokenBatch(numTokens)
|
||||
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, 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())
|
||||
data, _ := json.Marshal(batchProof)
|
||||
connection.Send(data)
|
||||
return
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,6 @@ func (ta *Client) Listen() {
|
|||
|
||||
var message Message
|
||||
json.Unmarshal(data, &message)
|
||||
log.Debugf("Received a Message: %v", message)
|
||||
switch message.MessageType {
|
||||
case postResultMessage:
|
||||
log.Debugf("Post result: %x", message.PostResult.Proof)
|
||||
|
@ -101,7 +100,7 @@ func (ta *Client) PurchaseTokens() {
|
|||
|
||||
// Post sends a Post Request to the server
|
||||
func (ta *Client) Post(message auditable.Message) bool {
|
||||
token, err := ta.paymentHandler.NextToken(message)
|
||||
token, err := ta.paymentHandler.NextToken(message, ta.connection.Hostname())
|
||||
if err == nil {
|
||||
data, _ := json.Marshal(Message{MessageType: postRequestMessage, PostRequest: postRequest{Token: token, Message: message}})
|
||||
ta.connection.Send(data)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package tokenboard
|
||||
|
||||
// NOTE: This is a sketch implementation, Not suitable for production use. The real auditable store is still being designed.
|
||||
|
||||
import (
|
||||
"cwtch.im/tapir"
|
||||
"cwtch.im/tapir/applications"
|
||||
|
@ -55,7 +57,7 @@ func (ta *Server) Listen() {
|
|||
|
||||
var message Message
|
||||
json.Unmarshal(data, &message)
|
||||
log.Debugf("Received a Message: %v", message)
|
||||
|
||||
switch message.MessageType {
|
||||
case postRequestMessage:
|
||||
postrequest := message.PostRequest
|
||||
|
@ -77,7 +79,7 @@ func (ta *Server) Listen() {
|
|||
}
|
||||
|
||||
func (ta *Server) postMessageRequest(token privacypass.SpentToken, message auditable.Message) {
|
||||
if err := ta.TokenService.SpendToken(token, message); err == nil {
|
||||
if err := ta.TokenService.SpendToken(token, append(message, ta.connection.ID().Hostname()...)); err == nil {
|
||||
log.Debugf("Token is valid")
|
||||
signedproof := ta.AuditableStore.Add(message)
|
||||
data, _ := json.Marshal(Message{MessageType: postResultMessage, PostResult: postResult{true, signedproof}})
|
||||
|
|
|
@ -47,21 +47,24 @@ func (fph *FreePaymentHandler) MakePayment() {
|
|||
ChainApplication(new(applications.ProofOfWorkApplication), applications.SuccessfulProofOfWorkCapability).
|
||||
ChainApplication(tokenApplication, applications.HasTokensCapability)
|
||||
client.Connect(fph.ServerHostname, powTokenApp)
|
||||
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()
|
||||
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) {
|
||||
|
@ -94,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()
|
||||
}()
|
||||
|
||||
|
@ -106,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)
|
||||
|
@ -114,12 +117,12 @@ func TestTokenBoardApp(t *testing.T) {
|
|||
sg.Done()
|
||||
}()
|
||||
|
||||
time.Sleep(time.Second * 30) // wait for server to initialize
|
||||
time.Sleep(time.Second * 60) // 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
6
go.mod
|
@ -2,5 +2,11 @@ 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
11
go.sum
|
@ -8,11 +8,19 @@ 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=
|
||||
|
@ -20,8 +28,9 @@ 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=
|
||||
|
|
|
@ -54,7 +54,6 @@ func (s *BaseOnionService) WaitForCapabilityOrClose(cid string, name tapir.Capab
|
|||
func (s *BaseOnionService) GetConnection(hostname string) (tapir.Connection, error) {
|
||||
var conn tapir.Connection
|
||||
s.connections.Range(func(key, value interface{}) bool {
|
||||
log.Debugf("Checking %v", key)
|
||||
connection := value.(tapir.Connection)
|
||||
if connection.Hostname() == hostname {
|
||||
if !connection.IsClosed() {
|
||||
|
|
|
@ -53,6 +53,7 @@ func (bp *BoltPersistence) Check(bucket string, name string) (bool, error) {
|
|||
val = b.Get([]byte(name))
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if val != nil {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package auditable
|
||||
|
||||
// WARNING NOTE: This is a sketch implementation, Not suitable for production use. The real auditable store is still being designed.
|
||||
|
||||
import (
|
||||
"cwtch.im/tapir/persistence"
|
||||
"cwtch.im/tapir/primitives"
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
package primitives
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"math"
|
||||
"math/big"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// BloomFilter implements a bloom filter
|
||||
type BloomFilter struct {
|
||||
B []bool
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// Init constructs a bloom filter of size m
|
||||
func (bf *BloomFilter) Init(m int64) {
|
||||
bf.B = make([]bool, m)
|
||||
|
||||
}
|
||||
|
||||
// Hash transforms a message to a set of bit flips
|
||||
func (bf *BloomFilter) Hash(msg []byte) []int {
|
||||
|
||||
// Not the fastest hash function ever, but cryptographic security is more important than speed.
|
||||
hash1 := sha256.Sum256(append([]byte("h1"), msg...))
|
||||
hash2 := sha256.Sum256(append([]byte("h2"), msg...))
|
||||
hash3 := sha256.Sum256(append([]byte("h3"), msg...))
|
||||
hash4 := sha256.Sum256(append([]byte("h4"), msg...))
|
||||
|
||||
m := int64(len(bf.B))
|
||||
// Number of bytes needed to pick a position from [0,m)
|
||||
B := int(math.Ceil(math.Log2(float64(m)) / 8.0))
|
||||
|
||||
p1 := big.NewInt(0).SetBytes(hash1[:B]).Int64()
|
||||
p2 := big.NewInt(0).SetBytes(hash2[:B]).Int64()
|
||||
p3 := big.NewInt(0).SetBytes(hash3[:B]).Int64()
|
||||
p4 := big.NewInt(0).SetBytes(hash4[:B]).Int64()
|
||||
|
||||
return []int{int(p1), int(p2), int(p3), int(p4)}
|
||||
}
|
||||
|
||||
// Insert updates the BloomFilter (suitable for concurrent use)
|
||||
func (bf *BloomFilter) Insert(msg []byte) {
|
||||
pos := bf.Hash(msg)
|
||||
bf.lock.Lock()
|
||||
defer bf.lock.Unlock()
|
||||
bf.B[pos[0]] = true
|
||||
bf.B[pos[1]] = true
|
||||
bf.B[pos[2]] = true
|
||||
bf.B[pos[3]] = true
|
||||
}
|
||||
|
||||
// Check returns true if the messages might be in the BloomFilter
|
||||
// (No false positives, possible false negatives due to the probabilistic nature of the filter)
|
||||
func (bf *BloomFilter) Check(msg []byte) bool {
|
||||
pos := bf.Hash(msg)
|
||||
if bf.B[pos[0]] && bf.B[pos[1]] && bf.B[pos[2]] && bf.B[pos[3]] {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package primitives
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBloomFilter_Insert(t *testing.T) {
|
||||
bf := new(BloomFilter)
|
||||
bf.Init(256)
|
||||
|
||||
fp := 0
|
||||
for i := 0; i < 256; i++ {
|
||||
input := []byte("test" + strconv.Itoa(256+i))
|
||||
if bf.Check(input) {
|
||||
t.Log("False Positive!")
|
||||
fp++
|
||||
}
|
||||
bf.Insert(input)
|
||||
}
|
||||
|
||||
t.Logf("Num false positives %v %v%%", fp, (float64(fp)/256.0)*100)
|
||||
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
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("")))
|
||||
|
||||
}
|
|
@ -1,437 +0,0 @@
|
|||
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)
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
package bulletproofs
|
||||
|
||||
type Generator struct {
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
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!")
|
||||
}
|
||||
}
|
|
@ -1,305 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
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())
|
||||
|
||||
}
|
|
@ -1,186 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -15,8 +15,6 @@ 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
|
||||
|
@ -58,8 +56,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
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package privacypass
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"cwtch.im/tapir/primitives/core"
|
||||
"github.com/bwesterb/go-ristretto"
|
||||
ristretto "github.com/gtank/ristretto255"
|
||||
)
|
||||
|
||||
// DLEQProof encapsulates a Chaum-Pedersen DLEQ Proof
|
||||
|
@ -13,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
|
||||
|
@ -21,49 +22,52 @@ type DLEQProof struct {
|
|||
// s := (t + ck) mod q
|
||||
//
|
||||
// Sends c,s to Vicky
|
||||
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)
|
||||
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)
|
||||
|
||||
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())
|
||||
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))
|
||||
|
||||
c := transcript.CommitToTranscriptScalar("c")
|
||||
s := new(ristretto.Scalar).Sub(t, new(ristretto.Scalar).Mul(c, k))
|
||||
s := new(ristretto.Scalar).Subtract(t, new(ristretto.Scalar).Multiply(c, k))
|
||||
return DLEQProof{c, s}
|
||||
}
|
||||
|
||||
// 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
|
||||
func VerifyDiscreteLogEquivalenceProof(dleq DLEQProof, X *ristretto.Point, Y *ristretto.Point, P *ristretto.Point, Q *ristretto.Point, transcript *core.Transcript) bool {
|
||||
func VerifyDiscreteLogEquivalenceProof(dleq DLEQProof, X *ristretto.Element, Y *ristretto.Element, P *ristretto.Element, Q *ristretto.Element, transcript *core.Transcript) bool {
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
A := new(ristretto.Point).Add(Xs, Yc)
|
||||
B := new(ristretto.Point).Add(Ps, Qc)
|
||||
A := new(ristretto.Element).Add(Xs, Yc)
|
||||
B := new(ristretto.Element).Add(Ps, Qc)
|
||||
|
||||
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())
|
||||
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))
|
||||
|
||||
return transcript.CommitToTranscriptScalar("c").Equals(dleq.C)
|
||||
return transcript.CommitToTranscriptScalar("c").Equal(dleq.C) == 1
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ import (
|
|||
"cwtch.im/tapir/primitives/core"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"github.com/bwesterb/go-ristretto"
|
||||
ristretto "github.com/gtank/ristretto255"
|
||||
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
|
@ -15,17 +16,22 @@ import (
|
|||
type Token struct {
|
||||
t []byte
|
||||
r *ristretto.Scalar
|
||||
W *ristretto.Point
|
||||
W *ristretto.Element
|
||||
}
|
||||
|
||||
// GetT returns the underlying bytes for token for use in constraint proofs.
|
||||
func (t Token) GetT() []byte {
|
||||
return t.t
|
||||
}
|
||||
|
||||
// BlindedToken encapsulates a Blinded Token
|
||||
type BlindedToken struct {
|
||||
P *ristretto.Point
|
||||
P *ristretto.Element
|
||||
}
|
||||
|
||||
// SignedToken encapsulates a Signed (Blinded) Token
|
||||
type SignedToken struct {
|
||||
Q *ristretto.Point
|
||||
Q *ristretto.Element
|
||||
}
|
||||
|
||||
// SpentToken encapsulates the parameters needed to spend a Token
|
||||
|
@ -37,7 +43,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
|
||||
|
@ -45,23 +52,25 @@ type TokenPaymentHandler interface {
|
|||
func (t *Token) GenBlindedToken() BlindedToken {
|
||||
t.t = make([]byte, 32)
|
||||
rand.Read(t.t)
|
||||
t.r = new(ristretto.Scalar).Rand()
|
||||
t.r = new(ristretto.Scalar)
|
||||
b := make([]byte, 64)
|
||||
rand.Read(b)
|
||||
t.r.FromUniformBytes(b)
|
||||
|
||||
Ht := sha3.Sum256(t.t)
|
||||
log.Debugf("token: %x", Ht)
|
||||
T := new(ristretto.Point).SetElligator(&Ht)
|
||||
P := new(ristretto.Point).ScalarMult(T, t.r)
|
||||
Ht := sha3.Sum512(t.t)
|
||||
T := new(ristretto.Element).FromUniformBytes(Ht[:])
|
||||
P := new(ristretto.Element).ScalarMult(t.r, T)
|
||||
return BlindedToken{P}
|
||||
}
|
||||
|
||||
// unblindSignedToken unblinds a token that has been signed by a server
|
||||
func (t *Token) unblindSignedToken(token SignedToken) {
|
||||
t.W = new(ristretto.Point).ScalarMult(token.Q, new(ristretto.Scalar).Inverse(t.r))
|
||||
t.W = new(ristretto.Element).ScalarMult(new(ristretto.Scalar).Invert(t.r), token.Q)
|
||||
}
|
||||
|
||||
// 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.Bytes()...))
|
||||
key := sha3.Sum256(append(t.t, t.W.Encode(nil)...))
|
||||
mac := hmac.New(sha3.New512, key[:])
|
||||
return SpentToken{t.t, mac.Sum(data)}
|
||||
}
|
||||
|
@ -76,28 +85,29 @@ func GenerateBlindedTokenBatch(num int) (tokens []*Token, blindedTokens []Blinde
|
|||
}
|
||||
|
||||
// verifyBatchProof verifies a given batch proof (see also UnblindSignedTokenBatch)
|
||||
func verifyBatchProof(dleq DLEQProof, Y *ristretto.Point, blindedTokens []BlindedToken, signedTokens []SignedToken, transcript *core.Transcript) bool {
|
||||
func verifyBatchProof(dleq DLEQProof, Y *ristretto.Element, blindedTokens []BlindedToken, signedTokens []SignedToken, transcript *core.Transcript) bool {
|
||||
transcript.NewProtocol(BatchProofProtocol)
|
||||
transcript.AddToTranscript(BatchProofX, new(ristretto.Point).SetBase().Bytes())
|
||||
transcript.AddToTranscript(BatchProofY, Y.Bytes())
|
||||
transcript.AddToTranscript(BatchProofX, new(ristretto.Element).Base().Encode(nil))
|
||||
transcript.AddToTranscript(BatchProofY, Y.Encode(nil))
|
||||
transcript.AddToTranscript(BatchProofPVector, []byte(fmt.Sprintf("%v", blindedTokens)))
|
||||
transcript.AddToTranscript(BatchProofQVector, []byte(fmt.Sprintf("%v", signedTokens)))
|
||||
prng := transcript.CommitToPRNG("w")
|
||||
M := new(ristretto.Point).SetZero()
|
||||
Z := new(ristretto.Point).SetZero()
|
||||
M := new(ristretto.Element).Zero()
|
||||
Z := new(ristretto.Element).Zero()
|
||||
for i := range blindedTokens {
|
||||
c := prng.Next()
|
||||
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)
|
||||
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)
|
||||
}
|
||||
return VerifyDiscreteLogEquivalenceProof(dleq, new(ristretto.Point).SetBase(), Y, M, Z, transcript)
|
||||
return VerifyDiscreteLogEquivalenceProof(dleq, new(ristretto.Element).Base(), 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.Point, proof DLEQProof, transcript *core.Transcript) bool {
|
||||
func UnblindSignedTokenBatch(tokens []*Token, blindedTokens []BlindedToken, signedTokens []SignedToken, Y *ristretto.Element, 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 {
|
||||
|
|
|
@ -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,10 +33,41 @@ 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")
|
||||
|
@ -65,5 +98,4 @@ func TestGenerateBlindedTokenBatch(t *testing.T) {
|
|||
if verified {
|
||||
t.Errorf("Something went wrong, the proof passed with wrong transcript: %s", wrongTranscript.OutputTranscriptToAudit())
|
||||
}
|
||||
db.Close()
|
||||
}
|
||||
|
|
|
@ -2,11 +2,12 @@ package privacypass
|
|||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"cwtch.im/tapir/persistence"
|
||||
"cwtch.im/tapir/primitives/core"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/bwesterb/go-ristretto"
|
||||
ristretto "github.com/gtank/ristretto255"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"sync"
|
||||
)
|
||||
|
@ -14,7 +15,7 @@ import (
|
|||
// TokenServer implements a token server.
|
||||
type TokenServer struct {
|
||||
k *ristretto.Scalar
|
||||
Y *ristretto.Point
|
||||
Y *ristretto.Element
|
||||
seen map[string]bool
|
||||
persistanceService persistence.Service
|
||||
mutex sync.Mutex
|
||||
|
@ -22,28 +23,53 @@ 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 {
|
||||
k := new(ristretto.Scalar).Rand()
|
||||
return TokenServer{k, new(ristretto.Point).ScalarMultBase(k), make(map[string]bool), nil, sync.Mutex{}}
|
||||
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{}}
|
||||
}
|
||||
|
||||
// NewTokenServerFromStore generates a new TokenServer backed by a persistence service.
|
||||
func NewTokenServerFromStore(persistenceService persistence.Service) TokenServer {
|
||||
k := new(ristretto.Scalar).Rand()
|
||||
func NewTokenServerFromStore(persistenceService persistence.Service) *TokenServer {
|
||||
tokenServer := NewTokenServer()
|
||||
persistenceService.Setup([]string{tokenBucket})
|
||||
return TokenServer{k, new(ristretto.Point).ScalarMultBase(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
|
||||
func (ts *TokenServer) SignBlindedToken(bt BlindedToken) SignedToken {
|
||||
Q := new(ristretto.Point).ScalarMult(bt.P, ts.k)
|
||||
Q := new(ristretto.Element).ScalarMult(ts.k, bt.P)
|
||||
return SignedToken{Q}
|
||||
}
|
||||
|
||||
|
@ -56,24 +82,39 @@ 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.Point).SetBase().Bytes())
|
||||
transcript.AddToTranscript(BatchProofY, ts.Y.Bytes())
|
||||
transcript.AddToTranscript(BatchProofX, new(ristretto.Element).Base().Encode(nil))
|
||||
transcript.AddToTranscript(BatchProofY, ts.Y.Encode(nil))
|
||||
transcript.AddToTranscript(BatchProofPVector, []byte(fmt.Sprintf("%v", blindedTokens)))
|
||||
transcript.AddToTranscript(BatchProofQVector, []byte(fmt.Sprintf("%v", signedTokens)))
|
||||
prng := transcript.CommitToPRNG("w")
|
||||
|
||||
M := new(ristretto.Point).SetZero()
|
||||
Z := new(ristretto.Point).SetZero()
|
||||
M := new(ristretto.Element).Zero()
|
||||
Z := new(ristretto.Element).Zero()
|
||||
|
||||
for i := range blindedTokens {
|
||||
c := prng.Next()
|
||||
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)
|
||||
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)
|
||||
}
|
||||
return DiscreteLogEquivalenceProof(ts.k, new(ristretto.Point).SetBase(), ts.Y, M, Z, transcript)
|
||||
return DiscreteLogEquivalenceProof(ts.k, new(ristretto.Element).Base(), ts.Y, M, Z, transcript)
|
||||
}
|
||||
|
||||
// SpendToken returns true a SpentToken is valid and has never been spent before, false otherwise.
|
||||
|
@ -90,13 +131,13 @@ func (ts *TokenServer) SpendToken(token SpentToken, data []byte) error {
|
|||
return fmt.Errorf("token: %v has already been spent", token)
|
||||
}
|
||||
}
|
||||
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()...))
|
||||
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)...))
|
||||
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
|
||||
|
|
32
service.go
32
service.go
|
@ -9,7 +9,6 @@ import (
|
|||
"golang.org/x/crypto/ed25519"
|
||||
"golang.org/x/crypto/nacl/secretbox"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
@ -43,7 +42,7 @@ type Connection interface {
|
|||
// Connection defines a Tapir Connection
|
||||
type connection struct {
|
||||
hostname string
|
||||
conn net.Conn
|
||||
conn io.ReadWriteCloser
|
||||
capabilities sync.Map
|
||||
encrypted bool
|
||||
key [32]byte
|
||||
|
@ -52,10 +51,11 @@ 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 net.Conn, app Application) Connection {
|
||||
func NewConnection(id *primitives.Identity, hostname string, outbound bool, conn io.ReadWriteCloser, app Application) Connection {
|
||||
connection := new(connection)
|
||||
connection.hostname = hostname
|
||||
connection.conn = conn
|
||||
|
@ -74,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
|
||||
}
|
||||
|
||||
|
@ -96,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
|
||||
}
|
||||
|
@ -119,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()
|
||||
}
|
||||
|
@ -134,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])
|
||||
|
@ -148,13 +161,18 @@ func (c *connection) Expect() []byte {
|
|||
return []byte{}
|
||||
}
|
||||
}
|
||||
len, _ := binary.Uvarint(buffer[0:2])
|
||||
length, _ := binary.Uvarint(buffer[0:2])
|
||||
if length+2 >= uint64(c.MaxLength) {
|
||||
return []byte{}
|
||||
}
|
||||
//cplog.Debugf("[%v -> %v] Wire Receive: (%d) %x", c.hostname, c.ID.Hostname(), len, buffer)
|
||||
return buffer[2 : len+2]
|
||||
return buffer[2 : length+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
|
||||
}
|
||||
|
@ -166,6 +184,8 @@ func (c *connection) Send(message []byte) {
|
|||
binary.PutUvarint(buffer[0:2], uint64(len(message)))
|
||||
copy(buffer[2:], message)
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if c.encrypted {
|
||||
var nonce [24]byte
|
||||
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
set -e
|
||||
pwd
|
||||
go test ${1} -coverprofile=applications.cover.out -v ./applications
|
||||
go test ${1} -coverprofile=applications.tokenboard.cover.out -v ./applications/tokenboard
|
||||
go test ${1} -coverprofile=primitives.cover.out -v ./primitives
|
||||
go test ${1} -coverprofile=primitives.auditable.cover.out -v ./primitives/auditable
|
||||
go test ${1} -coverprofile=primitives.core.cover.out -v ./primitives/core
|
||||
go test ${1} -coverprofile=primitives.privacypass.cover.out -v ./primitives/privacypass
|
||||
go test -race ${1} -coverprofile=applications.cover.out -v ./applications
|
||||
go test -race ${1} -coverprofile=applications.tokenboard.cover.out -v ./applications/tokenboard
|
||||
go test -race ${1} -coverprofile=primitives.cover.out -v ./primitives
|
||||
go test -race ${1} -coverprofile=primitives.auditable.cover.out -v ./primitives/auditable
|
||||
go test -race ${1} -coverprofile=primitives.core.cover.out -v ./primitives/core
|
||||
go test -race ${1} -coverprofile=primitives.privacypass.cover.out -v ./primitives/privacypass
|
||||
go test -bench "BenchmarkAuditableStore" -benchtime 1000x primitives/auditable/*.go
|
||||
echo "mode: set" > coverage.out && cat *.cover.out | grep -v mode: | sort -r | \
|
||||
awk '{if($1 != last) {print $0;last=$1}}' >> coverage.out
|
||||
|
|
Loading…
Reference in New Issue