2019-09-14 23:44:19 +00:00
package privacypass
import (
"crypto/hmac"
"crypto/rand"
"cwtch.im/tapir/primitives/core"
"fmt"
2019-09-15 05:50:06 +00:00
"git.openprivacy.ca/openprivacy/libricochet-go/log"
2019-09-15 21:20:05 +00:00
ristretto "github.com/gtank/ristretto255"
2019-09-14 23:44:19 +00:00
"golang.org/x/crypto/sha3"
)
// Token is an implementation of PrivacyPass
// Davidson A, Goldberg I, Sullivan N, Tankersley G, Valsorda F. Privacy pass: Bypassing internet challenges anonymously. Proceedings on Privacy Enhancing Technologies. 2018 Jun 1;2018(3):164-80.
type Token struct {
t [ ] byte
r * ristretto . Scalar
2019-09-15 21:20:05 +00:00
W * ristretto . Element
2019-09-14 23:44:19 +00:00
}
2019-12-02 20:37:25 +00:00
// GetT returns the underlying bytes for token for use in constraint proofs.
func ( t Token ) GetT ( ) [ ] byte {
return t . t
}
2019-09-14 23:44:19 +00:00
// BlindedToken encapsulates a Blinded Token
type BlindedToken struct {
2019-09-15 21:20:05 +00:00
P * ristretto . Element
2019-09-14 23:44:19 +00:00
}
// SignedToken encapsulates a Signed (Blinded) Token
type SignedToken struct {
2019-09-15 21:20:05 +00:00
Q * ristretto . Element
2019-09-14 23:44:19 +00:00
}
// SpentToken encapsulates the parameters needed to spend a Token
type SpentToken struct {
2019-09-15 05:50:06 +00:00
T [ ] byte
2019-09-14 23:44:19 +00:00
MAC [ ] byte
}
2019-09-15 05:50:06 +00:00
// TokenPaymentHandler defines an interface with external payment processors
type TokenPaymentHandler interface {
2019-09-15 21:20:05 +00:00
MakePayment ( )
2019-11-26 21:10:09 +00:00
// Next Token
NextToken ( data [ ] byte , hostname string ) ( SpentToken , error )
2019-09-15 05:50:06 +00:00
}
2019-09-14 23:44:19 +00:00
// GenBlindedToken initializes the Token
// GenToken() & Blind()
func ( t * Token ) GenBlindedToken ( ) BlindedToken {
t . t = make ( [ ] byte , 32 )
rand . Read ( t . t )
2019-09-15 21:20:05 +00:00
t . r = new ( ristretto . Scalar )
b := make ( [ ] byte , 64 )
rand . Read ( b )
t . r . FromUniformBytes ( b )
2019-09-14 23:44:19 +00:00
2019-09-15 21:20:05 +00:00
Ht := sha3 . Sum512 ( t . t )
T := new ( ristretto . Element ) . FromUniformBytes ( Ht [ : ] )
P := new ( ristretto . Element ) . ScalarMult ( t . r , T )
2019-09-14 23:44:19 +00:00
return BlindedToken { P }
}
// unblindSignedToken unblinds a token that has been signed by a server
func ( t * Token ) unblindSignedToken ( token SignedToken ) {
2019-09-15 21:20:05 +00:00
t . W = new ( ristretto . Element ) . ScalarMult ( new ( ristretto . Scalar ) . Invert ( t . r ) , token . Q )
2019-09-14 23:44:19 +00:00
}
// SpendToken binds the token with data and then redeems the token
func ( t * Token ) SpendToken ( data [ ] byte ) SpentToken {
2019-09-15 21:20:05 +00:00
key := sha3 . Sum256 ( append ( t . t , t . W . Encode ( nil ) ... ) )
2019-09-14 23:44:19 +00:00
mac := hmac . New ( sha3 . New512 , key [ : ] )
return SpentToken { t . t , mac . Sum ( data ) }
}
// GenerateBlindedTokenBatch generates a batch of blinded tokens (and their unblinded equivalents)
func GenerateBlindedTokenBatch ( num int ) ( tokens [ ] * Token , blindedTokens [ ] BlindedToken ) {
for i := 0 ; i < num ; i ++ {
tokens = append ( tokens , new ( Token ) )
blindedTokens = append ( blindedTokens , tokens [ i ] . GenBlindedToken ( ) )
}
return
}
// verifyBatchProof verifies a given batch proof (see also UnblindSignedTokenBatch)
2019-09-15 21:20:05 +00:00
func verifyBatchProof ( dleq DLEQProof , Y * ristretto . Element , blindedTokens [ ] BlindedToken , signedTokens [ ] SignedToken , transcript * core . Transcript ) bool {
transcript . NewProtocol ( BatchProofProtocol )
transcript . AddToTranscript ( BatchProofX , new ( ristretto . Element ) . Base ( ) . Encode ( nil ) )
transcript . AddToTranscript ( BatchProofY , Y . Encode ( nil ) )
transcript . AddToTranscript ( BatchProofPVector , [ ] byte ( fmt . Sprintf ( "%v" , blindedTokens ) ) )
transcript . AddToTranscript ( BatchProofQVector , [ ] byte ( fmt . Sprintf ( "%v" , signedTokens ) ) )
2019-09-14 23:44:19 +00:00
prng := transcript . CommitToPRNG ( "w" )
2019-09-15 21:20:05 +00:00
M := new ( ristretto . Element ) . Zero ( )
Z := new ( ristretto . Element ) . Zero ( )
2019-09-14 23:44:19 +00:00
for i := range blindedTokens {
c := prng . Next ( )
2019-09-15 21:20:05 +00:00
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 )
2019-09-14 23:44:19 +00:00
}
2019-09-15 21:20:05 +00:00
return VerifyDiscreteLogEquivalenceProof ( dleq , new ( ristretto . Element ) . Base ( ) , Y , M , Z , transcript )
2019-09-14 23:44:19 +00:00
}
// 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.
2019-09-15 21:20:05 +00:00
func UnblindSignedTokenBatch ( tokens [ ] * Token , blindedTokens [ ] BlindedToken , signedTokens [ ] SignedToken , Y * ristretto . Element , proof DLEQProof , transcript * core . Transcript ) bool {
2019-09-14 23:44:19 +00:00
verified := verifyBatchProof ( proof , Y , blindedTokens , signedTokens , transcript )
if ! verified {
2019-12-02 20:37:25 +00:00
log . Debugf ( "Failed to unblind tokens: %v" , transcript . OutputTranscriptToAudit ( ) )
2019-09-14 23:44:19 +00:00
return false
}
for i , t := range tokens {
t . unblindSignedToken ( signedTokens [ i ] )
}
return true
}