github.com/insolar/x-crypto@v0.0.0-20191031140942-75fab8a325f6/rsa/pss_test.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package rsa
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"compress/bzip2"
    11  	"encoding/hex"
    12  	"github.com/insolar/x-crypto"
    13  	_ "github.com/insolar/x-crypto/md5"
    14  	"github.com/insolar/x-crypto/rand"
    15  	"github.com/insolar/x-crypto/sha1"
    16  	_ "github.com/insolar/x-crypto/sha256"
    17  	"math/big"
    18  	"os"
    19  	"strconv"
    20  	"strings"
    21  	"testing"
    22  )
    23  
    24  func TestEMSAPSS(t *testing.T) {
    25  	// Test vector in file pss-int.txt from: ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
    26  	msg := []byte{
    27  		0x85, 0x9e, 0xef, 0x2f, 0xd7, 0x8a, 0xca, 0x00, 0x30, 0x8b,
    28  		0xdc, 0x47, 0x11, 0x93, 0xbf, 0x55, 0xbf, 0x9d, 0x78, 0xdb,
    29  		0x8f, 0x8a, 0x67, 0x2b, 0x48, 0x46, 0x34, 0xf3, 0xc9, 0xc2,
    30  		0x6e, 0x64, 0x78, 0xae, 0x10, 0x26, 0x0f, 0xe0, 0xdd, 0x8c,
    31  		0x08, 0x2e, 0x53, 0xa5, 0x29, 0x3a, 0xf2, 0x17, 0x3c, 0xd5,
    32  		0x0c, 0x6d, 0x5d, 0x35, 0x4f, 0xeb, 0xf7, 0x8b, 0x26, 0x02,
    33  		0x1c, 0x25, 0xc0, 0x27, 0x12, 0xe7, 0x8c, 0xd4, 0x69, 0x4c,
    34  		0x9f, 0x46, 0x97, 0x77, 0xe4, 0x51, 0xe7, 0xf8, 0xe9, 0xe0,
    35  		0x4c, 0xd3, 0x73, 0x9c, 0x6b, 0xbf, 0xed, 0xae, 0x48, 0x7f,
    36  		0xb5, 0x56, 0x44, 0xe9, 0xca, 0x74, 0xff, 0x77, 0xa5, 0x3c,
    37  		0xb7, 0x29, 0x80, 0x2f, 0x6e, 0xd4, 0xa5, 0xff, 0xa8, 0xba,
    38  		0x15, 0x98, 0x90, 0xfc,
    39  	}
    40  	salt := []byte{
    41  		0xe3, 0xb5, 0xd5, 0xd0, 0x02, 0xc1, 0xbc, 0xe5, 0x0c, 0x2b,
    42  		0x65, 0xef, 0x88, 0xa1, 0x88, 0xd8, 0x3b, 0xce, 0x7e, 0x61,
    43  	}
    44  	expected := []byte{
    45  		0x66, 0xe4, 0x67, 0x2e, 0x83, 0x6a, 0xd1, 0x21, 0xba, 0x24,
    46  		0x4b, 0xed, 0x65, 0x76, 0xb8, 0x67, 0xd9, 0xa4, 0x47, 0xc2,
    47  		0x8a, 0x6e, 0x66, 0xa5, 0xb8, 0x7d, 0xee, 0x7f, 0xbc, 0x7e,
    48  		0x65, 0xaf, 0x50, 0x57, 0xf8, 0x6f, 0xae, 0x89, 0x84, 0xd9,
    49  		0xba, 0x7f, 0x96, 0x9a, 0xd6, 0xfe, 0x02, 0xa4, 0xd7, 0x5f,
    50  		0x74, 0x45, 0xfe, 0xfd, 0xd8, 0x5b, 0x6d, 0x3a, 0x47, 0x7c,
    51  		0x28, 0xd2, 0x4b, 0xa1, 0xe3, 0x75, 0x6f, 0x79, 0x2d, 0xd1,
    52  		0xdc, 0xe8, 0xca, 0x94, 0x44, 0x0e, 0xcb, 0x52, 0x79, 0xec,
    53  		0xd3, 0x18, 0x3a, 0x31, 0x1f, 0xc8, 0x96, 0xda, 0x1c, 0xb3,
    54  		0x93, 0x11, 0xaf, 0x37, 0xea, 0x4a, 0x75, 0xe2, 0x4b, 0xdb,
    55  		0xfd, 0x5c, 0x1d, 0xa0, 0xde, 0x7c, 0xec, 0xdf, 0x1a, 0x89,
    56  		0x6f, 0x9d, 0x8b, 0xc8, 0x16, 0xd9, 0x7c, 0xd7, 0xa2, 0xc4,
    57  		0x3b, 0xad, 0x54, 0x6f, 0xbe, 0x8c, 0xfe, 0xbc,
    58  	}
    59  
    60  	hash := sha1.New()
    61  	hash.Write(msg)
    62  	hashed := hash.Sum(nil)
    63  
    64  	encoded, err := emsaPSSEncode(hashed, 1023, salt, sha1.New())
    65  	if err != nil {
    66  		t.Errorf("Error from emsaPSSEncode: %s\n", err)
    67  	}
    68  	if !bytes.Equal(encoded, expected) {
    69  		t.Errorf("Bad encoding. got %x, want %x", encoded, expected)
    70  	}
    71  
    72  	if err = emsaPSSVerify(hashed, encoded, 1023, len(salt), sha1.New()); err != nil {
    73  		t.Errorf("Bad verification: %s", err)
    74  	}
    75  }
    76  
    77  // TestPSSGolden tests all the test vectors in pss-vect.txt from
    78  // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
    79  func TestPSSGolden(t *testing.T) {
    80  	inFile, err := os.Open("testdata/pss-vect.txt.bz2")
    81  	if err != nil {
    82  		t.Fatalf("Failed to open input file: %s", err)
    83  	}
    84  	defer inFile.Close()
    85  
    86  	// The pss-vect.txt file contains RSA keys and then a series of
    87  	// signatures. A goroutine is used to preprocess the input by merging
    88  	// lines, removing spaces in hex values and identifying the start of
    89  	// new keys and signature blocks.
    90  	const newKeyMarker = "START NEW KEY"
    91  	const newSignatureMarker = "START NEW SIGNATURE"
    92  
    93  	values := make(chan string)
    94  
    95  	go func() {
    96  		defer close(values)
    97  		scanner := bufio.NewScanner(bzip2.NewReader(inFile))
    98  		var partialValue string
    99  		lastWasValue := true
   100  
   101  		for scanner.Scan() {
   102  			line := scanner.Text()
   103  			switch {
   104  			case len(line) == 0:
   105  				if len(partialValue) > 0 {
   106  					values <- strings.Replace(partialValue, " ", "", -1)
   107  					partialValue = ""
   108  					lastWasValue = true
   109  				}
   110  				continue
   111  			case strings.HasPrefix(line, "# ======") && lastWasValue:
   112  				values <- newKeyMarker
   113  				lastWasValue = false
   114  			case strings.HasPrefix(line, "# ------") && lastWasValue:
   115  				values <- newSignatureMarker
   116  				lastWasValue = false
   117  			case strings.HasPrefix(line, "#"):
   118  				continue
   119  			default:
   120  				partialValue += line
   121  			}
   122  		}
   123  		if err := scanner.Err(); err != nil {
   124  			panic(err)
   125  		}
   126  	}()
   127  
   128  	var key *PublicKey
   129  	var hashed []byte
   130  	hash := crypto.SHA1
   131  	h := hash.New()
   132  	opts := &PSSOptions{
   133  		SaltLength: PSSSaltLengthEqualsHash,
   134  	}
   135  
   136  	for marker := range values {
   137  		switch marker {
   138  		case newKeyMarker:
   139  			key = new(PublicKey)
   140  			nHex, ok := <-values
   141  			if !ok {
   142  				continue
   143  			}
   144  			key.N = bigFromHex(nHex)
   145  			key.E = intFromHex(<-values)
   146  			// We don't care for d, p, q, dP, dQ or qInv.
   147  			for i := 0; i < 6; i++ {
   148  				<-values
   149  			}
   150  		case newSignatureMarker:
   151  			msg := fromHex(<-values)
   152  			<-values // skip salt
   153  			sig := fromHex(<-values)
   154  
   155  			h.Reset()
   156  			h.Write(msg)
   157  			hashed = h.Sum(hashed[:0])
   158  
   159  			if err := VerifyPSS(key, hash, hashed, sig, opts); err != nil {
   160  				t.Error(err)
   161  			}
   162  		default:
   163  			t.Fatalf("unknown marker: " + marker)
   164  		}
   165  	}
   166  }
   167  
   168  // TestPSSOpenSSL ensures that we can verify a PSS signature from OpenSSL with
   169  // the default options. OpenSSL sets the salt length to be maximal.
   170  func TestPSSOpenSSL(t *testing.T) {
   171  	hash := crypto.SHA256
   172  	h := hash.New()
   173  	h.Write([]byte("testing"))
   174  	hashed := h.Sum(nil)
   175  
   176  	// Generated with `echo -n testing | openssl dgst -sign key.pem -sigopt rsa_padding_mode:pss -sha256 > sig`
   177  	sig := []byte{
   178  		0x95, 0x59, 0x6f, 0xd3, 0x10, 0xa2, 0xe7, 0xa2, 0x92, 0x9d,
   179  		0x4a, 0x07, 0x2e, 0x2b, 0x27, 0xcc, 0x06, 0xc2, 0x87, 0x2c,
   180  		0x52, 0xf0, 0x4a, 0xcc, 0x05, 0x94, 0xf2, 0xc3, 0x2e, 0x20,
   181  		0xd7, 0x3e, 0x66, 0x62, 0xb5, 0x95, 0x2b, 0xa3, 0x93, 0x9a,
   182  		0x66, 0x64, 0x25, 0xe0, 0x74, 0x66, 0x8c, 0x3e, 0x92, 0xeb,
   183  		0xc6, 0xe6, 0xc0, 0x44, 0xf3, 0xb4, 0xb4, 0x2e, 0x8c, 0x66,
   184  		0x0a, 0x37, 0x9c, 0x69,
   185  	}
   186  
   187  	if err := VerifyPSS(&rsaPrivateKey.PublicKey, hash, hashed, sig, nil); err != nil {
   188  		t.Error(err)
   189  	}
   190  }
   191  
   192  func TestPSSNilOpts(t *testing.T) {
   193  	hash := crypto.SHA256
   194  	h := hash.New()
   195  	h.Write([]byte("testing"))
   196  	hashed := h.Sum(nil)
   197  
   198  	SignPSS(rand.Reader, rsaPrivateKey, hash, hashed, nil)
   199  }
   200  
   201  func TestPSSSigning(t *testing.T) {
   202  	var saltLengthCombinations = []struct {
   203  		signSaltLength, verifySaltLength int
   204  		good                             bool
   205  	}{
   206  		{PSSSaltLengthAuto, PSSSaltLengthAuto, true},
   207  		{PSSSaltLengthEqualsHash, PSSSaltLengthAuto, true},
   208  		{PSSSaltLengthEqualsHash, PSSSaltLengthEqualsHash, true},
   209  		{PSSSaltLengthEqualsHash, 8, false},
   210  		{PSSSaltLengthAuto, PSSSaltLengthEqualsHash, false},
   211  		{8, 8, true},
   212  	}
   213  
   214  	hash := crypto.MD5
   215  	h := hash.New()
   216  	h.Write([]byte("testing"))
   217  	hashed := h.Sum(nil)
   218  	var opts PSSOptions
   219  
   220  	for i, test := range saltLengthCombinations {
   221  		opts.SaltLength = test.signSaltLength
   222  		sig, err := SignPSS(rand.Reader, rsaPrivateKey, hash, hashed, &opts)
   223  		if err != nil {
   224  			t.Errorf("#%d: error while signing: %s", i, err)
   225  			continue
   226  		}
   227  
   228  		opts.SaltLength = test.verifySaltLength
   229  		err = VerifyPSS(&rsaPrivateKey.PublicKey, hash, hashed, sig, &opts)
   230  		if (err == nil) != test.good {
   231  			t.Errorf("#%d: bad result, wanted: %t, got: %s", i, test.good, err)
   232  		}
   233  	}
   234  }
   235  
   236  func bigFromHex(hex string) *big.Int {
   237  	n, ok := new(big.Int).SetString(hex, 16)
   238  	if !ok {
   239  		panic("bad hex: " + hex)
   240  	}
   241  	return n
   242  }
   243  
   244  func intFromHex(hex string) int {
   245  	i, err := strconv.ParseInt(hex, 16, 32)
   246  	if err != nil {
   247  		panic(err)
   248  	}
   249  	return int(i)
   250  }
   251  
   252  func fromHex(hexStr string) []byte {
   253  	s, err := hex.DecodeString(hexStr)
   254  	if err != nil {
   255  		panic(err)
   256  	}
   257  	return s
   258  }