diff --git a/cmd/notary/tuf.go b/cmd/notary/tuf.go index 6df51366ae..73edfb351b 100644 --- a/cmd/notary/tuf.go +++ b/cmd/notary/tuf.go @@ -3,7 +3,9 @@ package main import ( "bytes" "crypto/sha256" + "crypto/x509" "encoding/json" + "encoding/pem" "fmt" "io/ioutil" "os" @@ -207,7 +209,7 @@ func tufList(cmd *cobra.Command, args []string) { "json", "", ) - c, err := bootstrapClient(remote, repo, kdb) + c, err := bootstrapClient(gun, remote, repo, kdb) if err != nil { return } @@ -244,7 +246,7 @@ func tufLookup(cmd *cobra.Command, args []string) { "json", "", ) - c, err := bootstrapClient(remote, repo, kdb) + c, err := bootstrapClient(gun, remote, repo, kdb) if err != nil { return } @@ -439,7 +441,7 @@ func saveRepo(repo *tuf.TufRepo, filestore store.MetadataStore) error { return nil } -func bootstrapClient(remote store.RemoteStore, repo *tuf.TufRepo, kdb *keys.KeyDB) (*client.Client, error) { +func bootstrapClient(gun string, remote store.RemoteStore, repo *tuf.TufRepo, kdb *keys.KeyDB) (*client.Client, error) { rootJSON, err := remote.GetMeta("root", 5<<20) if err != nil { return nil, err @@ -449,7 +451,10 @@ func bootstrapClient(remote store.RemoteStore, repo *tuf.TufRepo, kdb *keys.KeyD if err != nil { return nil, err } - // TODO: Validate the root file against the key store + err = validateRoot(gun, root) + if err != nil { + return nil, err + } err = repo.SetRoot(root) if err != nil { return nil, err @@ -461,6 +466,35 @@ func bootstrapClient(remote store.RemoteStore, repo *tuf.TufRepo, kdb *keys.KeyD ), nil } +func validateRoot(gun string, root *data.Signed) error { + rootSigned := &data.Root{} + err := json.Unmarshal(root.Signed, rootSigned) + if err != nil { + return err + } + certs := make(map[string]*data.PublicKey) + for _, kID := range rootSigned.Roles["root"].KeyIDs { + // TODO: currently assuming only one cert contained in + // public key entry + k, _ := pem.Decode([]byte(rootSigned.Keys["kid"].Public())) + rootCert, err := x509.ParseCertificates(k.Bytes) + if err != nil { + continue + } + err = caStore.Verify(gun, rootCert[0]) + if err != nil { + continue + } + certs[kID] = rootSigned.Keys[kID] + } + _, err = signed.VerifyRoot(root, 0, certs, 1) + if err != nil { + // failed to validate the signatures against the certificates + return err + } + return nil +} + func bootstrapRepo(gun string, repo *tuf.TufRepo) store.MetadataStore { filestore, err := store.NewFilesystemStore( path.Join(viper.GetString("tufDir"), gun), diff --git a/trustmanager/x509filestore.go b/trustmanager/x509filestore.go index 3d03c304a0..6d3685cfe7 100644 --- a/trustmanager/x509filestore.go +++ b/trustmanager/x509filestore.go @@ -203,6 +203,28 @@ func (s X509FileStore) GetVerifyOptions(dnsName string) (x509.VerifyOptions, err return opts, nil } +func (s X509FileStore) Verify(dnsName string, certs ...*x509.Certificate) error { + // If we have no Certificates loaded return error (we don't want to rever to using + // system CAs). + if len(s.fingerprintMap) == 0 { + return errors.New("no root CAs available") + } + + // TODO: determine which cert in rootCerts is the leaf and add + // the intermediates to verifyOpts.Intermediates + opts := x509.VerifyOptions{ + DNSName: dnsName, + Roots: s.GetCertificatePool(), + } + + // TODO: assuming only one cert ever passed and that it's the leaf + chains, err := certs[0].Verify(opts) + if len(chains) == 0 || err != nil { + return errors.New("Certificate did not verify") + } + return nil +} + func fileName(cert *x509.Certificate) string { return path.Join(cert.Subject.CommonName, string(FingerprintCert(cert))) } diff --git a/trustmanager/x509memstore.go b/trustmanager/x509memstore.go index d9c7e0923c..d5506efe8a 100644 --- a/trustmanager/x509memstore.go +++ b/trustmanager/x509memstore.go @@ -171,3 +171,27 @@ func (s X509MemStore) GetVerifyOptions(dnsName string) (x509.VerifyOptions, erro return opts, nil } + +// TODO: Create a parent Store object that implements the shared methods +// and gets embedded into this and the X509MemoryStore +func (s X509MemStore) Verify(dnsName string, certs ...*x509.Certificate) error { + // If we have no Certificates loaded return error (we don't want to rever to using + // system CAs). + if len(s.fingerprintMap) == 0 { + return errors.New("no root CAs available") + } + + // TODO: determine which cert in rootCerts is the leaf and add + // the intermediates to verifyOpts.Intermediates + opts := x509.VerifyOptions{ + DNSName: dnsName, + Roots: s.GetCertificatePool(), + } + + // TODO: assuming only one cert ever passed and that it's the leaf + chains, err := certs[0].Verify(opts) + if len(chains) == 0 || err != nil { + return errors.New("Certificate did not verify") + } + return nil +} diff --git a/trustmanager/x509store.go b/trustmanager/x509store.go index b644a38789..7de219e2cb 100644 --- a/trustmanager/x509store.go +++ b/trustmanager/x509store.go @@ -14,6 +14,7 @@ type X509Store interface { GetCertificates() []*x509.Certificate GetCertificatePool() *x509.CertPool GetVerifyOptions(dnsName string) (x509.VerifyOptions, error) + Verify(dnsName string, certs ...*x509.Certificate) error } type CertID string