go_study/fabric-main/common/crypto/expiration_test.go

326 lines
9.2 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package crypto
import (
"bytes"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"testing"
"time"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric-protos-go/msp"
"github.com/hyperledger/fabric/common/crypto/tlsgen"
"github.com/hyperledger/fabric/protoutil"
"github.com/stretchr/testify/require"
)
func TestX509CertExpiresAt(t *testing.T) {
certBytes, err := ioutil.ReadFile(filepath.Join("testdata", "cert.pem"))
require.NoError(t, err)
sId := &msp.SerializedIdentity{
IdBytes: certBytes,
}
serializedIdentity, err := proto.Marshal(sId)
require.NoError(t, err)
expirationTime := ExpiresAt(serializedIdentity)
require.Equal(t, time.Date(2027, 8, 17, 12, 19, 48, 0, time.UTC), expirationTime)
}
func TestX509InvalidCertExpiresAt(t *testing.T) {
certBytes, err := ioutil.ReadFile(filepath.Join("testdata", "badCert.pem"))
require.NoError(t, err)
sId := &msp.SerializedIdentity{
IdBytes: certBytes,
}
serializedIdentity, err := proto.Marshal(sId)
require.NoError(t, err)
expirationTime := ExpiresAt(serializedIdentity)
require.True(t, expirationTime.IsZero())
}
func TestIdemixIdentityExpiresAt(t *testing.T) {
idemixId := &msp.SerializedIdemixIdentity{
NymX: []byte{1, 2, 3},
NymY: []byte{1, 2, 3},
Ou: []byte("OU1"),
}
idemixBytes, err := proto.Marshal(idemixId)
require.NoError(t, err)
sId := &msp.SerializedIdentity{
IdBytes: idemixBytes,
}
serializedIdentity, err := proto.Marshal(sId)
require.NoError(t, err)
expirationTime := ExpiresAt(serializedIdentity)
require.True(t, expirationTime.IsZero())
}
func TestInvalidIdentityExpiresAt(t *testing.T) {
expirationTime := ExpiresAt([]byte{1, 2, 3})
require.True(t, expirationTime.IsZero())
}
func TestTrackExpiration(t *testing.T) {
ca, err := tlsgen.NewCA()
require.NoError(t, err)
now := time.Now()
expirationTime := certExpirationTime(ca.CertBytes())
timeUntilExpiration := expirationTime.Sub(now)
timeUntilOneMonthBeforeExpiration := timeUntilExpiration - 28*24*time.Hour
timeUntil2DaysBeforeExpiration := timeUntilExpiration - 2*24*time.Hour - time.Hour*12
monthBeforeExpiration := now.Add(timeUntilOneMonthBeforeExpiration)
twoDaysBeforeExpiration := now.Add(timeUntil2DaysBeforeExpiration)
tlsCert, err := ca.NewServerCertKeyPair("127.0.0.1")
require.NoError(t, err)
signingIdentity := protoutil.MarshalOrPanic(&msp.SerializedIdentity{
IdBytes: tlsCert.Cert,
})
warnShouldNotBeInvoked := func(format string, args ...interface{}) {
t.Fatalf(format, args...)
}
var formattedWarning string
warnShouldBeInvoked := func(format string, args ...interface{}) {
formattedWarning = fmt.Sprintf(format, args...)
}
var formattedInfo string
infoShouldBeInvoked := func(format string, args ...interface{}) {
formattedInfo = fmt.Sprintf(format, args...)
}
for _, testCase := range []struct {
description string
tls bool
serverCert []byte
clientCertChain [][]byte
sIDBytes []byte
info MessageFunc
warn MessageFunc
now time.Time
expectedInfoPrefix string
expectedWarn string
}{
{
description: "No TLS, enrollment cert isn't valid logs a warning",
warn: warnShouldNotBeInvoked,
sIDBytes: []byte{1, 2, 3},
},
{
description: "No TLS, enrollment cert expires soon",
sIDBytes: signingIdentity,
info: infoShouldBeInvoked,
warn: warnShouldBeInvoked,
now: monthBeforeExpiration,
expectedInfoPrefix: "The enrollment certificate will expire on",
expectedWarn: "The enrollment certificate will expire within one week",
},
{
description: "TLS, server cert expires soon",
info: infoShouldBeInvoked,
warn: warnShouldBeInvoked,
now: monthBeforeExpiration,
tls: true,
serverCert: tlsCert.Cert,
expectedInfoPrefix: "The server TLS certificate will expire on",
expectedWarn: "The server TLS certificate will expire within one week",
},
{
description: "TLS, server cert expires really soon",
info: infoShouldBeInvoked,
warn: warnShouldBeInvoked,
now: twoDaysBeforeExpiration,
tls: true,
serverCert: tlsCert.Cert,
expectedInfoPrefix: "The server TLS certificate will expire on",
expectedWarn: "The server TLS certificate expires within 2 days and 12 hours",
},
{
description: "TLS, server cert has expired",
info: infoShouldBeInvoked,
warn: warnShouldBeInvoked,
now: expirationTime.Add(time.Hour),
tls: true,
serverCert: tlsCert.Cert,
expectedWarn: "The server TLS certificate has expired",
},
{
description: "TLS, client cert expires soon",
info: infoShouldBeInvoked,
warn: warnShouldBeInvoked,
now: monthBeforeExpiration,
tls: true,
clientCertChain: [][]byte{tlsCert.Cert},
expectedInfoPrefix: "The client TLS certificate will expire on",
expectedWarn: "The client TLS certificate will expire within one week",
},
} {
t.Run(testCase.description, func(t *testing.T) {
defer func() {
formattedWarning = ""
formattedInfo = ""
}()
fakeTimeAfter := func(duration time.Duration, f func()) *time.Timer {
require.NotEmpty(t, testCase.expectedWarn)
threeWeeks := 3 * 7 * 24 * time.Hour
require.Equal(t, threeWeeks, duration)
f()
return nil
}
TrackExpiration(testCase.tls,
testCase.serverCert,
testCase.clientCertChain,
testCase.sIDBytes,
testCase.info,
testCase.warn,
testCase.now,
fakeTimeAfter)
if testCase.expectedInfoPrefix != "" {
require.True(t, strings.HasPrefix(formattedInfo, testCase.expectedInfoPrefix))
} else {
require.Empty(t, formattedInfo)
}
if testCase.expectedWarn != "" {
require.Equal(t, testCase.expectedWarn, formattedWarning)
} else {
require.Empty(t, formattedWarning)
}
})
}
}
func TestLogNonPubKeyMismatchErr(t *testing.T) {
ca, err := tlsgen.NewCA()
require.NoError(t, err)
aliceKeyPair, err := ca.NewClientCertKeyPair()
require.NoError(t, err)
bobKeyPair, err := ca.NewClientCertKeyPair()
require.NoError(t, err)
expected := &bytes.Buffer{}
expected.WriteString(fmt.Sprintf("Failed determining if public key of %s matches public key of %s: foo",
string(aliceKeyPair.Cert),
string(bobKeyPair.Cert)))
b := &bytes.Buffer{}
f := func(template string, args ...interface{}) {
fmt.Fprintf(b, template, args...)
}
LogNonPubKeyMismatchErr(f, errors.New("foo"), aliceKeyPair.TLSCert.Raw, bobKeyPair.TLSCert.Raw)
require.Equal(t, expected.String(), b.String())
}
func TestCertificatesWithSamePublicKey(t *testing.T) {
ca, err := tlsgen.NewCA()
require.NoError(t, err)
bobKeyPair, err := ca.NewClientCertKeyPair()
require.NoError(t, err)
bobCert := bobKeyPair.Cert
bob := pem2der(bobCert)
aliceCert := `-----BEGIN CERTIFICATE-----
MIIBNjCB3KADAgECAgELMAoGCCqGSM49BAMCMBAxDjAMBgNVBAUTBUFsaWNlMB4X
DTIwMDgxODIxMzU1NFoXDTIwMDgyMDIxMzU1NFowEDEOMAwGA1UEBRMFQWxpY2Uw
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQjZP5VD/RaczoPFbA4gkt1qb54R6SP
J/V5oxkhDboG9xWi0wpyghaMGwwxC7Q9wegEnyOVp9nXoLrQ8LUJ5BfZoycwJTAO
BgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwCgYIKoZIzj0EAwID
SQAwRgIhAK4le5XgH5edyhaQ9Sz7sFz3Zc4bbhPAzt9zQUYnoqK+AiEA5zcyLB/4
Oqe93lroE6GF9W7UoCZFzD7lXsWku/dgFOU=
-----END CERTIFICATE-----`
reIssuedAliceCert := `-----BEGIN CERTIFICATE-----
MIIBNDCB3KADAgECAgELMAoGCCqGSM49BAMCMBAxDjAMBgNVBAUTBUFsaWNlMB4X
DTIwMDgxODIxMzY1NFoXDTIwMDgyMDIxMzY1NFowEDEOMAwGA1UEBRMFQWxpY2Uw
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQjZP5VD/RaczoPFbA4gkt1qb54R6SP
J/V5oxkhDboG9xWi0wpyghaMGwwxC7Q9wegEnyOVp9nXoLrQ8LUJ5BfZoycwJTAO
BgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwCgYIKoZIzj0EAwID
RwAwRAIgDc8WyXFvsxCk97KS7D/LdYJxMpDKdHNFqpzJT9LddlsCIEr8KcMd/t5p
cRv6rqxvy5M+t0DhRtiwCen70YCUsksb
-----END CERTIFICATE-----`
alice := pem2der([]byte(aliceCert))
aliceMakesComeback := pem2der([]byte(reIssuedAliceCert))
for _, test := range []struct {
description string
errContains string
first []byte
second []byte
}{
{
description: "Bad first certificate",
errContains: "malformed certificate",
first: []byte{1, 2, 3},
second: bob,
},
{
description: "Bad second certificate",
errContains: "malformed certificate",
first: alice,
second: []byte{1, 2, 3},
},
{
description: "Different certificate",
errContains: ErrPubKeyMismatch.Error(),
first: alice,
second: bob,
},
{
description: "Same certificate",
first: alice,
second: alice,
},
{
description: "Same certificate but different validity period",
first: alice,
second: aliceMakesComeback,
},
} {
t.Run(test.description, func(t *testing.T) {
err := CertificatesWithSamePublicKey(test.first, test.second)
if test.errContains != "" {
require.Error(t, err)
require.Contains(t, err.Error(), test.errContains)
return
}
require.NoError(t, err)
})
}
}
func pem2der(p []byte) []byte {
b, _ := pem.Decode(p)
return b.Bytes
}