@@ -9,15 +9,17 @@ import (
99 "crypto/x509/pkix"
1010 "encoding/json"
1111 "encoding/pem"
12+ "fmt"
1213 "io/ioutil"
1314 "math/big"
1415 "os"
1516 "path/filepath"
1617 "testing"
1718 "text/template"
18-
1919 "time"
2020
21+ "github.com/Sirupsen/logrus"
22+ "github.com/docker/notary"
2123 "github.com/docker/notary/cryptoservice"
2224 "github.com/docker/notary/trustmanager"
2325 "github.com/docker/notary/trustpinning"
@@ -782,9 +784,9 @@ func testValidateRootRotationMissingNewSig(t *testing.T, keyAlg, rootKeyType str
782784 require .Error (t , err , "insuficient signatures on root" )
783785}
784786
785- func generateTestingCertificate (rootKey data.PrivateKey , gun string ) (* x509.Certificate , error ) {
787+ func generateTestingCertificate (rootKey data.PrivateKey , gun string , timeToExpire time. Duration ) (* x509.Certificate , error ) {
786788 startTime := time .Now ()
787- return cryptoservice .GenerateCertificate (rootKey , gun , startTime , startTime .AddDate ( 10 , 0 , 0 ))
789+ return cryptoservice .GenerateCertificate (rootKey , gun , startTime , startTime .Add ( timeToExpire ))
788790}
789791
790792func generateExpiredTestingCertificate (rootKey data.PrivateKey , gun string ) (* x509.Certificate , error ) {
@@ -800,3 +802,244 @@ func generateRootKeyIDs(r *data.SignedRoot) {
800802 }
801803 }
802804}
805+
806+ func TestCheckingCertExpiry (t * testing.T ) {
807+ gun := "notary"
808+ pass := func (keyName , alias string , createNew bool , attempts int ) (passphrase string , giveup bool , err error ) {
809+ return "password" , false , nil
810+ }
811+ memStore := trustmanager .NewKeyMemoryStore (pass )
812+ cs := cryptoservice .NewCryptoService (memStore )
813+ testPubKey , err := cs .Create (data .CanonicalRootRole , gun , data .ECDSAKey )
814+ require .NoError (t , err )
815+ testPrivKey , _ , err := memStore .GetKey (testPubKey .ID ())
816+ require .NoError (t , err )
817+
818+ almostExpiredCert , err := generateTestingCertificate (testPrivKey , gun , notary .Day * 30 )
819+ require .NoError (t , err )
820+ almostExpiredPubKey , err := trustmanager .ParsePEMPublicKey (trustmanager .CertToPEM (almostExpiredCert ))
821+ require .NoError (t , err )
822+
823+ // set up a logrus logger to capture warning output
824+ origLevel := logrus .GetLevel ()
825+ logrus .SetLevel (logrus .WarnLevel )
826+ defer logrus .SetLevel (origLevel )
827+ logBuf := bytes .NewBuffer (nil )
828+ logrus .SetOutput (logBuf )
829+
830+ rootRole , err := data .NewRole (data .CanonicalRootRole , 1 , []string {almostExpiredPubKey .ID ()}, nil )
831+ require .NoError (t , err )
832+ testRoot , err := data .NewRoot (
833+ map [string ]data.PublicKey {almostExpiredPubKey .ID (): almostExpiredPubKey },
834+ map [string ]* data.RootRole {
835+ data .CanonicalRootRole : & rootRole .RootRole ,
836+ data .CanonicalTimestampRole : & rootRole .RootRole ,
837+ data .CanonicalTargetsRole : & rootRole .RootRole ,
838+ data .CanonicalSnapshotRole : & rootRole .RootRole },
839+ false ,
840+ )
841+ testRoot .Signed .Version = 1
842+ require .NoError (t , err , "Failed to create new root" )
843+
844+ signedTestRoot , err := testRoot .ToSigned ()
845+ require .NoError (t , err )
846+
847+ err = signed .Sign (cs , signedTestRoot , []data.PublicKey {almostExpiredPubKey }, 1 , nil )
848+ require .NoError (t , err )
849+
850+ // This is a valid root certificate, but check that we get a Warn-level message that the certificate is near expiry
851+ _ , err = trustpinning .ValidateRoot (nil , signedTestRoot , gun , trustpinning.TrustPinConfig {})
852+ require .NoError (t , err )
853+ require .Contains (t , logBuf .String (), fmt .Sprintf ("certificate with CN %s is near expiry" , gun ))
854+
855+ expiredCert , err := generateExpiredTestingCertificate (testPrivKey , gun )
856+ require .NoError (t , err )
857+ expiredPubKey := trustmanager .CertToKey (expiredCert )
858+
859+ rootRole , err = data .NewRole (data .CanonicalRootRole , 1 , []string {expiredPubKey .ID ()}, nil )
860+ require .NoError (t , err )
861+ testRoot , err = data .NewRoot (
862+ map [string ]data.PublicKey {expiredPubKey .ID (): expiredPubKey },
863+ map [string ]* data.RootRole {
864+ data .CanonicalRootRole : & rootRole .RootRole ,
865+ data .CanonicalTimestampRole : & rootRole .RootRole ,
866+ data .CanonicalTargetsRole : & rootRole .RootRole ,
867+ data .CanonicalSnapshotRole : & rootRole .RootRole },
868+ false ,
869+ )
870+ testRoot .Signed .Version = 1
871+ require .NoError (t , err , "Failed to create new root" )
872+
873+ signedTestRoot , err = testRoot .ToSigned ()
874+ require .NoError (t , err )
875+
876+ err = signed .Sign (cs , signedTestRoot , []data.PublicKey {expiredPubKey }, 1 , nil )
877+ require .NoError (t , err )
878+
879+ // This is an invalid root certificate since it's expired
880+ _ , err = trustpinning .ValidateRoot (nil , signedTestRoot , gun , trustpinning.TrustPinConfig {})
881+ require .Error (t , err )
882+ }
883+
884+ func TestValidateRootWithExpiredIntermediate (t * testing.T ) {
885+ now := time .Now ()
886+ serialNumberLimit := new (big.Int ).Lsh (big .NewInt (1 ), 128 )
887+
888+ pass := func (keyName , alias string , createNew bool , attempts int ) (passphrase string , giveup bool , err error ) {
889+ return "password" , false , nil
890+ }
891+ memStore := trustmanager .NewKeyMemoryStore (pass )
892+ cs := cryptoservice .NewCryptoService (memStore )
893+
894+ // generate CA cert
895+ serialNumber , err := rand .Int (rand .Reader , serialNumberLimit )
896+ require .NoError (t , err )
897+ caTmpl := x509.Certificate {
898+ SerialNumber : serialNumber ,
899+ Subject : pkix.Name {
900+ CommonName : "notary testing CA" ,
901+ },
902+ NotBefore : now .Add (- time .Hour ),
903+ NotAfter : now .Add (time .Hour ),
904+ KeyUsage : x509 .KeyUsageCertSign ,
905+ BasicConstraintsValid : true ,
906+ IsCA : true ,
907+ MaxPathLen : 3 ,
908+ }
909+ caPrivKey , err := ecdsa .GenerateKey (elliptic .P256 (), rand .Reader )
910+ require .NoError (t , err )
911+ _ , err = x509 .CreateCertificate (
912+ rand .Reader ,
913+ & caTmpl ,
914+ & caTmpl ,
915+ caPrivKey .Public (),
916+ caPrivKey ,
917+ )
918+
919+ // generate expired intermediate
920+ intTmpl := x509.Certificate {
921+ SerialNumber : serialNumber ,
922+ Subject : pkix.Name {
923+ CommonName : "EXPIRED notary testing intermediate" ,
924+ },
925+ NotBefore : now .Add (- 2 * notary .Year ),
926+ NotAfter : now .Add (- notary .Year ),
927+ KeyUsage : x509 .KeyUsageCertSign ,
928+ BasicConstraintsValid : true ,
929+ IsCA : true ,
930+ MaxPathLen : 2 ,
931+ }
932+ intPrivKey , err := ecdsa .GenerateKey (elliptic .P256 (), rand .Reader )
933+ require .NoError (t , err )
934+ intCert , err := x509 .CreateCertificate (
935+ rand .Reader ,
936+ & intTmpl ,
937+ & caTmpl ,
938+ intPrivKey .Public (),
939+ caPrivKey ,
940+ )
941+ require .NoError (t , err )
942+
943+ // generate leaf
944+ serialNumber , err = rand .Int (rand .Reader , serialNumberLimit )
945+ require .NoError (t , err )
946+ leafTmpl := x509.Certificate {
947+ SerialNumber : serialNumber ,
948+ Subject : pkix.Name {
949+ CommonName : "docker.io/notary/test" ,
950+ },
951+ NotBefore : now .Add (- time .Hour ),
952+ NotAfter : now .Add (time .Hour ),
953+
954+ KeyUsage : x509 .KeyUsageKeyEncipherment | x509 .KeyUsageDigitalSignature ,
955+ ExtKeyUsage : []x509.ExtKeyUsage {x509 .ExtKeyUsageCodeSigning },
956+ BasicConstraintsValid : true ,
957+ }
958+
959+ leafPubKey , err := cs .Create ("root" , "docker.io/notary/test" , data .ECDSAKey )
960+ require .NoError (t , err )
961+ leafPrivKey , _ , err := cs .GetPrivateKey (leafPubKey .ID ())
962+ require .NoError (t , err )
963+ signer := leafPrivKey .CryptoSigner ()
964+ leafCert , err := x509 .CreateCertificate (
965+ rand .Reader ,
966+ & leafTmpl ,
967+ & intTmpl ,
968+ signer .Public (),
969+ intPrivKey ,
970+ )
971+
972+ rootBundleWriter := bytes .NewBuffer (nil )
973+ pem .Encode (
974+ rootBundleWriter ,
975+ & pem.Block {
976+ Type : "CERTIFICATE" ,
977+ Bytes : leafCert ,
978+ },
979+ )
980+ pem .Encode (
981+ rootBundleWriter ,
982+ & pem.Block {
983+ Type : "CERTIFICATE" ,
984+ Bytes : intCert ,
985+ },
986+ )
987+
988+ rootBundle := rootBundleWriter .Bytes ()
989+
990+ ecdsax509Key := data .NewECDSAx509PublicKey (rootBundle )
991+
992+ otherKey , err := cs .Create ("targets" , "docker.io/notary/test" , data .ED25519Key )
993+ require .NoError (t , err )
994+
995+ root := data.SignedRoot {
996+ Signatures : make ([]data.Signature , 0 ),
997+ Signed : data.Root {
998+ SignedCommon : data.SignedCommon {
999+ Type : "Root" ,
1000+ Expires : now .Add (time .Hour ),
1001+ Version : 1 ,
1002+ },
1003+ Keys : map [string ]data.PublicKey {
1004+ ecdsax509Key .ID (): ecdsax509Key ,
1005+ otherKey .ID (): otherKey ,
1006+ },
1007+ Roles : map [string ]* data.RootRole {
1008+ "root" : {
1009+ KeyIDs : []string {ecdsax509Key .ID ()},
1010+ Threshold : 1 ,
1011+ },
1012+ "targets" : {
1013+ KeyIDs : []string {otherKey .ID ()},
1014+ Threshold : 1 ,
1015+ },
1016+ "snapshot" : {
1017+ KeyIDs : []string {otherKey .ID ()},
1018+ Threshold : 1 ,
1019+ },
1020+ "timestamp" : {
1021+ KeyIDs : []string {otherKey .ID ()},
1022+ Threshold : 1 ,
1023+ },
1024+ },
1025+ },
1026+ Dirty : true ,
1027+ }
1028+
1029+ signedRoot , err := root .ToSigned ()
1030+ require .NoError (t , err )
1031+ err = signed .Sign (cs , signedRoot , []data.PublicKey {ecdsax509Key }, 1 , nil )
1032+ require .NoError (t , err )
1033+
1034+ tempBaseDir , err := ioutil .TempDir ("" , "notary-test-" )
1035+ defer os .RemoveAll (tempBaseDir )
1036+ require .NoError (t , err , "failed to create a temporary directory: %s" , err )
1037+
1038+ _ , err = trustpinning .ValidateRoot (
1039+ nil ,
1040+ signedRoot ,
1041+ "docker.io/notary/test" ,
1042+ trustpinning.TrustPinConfig {},
1043+ )
1044+ require .Error (t , err , "failed to invalidate expired intermediate certificate" )
1045+ }
0 commit comments