Skip to content

Commit 2536df9

Browse files
authored
Add deterministic blind RSA verifier (#379)
* Add deterministic blind RSA verifier
1 parent fa1d557 commit 2536df9

File tree

3 files changed

+141
-35
lines changed

3 files changed

+141
-35
lines changed

blindsign/blindrsa/blindrsa.go

+107-18
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ package blindrsa
44
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-rsa-blind-signatures-02
55

66
import (
7+
"crypto"
78
"crypto/rand"
89
"crypto/rsa"
10+
"crypto/sha256"
11+
"crypto/sha512"
912
"crypto/subtle"
1013
"errors"
1114
"hash"
@@ -15,21 +18,63 @@ import (
1518
"github.com/cloudflare/circl/blindsign"
1619
)
1720

21+
var errUnsupportedHashFunction = errors.New("unsupported hash function")
22+
1823
// An RSAVerifier represents a Verifier in the RSA blind signature protocol.
1924
// It carries state needed to produce and validate an RSA blind signature.
2025
type RSAVerifier struct {
2126
// Public key of the Signer
2227
pk *rsa.PublicKey
2328

29+
// Identifier of the cryptographic hash function used in producing the message signature
30+
cryptoHash crypto.Hash
31+
2432
// Hash function used in producing the message signature
2533
hash hash.Hash
2634
}
2735

36+
// A DeterminsiticRSAVerifier is an RSAVerifier that supports deterministic signatures.
37+
type DeterminsiticRSAVerifier struct {
38+
// Public key of the Signer
39+
pk *rsa.PublicKey
40+
41+
// Identifier of the cryptographic hash function used in producing the message signature
42+
cryptoHash crypto.Hash
43+
44+
// Hash function used in producing the message signature
45+
hash hash.Hash
46+
}
47+
48+
func convertHashFunction(hash crypto.Hash) hash.Hash {
49+
switch hash {
50+
case crypto.SHA256:
51+
return sha256.New()
52+
case crypto.SHA384:
53+
return sha512.New384()
54+
case crypto.SHA512:
55+
return sha512.New()
56+
default:
57+
panic(errUnsupportedHashFunction)
58+
}
59+
}
60+
61+
// NewDeterministicRSAVerifier creates a new RSAVerifier using the corresponding Signer parameters.
62+
func NewDeterministicRSAVerifier(pk *rsa.PublicKey, hash crypto.Hash) DeterminsiticRSAVerifier {
63+
h := convertHashFunction(hash)
64+
return DeterminsiticRSAVerifier{
65+
pk: pk,
66+
cryptoHash: hash,
67+
hash: h,
68+
}
69+
}
70+
2871
// NewRSAVerifier creates a new RSAVerifier using the corresponding Signer parameters.
29-
func NewRSAVerifier(pk *rsa.PublicKey, hash hash.Hash) RSAVerifier {
72+
func NewRSAVerifier(pk *rsa.PublicKey, hash crypto.Hash) RSAVerifier {
73+
h := convertHashFunction(hash)
3074
return RSAVerifier{
31-
pk: pk,
32-
hash: hash,
75+
pk: pk,
76+
cryptoHash: hash,
77+
hash: h,
3378
}
3479
}
3580

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

67-
func (v RSAVerifier) fixedBlind(message, salt []byte, r, rInv *big.Int) ([]byte, blindsign.VerifierState, error) {
68-
encodedMsg, err := encodeMessageEMSAPSS(message, v.pk, v.hash, salt)
112+
func fixedBlind(message, salt []byte, r, rInv *big.Int, pk *rsa.PublicKey, hash hash.Hash) ([]byte, blindsign.VerifierState, error) {
113+
encodedMsg, err := encodeMessageEMSAPSS(message, pk, hash, salt)
69114
if err != nil {
70115
return nil, nil, err
71116
}
72117

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

75-
bigE := big.NewInt(int64(v.pk.E))
76-
x := new(big.Int).Exp(r, bigE, v.pk.N)
120+
bigE := big.NewInt(int64(pk.E))
121+
x := new(big.Int).Exp(r, bigE, pk.N)
77122
z := new(big.Int).Set(m)
78123
z.Mul(z, x)
79-
z.Mod(z, v.pk.N)
124+
z.Mod(z, pk.N)
80125

81-
kLen := (v.pk.N.BitLen() + 7) / 8
126+
kLen := (pk.N.BitLen() + 7) / 8
82127
blindedMsg := make([]byte, kLen)
83128
z.FillBytes(blindedMsg)
84129

85130
return blindedMsg, RSAVerifierState{
86131
encodedMsg: encodedMsg,
87-
verifier: v,
132+
pk: pk,
133+
hash: hash,
88134
salt: salt,
89135
rInv: rInv,
90136
}, nil
91137
}
92138

139+
// Blind initializes the blind RSA protocol using an input message and source of randomness. The
140+
// signature is deterministic. This function fails if randomness was not provided.
141+
//
142+
// See the specification for more details:
143+
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-rsa-blind-signatures-02#section-5.1.1
144+
func (v DeterminsiticRSAVerifier) Blind(random io.Reader, message []byte) ([]byte, blindsign.VerifierState, error) {
145+
if random == nil {
146+
return nil, nil, ErrInvalidRandomness
147+
}
148+
149+
r, rInv, err := generateBlindingFactor(random, v.pk)
150+
if err != nil {
151+
return nil, nil, err
152+
}
153+
154+
return fixedBlind(message, nil, r, rInv, v.pk, v.hash)
155+
}
156+
157+
func verifyMessageSignature(message, signature []byte, saltLength int, pk *rsa.PublicKey, hash crypto.Hash) error {
158+
h := convertHashFunction(hash)
159+
h.Write(message)
160+
digest := h.Sum(nil)
161+
162+
err := rsa.VerifyPSS(pk, hash, digest, signature, &rsa.PSSOptions{
163+
Hash: hash,
164+
SaltLength: saltLength,
165+
})
166+
return err
167+
}
168+
169+
// Verify verifies the input (message, signature) pair and produces an error upon failure.
170+
func (v DeterminsiticRSAVerifier) Verify(message, signature []byte) error {
171+
return verifyMessageSignature(message, signature, 0, v.pk, v.cryptoHash)
172+
}
173+
93174
// Blind initializes the blind RSA protocol using an input message and source of randomness. The
94175
// signature includes a randomly generated PSS salt whose length equals the size of the underlying
95176
// hash function. This function fails if randomness was not provided.
@@ -112,7 +193,7 @@ func (v RSAVerifier) Blind(random io.Reader, message []byte) ([]byte, blindsign.
112193
return nil, nil, err
113194
}
114195

115-
return v.fixedBlind(message, salt, r, rInv)
196+
return fixedBlind(message, salt, r, rInv, v.pk, v.hash)
116197
}
117198

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

130-
return v.fixedBlind(message, salt, r, rInv)
211+
return fixedBlind(message, salt, r, rInv, v.pk, v.hash)
212+
}
213+
214+
// Verify verifies the input (message, signature) pair and produces an error upon failure.
215+
func (v RSAVerifier) Verify(message, signature []byte) error {
216+
return verifyMessageSignature(message, signature, v.hash.Size(), v.pk, v.cryptoHash)
131217
}
132218

133219
// An RSAVerifierState carries state needed to complete the blind signature protocol
134220
// as a verifier.
135221
type RSAVerifierState struct {
136-
// An RSA verifier carrying Signer verification state
137-
verifier RSAVerifier
222+
// Public key of the Signer
223+
pk *rsa.PublicKey
224+
225+
// Hash function used in producing the message signature
226+
hash hash.Hash
138227

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

171260
z := new(big.Int).SetBytes(data)
172261
s := new(big.Int).Set(state.rInv)
173262
s.Mul(s, z)
174-
s.Mod(s, state.verifier.pk.N)
263+
s.Mod(s, state.pk.N)
175264

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

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

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

blindsign/blindrsa/blindrsa_test.go

+27-16
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"crypto"
66
"crypto/rand"
77
"crypto/rsa"
8-
"crypto/sha512"
98
"crypto/x509"
109
"encoding/hex"
1110
"encoding/json"
@@ -15,6 +14,8 @@ import (
1514
"math/big"
1615
"os"
1716
"testing"
17+
18+
"github.com/cloudflare/circl/blindsign"
1819
)
1920

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

88-
func runSignatureProtocol(signer RSASigner, verifier RSAVerifier, message []byte, random io.Reader) ([]byte, error) {
89+
func runSignatureProtocol(signer RSASigner, verifier blindsign.Verifier, message []byte, random io.Reader) ([]byte, error) {
8990
blindedMsg, state, err := verifier.Blind(random, message)
9091
if err != nil {
9192
return nil, err
@@ -110,13 +111,7 @@ func runSignatureProtocol(signer RSASigner, verifier RSAVerifier, message []byte
110111
return nil, err
111112
}
112113

113-
hash := sha512.New()
114-
hash.Write(message)
115-
digest := hash.Sum(nil)
116-
err = rsa.VerifyPSS(verifier.pk, crypto.SHA512, digest, sig, &rsa.PSSOptions{
117-
Hash: crypto.SHA512,
118-
SaltLength: crypto.SHA512.Size(),
119-
})
114+
err = verifier.Verify(message, sig)
120115
if err != nil {
121116
return nil, err
122117
}
@@ -128,7 +123,23 @@ func TestRoundTrip(t *testing.T) {
128123
message := []byte("hello world")
129124
key := loadPrivateKey(t)
130125

131-
verifier := NewRSAVerifier(&key.PublicKey, sha512.New())
126+
verifier := NewRSAVerifier(&key.PublicKey, crypto.SHA512)
127+
signer := NewRSASigner(key)
128+
129+
sig, err := runSignatureProtocol(signer, verifier, message, rand.Reader)
130+
if err != nil {
131+
t.Fatal(err)
132+
}
133+
if sig == nil {
134+
t.Fatal("nil signature output")
135+
}
136+
}
137+
138+
func TestDeterministicRoundTrip(t *testing.T) {
139+
message := []byte("hello world")
140+
key := loadPrivateKey(t)
141+
142+
verifier := NewDeterministicRSAVerifier(&key.PublicKey, crypto.SHA512)
132143
signer := NewRSASigner(key)
133144

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

143-
func TestDeterministicSignFail(t *testing.T) {
154+
func TestDeterministicBlindFailure(t *testing.T) {
144155
message := []byte("hello world")
145156
key := loadPrivateKey(t)
146157

147-
verifier := NewRSAVerifier(&key.PublicKey, sha512.New())
158+
verifier := NewDeterministicRSAVerifier(&key.PublicKey, crypto.SHA512)
148159
signer := NewRSASigner(key)
149160

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

160-
verifier := NewRSAVerifier(&key.PublicKey, sha512.New())
171+
verifier := NewRSAVerifier(&key.PublicKey, crypto.SHA512)
161172
signer := NewRSASigner(key)
162173

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

196-
verifier := NewRSAVerifier(&key.PublicKey, sha512.New())
207+
verifier := NewRSAVerifier(&key.PublicKey, crypto.SHA512)
197208
signer := NewRSASigner(key)
198209

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

336347
signer := NewRSASigner(key)
337-
verifier := NewRSAVerifier(&key.PublicKey, sha512.New384())
348+
verifier := NewRSAVerifier(&key.PublicKey, crypto.SHA384)
338349

339-
blindedMsg, state, err := verifier.fixedBlind(vector.msg, vector.salt, r, rInv)
350+
blindedMsg, state, err := fixedBlind(vector.msg, vector.salt, r, rInv, verifier.pk, verifier.hash)
340351
if err != nil {
341352
t.Fatal(err)
342353
}

blindsign/blindsign.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,17 @@
77
// input during the BlindSign step.
88
package blindsign
99

10+
import "io"
11+
1012
// A Verifier represents a specific instance of a blind signature verifier.
1113
type Verifier interface {
1214
// Blind produces an encoded protocol message and VerifierState based on
1315
// the input message and Signer's public key.
14-
Blind(message []byte) ([]byte, VerifierState, error)
16+
Blind(random io.Reader, message []byte) ([]byte, VerifierState, error)
17+
18+
// Verify verifies a (message, signature) pair over and produces an error
19+
// if the signature is invalid.
20+
Verify(message, signature []byte) error
1521
}
1622

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

0 commit comments

Comments
 (0)