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 }