Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add deterministic blind RSA verifier #379

Merged
merged 3 commits into from
Oct 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 107 additions & 18 deletions blindsign/blindrsa/blindrsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ package blindrsa
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-rsa-blind-signatures-02

import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/sha512"
"crypto/subtle"
"errors"
"hash"
Expand All @@ -15,21 +18,63 @@ import (
"github.com/cloudflare/circl/blindsign"
)

var errUnsupportedHashFunction = errors.New("unsupported hash function")

// An RSAVerifier represents a Verifier in the RSA blind signature protocol.
// It carries state needed to produce and validate an RSA blind signature.
type RSAVerifier struct {
// Public key of the Signer
pk *rsa.PublicKey

// Identifier of the cryptographic hash function used in producing the message signature
cryptoHash crypto.Hash

// Hash function used in producing the message signature
hash hash.Hash
}

// A DeterminsiticRSAVerifier is an RSAVerifier that supports deterministic signatures.
type DeterminsiticRSAVerifier struct {
// Public key of the Signer
pk *rsa.PublicKey

// Identifier of the cryptographic hash function used in producing the message signature
cryptoHash crypto.Hash

// Hash function used in producing the message signature
hash hash.Hash
}

func convertHashFunction(hash crypto.Hash) hash.Hash {
switch hash {
case crypto.SHA256:
return sha256.New()
case crypto.SHA384:
return sha512.New384()
case crypto.SHA512:
return sha512.New()
default:
panic(errUnsupportedHashFunction)
}
}

// NewDeterministicRSAVerifier creates a new RSAVerifier using the corresponding Signer parameters.
func NewDeterministicRSAVerifier(pk *rsa.PublicKey, hash crypto.Hash) DeterminsiticRSAVerifier {
h := convertHashFunction(hash)
return DeterminsiticRSAVerifier{
pk: pk,
cryptoHash: hash,
hash: h,
}
}

// NewRSAVerifier creates a new RSAVerifier using the corresponding Signer parameters.
func NewRSAVerifier(pk *rsa.PublicKey, hash hash.Hash) RSAVerifier {
func NewRSAVerifier(pk *rsa.PublicKey, hash crypto.Hash) RSAVerifier {
h := convertHashFunction(hash)
return RSAVerifier{
pk: pk,
hash: hash,
pk: pk,
cryptoHash: hash,
hash: h,
}
}

Expand Down Expand Up @@ -64,32 +109,68 @@ func generateBlindingFactor(random io.Reader, key *rsa.PublicKey) (*big.Int, *bi
return r, rInv, nil
}

func (v RSAVerifier) fixedBlind(message, salt []byte, r, rInv *big.Int) ([]byte, blindsign.VerifierState, error) {
encodedMsg, err := encodeMessageEMSAPSS(message, v.pk, v.hash, salt)
func fixedBlind(message, salt []byte, r, rInv *big.Int, pk *rsa.PublicKey, hash hash.Hash) ([]byte, blindsign.VerifierState, error) {
encodedMsg, err := encodeMessageEMSAPSS(message, pk, hash, salt)
if err != nil {
return nil, nil, err
}

m := new(big.Int).SetBytes(encodedMsg)

bigE := big.NewInt(int64(v.pk.E))
x := new(big.Int).Exp(r, bigE, v.pk.N)
bigE := big.NewInt(int64(pk.E))
x := new(big.Int).Exp(r, bigE, pk.N)
z := new(big.Int).Set(m)
z.Mul(z, x)
z.Mod(z, v.pk.N)
z.Mod(z, pk.N)

kLen := (v.pk.N.BitLen() + 7) / 8
kLen := (pk.N.BitLen() + 7) / 8
blindedMsg := make([]byte, kLen)
z.FillBytes(blindedMsg)

return blindedMsg, RSAVerifierState{
encodedMsg: encodedMsg,
verifier: v,
pk: pk,
hash: hash,
salt: salt,
rInv: rInv,
}, nil
}

// Blind initializes the blind RSA protocol using an input message and source of randomness. The
// signature is deterministic. This function fails if randomness was not provided.
//
// See the specification for more details:
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-rsa-blind-signatures-02#section-5.1.1
func (v DeterminsiticRSAVerifier) Blind(random io.Reader, message []byte) ([]byte, blindsign.VerifierState, error) {
if random == nil {
return nil, nil, ErrInvalidRandomness
}

r, rInv, err := generateBlindingFactor(random, v.pk)
if err != nil {
return nil, nil, err
}

return fixedBlind(message, nil, r, rInv, v.pk, v.hash)
}

func verifyMessageSignature(message, signature []byte, saltLength int, pk *rsa.PublicKey, hash crypto.Hash) error {
h := convertHashFunction(hash)
h.Write(message)
digest := h.Sum(nil)

err := rsa.VerifyPSS(pk, hash, digest, signature, &rsa.PSSOptions{
Hash: hash,
SaltLength: saltLength,
})
return err
}

// Verify verifies the input (message, signature) pair and produces an error upon failure.
func (v DeterminsiticRSAVerifier) Verify(message, signature []byte) error {
return verifyMessageSignature(message, signature, 0, v.pk, v.cryptoHash)
}

// Blind initializes the blind RSA protocol using an input message and source of randomness. The
// signature includes a randomly generated PSS salt whose length equals the size of the underlying
// hash function. This function fails if randomness was not provided.
Expand All @@ -112,7 +193,7 @@ func (v RSAVerifier) Blind(random io.Reader, message []byte) ([]byte, blindsign.
return nil, nil, err
}

return v.fixedBlind(message, salt, r, rInv)
return fixedBlind(message, salt, r, rInv, v.pk, v.hash)
}

// FixedBlind runs the Blind function with fixed blind and salt inputs.
Expand All @@ -127,14 +208,22 @@ func (v RSAVerifier) FixedBlind(message, blind, salt []byte) ([]byte, blindsign.
return nil, nil, ErrInvalidBlind
}

return v.fixedBlind(message, salt, r, rInv)
return fixedBlind(message, salt, r, rInv, v.pk, v.hash)
}

// Verify verifies the input (message, signature) pair and produces an error upon failure.
func (v RSAVerifier) Verify(message, signature []byte) error {
return verifyMessageSignature(message, signature, v.hash.Size(), v.pk, v.cryptoHash)
}

// An RSAVerifierState carries state needed to complete the blind signature protocol
// as a verifier.
type RSAVerifierState struct {
// An RSA verifier carrying Signer verification state
verifier RSAVerifier
// Public key of the Signer
pk *rsa.PublicKey

// Hash function used in producing the message signature
hash hash.Hash

// The hashed and encoded message being signed
encodedMsg []byte
Expand Down Expand Up @@ -163,20 +252,20 @@ func verifyBlindSignature(pub *rsa.PublicKey, hashed, sig []byte) error {
// See the specification for more details:
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-rsa-blind-signatures-02#section-5.1.3
func (state RSAVerifierState) Finalize(data []byte) ([]byte, error) {
kLen := (state.verifier.pk.N.BitLen() + 7) / 8
kLen := (state.pk.N.BitLen() + 7) / 8
if len(data) != kLen {
return nil, ErrUnexpectedSize
}

z := new(big.Int).SetBytes(data)
s := new(big.Int).Set(state.rInv)
s.Mul(s, z)
s.Mod(s, state.verifier.pk.N)
s.Mod(s, state.pk.N)

sig := make([]byte, kLen)
s.FillBytes(sig)

err := verifyBlindSignature(state.verifier.pk, state.encodedMsg, sig)
err := verifyBlindSignature(state.pk, state.encodedMsg, sig)
if err != nil {
return nil, err
}
Expand All @@ -186,7 +275,7 @@ func (state RSAVerifierState) Finalize(data []byte) ([]byte, error) {

// CopyBlind returns an encoding of the blind value used in the protocol.
func (state RSAVerifierState) CopyBlind() []byte {
r := new(big.Int).ModInverse(state.rInv, state.verifier.pk.N)
r := new(big.Int).ModInverse(state.rInv, state.pk.N)
return r.Bytes()
}

Expand Down
43 changes: 27 additions & 16 deletions blindsign/blindrsa/blindrsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha512"
"crypto/x509"
"encoding/hex"
"encoding/json"
Expand All @@ -15,6 +14,8 @@ import (
"math/big"
"os"
"testing"

"github.com/cloudflare/circl/blindsign"
)

// 4096-bit RSA private key
Expand Down Expand Up @@ -85,7 +86,7 @@ func loadPrivateKey(t *testing.T) *rsa.PrivateKey {
return privateKey
}

func runSignatureProtocol(signer RSASigner, verifier RSAVerifier, message []byte, random io.Reader) ([]byte, error) {
func runSignatureProtocol(signer RSASigner, verifier blindsign.Verifier, message []byte, random io.Reader) ([]byte, error) {
blindedMsg, state, err := verifier.Blind(random, message)
if err != nil {
return nil, err
Expand All @@ -110,13 +111,7 @@ func runSignatureProtocol(signer RSASigner, verifier RSAVerifier, message []byte
return nil, err
}

hash := sha512.New()
hash.Write(message)
digest := hash.Sum(nil)
err = rsa.VerifyPSS(verifier.pk, crypto.SHA512, digest, sig, &rsa.PSSOptions{
Hash: crypto.SHA512,
SaltLength: crypto.SHA512.Size(),
})
err = verifier.Verify(message, sig)
if err != nil {
return nil, err
}
Expand All @@ -128,7 +123,23 @@ func TestRoundTrip(t *testing.T) {
message := []byte("hello world")
key := loadPrivateKey(t)

verifier := NewRSAVerifier(&key.PublicKey, sha512.New())
verifier := NewRSAVerifier(&key.PublicKey, crypto.SHA512)
signer := NewRSASigner(key)

sig, err := runSignatureProtocol(signer, verifier, message, rand.Reader)
if err != nil {
t.Fatal(err)
}
if sig == nil {
t.Fatal("nil signature output")
}
}

func TestDeterministicRoundTrip(t *testing.T) {
message := []byte("hello world")
key := loadPrivateKey(t)

verifier := NewDeterministicRSAVerifier(&key.PublicKey, crypto.SHA512)
signer := NewRSASigner(key)

sig, err := runSignatureProtocol(signer, verifier, message, rand.Reader)
Expand All @@ -140,11 +151,11 @@ func TestRoundTrip(t *testing.T) {
}
}

func TestDeterministicSignFail(t *testing.T) {
func TestDeterministicBlindFailure(t *testing.T) {
message := []byte("hello world")
key := loadPrivateKey(t)

verifier := NewRSAVerifier(&key.PublicKey, sha512.New())
verifier := NewDeterministicRSAVerifier(&key.PublicKey, crypto.SHA512)
signer := NewRSASigner(key)

_, err := runSignatureProtocol(signer, verifier, message, nil)
Expand All @@ -157,7 +168,7 @@ func TestRandomSignVerify(t *testing.T) {
message := []byte("hello world")
key := loadPrivateKey(t)

verifier := NewRSAVerifier(&key.PublicKey, sha512.New())
verifier := NewRSAVerifier(&key.PublicKey, crypto.SHA512)
signer := NewRSASigner(key)

sig1, err := runSignatureProtocol(signer, verifier, message, rand.Reader)
Expand Down Expand Up @@ -193,7 +204,7 @@ func TestFixedRandomSignVerify(t *testing.T) {
message := []byte("hello world")
key := loadPrivateKey(t)

verifier := NewRSAVerifier(&key.PublicKey, sha512.New())
verifier := NewRSAVerifier(&key.PublicKey, crypto.SHA512)
signer := NewRSASigner(key)

mockRand := &mockRandom{0}
Expand Down Expand Up @@ -334,9 +345,9 @@ func verifyTestVector(t *testing.T, vector testVector) {
}

signer := NewRSASigner(key)
verifier := NewRSAVerifier(&key.PublicKey, sha512.New384())
verifier := NewRSAVerifier(&key.PublicKey, crypto.SHA384)

blindedMsg, state, err := verifier.fixedBlind(vector.msg, vector.salt, r, rInv)
blindedMsg, state, err := fixedBlind(vector.msg, vector.salt, r, rInv, verifier.pk, verifier.hash)
if err != nil {
t.Fatal(err)
}
Expand Down
8 changes: 7 additions & 1 deletion blindsign/blindsign.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@
// input during the BlindSign step.
package blindsign

import "io"

// A Verifier represents a specific instance of a blind signature verifier.
type Verifier interface {
// Blind produces an encoded protocol message and VerifierState based on
// the input message and Signer's public key.
Blind(message []byte) ([]byte, VerifierState, error)
Blind(random io.Reader, message []byte) ([]byte, VerifierState, error)

// Verify verifies a (message, signature) pair over and produces an error
// if the signature is invalid.
Verify(message, signature []byte) error
}

// A VerifierState represents the protocol state used to run and complete a
Expand Down