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 2 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
176 changes: 102 additions & 74 deletions blindsign/blindrsa/blindrsa.go → blindsign/blindrsa/brsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,32 @@

// 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"
"crypto/rand"
"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

Expand All @@ -33,8 +38,8 @@
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,6 +50,14 @@
hash hash.Hash
}

type BRSAVerifier interface {
chris-wood marked this conversation as resolved.
Show resolved Hide resolved
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
}
chris-wood marked this conversation as resolved.
Show resolved Hide resolved

func convertHashFunction(hash crypto.Hash) hash.Hash {
switch hash {
case crypto.SHA256:
Expand All @@ -58,61 +71,78 @@
}
}

// 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
}

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)

Check failure on line 135 in blindsign/blindrsa/brsa.go

View workflow job for this annotation

GitHub Actions / Go-1.19/amd64

use of `fmt.Println` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)
chris-wood marked this conversation as resolved.
Show resolved Hide resolved
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)
Expand All @@ -127,7 +157,7 @@
blindedMsg := make([]byte, kLen)
z.FillBytes(blindedMsg)

return blindedMsg, RSAVerifierState{
return blindedMsg, BRSAVerifierState{
encodedMsg: encodedMsg,
pk: pk,
hash: hash,
Expand All @@ -141,34 +171,44 @@
//
// 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)
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, 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
Expand All @@ -177,48 +217,48 @@
//
// 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)
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, 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

Expand All @@ -235,23 +275,11 @@
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
Expand All @@ -265,7 +293,7 @@
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
}
Expand All @@ -274,28 +302,28 @@
}

// 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,
}
}
Expand All @@ -305,7 +333,7 @@
//
// 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
Expand All @@ -316,7 +344,7 @@
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
}
Expand Down