diff --git a/blindsign/blindrsa/blindrsa.go b/blindsign/blindrsa/brsa.go similarity index 58% rename from blindsign/blindrsa/blindrsa.go rename to blindsign/blindrsa/brsa.go index f0b9fdae..2aeb6956 100644 --- a/blindsign/blindrsa/blindrsa.go +++ b/blindsign/blindrsa/brsa.go @@ -2,6 +2,12 @@ package blindrsa // This package implements the blind RSA protocol based on the CFRG specification: // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-rsa-blind-signatures-02 +// +// Blind RSA is an example of a blind signature protocol is a two-party protocol +// for computing a digital signature. One party (the server) holds the signing +// key, and the other (the client) holds the message input. Blindness +// ensures that the server does not learn anything about the client's +// input during the BlindSign step. import ( "crypto" @@ -9,20 +15,19 @@ import ( "crypto/rsa" "crypto/sha256" "crypto/sha512" - "crypto/subtle" "errors" + "fmt" "hash" "io" "math/big" - - "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 { +// An RandomBRSAVerifier represents a Verifier in the RSA blind signature protocol. +// It carries state needed to produce and validate an RSA signature produced +// using the blind RSA protocol. +type RandomBRSAVerifier struct { // Public key of the Signer pk *rsa.PublicKey @@ -33,8 +38,8 @@ type RSAVerifier struct { hash hash.Hash } -// A DeterminsiticRSAVerifier is an RSAVerifier that supports deterministic signatures. -type DeterminsiticRSAVerifier struct { +// A DeterminsiticBRSAVerifier is a BRSAVerifier that supports deterministic signatures. +type DeterminsiticBRSAVerifier struct { // Public key of the Signer pk *rsa.PublicKey @@ -45,6 +50,14 @@ type DeterminsiticRSAVerifier struct { hash hash.Hash } +type BRSAVerifier interface { + Blind(random io.Reader, message []byte) ([]byte, BRSAVerifierState, error) + FixedBlind(message, blind, salt []byte) ([]byte, BRSAVerifierState, error) + Verify(message, signature []byte) error + Hash() hash.Hash + PublicKey() *rsa.PublicKey +} + func convertHashFunction(hash crypto.Hash) hash.Hash { switch hash { case crypto.SHA256: @@ -58,42 +71,58 @@ func convertHashFunction(hash crypto.Hash) hash.Hash { } } -// NewDeterministicRSAVerifier creates a new RSAVerifier using the corresponding Signer parameters. -func NewDeterministicRSAVerifier(pk *rsa.PublicKey, hash crypto.Hash) DeterminsiticRSAVerifier { +// NewDeterministicBRSAVerifier creates a new DeterminsiticBRSAVerifier using the corresponding Signer parameters. +func NewDeterministicBRSAVerifier(pk *rsa.PublicKey, hash crypto.Hash) BRSAVerifier { h := convertHashFunction(hash) - return DeterminsiticRSAVerifier{ + return DeterminsiticBRSAVerifier{ pk: pk, cryptoHash: hash, hash: h, } } -// NewRSAVerifier creates a new RSAVerifier using the corresponding Signer parameters. -func NewRSAVerifier(pk *rsa.PublicKey, hash crypto.Hash) RSAVerifier { +func (v DeterminsiticBRSAVerifier) Hash() hash.Hash { + return v.hash +} + +func (v DeterminsiticBRSAVerifier) PublicKey() *rsa.PublicKey { + return v.pk +} + +// NewBRSAVerifier creates a new BRSAVerifier using the corresponding Signer parameters. +func NewBRSAVerifier(pk *rsa.PublicKey, hash crypto.Hash) BRSAVerifier { h := convertHashFunction(hash) - return RSAVerifier{ + return RandomBRSAVerifier{ pk: pk, cryptoHash: hash, hash: h, } } -func encodeMessageEMSAPSS(message []byte, key *rsa.PublicKey, hash hash.Hash, salt []byte) ([]byte, error) { +func (v RandomBRSAVerifier) Hash() hash.Hash { + return v.hash +} + +func (v RandomBRSAVerifier) PublicKey() *rsa.PublicKey { + return v.pk +} + +func encodeMessageEMSAPSS(message []byte, N *big.Int, hash hash.Hash, salt []byte) ([]byte, error) { hash.Reset() // Ensure the hash state is cleared hash.Write(message) digest := hash.Sum(nil) hash.Reset() - emBits := key.N.BitLen() - 1 + emBits := N.BitLen() - 1 encodedMsg, err := emsaPSSEncode(digest[:], emBits, salt, hash) return encodedMsg, err } -func generateBlindingFactor(random io.Reader, key *rsa.PublicKey) (*big.Int, *big.Int, error) { +func generateBlindingFactor(random io.Reader, N *big.Int) (*big.Int, *big.Int, error) { randReader := random if randReader == nil { randReader = rand.Reader } - r, err := rand.Int(randReader, key.N) + r, err := rand.Int(randReader, N) if err != nil { return nil, nil, err } @@ -101,18 +130,19 @@ func generateBlindingFactor(random io.Reader, key *rsa.PublicKey) (*big.Int, *bi if r.Sign() == 0 { r = bigOne } - rInv := new(big.Int).ModInverse(r, key.N) + rInv := new(big.Int).ModInverse(r, N) if rInv == nil { + fmt.Println(r, N) return nil, nil, ErrInvalidBlind } return r, rInv, nil } -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) +func fixedBlind(message, salt []byte, r, rInv *big.Int, pk *rsa.PublicKey, hash hash.Hash) ([]byte, BRSAVerifierState, error) { + encodedMsg, err := encodeMessageEMSAPSS(message, pk.N, hash, salt) if err != nil { - return nil, nil, err + return nil, BRSAVerifierState{}, err } m := new(big.Int).SetBytes(encodedMsg) @@ -127,7 +157,7 @@ func fixedBlind(message, salt []byte, r, rInv *big.Int, pk *rsa.PublicKey, hash blindedMsg := make([]byte, kLen) z.FillBytes(blindedMsg) - return blindedMsg, RSAVerifierState{ + return blindedMsg, BRSAVerifierState{ encodedMsg: encodedMsg, pk: pk, hash: hash, @@ -141,34 +171,44 @@ func fixedBlind(message, salt []byte, r, rInv *big.Int, pk *rsa.PublicKey, hash // // 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) { +func (v DeterminsiticBRSAVerifier) Blind(random io.Reader, message []byte) ([]byte, BRSAVerifierState, error) { if random == nil { - return nil, nil, ErrInvalidRandomness + return nil, BRSAVerifierState{}, ErrInvalidRandomness } - r, rInv, err := generateBlindingFactor(random, v.pk) + r, rInv, err := generateBlindingFactor(random, v.pk.N) if err != nil { - return nil, nil, err + return nil, BRSAVerifierState{}, 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 +func saltLength(opts *rsa.PSSOptions) int { + if opts == nil { + return rsa.PSSSaltLengthAuto + } + return opts.SaltLength +} + +// FixedBlind runs the Blind function with fixed blind and salt inputs. +func (v DeterminsiticBRSAVerifier) FixedBlind(message, blind, salt []byte) ([]byte, BRSAVerifierState, error) { + if blind == nil { + return nil, BRSAVerifierState{}, ErrInvalidRandomness + } + + r := new(big.Int).SetBytes(blind) + rInv := new(big.Int).ModInverse(r, v.pk.N) + if rInv == nil { + return nil, BRSAVerifierState{}, ErrInvalidBlind + } + + 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 DeterminsiticRSAVerifier) Verify(message, signature []byte) error { - return verifyMessageSignature(message, signature, 0, v.pk, v.cryptoHash) +func (v DeterminsiticBRSAVerifier) Verify(message, signature []byte) error { + return verifyMessageSignature(message, signature, 0, convertToCustomPublicKey(v.pk), v.cryptoHash) } // Blind initializes the blind RSA protocol using an input message and source of randomness. The @@ -177,48 +217,48 @@ func (v DeterminsiticRSAVerifier) Verify(message, signature []byte) error { // // 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 RSAVerifier) Blind(random io.Reader, message []byte) ([]byte, blindsign.VerifierState, error) { +func (v RandomBRSAVerifier) Blind(random io.Reader, message []byte) ([]byte, BRSAVerifierState, error) { if random == nil { - return nil, nil, ErrInvalidRandomness + return nil, BRSAVerifierState{}, ErrInvalidRandomness } salt := make([]byte, v.hash.Size()) _, err := io.ReadFull(random, salt) if err != nil { - return nil, nil, err + return nil, BRSAVerifierState{}, err } - r, rInv, err := generateBlindingFactor(random, v.pk) + r, rInv, err := generateBlindingFactor(random, v.pk.N) if err != nil { - return nil, nil, err + return nil, BRSAVerifierState{}, err } return fixedBlind(message, salt, r, rInv, v.pk, v.hash) } // FixedBlind runs the Blind function with fixed blind and salt inputs. -func (v RSAVerifier) FixedBlind(message, blind, salt []byte) ([]byte, blindsign.VerifierState, error) { +func (v RandomBRSAVerifier) FixedBlind(message, blind, salt []byte) ([]byte, BRSAVerifierState, error) { if blind == nil { - return nil, nil, ErrInvalidRandomness + return nil, BRSAVerifierState{}, ErrInvalidRandomness } r := new(big.Int).SetBytes(blind) rInv := new(big.Int).ModInverse(r, v.pk.N) if rInv == nil { - return nil, nil, ErrInvalidBlind + return nil, BRSAVerifierState{}, ErrInvalidBlind } 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) +func (v RandomBRSAVerifier) Verify(message, signature []byte) error { + return verifyMessageSignature(message, signature, v.hash.Size(), convertToCustomPublicKey(v.pk), v.cryptoHash) } -// An RSAVerifierState carries state needed to complete the blind signature protocol +// An BRSAVerifierState carries state needed to complete the blind signature protocol // as a verifier. -type RSAVerifierState struct { +type BRSAVerifierState struct { // Public key of the Signer pk *rsa.PublicKey @@ -235,23 +275,11 @@ type RSAVerifierState struct { rInv *big.Int } -func verifyBlindSignature(pub *rsa.PublicKey, hashed, sig []byte) error { - m := new(big.Int).SetBytes(hashed) - bigSig := new(big.Int).SetBytes(sig) - - c := encrypt(new(big.Int), pub, bigSig) - if subtle.ConstantTimeCompare(m.Bytes(), c.Bytes()) == 1 { - return nil - } else { - return rsa.ErrVerification - } -} - // Finalize computes and outputs the final signature, if it's valid. Otherwise, it returns an 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) { +func (state BRSAVerifierState) Finalize(data []byte) ([]byte, error) { kLen := (state.pk.N.BitLen() + 7) / 8 if len(data) != kLen { return nil, ErrUnexpectedSize @@ -265,7 +293,7 @@ func (state RSAVerifierState) Finalize(data []byte) ([]byte, error) { sig := make([]byte, kLen) s.FillBytes(sig) - err := verifyBlindSignature(state.pk, state.encodedMsg, sig) + err := verifyBlindSignature(convertToCustomPublicKey(state.pk), state.encodedMsg, sig) if err != nil { return nil, err } @@ -274,28 +302,28 @@ 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 { +func (state BRSAVerifierState) CopyBlind() []byte { r := new(big.Int).ModInverse(state.rInv, state.pk.N) return r.Bytes() } // CopySalt returns an encoding of the per-message salt used in the protocol. -func (state RSAVerifierState) CopySalt() []byte { +func (state BRSAVerifierState) CopySalt() []byte { salt := make([]byte, len(state.salt)) copy(salt, state.salt) return salt } -// An RSASigner represents the Signer in the blind RSA protocol. +// An BRSASigner represents the Signer in the blind RSA protocol. // It carries the raw RSA private key used for signing blinded messages. -type RSASigner struct { +type BRSASigner struct { // An RSA private key sk *rsa.PrivateKey } -// NewRSASigner creates a new Signer for the blind RSA protocol using an RSA private key. -func NewRSASigner(sk *rsa.PrivateKey) RSASigner { - return RSASigner{ +// NewBRSASigner creates a new Signer for the blind RSA protocol using an RSA private key. +func NewBRSASigner(sk *rsa.PrivateKey) BRSASigner { + return BRSASigner{ sk: sk, } } @@ -305,7 +333,7 @@ func NewRSASigner(sk *rsa.PrivateKey) RSASigner { // // See the specification for more details: // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-rsa-blind-signatures-02#section-5.1.2 -func (signer RSASigner) BlindSign(data []byte) ([]byte, error) { +func (signer BRSASigner) BlindSign(data []byte) ([]byte, error) { kLen := (signer.sk.N.BitLen() + 7) / 8 if len(data) != kLen { return nil, ErrUnexpectedSize @@ -316,7 +344,7 @@ func (signer RSASigner) BlindSign(data []byte) ([]byte, error) { return nil, ErrInvalidMessageLength } - s, err := decryptAndCheck(rand.Reader, signer.sk, m) + s, err := decryptAndCheck(rand.Reader, convertToCustomPrivateKey(signer.sk), m) if err != nil { return nil, err } diff --git a/blindsign/blindrsa/blindrsa_test.go b/blindsign/blindrsa/brsa_test.go similarity index 59% rename from blindsign/blindrsa/blindrsa_test.go rename to blindsign/blindrsa/brsa_test.go index a7d4f8bf..ca31e5f6 100644 --- a/blindsign/blindrsa/blindrsa_test.go +++ b/blindsign/blindrsa/brsa_test.go @@ -14,79 +14,53 @@ import ( "math/big" "os" "testing" - - "github.com/cloudflare/circl/blindsign" ) -// 4096-bit RSA private key +// 2048-bit RSA private key const testPrivateKey = ` -----BEGIN RSA PRIVATE KEY----- -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 -----END RSA PRIVATE KEY-----` -func loadPrivateKey(t *testing.T) *rsa.PrivateKey { +func loadPrivateKey() (*rsa.PrivateKey, error) { block, _ := pem.Decode([]byte(testPrivateKey)) if block == nil || block.Type != "RSA PRIVATE KEY" { - t.Fatal("PEM private key decoding failed") + return nil, fmt.Errorf("PEM private key decoding failed") } privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { - t.Fatal(err) + return nil, err } - return privateKey + return privateKey, nil } -func runSignatureProtocol(signer RSASigner, verifier blindsign.Verifier, message []byte, random io.Reader) ([]byte, error) { +func runSignatureProtocol(signer BRSASigner, verifier BRSAVerifier, message []byte, random io.Reader) ([]byte, error) { blindedMsg, state, err := verifier.Blind(random, message) if err != nil { return nil, err @@ -121,10 +95,13 @@ func runSignatureProtocol(signer RSASigner, verifier blindsign.Verifier, message func TestRoundTrip(t *testing.T) { message := []byte("hello world") - key := loadPrivateKey(t) + key, err := loadPrivateKey() + if err != nil { + t.Fatal(err) + } - verifier := NewRSAVerifier(&key.PublicKey, crypto.SHA512) - signer := NewRSASigner(key) + verifier := NewBRSAVerifier(&key.PublicKey, crypto.SHA512) + signer := NewBRSASigner(key) sig, err := runSignatureProtocol(signer, verifier, message, rand.Reader) if err != nil { @@ -137,10 +114,13 @@ func TestRoundTrip(t *testing.T) { func TestDeterministicRoundTrip(t *testing.T) { message := []byte("hello world") - key := loadPrivateKey(t) + key, err := loadPrivateKey() + if err != nil { + t.Fatal(err) + } - verifier := NewDeterministicRSAVerifier(&key.PublicKey, crypto.SHA512) - signer := NewRSASigner(key) + verifier := NewDeterministicBRSAVerifier(&key.PublicKey, crypto.SHA512) + signer := NewBRSASigner(key) sig, err := runSignatureProtocol(signer, verifier, message, rand.Reader) if err != nil { @@ -153,12 +133,15 @@ func TestDeterministicRoundTrip(t *testing.T) { func TestDeterministicBlindFailure(t *testing.T) { message := []byte("hello world") - key := loadPrivateKey(t) + key, err := loadPrivateKey() + if err != nil { + t.Fatal(err) + } - verifier := NewDeterministicRSAVerifier(&key.PublicKey, crypto.SHA512) - signer := NewRSASigner(key) + verifier := NewDeterministicBRSAVerifier(&key.PublicKey, crypto.SHA512) + signer := NewBRSASigner(key) - _, err := runSignatureProtocol(signer, verifier, message, nil) + _, err = runSignatureProtocol(signer, verifier, message, nil) if err == nil { t.Fatal("Expected signature generation to fail with empty randomness") } @@ -166,10 +149,13 @@ func TestDeterministicBlindFailure(t *testing.T) { func TestRandomSignVerify(t *testing.T) { message := []byte("hello world") - key := loadPrivateKey(t) + key, err := loadPrivateKey() + if err != nil { + t.Fatal(err) + } - verifier := NewRSAVerifier(&key.PublicKey, crypto.SHA512) - signer := NewRSASigner(key) + verifier := NewBRSAVerifier(&key.PublicKey, crypto.SHA512) + signer := NewBRSASigner(key) sig1, err := runSignatureProtocol(signer, verifier, message, rand.Reader) if err != nil { @@ -202,10 +188,13 @@ func (r *mockRandom) Read(p []byte) (n int, err error) { func TestFixedRandomSignVerify(t *testing.T) { message := []byte("hello world") - key := loadPrivateKey(t) + key, err := loadPrivateKey() + if err != nil { + t.Fatal(err) + } - verifier := NewRSAVerifier(&key.PublicKey, crypto.SHA512) - signer := NewRSASigner(key) + verifier := NewBRSAVerifier(&key.PublicKey, crypto.SHA512) + signer := NewBRSASigner(key) mockRand := &mockRandom{0} sig1, err := runSignatureProtocol(signer, verifier, message, mockRand) @@ -329,7 +318,11 @@ func (tvl *testVectorList) UnmarshalJSON(data []byte) error { } func verifyTestVector(t *testing.T, vector testVector) { - key := loadPrivateKey(t) + key, err := loadPrivateKey() + if err != nil { + t.Fatal(err) + } + key.PublicKey.N = vector.n key.PublicKey.E = vector.e key.D = vector.d @@ -344,10 +337,10 @@ func verifyTestVector(t *testing.T, vector testVector) { t.Fatal("Failed to compute blind inverse") } - signer := NewRSASigner(key) - verifier := NewRSAVerifier(&key.PublicKey, crypto.SHA384) + signer := NewBRSASigner(key) + verifier := NewBRSAVerifier(&key.PublicKey, crypto.SHA384) - blindedMsg, state, err := fixedBlind(vector.msg, vector.salt, r, rInv, verifier.pk, verifier.hash) + blindedMsg, state, err := fixedBlind(vector.msg, vector.salt, r, rInv, verifier.PublicKey(), verifier.Hash()) if err != nil { t.Fatal(err) } @@ -362,8 +355,8 @@ func verifyTestVector(t *testing.T, vector testVector) { t.Fatal(err) } - if !bytes.Equal(state.(RSAVerifierState).encodedMsg, vector.encodedMessage) { - t.Errorf("Encoded message mismatch: expected %x, got %x", state.(RSAVerifierState).encodedMsg, vector.encodedMessage) + if !bytes.Equal(state.encodedMsg, vector.encodedMessage) { + t.Errorf("Encoded message mismatch: expected %x, got %x", state.encodedMsg, vector.encodedMessage) } if !bytes.Equal(sig, vector.sig) { @@ -387,3 +380,50 @@ func TestVectors(t *testing.T) { verifyTestVector(t, vector) } } + +func BenchmarkBRSA(b *testing.B) { + message := []byte("hello world") + key, err := loadStrongRSAKey() + if err != nil { + b.Fatal(err) + } + + verifier := NewBRSAVerifier(&key.PublicKey, crypto.SHA512) + signer := NewBRSASigner(key) + + var blindedMsg []byte + var state BRSAVerifierState + b.Run("Blind", func(b *testing.B) { + for n := 0; n < b.N; n++ { + blindedMsg, state, err = verifier.Blind(rand.Reader, message) + if err != nil { + b.Fatal(err) + } + } + }) + + var blindedSig []byte + b.Run("BlindSign", func(b *testing.B) { + for n := 0; n < b.N; n++ { + blindedSig, err = signer.BlindSign(blindedMsg) + if err != nil { + b.Fatal(err) + } + } + }) + + var sig []byte + b.Run("Finalize", func(b *testing.B) { + for n := 0; n < b.N; n++ { + sig, err = state.Finalize(blindedSig) + if err != nil { + b.Fatal(err) + } + } + }) + + err = verifier.Verify(message, sig) + if err != nil { + b.Fatal(err) + } +} diff --git a/blindsign/blindrsa/common.go b/blindsign/blindrsa/common.go new file mode 100644 index 00000000..12360f23 --- /dev/null +++ b/blindsign/blindrsa/common.go @@ -0,0 +1,56 @@ +package blindrsa + +import ( + "crypto" + "crypto/rsa" + "crypto/subtle" + "encoding/binary" + "math/big" +) + +func encodeMessageMetadata(message, metadata []byte) []byte { + lenBuffer := []byte{'m', 's', 'g', 0, 0, 0, 0} + + binary.BigEndian.PutUint32(lenBuffer[3:], uint32(len(metadata))) + framedMetadata := append(lenBuffer, metadata...) + return append(framedMetadata, message...) +} + +func verifyPSS(pub *BigPublicKey, hash crypto.Hash, digest []byte, sig []byte, opts *rsa.PSSOptions) error { + if len(sig) != pub.Size() { + return rsa.ErrVerification + } + s := new(big.Int).SetBytes(sig) + m := encrypt(new(big.Int), pub.N, pub.e, s) + emBits := pub.N.BitLen() - 1 + emLen := (emBits + 7) / 8 + if m.BitLen() > emLen*8 { + return rsa.ErrVerification + } + em := m.FillBytes(make([]byte, emLen)) + return emsaPSSVerify(digest, em, emBits, saltLength(opts), hash.New()) +} + +func verifyMessageSignature(message, signature []byte, saltLength int, pk *BigPublicKey, hash crypto.Hash) error { + h := convertHashFunction(hash) + h.Write(message) + digest := h.Sum(nil) + + err := verifyPSS(pk, hash, digest, signature, &rsa.PSSOptions{ + Hash: hash, + SaltLength: saltLength, + }) + return err +} + +func verifyBlindSignature(pub *BigPublicKey, hashed, sig []byte) error { + m := new(big.Int).SetBytes(hashed) + bigSig := new(big.Int).SetBytes(sig) + + c := encrypt(new(big.Int), pub.N, pub.e, bigSig) + if subtle.ConstantTimeCompare(m.Bytes(), c.Bytes()) == 1 { + return nil + } else { + return rsa.ErrVerification + } +} diff --git a/blindsign/blindrsa/format_test_vectors.py b/blindsign/blindrsa/format_test_vectors.py new file mode 100644 index 00000000..073b8c94 --- /dev/null +++ b/blindsign/blindrsa/format_test_vectors.py @@ -0,0 +1,36 @@ +import sys +import json +import textwrap + +def wrap_line(value): + return textwrap.fill(value, width=65) + +def format_vector(vector_keys, vector_fname): + with open(vector_fname, "r") as fh: + data = json.load(fh) + formatted = "~~~\n" + for i, entry in enumerate(data): + formatted = formatted + ("// Test vector %d" % (i+1)) + "\n" + for key in vector_keys: + if key in entry: + formatted = formatted + wrap_line(key + ": " + str(entry[key])) + "\n" + formatted = formatted + "\n" + print(formatted + "~~~\n") + +ordered_keys = [ + "p", + "q", + "d", + "e", + "N", + "msg", + "metadata", + "eprime", + "rand", + "blind", + "salt", + "blinded_msg", + "blinded_sig", + "sig", +] +format_vector(ordered_keys, sys.argv[1]) diff --git a/blindsign/blindrsa/pbrsa.go b/blindsign/blindrsa/pbrsa.go new file mode 100644 index 00000000..2e9f03c1 --- /dev/null +++ b/blindsign/blindrsa/pbrsa.go @@ -0,0 +1,348 @@ +package blindrsa + +// This package implements a partially blind RSA protocol. + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "hash" + "io" + "math/big" + + "golang.org/x/crypto/hkdf" +) + +// BigPublicKey is the same as an rsa.PublicKey struct, except the public +// key is represented as a big integer as opposed to an int. For the partially +// blind scheme, this is required since the public key will typically be +// any value in the RSA group. +type BigPublicKey struct { + N *big.Int + e *big.Int +} + +// Size returns the size of the public key. +func (pub *BigPublicKey) Size() int { + return (pub.N.BitLen() + 7) / 8 +} + +// Marshal encodes the public key exponent (e). +func (pub *BigPublicKey) Marshal() []byte { + buf := make([]byte, (pub.e.BitLen()+7)/8) + pub.e.FillBytes(buf) + return buf +} + +// CustomPublicKey is similar to rsa.PrivateKey, containing information needed +// for a private key used in the partially blind signature protocol. +type BigPrivateKey struct { + d *big.Int + pk *BigPublicKey + p *big.Int + q *big.Int +} + +// A PBRSAVerifier represents a Verifier in the RSA blind signature protocol. +// It carries state needed to produce and validate an RSA signature produced +// using the blind RSA protocol. +type PBRSAVerifier struct { + // Public key of the Signer + pk *BigPublicKey + + // 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 newCustomPublicKey(pk *rsa.PublicKey) *BigPublicKey { + return &BigPublicKey{ + N: pk.N, + e: new(big.Int).SetInt64(int64(pk.E)), + } +} + +// NewPBRSAVerifier creates a new PBRSAVerifier using the corresponding Signer parameters. +func NewPBRSAVerifier(pk *rsa.PublicKey, hash crypto.Hash) PBRSAVerifier { + h := convertHashFunction(hash) + return PBRSAVerifier{ + pk: newCustomPublicKey(pk), + cryptoHash: hash, + hash: h, + } +} + +// augmentPublicKey tweaks the public key based on the input metadata. +// +// See the specification for more details: +// https://datatracker.ietf.org/doc/html/draft-amjad-cfrg-partially-blind-rsa-00#name-public-key-augmentation +// +// See the following issue for more discussion on HKDF vs hash-to-field: +// https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/issues/202 +func augmentPublicKey(h crypto.Hash, pk *BigPublicKey, metadata []byte) *BigPublicKey { + // expandLen = ceil((ceil(log2(\lambda)) + k) / 8), where k is the security parameter of the suite (e.g., k = 128). + // We stretch the input metadata beyond \lambda bits s.t. the output bytes are indifferentiable from truly random bytes + lambda := pk.N.BitLen() / 2 + expandLen := uint((lambda + 128) / 8) + + hkdfSalt := make([]byte, (pk.N.BitLen()+7)/8) + pk.N.FillBytes(hkdfSalt) + hkdfInput := append([]byte("key"), append(metadata, 0x00)...) + + hkdf := hkdf.New(h.New, hkdfInput, hkdfSalt, []byte("PBRSA")) + bytes := make([]byte, expandLen) + _, err := hkdf.Read(bytes) + if err != nil { + panic(err) + } + + // H_MD(D) = 1 || G(x), where G(x) is output of length \lambda-2 bits + // We do this by sampling \lambda bits, clearing the top two bits (so the output is \lambda-2 bits) + // and setting the bottom bit (so the result is odd). + hmd := new(big.Int).SetBytes(bytes[:lambda/8]) + hmd.SetBit(hmd, 0, 1) + hmd.SetBit(hmd, lambda-1, 0) + hmd.SetBit(hmd, lambda-2, 0) + + // Compute e_MD = e * H_MD(D) + newE := new(big.Int).Mul(hmd, pk.e) + return &BigPublicKey{ + N: pk.N, + e: newE, + } +} + +func convertToCustomPublicKey(pk *rsa.PublicKey) *BigPublicKey { + return &BigPublicKey{ + N: pk.N, + e: new(big.Int).SetInt64(int64(pk.E)), + } +} + +func convertToCustomPrivateKey(sk *rsa.PrivateKey) *BigPrivateKey { + return &BigPrivateKey{ + pk: &BigPublicKey{ + N: sk.N, + e: new(big.Int).SetInt64(int64(sk.PublicKey.E)), + }, + d: sk.D, + p: sk.Primes[0], + q: sk.Primes[1], + } +} + +// augmentPrivateKey tweaks the private key using the metadata as input. +// +// See the specification for more details: +// https://datatracker.ietf.org/doc/html/draft-amjad-cfrg-partially-blind-rsa-00#name-private-key-augmentation +func augmentPrivateKey(h crypto.Hash, sk *BigPrivateKey, metadata []byte) *BigPrivateKey { + // pih(N) = (p-1)(q-1) + pm1 := new(big.Int).Set(sk.p) + pm1.Sub(pm1, new(big.Int).SetInt64(int64(1))) + qm1 := new(big.Int).Set(sk.q) + qm1.Sub(qm1, new(big.Int).SetInt64(int64(1))) + phi := new(big.Int).Mul(pm1, qm1) + + // d = e^-1 mod phi(N) + pk := augmentPublicKey(h, sk.pk, metadata) + bigE := new(big.Int).Mod(pk.e, phi) + d := new(big.Int).ModInverse(bigE, phi) + return &BigPrivateKey{ + pk: pk, + d: d, + p: sk.p, + q: sk.q, + } +} + +func fixedPartiallyBlind(message, rand, salt []byte, r, rInv *big.Int, pk *BigPublicKey, hash hash.Hash) ([]byte, PBRSAVerifierState, error) { + encodedMsg, err := encodeMessageEMSAPSS(message, pk.N, hash, salt) + if err != nil { + return nil, PBRSAVerifierState{}, err + } + + m := new(big.Int).SetBytes(encodedMsg) + + bigE := pk.e + x := new(big.Int).Exp(r, bigE, pk.N) + z := new(big.Int).Set(m) + z.Mul(z, x) + z.Mod(z, pk.N) + + kLen := (pk.N.BitLen() + 7) / 8 + blindedMsg := make([]byte, kLen) + z.FillBytes(blindedMsg) + + return blindedMsg, PBRSAVerifierState{ + encodedMsg: encodedMsg, + pk: pk, + hash: hash, + salt: salt, + rInv: rInv, + rand: rand, + }, nil +} + +// 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. +// +// See the specification for more details: +// https://datatracker.ietf.org/doc/html/draft-amjad-cfrg-partially-blind-rsa-00#name-blind +func (v PBRSAVerifier) Blind(random io.Reader, message, metadata []byte) ([]byte, PBRSAVerifierState, error) { + if random == nil { + return nil, PBRSAVerifierState{}, ErrInvalidRandomness + } + + salt := make([]byte, v.hash.Size()) + _, err := random.Read(salt) + if err != nil { + return nil, PBRSAVerifierState{}, err + } + + r, rInv, err := generateBlindingFactor(random, v.pk.N) + if err != nil { + return nil, PBRSAVerifierState{}, err + } + + // Pick a random string rand of length 32 bytes + rand := make([]byte, 32) + random.Read(rand) + + // M' = M || rand + msgPrime := append(rand, message...) + + // Compute e_MD = e * H_MD(D) + metadataKey := augmentPublicKey(v.cryptoHash, v.pk, metadata) + + // Do the rest with (M', D) as the message being signed + inputMsg := encodeMessageMetadata(msgPrime, metadata) + + return fixedPartiallyBlind(inputMsg, rand, salt, r, rInv, metadataKey, v.hash) +} + +// Verify verifies the input (message, signature) pair and produces an error upon failure. +// +// See the specification for more details: +// https://datatracker.ietf.org/doc/html/draft-amjad-cfrg-partially-blind-rsa-00#name-verification-2 +func (v PBRSAVerifier) Verify(message, metadata, rand, signature []byte) error { + msgPrime := append(rand, message...) + metadataKey := augmentPublicKey(v.cryptoHash, v.pk, metadata) + inputMsg := encodeMessageMetadata(msgPrime, metadata) + return verifyMessageSignature(inputMsg, signature, v.hash.Size(), metadataKey, v.cryptoHash) +} + +// A PBRSAVerifierState carries state needed to complete the blind signature protocol +// as a verifier. +type PBRSAVerifierState struct { + // Public key of the Signer + pk *BigPublicKey + + // Hash function used in producing the message signature + hash hash.Hash + + // The hashed and encoded message being signed + encodedMsg []byte + + // The salt used when encoding the message + salt []byte + + // The random component attached to each message + rand []byte + + // Inverse of the blinding factor produced by the Verifier + rInv *big.Int +} + +// Finalize computes and outputs the final signature, if it's valid. Otherwise, it returns an error. +// +// See the specification for more details: +// https://datatracker.ietf.org/doc/html/draft-amjad-cfrg-partially-blind-rsa-00#name-finalize +func (state PBRSAVerifierState) Finalize(data []byte) ([]byte, error) { + 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.pk.N) + + sig := make([]byte, kLen) + s.FillBytes(sig) + + err := verifyBlindSignature(state.pk, state.encodedMsg, sig) + if err != nil { + return nil, err + } + + return sig, nil +} + +// CopyBlind returns an encoding of the blind value used in the protocol. +func (state PBRSAVerifierState) CopyBlind() []byte { + r := new(big.Int).ModInverse(state.rInv, state.pk.N) + return r.Bytes() +} + +// CopySalt returns an encoding of the per-message salt used in the protocol. +func (state PBRSAVerifierState) CopySalt() []byte { + salt := make([]byte, len(state.salt)) + copy(salt, state.salt) + return salt +} + +// CopyRand returns the random component of the per-message randomness. +func (state PBRSAVerifierState) CopyRand() []byte { + rand := make([]byte, len(state.rand)) + copy(rand, state.rand) + return rand +} + +// An PBRSASigner represents the Signer in the blind RSA protocol. +// It carries the raw RSA private key used for signing blinded messages. +type PBRSASigner struct { + // An RSA private key + sk *BigPrivateKey + h crypto.Hash +} + +// NewPBRSASigner creates a new Signer for the blind RSA protocol using an RSA private key. +func NewPBRSASigner(sk *rsa.PrivateKey, h crypto.Hash) PBRSASigner { + return PBRSASigner{ + sk: convertToCustomPrivateKey(sk), + h: h, + } +} + +// BlindSign blindly computes the RSA operation using the Signer's private key on the blinded +// message input, if it's of valid length, and returns an error should the function fail. +// +// See the specification for more details: +// https://datatracker.ietf.org/doc/html/draft-amjad-cfrg-partially-blind-rsa-00#name-blindsign +func (signer PBRSASigner) BlindSign(data, metadata []byte) ([]byte, error) { + kLen := (signer.sk.pk.N.BitLen() + 7) / 8 + if len(data) != kLen { + return nil, ErrUnexpectedSize + } + + m := new(big.Int).SetBytes(data) + if m.Cmp(signer.sk.pk.N) > 0 { + return nil, ErrInvalidMessageLength + } + + skPrime := augmentPrivateKey(signer.h, signer.sk, metadata) + + s, err := decryptAndCheck(rand.Reader, skPrime, m) + if err != nil { + return nil, err + } + + blindSig := make([]byte, kLen) + s.FillBytes(blindSig) + + return blindSig, nil +} diff --git a/blindsign/blindrsa/pbrsa_test.go b/blindsign/blindrsa/pbrsa_test.go new file mode 100644 index 00000000..0e7f0593 --- /dev/null +++ b/blindsign/blindrsa/pbrsa_test.go @@ -0,0 +1,339 @@ +package blindrsa + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/hex" + "encoding/json" + "encoding/pem" + "fmt" + "io" + "io/ioutil" + "math/big" + "os" + "testing" +) + +const ( + pbrsaTestVectorOutEnvironmentKey = "PBRSA_TEST_VECTORS_OUT" + pbrsaTestVectorInEnvironmentKey = "PBRSA_TEST_VECTORS_IN" +) + +func loadStrongRSAKey() (*rsa.PrivateKey, error) { + // https://gist.github.com/chris-wood/b77536febb25a5a11af428afff77820a + pEnc := "dcd90af1be463632c0d5ea555256a20605af3db667475e190e3af12a34a3324c46a3094062c59fb4b249e0ee6afba8bee14e0276d126c99f4784b23009bf6168ff628ac1486e5ae8e23ce4d362889de4df63109cbd90ef93db5ae64372bfe1c55f832766f21e94ea3322eb2182f10a891546536ba907ad74b8d72469bea396f3" + qEnc := "f8ba5c89bd068f57234a3cf54a1c89d5b4cd0194f2633ca7c60b91a795a56fa8c8686c0e37b1c4498b851e3420d08bea29f71d195cfbd3671c6ddc49cf4c1db5b478231ea9d91377ffa98fe95685fca20ba4623212b2f2def4da5b281ed0100b651f6db32112e4017d831c0da668768afa7141d45bbc279f1e0f8735d74395b3" + NEnc := "d6930820f71fe517bf3259d14d40209b02a5c0d3d61991c731dd7da39f8d69821552e2318d6c9ad897e603887a476ea3162c1205da9ac96f02edf31df049bd55f142134c17d4382a0e78e275345f165fbe8e49cdca6cf5c726c599dd39e09e75e0f330a33121e73976e4facba9cfa001c28b7c96f8134f9981db6750b43a41710f51da4240fe03106c12acb1e7bb53d75ec7256da3fddd0718b89c365410fce61bc7c99b115fb4c3c318081fa7e1b65a37774e8e50c96e8ce2b2cc6b3b367982366a2bf9924c4bafdb3ff5e722258ab705c76d43e5f1f121b984814e98ea2b2b8725cd9bc905c0bc3d75c2a8db70a7153213c39ae371b2b5dc1dafcb19d6fae9" + eEnc := "010001" + dEnc := "4e21356983722aa1adedb084a483401c1127b781aac89eab103e1cfc52215494981d18dd8028566d9d499469c25476358de23821c78a6ae43005e26b394e3051b5ca206aa9968d68cae23b5affd9cbb4cb16d64ac7754b3cdba241b72ad6ddfc000facdb0f0dd03abd4efcfee1730748fcc47b7621182ef8af2eeb7c985349f62ce96ab373d2689baeaea0e28ea7d45f2d605451920ca4ea1f0c08b0f1f6711eaa4b7cca66d58a6b916f9985480f90aca97210685ac7b12d2ec3e30a1c7b97b65a18d38a93189258aa346bf2bc572cd7e7359605c20221b8909d599ed9d38164c9c4abf396f897b9993c1e805e574d704649985b600fa0ced8e5427071d7049d" + + p := new(big.Int).SetBytes(mustDecodeHex(pEnc)) + q := new(big.Int).SetBytes(mustDecodeHex(qEnc)) + N := new(big.Int).SetBytes(mustDecodeHex(NEnc)) + e := new(big.Int).SetBytes(mustDecodeHex(eEnc)) + d := new(big.Int).SetBytes(mustDecodeHex(dEnc)) + + primes := make([]*big.Int, 2) + primes[0] = p + primes[1] = q + + key := &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{ + N: N, + E: int(e.Int64()), + }, + D: d, + Primes: primes, + } + + return key, nil +} + +func runPBRSA(signer PBRSASigner, verifier PBRSAVerifier, message, metadata []byte, random io.Reader) ([]byte, error) { + blindedMsg, state, err := verifier.Blind(random, message, metadata) + if err != nil { + return nil, err + } + + kLen := (signer.sk.pk.N.BitLen() + 7) / 8 + if len(blindedMsg) != kLen { + return nil, fmt.Errorf("Protocol message (blind message) length mismatch, expected %d, got %d", kLen, len(blindedMsg)) + } + + blindedSig, err := signer.BlindSign(blindedMsg, metadata) + if err != nil { + return nil, err + } + + if len(blindedSig) != kLen { + return nil, fmt.Errorf("Protocol message (blind signature) length mismatch, expected %d, got %d", kLen, len(blindedMsg)) + } + + sig, err := state.Finalize(blindedSig) + if err != nil { + return nil, err + } + + err = verifier.Verify(message, metadata, state.CopyRand(), sig) + if err != nil { + return nil, err + } + + return sig, nil +} + +func mustDecodeHex(h string) []byte { + b, err := hex.DecodeString(h) + if err != nil { + panic(err) + } + return b +} + +func TestPBRSARoundTrip(t *testing.T) { + message := []byte("hello world") + metadata := []byte("metadata") + key, err := loadStrongRSAKey() + if err != nil { + t.Fatal(err) + } + + hash := crypto.SHA384 + verifier := NewPBRSAVerifier(&key.PublicKey, hash) + signer := NewPBRSASigner(key, hash) + + sig, err := runPBRSA(signer, verifier, message, metadata, rand.Reader) + if err != nil { + t.Fatal(err) + } + if sig == nil { + t.Fatal("nil signature output") + } +} + +type encodedPBRSATestVector struct { + Message string `json:"msg"` + Metadata string `json:"metadata"` + P string `json:"p"` + Q string `json:"q"` + D string `json:"d"` + E string `json:"e"` + N string `json:"N"` + Eprime string `json:"eprime"` + Rand string `json:"rand"` + Blind string `json:"blind"` + Salt string `json:"salt"` + Request string `json:"blinded_msg"` + Response string `json:"blinded_sig"` + Signature string `json:"sig"` +} + +type rawPBRSATestVector struct { + t *testing.T + privateKey *rsa.PrivateKey + message []byte + metadata []byte + metadataKey []byte + rand []byte + blind []byte + salt []byte + request []byte + response []byte + signature []byte +} + +func mustHex(d []byte) string { + return hex.EncodeToString(d) +} + +func mustMarshalPrivateKey(key *rsa.PrivateKey) []byte { + der, err := x509.MarshalPKCS8PrivateKey(key) + if err != nil { + panic(err) + } + + block := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: der, + } + + return pem.EncodeToMemory(block) +} + +func mustMarshalPublicKey(key *rsa.PublicKey) []byte { + enc, err := x509.MarshalPKIXPublicKey(key) + if err != nil { + panic(err) + } + return enc +} + +func (tv rawPBRSATestVector) MarshalJSON() ([]byte, error) { + pEnc := mustHex(tv.privateKey.Primes[0].Bytes()) + qEnc := mustHex(tv.privateKey.Primes[1].Bytes()) + nEnc := mustHex(tv.privateKey.N.Bytes()) + e := new(big.Int).SetInt64(int64(tv.privateKey.PublicKey.E)) + eEnc := mustHex(e.Bytes()) + dEnc := mustHex(tv.privateKey.D.Bytes()) + ePrimeEnc := mustHex(tv.metadataKey) + return json.Marshal(encodedPBRSATestVector{ + P: pEnc, + Q: qEnc, + D: dEnc, + E: eEnc, + N: nEnc, + Eprime: ePrimeEnc, + Message: mustHex(tv.message), + Metadata: mustHex(tv.metadata), + Rand: mustHex(tv.rand), + Blind: mustHex(tv.blind), + Salt: mustHex(tv.salt), + Request: mustHex(tv.request), + Response: mustHex(tv.response), + Signature: mustHex(tv.signature), + }) +} + +func generatePBRSATestVector(t *testing.T, msg, metadata []byte) rawPBRSATestVector { + key, err := loadStrongRSAKey() + if err != nil { + t.Fatal(err) + } + + hash := crypto.SHA384 + verifier := NewPBRSAVerifier(&key.PublicKey, hash) + signer := NewPBRSASigner(key, hash) + + publicKey := newCustomPublicKey(&key.PublicKey) + metadataKey := augmentPublicKey(hash, publicKey, metadata) + + blindedMsg, state, err := verifier.Blind(rand.Reader, msg, metadata) + if err != nil { + t.Fatal(err) + } + + blindedSig, err := signer.BlindSign(blindedMsg, metadata) + if err != nil { + t.Fatal(err) + } + + sig, err := state.Finalize(blindedSig) + if err != nil { + t.Fatal(err) + } + + err = verifier.Verify(msg, metadata, state.CopyRand(), sig) + if err != nil { + t.Fatal(err) + } + + return rawPBRSATestVector{ + message: msg, + metadata: metadata, + privateKey: key, + metadataKey: metadataKey.Marshal(), + rand: state.CopyRand(), + salt: state.CopySalt(), + blind: state.CopyBlind(), + request: blindedMsg, + response: blindedSig, + signature: sig, + } +} + +func TestPBRSAGenerateTestVector(t *testing.T) { + var testCases = []struct { + msg []byte + metadata []byte + }{ + { + []byte("hello world"), + []byte("metadata"), + }, + { + []byte("hello world"), + []byte(""), + }, + { + []byte(""), + []byte("metadata"), + }, + { + []byte(""), + []byte(""), + }, + } + + vectors := []rawPBRSATestVector{} + for _, testCase := range testCases { + vectors = append(vectors, generatePBRSATestVector(t, testCase.msg, testCase.metadata)) + } + + // Encode the test vectors + encoded, err := json.Marshal(vectors) + if err != nil { + t.Fatalf("Error producing test vectors: %v", err) + } + + // TODO(caw): verify that we process them correctly + // verifyPBRSATestVectors(t, encoded) + + var outputFile string + if outputFile = os.Getenv(pbrsaTestVectorOutEnvironmentKey); len(outputFile) > 0 { + err := ioutil.WriteFile(outputFile, encoded, 0644) + if err != nil { + t.Fatalf("Error writing test vectors: %v", err) + } + } +} + +func BenchmarkPBRSA(b *testing.B) { + message := []byte("hello world") + metadata := []byte("good doggo") + key, err := loadStrongRSAKey() + if err != nil { + b.Fatal(err) + } + + hash := crypto.SHA384 + verifier := NewPBRSAVerifier(&key.PublicKey, hash) + signer := NewPBRSASigner(key, hash) + + var blindedMsg []byte + var state PBRSAVerifierState + b.Run("Blind", func(b *testing.B) { + for n := 0; n < b.N; n++ { + blindedMsg, state, err = verifier.Blind(rand.Reader, message, metadata) + if err != nil { + b.Fatal(err) + } + } + }) + + var blindedSig []byte + b.Run("BlindSign", func(b *testing.B) { + for n := 0; n < b.N; n++ { + blindedSig, err = signer.BlindSign(blindedMsg, metadata) + if err != nil { + b.Fatal(err) + } + } + }) + + var sig []byte + b.Run("Finalize", func(b *testing.B) { + for n := 0; n < b.N; n++ { + sig, err = state.Finalize(blindedSig) + if err != nil { + b.Fatal(err) + } + } + }) + + err = verifier.Verify(message, metadata, state.rand, sig) + if err != nil { + b.Fatal(err) + } +} diff --git a/blindsign/blindrsa/pss.go b/blindsign/blindrsa/pss.go index 79ab75a8..3e247efe 100644 --- a/blindsign/blindrsa/pss.go +++ b/blindsign/blindrsa/pss.go @@ -35,6 +35,8 @@ package blindrsa // This file implements the RSASSA-PSS signature scheme according to RFC 8017. import ( + "bytes" + "crypto/rsa" "errors" "hash" ) @@ -126,3 +128,103 @@ func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byt // 13. Output EM. return em, nil } + +func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash hash.Hash) error { + // See RFC 8017, Section 9.1.2. + + hLen := hash.Size() + if sLen == rsa.PSSSaltLengthEqualsHash { + sLen = hLen + } + emLen := (emBits + 7) / 8 + if emLen != len(em) { + return errors.New("rsa: internal error: inconsistent length") + } + + // 1. If the length of M is greater than the input limitation for the + // hash function (2^61 - 1 octets for SHA-1), output "inconsistent" + // and stop. + // + // 2. Let mHash = Hash(M), an octet string of length hLen. + if hLen != len(mHash) { + return rsa.ErrVerification + } + + // 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop. + if emLen < hLen+sLen+2 { + return rsa.ErrVerification + } + + // 4. If the rightmost octet of EM does not have hexadecimal value + // 0xbc, output "inconsistent" and stop. + if em[emLen-1] != 0xbc { + return rsa.ErrVerification + } + + // 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and + // let H be the next hLen octets. + db := em[:emLen-hLen-1] + h := em[emLen-hLen-1 : emLen-1] + + // 6. If the leftmost 8 * emLen - emBits bits of the leftmost octet in + // maskedDB are not all equal to zero, output "inconsistent" and + // stop. + var bitMask byte = 0xff >> (8*emLen - emBits) + if em[0] & ^bitMask != 0 { + return rsa.ErrVerification + } + + // 7. Let dbMask = MGF(H, emLen - hLen - 1). + // + // 8. Let DB = maskedDB \xor dbMask. + mgf1XOR(db, hash, h) + + // 9. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in DB + // to zero. + db[0] &= bitMask + + // If we don't know the salt length, look for the 0x01 delimiter. + if sLen == rsa.PSSSaltLengthAuto { + psLen := bytes.IndexByte(db, 0x01) + if psLen < 0 { + return rsa.ErrVerification + } + sLen = len(db) - psLen - 1 + } + + // 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero + // or if the octet at position emLen - hLen - sLen - 1 (the leftmost + // position is "position 1") does not have hexadecimal value 0x01, + // output "inconsistent" and stop. + psLen := emLen - hLen - sLen - 2 + for _, e := range db[:psLen] { + if e != 0x00 { + return rsa.ErrVerification + } + } + if db[psLen] != 0x01 { + return rsa.ErrVerification + } + + // 11. Let salt be the last sLen octets of DB. + salt := db[len(db)-sLen:] + + // 12. Let + // M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ; + // M' is an octet string of length 8 + hLen + sLen with eight + // initial zero octets. + // + // 13. Let H' = Hash(M'), an octet string of length hLen. + var prefix [8]byte + hash.Write(prefix[:]) + hash.Write(mHash) + hash.Write(salt) + + h0 := hash.Sum(nil) + + // 14. If H = H', output "consistent." Otherwise, output "inconsistent." + if !bytes.Equal(h0, h) { // TODO: constant time? + return rsa.ErrVerification + } + return nil +} diff --git a/blindsign/blindrsa/rsa.go b/blindsign/blindrsa/rsa.go index 648d2730..b689e169 100644 --- a/blindsign/blindrsa/rsa.go +++ b/blindsign/blindrsa/rsa.go @@ -77,21 +77,20 @@ func mgf1XOR(out []byte, hash hash.Hash, seed []byte) { } } -func encrypt(c *big.Int, pub *rsa.PublicKey, m *big.Int) *big.Int { - e := big.NewInt(int64(pub.E)) - c.Exp(m, e, pub.N) +func encrypt(c *big.Int, N *big.Int, e *big.Int, m *big.Int) *big.Int { + c.Exp(m, e, N) return c } // decrypt performs an RSA decryption, resulting in a plaintext integer. If a // random source is given, RSA blinding is used. -func decrypt(random io.Reader, priv *rsa.PrivateKey, c *big.Int) (m *big.Int, err error) { +func decrypt(random io.Reader, priv *BigPrivateKey, c *big.Int) (m *big.Int, err error) { // TODO(agl): can we get away with reusing blinds? - if c.Cmp(priv.N) > 0 { + if c.Cmp(priv.pk.N) > 0 { err = rsa.ErrDecryption return } - if priv.N.Sign() == 0 { + if priv.pk.N.Sign() == 0 { return nil, rsa.ErrDecryption } @@ -105,65 +104,37 @@ func decrypt(random io.Reader, priv *rsa.PrivateKey, c *big.Int) (m *big.Int, er var r *big.Int ir = new(big.Int) for { - r, err = rand.Int(random, priv.N) + r, err = rand.Int(random, priv.pk.N) if err != nil { return } if r.Cmp(bigZero) == 0 { r = bigOne } - ok := ir.ModInverse(r, priv.N) + ok := ir.ModInverse(r, priv.pk.N) if ok != nil { break } } - bigE := big.NewInt(int64(priv.E)) - rpowe := new(big.Int).Exp(r, bigE, priv.N) // N != 0 + rpowe := new(big.Int).Exp(r, priv.pk.e, priv.pk.N) // N != 0 cCopy := new(big.Int).Set(c) cCopy.Mul(cCopy, rpowe) - cCopy.Mod(cCopy, priv.N) + cCopy.Mod(cCopy, priv.pk.N) c = cCopy } - if priv.Precomputed.Dp == nil { - m = new(big.Int).Exp(c, priv.D, priv.N) - } else { - // We have the precalculated values needed for the CRT. - m = new(big.Int).Exp(c, priv.Precomputed.Dp, priv.Primes[0]) - m2 := new(big.Int).Exp(c, priv.Precomputed.Dq, priv.Primes[1]) - m.Sub(m, m2) - if m.Sign() < 0 { - m.Add(m, priv.Primes[0]) - } - m.Mul(m, priv.Precomputed.Qinv) - m.Mod(m, priv.Primes[0]) - m.Mul(m, priv.Primes[1]) - m.Add(m, m2) - - for i, values := range priv.Precomputed.CRTValues { - prime := priv.Primes[2+i] - m2.Exp(c, values.Exp, prime) - m2.Sub(m2, m) - m2.Mul(m2, values.Coeff) - m2.Mod(m2, prime) - if m2.Sign() < 0 { - m2.Add(m2, prime) - } - m2.Mul(m2, values.R) - m.Add(m, m2) - } - } + m = new(big.Int).Exp(c, priv.d, priv.pk.N) if ir != nil { // Unblind. m.Mul(m, ir) - m.Mod(m, priv.N) + m.Mod(m, priv.pk.N) } return m, nil } -func decryptAndCheck(random io.Reader, priv *rsa.PrivateKey, c *big.Int) (m *big.Int, err error) { +func decryptAndCheck(random io.Reader, priv *BigPrivateKey, c *big.Int) (m *big.Int, err error) { m, err = decrypt(random, priv, c) if err != nil { return nil, err @@ -171,7 +142,7 @@ func decryptAndCheck(random io.Reader, priv *rsa.PrivateKey, c *big.Int) (m *big // In order to defend against errors in the CRT computation, m^e is // calculated, which should match the original ciphertext. - check := encrypt(new(big.Int), &priv.PublicKey, m) + check := encrypt(new(big.Int), priv.pk.N, priv.pk.e, m) if c.Cmp(check) != 0 { return nil, errors.New("rsa: internal error") } diff --git a/blindsign/blindsign.go b/blindsign/blindsign.go deleted file mode 100644 index dabba89e..00000000 --- a/blindsign/blindsign.go +++ /dev/null @@ -1,42 +0,0 @@ -// Package blindsign provides a blind signature protocol. -// -// A blind signature protocol is a two-party protocol for computing -// a digital signature. One party (the server) holds the signing key, -// and the other (the client) holds the message input. Blindness -// ensures that the server does not learn anything about the client's -// 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(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 -// specific blind signature protocol. -type VerifierState interface { - // Finalize completes the blind signature protocol and produces a signature - // over the corresponding Verifier-provided message. - Finalize(data []byte) ([]byte, error) - - // CopyBlind returns an encoding of the blind value used in the protocol. - CopyBlind() []byte - - // CopySalt returns an encoding of the per-message salt used in the protocol. - CopySalt() []byte -} - -// A Signer represents a specific instance of a blind signature signer. -type Signer interface { - // Blindly signs the input message using the Signer's private key - // and produces an encoded blind signature protocol message as output. - BlindSign(data []byte) ([]byte, error) -}