...

Source file src/github.com/cloudflare/circl/blindsign/blindrsa/brsa_test.go

Documentation: github.com/cloudflare/circl/blindsign/blindrsa

     1  package blindrsa
     2  
     3  import (
     4  	"bytes"
     5  	"crypto"
     6  	"crypto/rand"
     7  	"crypto/rsa"
     8  	"crypto/x509"
     9  	"encoding/hex"
    10  	"encoding/json"
    11  	"encoding/pem"
    12  	"fmt"
    13  	"io"
    14  	"math/big"
    15  	"os"
    16  	"strings"
    17  	"testing"
    18  
    19  	"github.com/cloudflare/circl/internal/test"
    20  )
    21  
    22  // 2048-bit RSA private key
    23  const testPrivateKey = `
    24  -----BEGIN RSA PRIVATE KEY-----
    25  MIIEowIBAAKCAQEAyxrta2qV9bHOATpM/KsluUsuZKIwNOQlCn6rQ8DfOowSmTrx
    26  KxEZCNS0cb7DHUtsmtnN2pBhKi7pA1I+beWiJNawLwnlw3TQz+Adj1KcUAp4ovZ5
    27  CPpoK1orQwyB6vGvcte155T8mKMTknaHl1fORTtSbvm/bOuZl5uEI7kPRGGiKvN6
    28  qwz1cz91l6vkTTHHMttooYHGy75gfYwOUuBlX9mZbcWE7KC+h6+814ozfRex26no
    29  KLvYHikTFxROf/ifVWGXCbCWy7nqR0zq0mTCBz/kl0DAHwDhCRBgZpg9IeX4Pwhu
    30  LoI8h5zUPO9wDSo1Kpur1hLQPK0C2xNLfiJaXwIDAQABAoIBAC8wm3c4tYz3efDJ
    31  Ffgi38n0kNvq3x5636xXj/1XA8a7otqdWklyWIm3uhEvjG/zBVHZRz4AC8NcUOFn
    32  q3+nOgwrIZZcS1klfBrAbL3PKOhj9nGOqMKQQ8HG2oRilJD9BJG/UtFyyVnBkhuW
    33  lJxyV0e4p8eHGZX6C56xEHuoVMbDKm9HR8XRwwTHRn1VsICqIzo6Uv/fJhFMu1Qf
    34  +mtpa3oJb43P9pygirWO+w+3U6pRhccwAWlrvOjAmeP0Ndy7/gXn26rSPbKmWcI6
    35  3VIUB/FQsa8tkFTEFkIp1oQLejKk+EgUk66JWc8K6o3vDDyfdbmjTHVxi3ByyNur
    36  F87+ykkCgYEA73MLD1FLwPWdmV/V+ZiMTEwTXRBc1W1D7iigNclp9VDAzXFI6ofs
    37  3v+5N8hcZIdEBd9W6utHi/dBiEogDuSjljPRCqPsQENm2itTHzmNRvvI8wV1KQbP
    38  eJOd0vPMl5iup8nYL+9ASfGYeX5FKlttKEm4ZIY0XUsx9pERoq4PlEsCgYEA2STJ
    39  68thMWv9xKuz26LMQDzImJ5OSQD0hsts9Ge01G/rh0Dv/sTzO5wtLsiyDA/ZWkzB
    40  8J+rO/y2xqBD9VkYKaGB/wdeJP0Z+n7sETetiKPbXPfgAi7VAe77Rmst/oEcGLUg
    41  tm+XnfJSInoLU5HmtIdLg0kcQLVbN5+ZMmtkPb0CgYBSbhczmbfrYGJ1p0FBIFvD
    42  9DiCRBzBOFE3TnMAsSqx0a/dyY7hdhN8HSqE4ouz68DmCKGiU4aYz3CW23W3ysvp
    43  7EKdWBr/cHSazGlcCXLyKcFer9VKX1bS2nZtZZJb6arOhjTPI5zNF8d2o5pp33lv
    44  chlxOaYTK8yyZfRdPXCNiwKBgQDV77oFV66dm7E9aJHerkmgbIKSYz3sDUXd3GSv
    45  c9Gkj9Q0wNTzZKXkMB4P/un0mlTh88gMQ7PYeUa28UWjX7E/qwFB+8dUmA1VUGFT
    46  IVEW06GXuhv46p0wt3zXx1dcbWX6LdJaDB4MHqevkiDAqHntmXLbmVd9pXCGn/a2
    47  xznO3QKBgHkPJPEiCzRugzgN9UxOT5tNQCSGMOwJUd7qP0TWgvsWHT1N07JLgC8c
    48  Yg0f1rCxEAQo5BVppiQFp0FA7W52DUnMEfBtiehZ6xArW7crO91gFRqKBWZ3Jjyz
    49  /JcS8m5UgQxC8mmb/2wLD5TDvWw+XCfjUgWmvqIi5dcJgmuTAn5X
    50  -----END RSA PRIVATE KEY-----`
    51  
    52  func loadPrivateKey() (*rsa.PrivateKey, error) {
    53  	block, _ := pem.Decode([]byte(testPrivateKey))
    54  	if block == nil || block.Type != "RSA PRIVATE KEY" {
    55  		return nil, fmt.Errorf("PEM private key decoding failed")
    56  	}
    57  
    58  	privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	return privateKey, nil
    64  }
    65  
    66  func mustDecodeHex(h string) []byte {
    67  	b, err := hex.DecodeString(h)
    68  	if err != nil {
    69  		panic(err)
    70  	}
    71  	return b
    72  }
    73  
    74  func loadStrongRSAKey() *rsa.PrivateKey {
    75  	// https://gist.github.com/chris-wood/b77536febb25a5a11af428afff77820a
    76  	pEnc := "dcd90af1be463632c0d5ea555256a20605af3db667475e190e3af12a34a3324c46a3094062c59fb4b249e0ee6afba8bee14e0276d126c99f4784b23009bf6168ff628ac1486e5ae8e23ce4d362889de4df63109cbd90ef93db5ae64372bfe1c55f832766f21e94ea3322eb2182f10a891546536ba907ad74b8d72469bea396f3"
    77  	qEnc := "f8ba5c89bd068f57234a3cf54a1c89d5b4cd0194f2633ca7c60b91a795a56fa8c8686c0e37b1c4498b851e3420d08bea29f71d195cfbd3671c6ddc49cf4c1db5b478231ea9d91377ffa98fe95685fca20ba4623212b2f2def4da5b281ed0100b651f6db32112e4017d831c0da668768afa7141d45bbc279f1e0f8735d74395b3"
    78  	NEnc := "d6930820f71fe517bf3259d14d40209b02a5c0d3d61991c731dd7da39f8d69821552e2318d6c9ad897e603887a476ea3162c1205da9ac96f02edf31df049bd55f142134c17d4382a0e78e275345f165fbe8e49cdca6cf5c726c599dd39e09e75e0f330a33121e73976e4facba9cfa001c28b7c96f8134f9981db6750b43a41710f51da4240fe03106c12acb1e7bb53d75ec7256da3fddd0718b89c365410fce61bc7c99b115fb4c3c318081fa7e1b65a37774e8e50c96e8ce2b2cc6b3b367982366a2bf9924c4bafdb3ff5e722258ab705c76d43e5f1f121b984814e98ea2b2b8725cd9bc905c0bc3d75c2a8db70a7153213c39ae371b2b5dc1dafcb19d6fae9"
    79  	eEnc := "010001"
    80  	dEnc := "4e21356983722aa1adedb084a483401c1127b781aac89eab103e1cfc52215494981d18dd8028566d9d499469c25476358de23821c78a6ae43005e26b394e3051b5ca206aa9968d68cae23b5affd9cbb4cb16d64ac7754b3cdba241b72ad6ddfc000facdb0f0dd03abd4efcfee1730748fcc47b7621182ef8af2eeb7c985349f62ce96ab373d2689baeaea0e28ea7d45f2d605451920ca4ea1f0c08b0f1f6711eaa4b7cca66d58a6b916f9985480f90aca97210685ac7b12d2ec3e30a1c7b97b65a18d38a93189258aa346bf2bc572cd7e7359605c20221b8909d599ed9d38164c9c4abf396f897b9993c1e805e574d704649985b600fa0ced8e5427071d7049d"
    81  
    82  	p := new(big.Int).SetBytes(mustDecodeHex(pEnc))
    83  	q := new(big.Int).SetBytes(mustDecodeHex(qEnc))
    84  	N := new(big.Int).SetBytes(mustDecodeHex(NEnc))
    85  	e := new(big.Int).SetBytes(mustDecodeHex(eEnc))
    86  	d := new(big.Int).SetBytes(mustDecodeHex(dEnc))
    87  
    88  	primes := make([]*big.Int, 2)
    89  	primes[0] = p
    90  	primes[1] = q
    91  
    92  	key := &rsa.PrivateKey{
    93  		PublicKey: rsa.PublicKey{
    94  			N: N,
    95  			E: int(e.Int64()),
    96  		},
    97  		D:      d,
    98  		Primes: primes,
    99  	}
   100  
   101  	return key
   102  }
   103  
   104  func runSignatureProtocol(signer Signer, verifier Verifier, message []byte, random io.Reader) ([]byte, error) {
   105  	blindedMsg, state, err := verifier.Blind(random, message)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  
   110  	kLen := (signer.sk.N.BitLen() + 7) / 8
   111  	if len(blindedMsg) != kLen {
   112  		return nil, fmt.Errorf("Protocol message (blind message) length mismatch, expected %d, got %d", kLen, len(blindedMsg))
   113  	}
   114  
   115  	blindedSig, err := signer.BlindSign(blindedMsg)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	if len(blindedSig) != kLen {
   121  		return nil, fmt.Errorf("Protocol message (blind signature) length mismatch, expected %d, got %d", kLen, len(blindedMsg))
   122  	}
   123  
   124  	sig, err := state.Finalize(blindedSig)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  
   129  	err = verifier.Verify(message, sig)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  
   134  	return sig, nil
   135  }
   136  
   137  func TestRoundTrip(t *testing.T) {
   138  	message := []byte("hello world")
   139  	key, err := loadPrivateKey()
   140  	if err != nil {
   141  		t.Fatal(err)
   142  	}
   143  
   144  	verifier := NewVerifier(&key.PublicKey, crypto.SHA512)
   145  	signer := NewSigner(key)
   146  
   147  	sig, err := runSignatureProtocol(signer, verifier, message, rand.Reader)
   148  	if err != nil {
   149  		t.Fatal(err)
   150  	}
   151  	if sig == nil {
   152  		t.Fatal("nil signature output")
   153  	}
   154  }
   155  
   156  func TestDeterministicRoundTrip(t *testing.T) {
   157  	message := []byte("hello world")
   158  	key, err := loadPrivateKey()
   159  	if err != nil {
   160  		t.Fatal(err)
   161  	}
   162  
   163  	verifier := NewDeterministicVerifier(&key.PublicKey, crypto.SHA512)
   164  	signer := NewSigner(key)
   165  
   166  	sig, err := runSignatureProtocol(signer, verifier, message, rand.Reader)
   167  	if err != nil {
   168  		t.Fatal(err)
   169  	}
   170  	if sig == nil {
   171  		t.Fatal("nil signature output")
   172  	}
   173  }
   174  
   175  func TestDeterministicBlindFailure(t *testing.T) {
   176  	message := []byte("hello world")
   177  	key, err := loadPrivateKey()
   178  	if err != nil {
   179  		t.Fatal(err)
   180  	}
   181  
   182  	verifier := NewDeterministicVerifier(&key.PublicKey, crypto.SHA512)
   183  	signer := NewSigner(key)
   184  
   185  	_, err = runSignatureProtocol(signer, verifier, message, nil)
   186  	if err == nil {
   187  		t.Fatal("Expected signature generation to fail with empty randomness")
   188  	}
   189  }
   190  
   191  func TestRandomSignVerify(t *testing.T) {
   192  	message := []byte("hello world")
   193  	key, err := loadPrivateKey()
   194  	if err != nil {
   195  		t.Fatal(err)
   196  	}
   197  
   198  	verifier := NewVerifier(&key.PublicKey, crypto.SHA512)
   199  	signer := NewSigner(key)
   200  
   201  	sig1, err := runSignatureProtocol(signer, verifier, message, rand.Reader)
   202  	if err != nil {
   203  		t.Fatal(err)
   204  	}
   205  	sig2, err := runSignatureProtocol(signer, verifier, message, rand.Reader)
   206  	if err != nil {
   207  		t.Fatal(err)
   208  	}
   209  
   210  	if sig1 == nil || sig2 == nil {
   211  		t.Fatal("nil signature output")
   212  	}
   213  	if bytes.Equal(sig1, sig2) {
   214  		t.Fatal("random signatures matched when they should differ")
   215  	}
   216  }
   217  
   218  type mockRandom struct {
   219  	counter uint8
   220  }
   221  
   222  func (r *mockRandom) Read(p []byte) (n int, err error) {
   223  	for i := range p {
   224  		p[i] = r.counter
   225  		r.counter = r.counter + 1
   226  	}
   227  	return len(p), nil
   228  }
   229  
   230  func TestFixedRandomSignVerify(t *testing.T) {
   231  	message := []byte("hello world")
   232  	key, err := loadPrivateKey()
   233  	if err != nil {
   234  		t.Fatal(err)
   235  	}
   236  
   237  	verifier := NewVerifier(&key.PublicKey, crypto.SHA512)
   238  	signer := NewSigner(key)
   239  
   240  	mockRand := &mockRandom{0}
   241  	sig1, err := runSignatureProtocol(signer, verifier, message, mockRand)
   242  	if err != nil {
   243  		t.Fatal(err)
   244  	}
   245  	mockRand = &mockRandom{0}
   246  	sig2, err := runSignatureProtocol(signer, verifier, message, mockRand)
   247  	if err != nil {
   248  		t.Fatal(err)
   249  	}
   250  
   251  	if sig1 == nil || sig2 == nil {
   252  		t.Fatal("nil signature output")
   253  	}
   254  	if !bytes.Equal(sig1, sig2) {
   255  		t.Fatal("random signatures with fixed random seeds differ when they should be equal")
   256  	}
   257  }
   258  
   259  type rawTestVector struct {
   260  	Name           string `json:"name"`
   261  	P              string `json:"p"`
   262  	Q              string `json:"q"`
   263  	N              string `json:"n"`
   264  	E              string `json:"e"`
   265  	D              string `json:"d"`
   266  	Msg            string `json:"msg"`
   267  	MsgPrefix      string `json:"msg_prefix"`
   268  	InputMsg       string `json:"input_msg"`
   269  	Salt           string `json:"salt"`
   270  	SaltLen        string `json:"sLen"`
   271  	IsRandomized   string `json:"is_randomized"`
   272  	Inv            string `json:"inv"`
   273  	BlindedMessage string `json:"blinded_msg"`
   274  	BlindSig       string `json:"blind_sig"`
   275  	Sig            string `json:"sig"`
   276  }
   277  
   278  type testVector struct {
   279  	t              *testing.T
   280  	name           string
   281  	p              *big.Int
   282  	q              *big.Int
   283  	n              *big.Int
   284  	e              int
   285  	d              *big.Int
   286  	msg            []byte
   287  	msgPrefix      []byte
   288  	inputMsg       []byte
   289  	salt           []byte
   290  	saltLen        int
   291  	isRandomized   bool
   292  	blindInverse   *big.Int
   293  	blindedMessage []byte
   294  	blindSig       []byte
   295  	sig            []byte
   296  }
   297  
   298  type testVectorList struct {
   299  	t       *testing.T
   300  	vectors []testVector
   301  }
   302  
   303  func mustUnhexBigInt(number string) *big.Int {
   304  	data := mustUnhex(number)
   305  	value := new(big.Int)
   306  	value.SetBytes(data)
   307  	return value
   308  }
   309  
   310  func mustUnhex(value string) []byte {
   311  	value = strings.TrimPrefix(value, "0x")
   312  	data, err := hex.DecodeString(value)
   313  	if err != nil {
   314  		panic(err)
   315  	}
   316  
   317  	return data
   318  }
   319  
   320  func mustUnhexInt(value string) int {
   321  	number := mustUnhexBigInt(value)
   322  	result := int(number.Int64())
   323  	return result
   324  }
   325  
   326  func (tv *testVector) UnmarshalJSON(data []byte) error {
   327  	raw := rawTestVector{}
   328  	err := json.Unmarshal(data, &raw)
   329  	if err != nil {
   330  		return err
   331  	}
   332  
   333  	tv.name = raw.Name
   334  	tv.p = mustUnhexBigInt(raw.P)
   335  	tv.q = mustUnhexBigInt(raw.Q)
   336  	tv.n = mustUnhexBigInt(raw.N)
   337  	tv.e = mustUnhexInt(raw.E)
   338  	tv.d = mustUnhexBigInt(raw.D)
   339  	tv.msg = mustUnhex(raw.Msg)
   340  	tv.msgPrefix = mustUnhex(raw.MsgPrefix)
   341  	tv.inputMsg = mustUnhex(raw.InputMsg)
   342  	tv.salt = mustUnhex(raw.Salt)
   343  	tv.saltLen = mustUnhexInt(raw.SaltLen)
   344  	tv.isRandomized = mustUnhexInt(raw.IsRandomized) != 0
   345  	tv.blindedMessage = mustUnhex(raw.BlindedMessage)
   346  	tv.blindInverse = mustUnhexBigInt(raw.Inv)
   347  	tv.blindSig = mustUnhex(raw.BlindSig)
   348  	tv.sig = mustUnhex(raw.Sig)
   349  
   350  	return nil
   351  }
   352  
   353  func (tvl testVectorList) MarshalJSON() ([]byte, error) {
   354  	return json.Marshal(tvl.vectors)
   355  }
   356  
   357  func (tvl *testVectorList) UnmarshalJSON(data []byte) error {
   358  	err := json.Unmarshal(data, &tvl.vectors)
   359  	if err != nil {
   360  		return err
   361  	}
   362  
   363  	for i := range tvl.vectors {
   364  		tvl.vectors[i].t = tvl.t
   365  	}
   366  
   367  	return nil
   368  }
   369  
   370  func verifyTestVector(t *testing.T, vector testVector) {
   371  	key := new(rsa.PrivateKey)
   372  	key.PublicKey.N = vector.n
   373  	key.PublicKey.E = vector.e
   374  	key.D = vector.d
   375  	key.Primes = []*big.Int{vector.p, vector.q}
   376  	key.Precomputed.Dp = nil // Remove precomputed CRT values
   377  
   378  	// Recompute the original blind
   379  	rInv := new(big.Int).Set(vector.blindInverse)
   380  	r := new(big.Int).ModInverse(rInv, key.N)
   381  	if r == nil {
   382  		t.Fatal("Failed to compute blind inverse")
   383  	}
   384  
   385  	signer := NewSigner(key)
   386  
   387  	var verifier Verifier
   388  	switch vector.name {
   389  	case "RSABSSA-SHA384-PSS-Deterministic":
   390  		verifier = NewVerifier(&key.PublicKey, crypto.SHA384)
   391  	case "RSABSSA-SHA384-PSSZERO-Deterministic":
   392  		verifier = NewDeterministicVerifier(&key.PublicKey, crypto.SHA384)
   393  	case "RSABSSA-SHA384-PSS-Randomized", "RSABSSA-SHA384-PSSZERO-Randomized":
   394  		t.Skipf("variant %v not supported yet", vector.name)
   395  	default:
   396  		t.Fatal("variant not supported")
   397  	}
   398  
   399  	inputMsg := prepareMsg(vector.msg, vector.msgPrefix)
   400  	got := hex.EncodeToString(inputMsg)
   401  	want := hex.EncodeToString(vector.inputMsg)
   402  	if got != want {
   403  		test.ReportError(t, got, want)
   404  	}
   405  
   406  	blindedMsg, state, err := fixedBlind(inputMsg, vector.salt, r, rInv, &key.PublicKey, verifier.Hash())
   407  	test.CheckNoErr(t, err, "fixedBlind failed")
   408  	got = hex.EncodeToString(blindedMsg)
   409  	want = hex.EncodeToString(vector.blindedMessage)
   410  	if got != want {
   411  		test.ReportError(t, got, want)
   412  	}
   413  
   414  	blindSig, err := signer.BlindSign(blindedMsg)
   415  	test.CheckNoErr(t, err, "blindSign failed")
   416  	got = hex.EncodeToString(blindSig)
   417  	want = hex.EncodeToString(vector.blindSig)
   418  	if got != want {
   419  		test.ReportError(t, got, want)
   420  	}
   421  
   422  	sig, err := state.Finalize(blindSig)
   423  	test.CheckNoErr(t, err, "finalize failed")
   424  	got = hex.EncodeToString(sig)
   425  	want = hex.EncodeToString(vector.sig)
   426  	if got != want {
   427  		test.ReportError(t, got, want)
   428  	}
   429  
   430  	err = verifier.Verify(inputMsg, sig)
   431  	test.CheckNoErr(t, err, "verification failed")
   432  }
   433  
   434  func TestVectors(t *testing.T) {
   435  	data, err := os.ReadFile("testdata/test_vectors_rfc9474.json")
   436  	if err != nil {
   437  		t.Fatal("Failed reading test vectors:", err)
   438  	}
   439  
   440  	tvl := &testVectorList{}
   441  	err = tvl.UnmarshalJSON(data)
   442  	if err != nil {
   443  		t.Fatal("Failed deserializing test vectors:", err)
   444  	}
   445  
   446  	for _, vector := range tvl.vectors {
   447  		t.Run(vector.name, func(tt *testing.T) {
   448  			verifyTestVector(tt, vector)
   449  		})
   450  	}
   451  }
   452  
   453  func BenchmarkBRSA(b *testing.B) {
   454  	message := []byte("hello world")
   455  	key := loadStrongRSAKey()
   456  
   457  	verifier := NewVerifier(&key.PublicKey, crypto.SHA512)
   458  	signer := NewSigner(key)
   459  
   460  	var err error
   461  	var blindedMsg []byte
   462  	var state VerifierState
   463  	b.Run("Blind", func(b *testing.B) {
   464  		for n := 0; n < b.N; n++ {
   465  			blindedMsg, state, err = verifier.Blind(rand.Reader, message)
   466  			if err != nil {
   467  				b.Fatal(err)
   468  			}
   469  		}
   470  	})
   471  
   472  	var blindedSig []byte
   473  	b.Run("BlindSign", func(b *testing.B) {
   474  		for n := 0; n < b.N; n++ {
   475  			blindedSig, err = signer.BlindSign(blindedMsg)
   476  			if err != nil {
   477  				b.Fatal(err)
   478  			}
   479  		}
   480  	})
   481  
   482  	var sig []byte
   483  	b.Run("Finalize", func(b *testing.B) {
   484  		for n := 0; n < b.N; n++ {
   485  			sig, err = state.Finalize(blindedSig)
   486  			if err != nil {
   487  				b.Fatal(err)
   488  			}
   489  		}
   490  	})
   491  
   492  	err = verifier.Verify(message, sig)
   493  	if err != nil {
   494  		b.Fatal(err)
   495  	}
   496  }
   497  
   498  func Example_blindrsa() {
   499  	// Setup (offline)
   500  
   501  	// Server: generate an RSA keypair.
   502  	sk, err := rsa.GenerateKey(rand.Reader, 2048)
   503  	if err != nil {
   504  		fmt.Fprintf(os.Stderr, "failed to generate RSA key: %v", err)
   505  		return
   506  	}
   507  	pk := &sk.PublicKey
   508  	server := NewSigner(sk)
   509  
   510  	// Client: stores Server's public key.
   511  	verifier := NewVerifier(pk, crypto.SHA384)
   512  
   513  	// Protocol (online)
   514  
   515  	// Client blinds a message.
   516  	msg := []byte("alice and bob")
   517  	blindedMsg, state, err := verifier.Blind(rand.Reader, msg)
   518  	if err != nil {
   519  		fmt.Fprintf(os.Stderr, "client failed to generate blinded message: %v", err)
   520  		return
   521  	}
   522  
   523  	// Server signs a blinded message, and produces a blinded signature.
   524  	blindedSignature, err := server.BlindSign(blindedMsg)
   525  	if err != nil {
   526  		fmt.Fprintf(os.Stderr, "server failed to sign: %v", err)
   527  		return
   528  	}
   529  
   530  	// Client builds a signature from the previous state and the blinded signature.
   531  	signature, err := state.Finalize(blindedSignature)
   532  	if err != nil {
   533  		fmt.Fprintf(os.Stderr, "client failed to obtain signature: %v", err)
   534  		return
   535  	}
   536  
   537  	// Client verifies the signature is valid.
   538  	ok := verifier.Verify(msg, signature)
   539  	fmt.Printf("Valid signature: %v", ok == nil)
   540  	// Output: Valid signature: true
   541  }
   542  

View as plain text