diff --git a/blindsign/blindrsa/blindrsa.go b/blindsign/blindrsa/blindrsa.go deleted file mode 100644 index f0b9fdae4..000000000 --- a/blindsign/blindrsa/blindrsa.go +++ /dev/null @@ -1,342 +0,0 @@ -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 - -import ( - "crypto" - "crypto/rand" - "crypto/rsa" - "crypto/sha256" - "crypto/sha512" - "crypto/subtle" - "errors" - "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 { - // 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 crypto.Hash) RSAVerifier { - h := convertHashFunction(hash) - return RSAVerifier{ - 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 -} - -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(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, RSAVerifierState{ - encodedMsg: encodedMsg, - 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. -// -// 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) { - if random == nil { - return nil, nil, ErrInvalidRandomness - } - - salt := make([]byte, v.hash.Size()) - _, err := io.ReadFull(random, salt) - if err != nil { - return nil, nil, err - } - - r, rInv, err := generateBlindingFactor(random, v.pk) - if err != nil { - return nil, nil, 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) { - if blind == nil { - return nil, nil, ErrInvalidRandomness - } - - r := new(big.Int).SetBytes(blind) - rInv := new(big.Int).ModInverse(r, v.pk.N) - if rInv == nil { - return nil, nil, 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) -} - -// An RSAVerifierState carries state needed to complete the blind signature protocol -// as a verifier. -type RSAVerifierState struct { - // 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 - - // The salt used when encoding the message - salt []byte - - // Inverse of the blinding factor produced by the Verifier - 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) { - 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 RSAVerifierState) 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 { - salt := make([]byte, len(state.salt)) - copy(salt, state.salt) - return salt -} - -// An RSASigner represents the Signer in the blind RSA protocol. -// It carries the raw RSA private key used for signing blinded messages. -type RSASigner 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{ - sk: sk, - } -} - -// 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-irtf-cfrg-rsa-blind-signatures-02#section-5.1.2 -func (signer RSASigner) BlindSign(data []byte) ([]byte, error) { - kLen := (signer.sk.N.BitLen() + 7) / 8 - if len(data) != kLen { - return nil, ErrUnexpectedSize - } - - m := new(big.Int).SetBytes(data) - if m.Cmp(signer.sk.N) > 0 { - return nil, ErrInvalidMessageLength - } - - s, err := decryptAndCheck(rand.Reader, signer.sk, m) - if err != nil { - return nil, err - } - - blindSig := make([]byte, kLen) - s.FillBytes(blindSig) - - return blindSig, nil -} - -var ( - // ErrUnexpectedSize is the error used if the size of a parameter does not match its expected value. - ErrUnexpectedSize = errors.New("blindsign/blindrsa: unexpected input size") - - // ErrInvalidMessageLength is the error used if the size of a protocol message does not match its expected value. - ErrInvalidMessageLength = errors.New("blindsign/blindrsa: invalid message length") - - // ErrInvalidBlind is the error used if the blind generated by the Verifier fails. - ErrInvalidBlind = errors.New("blindsign/blindrsa: invalid blind") - - // ErrInvalidRandomness is the error used if caller did not provide randomness to the Blind() function. - ErrInvalidRandomness = errors.New("blindsign/blindrsa: invalid random parameter") -) diff --git a/blindsign/blindrsa/brsa.go b/blindsign/blindrsa/brsa.go new file mode 100644 index 000000000..390de6940 --- /dev/null +++ b/blindsign/blindrsa/brsa.go @@ -0,0 +1,323 @@ +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 +// +// 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" + "hash" + "io" + "math/big" + + "github.com/cloudflare/circl/blindsign/blindrsa/internal/common" + "github.com/cloudflare/circl/blindsign/blindrsa/internal/keys" +) + +// 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 + + // 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 determinsiticBRSAVerifier is a BRSAVerifier that supports deterministic signatures. +type determinsiticBRSAVerifier 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 +} + +// Verifier is a type that implements the client side of the blind RSA +// 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 Verifier. + Hash() hash.Hash +} + +// NewDeterministicVerifier creates a new DeterminsiticBRSAVerifier using the corresponding Signer parameters. +// This corresponds to the RSABSSA-SHA384-PSSZERO-Deterministic variant. See the specification for more details: +// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-rsa-blind-signatures#name-rsabssa-variants +func NewDeterministicVerifier(pk *rsa.PublicKey, hash crypto.Hash) Verifier { + h := common.ConvertHashFunction(hash) + return determinsiticBRSAVerifier{ + pk: pk, + cryptoHash: hash, + hash: h, + } +} + +// 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. +// This corresponds to the RSABSSA-SHA384-PSS-Deterministic variant. See the specification for more details: +// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-rsa-blind-signatures#name-rsabssa-variants +func NewVerifier(pk *rsa.PublicKey, hash crypto.Hash) Verifier { + h := common.ConvertHashFunction(hash) + return randomBRSAVerifier{ + pk: pk, + cryptoHash: hash, + hash: h, + } +} + +// 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, VerifierState, error) { + encodedMsg, err := common.EncodeMessageEMSAPSS(message, pk.N, hash, salt) + if err != nil { + return nil, VerifierState{}, err + } + + m := new(big.Int).SetBytes(encodedMsg) + + 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, pk.N) + + kLen := (pk.N.BitLen() + 7) / 8 + blindedMsg := make([]byte, kLen) + z.FillBytes(blindedMsg) + + return blindedMsg, VerifierState{ + encodedMsg: encodedMsg, + 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 determinsiticBRSAVerifier) Blind(random io.Reader, message []byte) ([]byte, VerifierState, error) { + if random == nil { + return nil, VerifierState{}, common.ErrInvalidRandomness + } + + r, rInv, err := common.GenerateBlindingFactor(random, v.pk.N) + if err != nil { + return nil, VerifierState{}, err + } + + return fixedBlind(message, nil, r, rInv, v.pk, v.hash) +} + +// 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{}, common.ErrInvalidRandomness + } + + r := new(big.Int).SetBytes(blind) + if r.Cmp(v.pk.N) >= 0 { + return nil, VerifierState{}, common.ErrInvalidBlind + } + rInv := new(big.Int).ModInverse(r, v.pk.N) + if rInv == nil { + return nil, VerifierState{}, common.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 determinsiticBRSAVerifier) Verify(message, signature []byte) error { + return common.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 +// 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-irtf-cfrg-rsa-blind-signatures-02#section-5.1.1 +func (v randomBRSAVerifier) Blind(random io.Reader, message []byte) ([]byte, VerifierState, error) { + if random == nil { + return nil, VerifierState{}, common.ErrInvalidRandomness + } + + salt := make([]byte, v.hash.Size()) + _, err := io.ReadFull(random, salt) + if err != nil { + return nil, VerifierState{}, err + } + + r, rInv, err := common.GenerateBlindingFactor(random, v.pk.N) + if err != nil { + 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 randomBRSAVerifier) FixedBlind(message, blind, salt []byte) ([]byte, VerifierState, error) { + if blind == nil { + return nil, VerifierState{}, common.ErrInvalidRandomness + } + + r := new(big.Int).SetBytes(blind) + if r.Cmp(v.pk.N) >= 0 { + return nil, VerifierState{}, common.ErrInvalidBlind + } + + rInv := new(big.Int).ModInverse(r, v.pk.N) + if rInv == nil { + return nil, VerifierState{}, common.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 randomBRSAVerifier) Verify(message, signature []byte) error { + return common.VerifyMessageSignature(message, signature, v.hash.Size(), keys.NewBigPublicKey(v.pk), v.cryptoHash) +} + +// An VerifierState carries state needed to complete the blind signature protocol +// as a verifier. +type VerifierState struct { + // 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 + + // The salt used when encoding the message + salt []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-irtf-cfrg-rsa-blind-signatures-02#section-5.1.3 +func (state VerifierState) Finalize(data []byte) ([]byte, error) { + kLen := (state.pk.N.BitLen() + 7) / 8 + if len(data) != kLen { + return nil, common.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 := common.VerifyBlindSignature(keys.NewBigPublicKey(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 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 VerifierState) CopySalt() []byte { + salt := make([]byte, len(state.salt)) + copy(salt, state.salt) + return salt +} + +// An Signer represents the Signer in the blind RSA protocol. +// It carries the raw RSA private key used for signing blinded messages. +type Signer struct { + // An RSA private key + sk *rsa.PrivateKey +} + +// 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, + } +} + +// 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-irtf-cfrg-rsa-blind-signatures-02#section-5.1.2 +func (signer Signer) BlindSign(data []byte) ([]byte, error) { + kLen := (signer.sk.N.BitLen() + 7) / 8 + if len(data) != kLen { + return nil, common.ErrUnexpectedSize + } + + m := new(big.Int).SetBytes(data) + if m.Cmp(signer.sk.N) > 0 { + return nil, common.ErrInvalidMessageLength + } + + s, err := common.DecryptAndCheck(rand.Reader, keys.NewBigPrivateKey(signer.sk), m) + if err != nil { + return nil, err + } + + blindSig := make([]byte, kLen) + s.FillBytes(blindSig) + + return blindSig, nil +} + +var ( + ErrUnexpectedSize = common.ErrUnexpectedSize + ErrInvalidMessageLength = common.ErrInvalidMessageLength + ErrInvalidBlind = common.ErrInvalidBlind + ErrInvalidRandomness = common.ErrInvalidRandomness + ErrUnsupportedHashFunction = common.ErrUnsupportedHashFunction +) diff --git a/blindsign/blindrsa/blindrsa_test.go b/blindsign/blindrsa/brsa_test.go similarity index 51% rename from blindsign/blindrsa/blindrsa_test.go rename to blindsign/blindrsa/brsa_test.go index a7d4f8bfd..ec002608e 100644 --- a/blindsign/blindrsa/blindrsa_test.go +++ b/blindsign/blindrsa/brsa_test.go @@ -14,79 +14,91 @@ 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, nil +} + +func mustDecodeHex(h string) []byte { + b, err := hex.DecodeString(h) + if err != nil { + panic(err) } + return b +} - return privateKey +func loadStrongRSAKey() *rsa.PrivateKey { + // 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 } -func runSignatureProtocol(signer RSASigner, verifier blindsign.Verifier, message []byte, random io.Reader) ([]byte, error) { +func runSignatureProtocol(signer Signer, verifier Verifier, message []byte, random io.Reader) ([]byte, error) { blindedMsg, state, err := verifier.Blind(random, message) if err != nil { return nil, err @@ -121,10 +133,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 := NewVerifier(&key.PublicKey, crypto.SHA512) + signer := NewSigner(key) sig, err := runSignatureProtocol(signer, verifier, message, rand.Reader) if err != nil { @@ -137,10 +152,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 := NewDeterministicVerifier(&key.PublicKey, crypto.SHA512) + signer := NewSigner(key) sig, err := runSignatureProtocol(signer, verifier, message, rand.Reader) if err != nil { @@ -153,12 +171,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 := NewDeterministicVerifier(&key.PublicKey, crypto.SHA512) + signer := NewSigner(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 +187,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 := NewVerifier(&key.PublicKey, crypto.SHA512) + signer := NewSigner(key) sig1, err := runSignatureProtocol(signer, verifier, message, rand.Reader) if err != nil { @@ -202,10 +226,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 := NewVerifier(&key.PublicKey, crypto.SHA512) + signer := NewSigner(key) mockRand := &mockRandom{0} sig1, err := runSignatureProtocol(signer, verifier, message, mockRand) @@ -329,7 +356,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 +375,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 := NewSigner(key) + verifier := NewVerifier(&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, &key.PublicKey, verifier.Hash()) if err != nil { t.Fatal(err) } @@ -362,8 +393,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 +418,48 @@ func TestVectors(t *testing.T) { verifyTestVector(t, vector) } } + +func BenchmarkBRSA(b *testing.B) { + message := []byte("hello world") + key := loadStrongRSAKey() + + verifier := NewVerifier(&key.PublicKey, crypto.SHA512) + signer := NewSigner(key) + + var err error + var blindedMsg []byte + var state VerifierState + 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/internal/common/common.go b/blindsign/blindrsa/internal/common/common.go new file mode 100644 index 000000000..cc2688c41 --- /dev/null +++ b/blindsign/blindrsa/internal/common/common.go @@ -0,0 +1,145 @@ +package common + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/sha512" + "crypto/subtle" + "errors" + "hash" + "io" + "math/big" + + "github.com/cloudflare/circl/blindsign/blindrsa/internal/keys" +) + +// ConvertHashFunction converts a crypto.Hash function to an equivalent hash.Hash type. +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) + } +} + +// EncodeMessageEMSAPSS hashes the input message and then encodes it using PSS encoding. +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 := N.BitLen() - 1 + encodedMsg, err := emsaPSSEncode(digest[:], emBits, salt, hash) + return encodedMsg, err +} + +// GenerateBlindingFactor generates a blinding factor and its multiplicative inverse +// to use for RSA blinding. +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, N) + if err != nil { + return nil, nil, err + } + + if r.Sign() == 0 { + r.SetInt64(1) + } + rInv := new(big.Int).ModInverse(r, N) + if rInv == nil { + return nil, nil, ErrInvalidBlind + } + + return r, rInv, nil +} + +// VerifyMessageSignature verifies the input message signature against the expected public key +func VerifyMessageSignature(message, signature []byte, saltLength int, pk *keys.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 +} + +// DecryptAndCheck checks that the private key operation is consistent (fault attack detection). +func DecryptAndCheck(random io.Reader, priv *keys.BigPrivateKey, c *big.Int) (m *big.Int, err error) { + m, err = decrypt(random, priv, c) + if err != nil { + return nil, err + } + + // 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.Pk.N, priv.Pk.E, m) + if c.Cmp(check) != 0 { + return nil, errors.New("rsa: internal error") + } + return m, nil +} + +// VerifyBlindSignature verifies the signature of the hashed and encoded message against the input public key. +func VerifyBlindSignature(pub *keys.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 + } +} + +func saltLength(opts *rsa.PSSOptions) int { + if opts == nil { + return rsa.PSSSaltLengthAuto + } + return opts.SaltLength +} + +func verifyPSS(pub *keys.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()) +} + +var ( + // ErrUnexpectedSize is the error used if the size of a parameter does not match its expected value. + ErrUnexpectedSize = errors.New("blindsign/blindrsa: unexpected input size") + + // ErrInvalidMessageLength is the error used if the size of a protocol message does not match its expected value. + ErrInvalidMessageLength = errors.New("blindsign/blindrsa: invalid message length") + + // ErrInvalidBlind is the error used if the blind generated by the Verifier fails. + ErrInvalidBlind = errors.New("blindsign/blindrsa: invalid blind") + + // ErrInvalidRandomness is the error used if caller did not provide randomness to the Blind() function. + ErrInvalidRandomness = errors.New("blindsign/blindrsa: invalid random parameter") + + // ErrUnsupportedHashFunction is the error used if the specified hash is not supported. + ErrUnsupportedHashFunction = errors.New("blindsign/blindrsa: unsupported hash function") +) diff --git a/blindsign/blindrsa/pss.go b/blindsign/blindrsa/internal/common/pss.go similarity index 58% rename from blindsign/blindrsa/pss.go rename to blindsign/blindrsa/internal/common/pss.go index 79ab75a87..000b1664f 100644 --- a/blindsign/blindrsa/pss.go +++ b/blindsign/blindrsa/internal/common/pss.go @@ -26,7 +26,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -package blindrsa +package common // Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -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/internal/common/rsa.go similarity index 65% rename from blindsign/blindrsa/rsa.go rename to blindsign/blindrsa/internal/common/rsa.go index 648d2730b..d39b4a276 100644 --- a/blindsign/blindrsa/rsa.go +++ b/blindsign/blindrsa/internal/common/rsa.go @@ -26,15 +26,16 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -package blindrsa +package common import ( "crypto/rand" "crypto/rsa" - "errors" "hash" "io" "math/big" + + "github.com/cloudflare/circl/blindsign/blindrsa/internal/keys" ) var ( @@ -77,21 +78,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 *keys.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,75 +105,32 @@ 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) { - m, err = decrypt(random, priv, c) - if err != nil { - return nil, err - } - - // 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) - if c.Cmp(check) != 0 { - return nil, errors.New("rsa: internal error") - } - return m, nil -} diff --git a/blindsign/blindrsa/internal/keys/big_keys.go b/blindsign/blindrsa/internal/keys/big_keys.go new file mode 100644 index 000000000..f6ab42983 --- /dev/null +++ b/blindsign/blindrsa/internal/keys/big_keys.go @@ -0,0 +1,57 @@ +package keys + +import ( + "crypto/rsa" + "math/big" +) + +// 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 +} + +// NewBigPublicKey creates a BigPublicKey from a rsa.PublicKey. +func NewBigPublicKey(pk *rsa.PublicKey) *BigPublicKey { + return &BigPublicKey{ + N: pk.N, + E: new(big.Int).SetInt64(int64(pk.E)), + } +} + +// CustomPublicKey is similar to rsa.PrivateKey, containing information needed +// for a private key used in the partially blind signature protocol. +type BigPrivateKey struct { + Pk *BigPublicKey + D *big.Int + P *big.Int + Q *big.Int +} + +// NewBigPrivateKey creates a BigPrivateKey from a rsa.PrivateKey. +func NewBigPrivateKey(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], + } +} diff --git a/blindsign/blindrsa/partiallyblindrsa/pbrsa.go b/blindsign/blindrsa/partiallyblindrsa/pbrsa.go new file mode 100644 index 000000000..f168b3e56 --- /dev/null +++ b/blindsign/blindrsa/partiallyblindrsa/pbrsa.go @@ -0,0 +1,334 @@ +// Package partiallyblindrsa implements a partially blind RSA protocol. +package partiallyblindrsa + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "encoding/binary" + "errors" + "hash" + "io" + "math/big" + + "github.com/cloudflare/circl/blindsign/blindrsa/internal/common" + "github.com/cloudflare/circl/blindsign/blindrsa/internal/keys" + "golang.org/x/crypto/hkdf" +) + +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...) +} + +// A randomizedVerifier represents a Verifier in the partially blind RSA signature protocol. +// It carries state needed to produce and validate an RSA signature produced +// using the blind RSA protocol. +type randomizedVerifier struct { + // Public key of the Signer + pk *keys.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 +} + +// NewVerifier creates a new PBRSAVerifier using the corresponding Signer parameters. +// This corresponds to the RSAPBSSA-SHA384-PSS-Deterministic variant. See the specification for more details: +// https://datatracker.ietf.org/doc/html/draft-amjad-cfrg-partially-blind-rsa#name-rsapbssa-variants +func NewVerifier(pk *rsa.PublicKey, hash crypto.Hash) Verifier { + h := common.ConvertHashFunction(hash) + return randomizedVerifier{ + pk: keys.NewBigPublicKey(pk), + cryptoHash: hash, + hash: h, + } +} + +// derivePublicKey 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 derivePublicKey(h crypto.Hash, pk *keys.BigPublicKey, metadata []byte) *keys.BigPublicKey { + // expandLen = ceil((ceil(log2(\lambda)/2) + 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). + newE := new(big.Int).SetBytes(bytes[:lambda/8]) + newE.SetBit(newE, 0, 1) + newE.SetBit(newE, lambda-1, 0) + newE.SetBit(newE, lambda-2, 0) + + // Compute e_MD = e * H_MD(D) + return &keys.BigPublicKey{ + N: pk.N, + E: newE, + } +} + +// deriveKeyPair 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 deriveKeyPair(h crypto.Hash, sk *keys.BigPrivateKey, metadata []byte) *keys.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 := derivePublicKey(h, sk.Pk, metadata) + bigE := new(big.Int).Mod(pk.E, phi) + d := new(big.Int).ModInverse(bigE, phi) + return &keys.BigPrivateKey{ + Pk: pk, + D: d, + P: sk.P, + Q: sk.Q, + } +} + +func fixedPartiallyBlind(message, salt []byte, r, rInv *big.Int, pk *keys.BigPublicKey, hash hash.Hash) ([]byte, VerifierState, error) { + encodedMsg, err := common.EncodeMessageEMSAPSS(message, pk.N, hash, salt) + if err != nil { + return nil, VerifierState{}, 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, VerifierState{ + encodedMsg: encodedMsg, + pk: pk, + hash: hash, + salt: salt, + rInv: rInv, + }, nil +} + +// Verifier is a type that implements the client side of the partially blind RSA +// protocol, described in https://datatracker.ietf.org/doc/html/draft-amjad-cfrg-partially-blind-rsa-00 +type Verifier interface { + // Blind initializes the partially 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. + Blind(random io.Reader, message, metadata []byte) ([]byte, VerifierState, error) + + // FixedBlind initializes the partially blind RSA protocol using an input message, metadata, and randomness values. + FixedBlind(message, metadata, salt, blind, blindInv []byte) ([]byte, VerifierState, error) + + // Verify verifies the input (message, signature) pair using the augmented public key + // and produces an error upon failure. + Verify(message, signature, metadata []byte) error + + // Hash returns the hash function associated with the Verifier. + Hash() hash.Hash +} + +// Blind initializes the partially 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 randomizedVerifier) Blind(random io.Reader, message, metadata []byte) ([]byte, VerifierState, error) { + if random == nil { + return nil, VerifierState{}, common.ErrInvalidRandomness + } + + salt := make([]byte, v.hash.Size()) + _, err := io.ReadFull(rand.Reader, salt) + if err != nil { + return nil, VerifierState{}, err + } + + r, rInv, err := common.GenerateBlindingFactor(random, v.pk.N) + if err != nil { + return nil, VerifierState{}, err + } + + return v.FixedBlind(message, metadata, salt, r.Bytes(), rInv.Bytes()) +} + +// FixedBlind initializes the partially blind RSA using fixed randomness as input. +func (v randomizedVerifier) FixedBlind(message, metadata, salt, blind, blindInv []byte) ([]byte, VerifierState, error) { + r := new(big.Int).SetBytes(blind) + rInv := new(big.Int).SetBytes(blindInv) + metadataKey := derivePublicKey(v.cryptoHash, v.pk, metadata) + inputMsg := encodeMessageMetadata(message, metadata) + return fixedPartiallyBlind(inputMsg, salt, r, rInv, metadataKey, v.hash) +} + +// Verify verifies the input (message, signature) pair using the augmented public key +// 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 randomizedVerifier) Verify(message, metadata, signature []byte) error { + metadataKey := derivePublicKey(v.cryptoHash, v.pk, metadata) + inputMsg := encodeMessageMetadata(message, metadata) + return common.VerifyMessageSignature(inputMsg, signature, v.hash.Size(), metadataKey, v.cryptoHash) +} + +// Hash returns the hash function associated with the Verifier. +func (v randomizedVerifier) Hash() hash.Hash { + return v.hash +} + +// A VerifierState carries state needed to complete the blind signature protocol +// as a verifier. +type VerifierState struct { + // Public key of the Signer + pk *keys.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 + + // 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 VerifierState) Finalize(data []byte) ([]byte, error) { + kLen := (state.pk.N.BitLen() + 7) / 8 + if len(data) != kLen { + return nil, common.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 := common.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 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 VerifierState) CopySalt() []byte { + salt := make([]byte, len(state.salt)) + copy(salt, state.salt) + return salt +} + +// An Signer represents the Signer in the blind RSA protocol. +// It carries the raw RSA private key used for signing blinded messages. +type Signer struct { + // An RSA private key + sk *keys.BigPrivateKey + h crypto.Hash +} + +// isSafePrime returns true if the input prime p is safe, i.e., p = (2 * q) + 1 for some prime q +func isSafePrime(p *big.Int) bool { + q := new(big.Int).Set(p) + q.Sub(q, big.NewInt(1)) + q.Div(q, big.NewInt(2)) + return q.ProbablyPrime(20) +} + +// NewSigner creates a new Signer for the blind RSA protocol using an RSA private key. +func NewSigner(sk *rsa.PrivateKey, h crypto.Hash) (Signer, error) { + bigSk := keys.NewBigPrivateKey(sk) + if !(isSafePrime(bigSk.P) && isSafePrime(bigSk.Q)) { + return Signer{}, ErrInvalidPrivateKey + } + + return Signer{ + sk: bigSk, + h: h, + }, nil +} + +// 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 Signer) BlindSign(data, metadata []byte) ([]byte, error) { + kLen := (signer.sk.Pk.N.BitLen() + 7) / 8 + if len(data) != kLen { + return nil, common.ErrUnexpectedSize + } + + m := new(big.Int).SetBytes(data) + if m.Cmp(signer.sk.Pk.N) > 0 { + return nil, common.ErrInvalidMessageLength + } + + skPrime := deriveKeyPair(signer.h, signer.sk, metadata) + + s, err := common.DecryptAndCheck(rand.Reader, skPrime, m) + if err != nil { + return nil, err + } + + blindSig := make([]byte, kLen) + s.FillBytes(blindSig) + + return blindSig, nil +} + +var ( + // ErrInvalidPrivateKey is the error used if a private key is invalid + ErrInvalidPrivateKey = errors.New("blindsign/blindrsa/partiallyblindrsa: invalid private key") + ErrUnexpectedSize = common.ErrUnexpectedSize + ErrInvalidMessageLength = common.ErrInvalidMessageLength + ErrInvalidRandomness = common.ErrInvalidRandomness +) diff --git a/blindsign/blindrsa/partiallyblindrsa/pbrsa_test.go b/blindsign/blindrsa/partiallyblindrsa/pbrsa_test.go new file mode 100644 index 000000000..6e4690d1f --- /dev/null +++ b/blindsign/blindrsa/partiallyblindrsa/pbrsa_test.go @@ -0,0 +1,356 @@ +package partiallyblindrsa + +import ( + "bytes" + "crypto" + "crypto/rand" + "crypto/rsa" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "math/big" + "os" + "testing" + + "github.com/cloudflare/circl/blindsign/blindrsa/internal/keys" +) + +const ( + pbrsaTestVectorOutEnvironmentKey = "PBRSA_TEST_VECTORS_OUT" + pbrsaTestVectorInEnvironmentKey = "PBRSA_TEST_VECTORS_IN" +) + +func loadStrongRSAKey() *rsa.PrivateKey { + // 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 +} + +func runPBRSA(signer Signer, verifier Verifier, 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, 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 := loadStrongRSAKey() + + hash := crypto.SHA384 + verifier := NewVerifier(&key.PublicKey, hash) + signer, err := NewSigner(key, hash) + if err != nil { + t.Fatal(err) + } + + 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"` + Info string `json:"info"` + P string `json:"p"` + Q string `json:"q"` + D string `json:"d"` + E string `json:"e"` + N string `json:"N"` + Eprime string `json:"eprime"` + 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 { + privateKey *rsa.PrivateKey + message []byte + info []byte + infoKey []byte + blind []byte + salt []byte + request []byte + response []byte + signature []byte +} + +func mustHex(d []byte) string { + return hex.EncodeToString(d) +} + +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.infoKey) + return json.Marshal(encodedPBRSATestVector{ + P: pEnc, + Q: qEnc, + D: dEnc, + E: eEnc, + N: nEnc, + Eprime: ePrimeEnc, + Message: mustHex(tv.message), + Info: mustHex(tv.info), + 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 := loadStrongRSAKey() + + hash := crypto.SHA384 + verifier := NewVerifier(&key.PublicKey, hash) + signer, err := NewSigner(key, hash) + if err != nil { + t.Fatal(err) + } + + publicKey := keys.NewBigPublicKey(&key.PublicKey) + metadataKey := derivePublicKey(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, sig) + if err != nil { + t.Fatal(err) + } + + return rawPBRSATestVector{ + message: msg, + info: metadata, + privateKey: key, + infoKey: metadataKey.Marshal(), + salt: state.CopySalt(), + blind: state.CopyBlind(), + request: blindedMsg, + response: blindedSig, + signature: sig, + } +} + +func verifyTestVector(t *testing.T, vector rawPBRSATestVector) { + key := loadStrongRSAKey() + + key.PublicKey.N = vector.privateKey.N + key.PublicKey.E = vector.privateKey.E + key.D = vector.privateKey.D + key.Primes[0] = vector.privateKey.Primes[0] + key.Primes[1] = vector.privateKey.Primes[1] + key.Precomputed.Dp = nil // Remove precomputed CRT values + + hash := crypto.SHA384 + signer, err := NewSigner(key, hash) + if err != nil { + t.Fatal(err) + } + verifier := NewVerifier(&key.PublicKey, crypto.SHA384) + + r := new(big.Int).SetBytes(vector.blind) + rInv := new(big.Int).ModInverse(r, key.N) + if r == nil { + t.Fatal("Failed to compute blind inverse") + } + + blindedMsg, state, err := verifier.FixedBlind(vector.message, vector.info, vector.salt, r.Bytes(), rInv.Bytes()) + if err != nil { + t.Fatal(err) + } + + blindSig, err := signer.BlindSign(blindedMsg, vector.info) + if err != nil { + t.Fatal(err) + } + + sig, err := state.Finalize(blindSig) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(sig, vector.signature) { + t.Errorf("Signature mismatch: expected %x, got %x", sig, vector.signature) + } +} + +func TestPBRSAGenerateTestVector(t *testing.T) { + 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)) + } + + for _, vector := range vectors { + verifyTestVector(t, vector) + } + + // Encode the test vectors + encoded, err := json.Marshal(vectors) + if err != nil { + t.Fatalf("Error producing test vectors: %v", err) + } + + var outputFile string + if outputFile = os.Getenv(pbrsaTestVectorOutEnvironmentKey); len(outputFile) > 0 { + err := os.WriteFile(outputFile, encoded, 0o600) + 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 := loadStrongRSAKey() + + hash := crypto.SHA384 + verifier := NewVerifier(&key.PublicKey, hash) + signer, err := NewSigner(key, hash) + if err != nil { + b.Fatal(err) + } + + var blindedMsg []byte + var state VerifierState + 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, sig) + if err != nil { + b.Fatal(err) + } +} diff --git a/blindsign/blindsign.go b/blindsign/blindsign.go deleted file mode 100644 index dabba89e4..000000000 --- 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) -} diff --git a/blindsign/doc.go b/blindsign/doc.go new file mode 100644 index 000000000..e9ebecd9b --- /dev/null +++ b/blindsign/doc.go @@ -0,0 +1,2 @@ +// Package blindsign provides blind signature schemes. +package blindsign