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 partially blind RSA implementation #445

Merged
merged 26 commits into from Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4ccf65b
Add partially blind RSA implementation
chris-wood Feb 7, 2023
8b4d397
Apply linter
chris-wood Jun 13, 2023
8d0cc8c
Drop internal PrepareRandom function and rename the type
chris-wood Jun 14, 2023
9e2733b
Remove a couple more dead things
chris-wood Jun 14, 2023
7ab7309
Address Bas comments
chris-wood Jun 14, 2023
dd6b980
Apply changes from code review
chris-wood Jun 14, 2023
00ed5cc
Shuffle around the package contents per Armando's feedback
chris-wood Jun 15, 2023
dc62169
Add safe prime check for the partially blind RSA constructor
chris-wood Jun 15, 2023
224ed1b
gofumptd
chris-wood Jun 15, 2023
9edeed1
Update blindsign/blindrsa/brsa.go
chris-wood Jun 15, 2023
1160ea5
Update blindsign/blindrsa/brsa.go
chris-wood Jun 15, 2023
2698bbc
Update blindsign/blindrsa/common.go
chris-wood Jun 15, 2023
192c90f
Armando's comments on brsa
chris-wood Jun 15, 2023
fca31fe
File perms
chris-wood Jun 15, 2023
5c2c660
Update blindsign/blindrsa/partiallyblindrsa/pbrsa.go
chris-wood Jun 15, 2023
ad8b544
Update blindsign/blindrsa/partiallyblindrsa/pbrsa.go
chris-wood Jun 15, 2023
5d09f22
Update blindsign/blindrsa/partiallyblindrsa/pbrsa.go
chris-wood Jun 15, 2023
dd59736
Update blindsign/blindrsa/partiallyblindrsa/pbrsa.go
chris-wood Jun 15, 2023
3f9ad52
Update blindsign/blindrsa/partiallyblindrsa/pbrsa.go
chris-wood Jun 15, 2023
02e0f5d
Final Armando pass
chris-wood Jun 15, 2023
cf4a29b
Update blindsign/blindrsa/partiallyblindrsa/pbrsa_test.go
chris-wood Jun 15, 2023
47393f1
Fix comment
chris-wood Jun 15, 2023
8653635
Refactoring to hide internals.
armfazh Jun 20, 2023
4d5ca64
Merge pull request #1 from armfazh/pull445
chris-wood Jun 20, 2023
4d19adb
Updates based on latest draft changes
chris-wood Jun 28, 2023
739263e
Add test vector verification
chris-wood Jun 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
212 changes: 100 additions & 112 deletions blindsign/blindrsa/blindrsa.go → blindsign/blindrsa/brsa.go
@@ -1,28 +1,32 @@
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
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-rsa-blind-signatures
//
// 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"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/sha512"
"crypto/subtle"
"errors"
"hash"
"io"
"math/big"

"github.com/cloudflare/circl/blindsign"
"github.com/cloudflare/circl/blindsign/blindrsa/internal/keys"
)

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

Expand All @@ -33,8 +37,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

Expand All @@ -45,74 +49,57 @@ type DeterminsiticRSAVerifier struct {
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)
}
// PBRSAVerifier is a type that implements the client side of the blind RSA
chris-wood marked this conversation as resolved.
Show resolved Hide resolved
// protocol, described in https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-rsa-blind-signatures
type Verifier interface {
// 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.
Blind(random io.Reader, message []byte) ([]byte, VerifierState, error)

// FixedBlind runs the Blind function with fixed blind and salt inputs.
FixedBlind(message, blind, salt []byte) ([]byte, VerifierState, error)

// Verify verifies the input (message, signature) pair and produces an error upon failure.
Verify(message, signature []byte) error

// Hash returns the hash function associated with the BRSAVerifier.
chris-wood marked this conversation as resolved.
Show resolved Hide resolved
Hash() hash.Hash
}

// NewDeterministicRSAVerifier creates a new RSAVerifier using the corresponding Signer parameters.
func NewDeterministicRSAVerifier(pk *rsa.PublicKey, hash crypto.Hash) DeterminsiticRSAVerifier {
h := convertHashFunction(hash)
return DeterminsiticRSAVerifier{
// NewDeterministicVerifier creates a new DeterminsiticBRSAVerifier using the corresponding Signer parameters.
func NewDeterministicVerifier(pk *rsa.PublicKey, hash crypto.Hash) Verifier {
h := ConvertHashFunction(hash)
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 {
h := convertHashFunction(hash)
return RSAVerifier{
// Hash returns the hash function associated with the BRSAVerifier.
func (v determinsiticBRSAVerifier) Hash() hash.Hash {
return v.hash
}

// NewVerifier creates a new BRSAVerifier using the corresponding Signer parameters.
func NewVerifier(pk *rsa.PublicKey, hash crypto.Hash) Verifier {
h := ConvertHashFunction(hash)
return randomBRSAVerifier{
pk: pk,
cryptoHash: hash,
hash: h,
}
}

func encodeMessageEMSAPSS(message []byte, key *rsa.PublicKey, 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
encodedMsg, err := emsaPSSEncode(digest[:], emBits, salt, hash)
return encodedMsg, err
}

func generateBlindingFactor(random io.Reader, key *rsa.PublicKey) (*big.Int, *big.Int, error) {
randReader := random
if randReader == nil {
randReader = rand.Reader
}
r, err := rand.Int(randReader, key.N)
if err != nil {
return nil, nil, err
}

if r.Sign() == 0 {
r = bigOne
}
rInv := new(big.Int).ModInverse(r, key.N)
if rInv == nil {
return nil, nil, ErrInvalidBlind
}

return r, rInv, nil
// Hash returns the hash function associated with the BRSAVerifier.
func (v randomBRSAVerifier) Hash() hash.Hash {
return v.hash
}

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, VerifierState, error) {
encodedMsg, err := EncodeMessageEMSAPSS(message, pk.N, hash, salt)
if err != nil {
return nil, nil, err
return nil, VerifierState{}, err
}

m := new(big.Int).SetBytes(encodedMsg)
Expand All @@ -127,7 +114,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, VerifierState{
encodedMsg: encodedMsg,
pk: pk,
hash: hash,
Expand All @@ -141,34 +128,47 @@ 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, VerifierState, error) {
if random == nil {
return nil, nil, ErrInvalidRandomness
return nil, VerifierState{}, 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, VerifierState{}, 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)
func saltLength(opts *rsa.PSSOptions) int {
if opts == nil {
return rsa.PSSSaltLengthAuto
}
return opts.SaltLength
}

err := rsa.VerifyPSS(pk, hash, digest, signature, &rsa.PSSOptions{
Hash: hash,
SaltLength: saltLength,
})
return err
// FixedBlind runs the Blind function with fixed blind and salt inputs.
func (v determinsiticBRSAVerifier) FixedBlind(message, blind, salt []byte) ([]byte, VerifierState, error) {
if blind == nil {
return nil, VerifierState{}, ErrInvalidRandomness
}

r := new(big.Int).SetBytes(blind)
chris-wood marked this conversation as resolved.
Show resolved Hide resolved
if r.Cmp(v.pk.N) < 0 {
chris-wood marked this conversation as resolved.
Show resolved Hide resolved
return nil, VerifierState{}, ErrInvalidBlind
}
rInv := new(big.Int).ModInverse(r, v.pk.N)
if rInv == nil {
return nil, VerifierState{}, 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, keys.NewBigPublicKey(v.pk), v.cryptoHash)
}

// Blind initializes the blind RSA protocol using an input message and source of randomness. The
Expand All @@ -177,48 +177,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, VerifierState, error) {
if random == nil {
return nil, nil, ErrInvalidRandomness
return nil, VerifierState{}, ErrInvalidRandomness
}

salt := make([]byte, v.hash.Size())
_, err := io.ReadFull(random, salt)
if err != nil {
return nil, nil, err
return nil, VerifierState{}, 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, VerifierState{}, 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, VerifierState, error) {
chris-wood marked this conversation as resolved.
Show resolved Hide resolved
if blind == nil {
return nil, nil, ErrInvalidRandomness
return nil, VerifierState{}, ErrInvalidRandomness
}

r := new(big.Int).SetBytes(blind)
chris-wood marked this conversation as resolved.
Show resolved Hide resolved
rInv := new(big.Int).ModInverse(r, v.pk.N)
if rInv == nil {
return nil, nil, ErrInvalidBlind
return nil, VerifierState{}, 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(), keys.NewBigPublicKey(v.pk), v.cryptoHash)
}

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

Expand All @@ -235,23 +235,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 VerifierState) Finalize(data []byte) ([]byte, error) {
kLen := (state.pk.N.BitLen() + 7) / 8
if len(data) != kLen {
return nil, ErrUnexpectedSize
Expand All @@ -265,7 +253,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(keys.NewBigPublicKey(state.pk), state.encodedMsg, sig)
if err != nil {
return nil, err
}
Expand All @@ -274,28 +262,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 VerifierState) 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 VerifierState) 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 Signer represents the Signer in the blind RSA protocol.
// It carries the raw RSA private key used for signing blinded messages.
type RSASigner struct {
type Signer 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{
// NewSigner creates a new Signer for the blind RSA protocol using an RSA private key.
func NewSigner(sk *rsa.PrivateKey) Signer {
return Signer{
sk: sk,
}
}
Expand All @@ -305,7 +293,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 Signer) BlindSign(data []byte) ([]byte, error) {
kLen := (signer.sk.N.BitLen() + 7) / 8
if len(data) != kLen {
return nil, ErrUnexpectedSize
Expand All @@ -316,7 +304,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, keys.NewBigPrivateKey(signer.sk), m)
if err != nil {
return nil, err
}
Expand Down