github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/common/crypto/expiration_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package crypto
     8  
     9  import (
    10  	"bytes"
    11  	"encoding/pem"
    12  	"errors"
    13  	"fmt"
    14  	"io/ioutil"
    15  	"path/filepath"
    16  	"strings"
    17  	"testing"
    18  	"time"
    19  
    20  	"github.com/golang/protobuf/proto"
    21  	"github.com/hyperledger/fabric-protos-go/msp"
    22  	"github.com/osdi23p228/fabric/common/crypto/tlsgen"
    23  	"github.com/osdi23p228/fabric/protoutil"
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  )
    27  
    28  func TestX509CertExpiresAt(t *testing.T) {
    29  	certBytes, err := ioutil.ReadFile(filepath.Join("testdata", "cert.pem"))
    30  	assert.NoError(t, err)
    31  	sId := &msp.SerializedIdentity{
    32  		IdBytes: certBytes,
    33  	}
    34  	serializedIdentity, err := proto.Marshal(sId)
    35  	assert.NoError(t, err)
    36  	expirationTime := ExpiresAt(serializedIdentity)
    37  	assert.Equal(t, time.Date(2027, 8, 17, 12, 19, 48, 0, time.UTC), expirationTime)
    38  }
    39  
    40  func TestX509InvalidCertExpiresAt(t *testing.T) {
    41  	certBytes, err := ioutil.ReadFile(filepath.Join("testdata", "badCert.pem"))
    42  	assert.NoError(t, err)
    43  	sId := &msp.SerializedIdentity{
    44  		IdBytes: certBytes,
    45  	}
    46  	serializedIdentity, err := proto.Marshal(sId)
    47  	assert.NoError(t, err)
    48  	expirationTime := ExpiresAt(serializedIdentity)
    49  	assert.True(t, expirationTime.IsZero())
    50  }
    51  
    52  func TestIdemixIdentityExpiresAt(t *testing.T) {
    53  	idemixId := &msp.SerializedIdemixIdentity{
    54  		NymX: []byte{1, 2, 3},
    55  		NymY: []byte{1, 2, 3},
    56  		Ou:   []byte("OU1"),
    57  	}
    58  	idemixBytes, err := proto.Marshal(idemixId)
    59  	assert.NoError(t, err)
    60  	sId := &msp.SerializedIdentity{
    61  		IdBytes: idemixBytes,
    62  	}
    63  	serializedIdentity, err := proto.Marshal(sId)
    64  	assert.NoError(t, err)
    65  	expirationTime := ExpiresAt(serializedIdentity)
    66  	assert.True(t, expirationTime.IsZero())
    67  }
    68  
    69  func TestInvalidIdentityExpiresAt(t *testing.T) {
    70  	expirationTime := ExpiresAt([]byte{1, 2, 3})
    71  	assert.True(t, expirationTime.IsZero())
    72  }
    73  
    74  func TestTrackExpiration(t *testing.T) {
    75  	ca, err := tlsgen.NewCA()
    76  	assert.NoError(t, err)
    77  
    78  	now := time.Now()
    79  	expirationTime := certExpirationTime(ca.CertBytes())
    80  
    81  	timeUntilExpiration := expirationTime.Sub(now)
    82  	timeUntilOneMonthBeforeExpiration := timeUntilExpiration - 28*24*time.Hour
    83  	timeUntil2DaysBeforeExpiration := timeUntilExpiration - 2*24*time.Hour - time.Hour*12
    84  
    85  	monthBeforeExpiration := now.Add(timeUntilOneMonthBeforeExpiration)
    86  	twoDaysBeforeExpiration := now.Add(timeUntil2DaysBeforeExpiration)
    87  
    88  	tlsCert, err := ca.NewServerCertKeyPair("127.0.0.1")
    89  	assert.NoError(t, err)
    90  
    91  	signingIdentity := protoutil.MarshalOrPanic(&msp.SerializedIdentity{
    92  		IdBytes: tlsCert.Cert,
    93  	})
    94  
    95  	warnShouldNotBeInvoked := func(format string, args ...interface{}) {
    96  		t.Fatalf(format, args...)
    97  	}
    98  
    99  	var formattedWarning string
   100  	warnShouldBeInvoked := func(format string, args ...interface{}) {
   101  		formattedWarning = fmt.Sprintf(format, args...)
   102  	}
   103  
   104  	var formattedInfo string
   105  	infoShouldBeInvoked := func(format string, args ...interface{}) {
   106  		formattedInfo = fmt.Sprintf(format, args...)
   107  	}
   108  
   109  	for _, testCase := range []struct {
   110  		description        string
   111  		tls                bool
   112  		serverCert         []byte
   113  		clientCertChain    [][]byte
   114  		sIDBytes           []byte
   115  		info               MessageFunc
   116  		warn               MessageFunc
   117  		now                time.Time
   118  		expectedInfoPrefix string
   119  		expectedWarn       string
   120  	}{
   121  		{
   122  			description: "No TLS, enrollment cert isn't valid logs a warning",
   123  			warn:        warnShouldNotBeInvoked,
   124  			sIDBytes:    []byte{1, 2, 3},
   125  		},
   126  		{
   127  			description:        "No TLS, enrollment cert expires soon",
   128  			sIDBytes:           signingIdentity,
   129  			info:               infoShouldBeInvoked,
   130  			warn:               warnShouldBeInvoked,
   131  			now:                monthBeforeExpiration,
   132  			expectedInfoPrefix: "The enrollment certificate will expire on",
   133  			expectedWarn:       "The enrollment certificate will expire within one week",
   134  		},
   135  		{
   136  			description:        "TLS, server cert expires soon",
   137  			info:               infoShouldBeInvoked,
   138  			warn:               warnShouldBeInvoked,
   139  			now:                monthBeforeExpiration,
   140  			tls:                true,
   141  			serverCert:         tlsCert.Cert,
   142  			expectedInfoPrefix: "The server TLS certificate will expire on",
   143  			expectedWarn:       "The server TLS certificate will expire within one week",
   144  		},
   145  		{
   146  			description:        "TLS, server cert expires really soon",
   147  			info:               infoShouldBeInvoked,
   148  			warn:               warnShouldBeInvoked,
   149  			now:                twoDaysBeforeExpiration,
   150  			tls:                true,
   151  			serverCert:         tlsCert.Cert,
   152  			expectedInfoPrefix: "The server TLS certificate will expire on",
   153  			expectedWarn:       "The server TLS certificate expires within 2 days and 12 hours",
   154  		},
   155  		{
   156  			description:  "TLS, server cert has expired",
   157  			info:         infoShouldBeInvoked,
   158  			warn:         warnShouldBeInvoked,
   159  			now:          expirationTime.Add(time.Hour),
   160  			tls:          true,
   161  			serverCert:   tlsCert.Cert,
   162  			expectedWarn: "The server TLS certificate has expired",
   163  		},
   164  		{
   165  			description:        "TLS, client cert expires soon",
   166  			info:               infoShouldBeInvoked,
   167  			warn:               warnShouldBeInvoked,
   168  			now:                monthBeforeExpiration,
   169  			tls:                true,
   170  			clientCertChain:    [][]byte{tlsCert.Cert},
   171  			expectedInfoPrefix: "The client TLS certificate will expire on",
   172  			expectedWarn:       "The client TLS certificate will expire within one week",
   173  		},
   174  	} {
   175  		t.Run(testCase.description, func(t *testing.T) {
   176  			defer func() {
   177  				formattedWarning = ""
   178  				formattedInfo = ""
   179  			}()
   180  
   181  			fakeTimeAfter := func(duration time.Duration, f func()) *time.Timer {
   182  				assert.NotEmpty(t, testCase.expectedWarn)
   183  				threeWeeks := 3 * 7 * 24 * time.Hour
   184  				assert.Equal(t, threeWeeks, duration)
   185  				f()
   186  				return nil
   187  			}
   188  
   189  			TrackExpiration(testCase.tls,
   190  				testCase.serverCert,
   191  				testCase.clientCertChain,
   192  				testCase.sIDBytes,
   193  				testCase.info,
   194  				testCase.warn,
   195  				testCase.now,
   196  				fakeTimeAfter)
   197  
   198  			if testCase.expectedInfoPrefix != "" {
   199  				require.True(t, strings.HasPrefix(formattedInfo, testCase.expectedInfoPrefix))
   200  			} else {
   201  				require.Empty(t, formattedInfo)
   202  			}
   203  
   204  			if testCase.expectedWarn != "" {
   205  				assert.Equal(t, testCase.expectedWarn, formattedWarning)
   206  			} else {
   207  				assert.Empty(t, formattedWarning)
   208  			}
   209  
   210  		})
   211  	}
   212  }
   213  
   214  func TestLogNonPubKeyMismatchErr(t *testing.T) {
   215  	ca, err := tlsgen.NewCA()
   216  	require.NoError(t, err)
   217  
   218  	aliceKeyPair, err := ca.NewClientCertKeyPair()
   219  	require.NoError(t, err)
   220  
   221  	bobKeyPair, err := ca.NewClientCertKeyPair()
   222  	require.NoError(t, err)
   223  
   224  	expected := &bytes.Buffer{}
   225  	expected.WriteString(fmt.Sprintf("Failed determining if public key of %s matches public key of %s: foo",
   226  		string(aliceKeyPair.Cert),
   227  		string(bobKeyPair.Cert)))
   228  
   229  	b := &bytes.Buffer{}
   230  	f := func(template string, args ...interface{}) {
   231  		fmt.Fprintf(b, template, args...)
   232  	}
   233  
   234  	LogNonPubKeyMismatchErr(f, errors.New("foo"), aliceKeyPair.TLSCert.Raw, bobKeyPair.TLSCert.Raw)
   235  
   236  	require.Equal(t, expected.String(), b.String())
   237  }
   238  
   239  func TestCertificatesWithSamePublicKey(t *testing.T) {
   240  	ca, err := tlsgen.NewCA()
   241  	require.NoError(t, err)
   242  
   243  	bobKeyPair, err := ca.NewClientCertKeyPair()
   244  	require.NoError(t, err)
   245  
   246  	bobCert := bobKeyPair.Cert
   247  	bob := pem2der(bobCert)
   248  
   249  	aliceCert := `-----BEGIN CERTIFICATE-----
   250  MIIBNjCB3KADAgECAgELMAoGCCqGSM49BAMCMBAxDjAMBgNVBAUTBUFsaWNlMB4X
   251  DTIwMDgxODIxMzU1NFoXDTIwMDgyMDIxMzU1NFowEDEOMAwGA1UEBRMFQWxpY2Uw
   252  WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQjZP5VD/RaczoPFbA4gkt1qb54R6SP
   253  J/V5oxkhDboG9xWi0wpyghaMGwwxC7Q9wegEnyOVp9nXoLrQ8LUJ5BfZoycwJTAO
   254  BgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwCgYIKoZIzj0EAwID
   255  SQAwRgIhAK4le5XgH5edyhaQ9Sz7sFz3Zc4bbhPAzt9zQUYnoqK+AiEA5zcyLB/4
   256  Oqe93lroE6GF9W7UoCZFzD7lXsWku/dgFOU=
   257  -----END CERTIFICATE-----`
   258  
   259  	reIssuedAliceCert := `-----BEGIN CERTIFICATE-----
   260  MIIBNDCB3KADAgECAgELMAoGCCqGSM49BAMCMBAxDjAMBgNVBAUTBUFsaWNlMB4X
   261  DTIwMDgxODIxMzY1NFoXDTIwMDgyMDIxMzY1NFowEDEOMAwGA1UEBRMFQWxpY2Uw
   262  WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQjZP5VD/RaczoPFbA4gkt1qb54R6SP
   263  J/V5oxkhDboG9xWi0wpyghaMGwwxC7Q9wegEnyOVp9nXoLrQ8LUJ5BfZoycwJTAO
   264  BgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwCgYIKoZIzj0EAwID
   265  RwAwRAIgDc8WyXFvsxCk97KS7D/LdYJxMpDKdHNFqpzJT9LddlsCIEr8KcMd/t5p
   266  cRv6rqxvy5M+t0DhRtiwCen70YCUsksb
   267  -----END CERTIFICATE-----`
   268  
   269  	alice := pem2der([]byte(aliceCert))
   270  	aliceMakesComeback := pem2der([]byte(reIssuedAliceCert))
   271  
   272  	for _, test := range []struct {
   273  		description string
   274  		errContains string
   275  		first       []byte
   276  		second      []byte
   277  	}{
   278  		{
   279  			description: "Bad first certificate",
   280  			errContains: "asn1:",
   281  			first:       []byte{1, 2, 3},
   282  			second:      bob,
   283  		},
   284  
   285  		{
   286  			description: "Bad second certificate",
   287  			errContains: "asn1:",
   288  			first:       alice,
   289  			second:      []byte{1, 2, 3},
   290  		},
   291  
   292  		{
   293  			description: "Different certificate",
   294  			errContains: ErrPubKeyMismatch.Error(),
   295  			first:       alice,
   296  			second:      bob,
   297  		},
   298  
   299  		{
   300  			description: "Same certificate",
   301  			first:       alice,
   302  			second:      alice,
   303  		},
   304  
   305  		{
   306  			description: "Same certificate but different validity period",
   307  			first:       alice,
   308  			second:      aliceMakesComeback,
   309  		},
   310  	} {
   311  		t.Run(test.description, func(t *testing.T) {
   312  			err := CertificatesWithSamePublicKey(test.first, test.second)
   313  			if test.errContains != "" {
   314  				require.Error(t, err)
   315  				require.Contains(t, err.Error(), test.errContains)
   316  				return
   317  			}
   318  
   319  			require.NoError(t, err)
   320  		})
   321  	}
   322  }
   323  
   324  func pem2der(p []byte) []byte {
   325  	b, _ := pem.Decode(p)
   326  	return b.Bytes
   327  }