From 4185806e4d011dbd822b7a2b1db53400549f122d Mon Sep 17 00:00:00 2001 From: Chris Wood Date: Mon, 17 Oct 2022 08:20:57 -0400 Subject: [PATCH 1/3] Add deterministic blind RSA verifier --- blindsign/blindrsa/blindrsa.go | 127 ++++++++++++++++++++++++---- blindsign/blindrsa/blindrsa_test.go | 43 ++++++---- blindsign/blindsign.go | 8 +- 3 files changed, 143 insertions(+), 35 deletions(-) diff --git a/blindsign/blindrsa/blindrsa.go b/blindsign/blindrsa/blindrsa.go index 3fd6be877..1ea7cc04f 100644 --- a/blindsign/blindrsa/blindrsa.go +++ b/blindsign/blindrsa/blindrsa.go @@ -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" @@ -15,21 +18,65 @@ 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, } } @@ -64,32 +111,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. @@ -112,7 +195,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. @@ -127,14 +210,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 @@ -163,7 +254,7 @@ 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 } @@ -171,12 +262,12 @@ func (state RSAVerifierState) Finalize(data []byte) ([]byte, error) { 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 } @@ -186,7 +277,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() } diff --git a/blindsign/blindrsa/blindrsa_test.go b/blindsign/blindrsa/blindrsa_test.go index 62a37cb53..a7d4f8bfd 100644 --- a/blindsign/blindrsa/blindrsa_test.go +++ b/blindsign/blindrsa/blindrsa_test.go @@ -5,7 +5,6 @@ import ( "crypto" "crypto/rand" "crypto/rsa" - "crypto/sha512" "crypto/x509" "encoding/hex" "encoding/json" @@ -15,6 +14,8 @@ import ( "math/big" "os" "testing" + + "github.com/cloudflare/circl/blindsign" ) // 4096-bit RSA private key @@ -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 @@ -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 } @@ -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) @@ -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) @@ -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) @@ -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} @@ -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) } diff --git a/blindsign/blindsign.go b/blindsign/blindsign.go index f37d3c136..dabba89e4 100644 --- a/blindsign/blindsign.go +++ b/blindsign/blindsign.go @@ -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 From d851886100c5022b04f12bc98aac4277892afc2f Mon Sep 17 00:00:00 2001 From: Chris Wood Date: Mon, 17 Oct 2022 08:52:24 -0400 Subject: [PATCH 2/3] Fix style nit --- blindsign/blindrsa/blindrsa.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blindsign/blindrsa/blindrsa.go b/blindsign/blindrsa/blindrsa.go index 1ea7cc04f..1ab63813a 100644 --- a/blindsign/blindrsa/blindrsa.go +++ b/blindsign/blindrsa/blindrsa.go @@ -19,7 +19,7 @@ import ( ) var ( - errUnsupportedHashFunction = errors.New("Unsupported hash function") + errUnsupportedHashFunction = errors.New("unsupported hash function") ) // An RSAVerifier represents a Verifier in the RSA blind signature protocol. From a83ded0a569bae499a9885732c75258b5cad36f7 Mon Sep 17 00:00:00 2001 From: Chris Wood Date: Mon, 17 Oct 2022 14:31:05 -0400 Subject: [PATCH 3/3] Linter --- blindsign/blindrsa/blindrsa.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/blindsign/blindrsa/blindrsa.go b/blindsign/blindrsa/blindrsa.go index 1ab63813a..21375585a 100644 --- a/blindsign/blindrsa/blindrsa.go +++ b/blindsign/blindrsa/blindrsa.go @@ -18,9 +18,7 @@ import ( "github.com/cloudflare/circl/blindsign" ) -var ( - errUnsupportedHashFunction = errors.New("unsupported hash function") -) +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.