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