func (s *snapassertsSuite) TestCrossCheckHappy(c *C) { digest := makeDigest(12) size := uint64(len(fakeSnap(12))) headers := map[string]interface{}{ "snap-id": "snap-id-1", "snap-sha3-384": digest, "snap-size": fmt.Sprintf("%d", size), "snap-revision": "12", "developer-id": s.dev1Acct.AccountID(), "timestamp": time.Now().Format(time.RFC3339), } snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") c.Assert(err, IsNil) err = s.localDB.Add(snapRev) c.Assert(err, IsNil) si := &snap.SideInfo{ SnapID: "snap-id-1", Revision: snap.R(12), } // everything cross checks err = snapasserts.CrossCheck("foo", digest, size, si, s.localDB) c.Check(err, IsNil) }
func (s *snapassertsSuite) TestCrossCheckErrors(c *C) { digest := makeDigest(12) size := uint64(len(fakeSnap(12))) headers := map[string]interface{}{ "snap-id": "snap-id-1", "snap-sha3-384": digest, "snap-size": fmt.Sprintf("%d", size), "snap-revision": "12", "developer-id": s.dev1Acct.AccountID(), "timestamp": time.Now().Format(time.RFC3339), } snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") c.Assert(err, IsNil) err = s.localDB.Add(snapRev) c.Assert(err, IsNil) si := &snap.SideInfo{ SnapID: "snap-id-1", Revision: snap.R(12), } // different size err = snapasserts.CrossCheck("foo", digest, size+1, si, s.localDB) c.Check(err, ErrorMatches, fmt.Sprintf(`snap "foo" file does not have expected size according to signatures \(download is broken or tampered\): %d != %d`, size+1, size)) // mismatched revision vs what we got from store original info err = snapasserts.CrossCheck("foo", digest, size, &snap.SideInfo{ SnapID: "snap-id-1", Revision: snap.R(21), }, s.localDB) c.Check(err, ErrorMatches, `snap "foo" does not have expected ID or revision according to assertions \(metadata is broken or tampered\): 21 / snap-id-1 != 12 / snap-id-1`) // mismatched snap id vs what we got from store original info err = snapasserts.CrossCheck("foo", digest, size, &snap.SideInfo{ SnapID: "snap-id-other", Revision: snap.R(12), }, s.localDB) c.Check(err, ErrorMatches, `snap "foo" does not have expected ID or revision according to assertions \(metadata is broken or tampered\): 12 / snap-id-other != 12 / snap-id-1`) // changed name err = snapasserts.CrossCheck("baz", digest, size, si, s.localDB) c.Check(err, ErrorMatches, `cannot install snap "baz" that is undergoing a rename to "foo"`) }
// FetchAndCheckSnapAssertions fetches and cross checks the snap assertions matching the given snap file using the provided asserts.Fetcher and assertion database. func FetchAndCheckSnapAssertions(snapPath string, info *snap.Info, f asserts.Fetcher, db asserts.RODatabase) error { sha3_384, size, err := asserts.SnapFileSHA3_384(snapPath) if err != nil { return err } if err := snapasserts.FetchSnapAssertions(f, sha3_384); err != nil { return fmt.Errorf("cannot fetch snap signatures/assertions: %v", err) } // cross checks return snapasserts.CrossCheck(info.Name(), sha3_384, size, &info.SideInfo, db) }
// doValidateSnap fetches the relevant assertions for the snap being installed and cross checks them with the snap. func doValidateSnap(t *state.Task, _ *tomb.Tomb) error { t.State().Lock() defer t.State().Unlock() ss, err := snapstate.TaskSnapSetup(t) if err != nil { return nil } sha3_384, snapSize, err := asserts.SnapFileSHA3_384(ss.SnapPath) if err != nil { return err } err = doFetch(t.State(), ss.UserID, func(f asserts.Fetcher) error { return snapasserts.FetchSnapAssertions(f, sha3_384) }) if notFound, ok := err.(*store.AssertionNotFoundError); ok { if notFound.Ref.Type == asserts.SnapRevisionType { return fmt.Errorf("cannot verify snap %q, no matching signatures found", ss.Name()) } else { return fmt.Errorf("cannot find supported signatures to verify snap %q and its hash (%v)", ss.Name(), notFound) } } if err != nil { return err } db := DB(t.State()) err = snapasserts.CrossCheck(ss.Name(), sha3_384, snapSize, ss.SideInfo, db) if err != nil { // TODO: trigger a global sanity check // that will generate the changes to deal with this // for things like snap-decl revocation and renames? return err } // TODO: set DeveloperID from assertions return nil }
func (s *snapassertsSuite) TestCrossCheckRevokedSnapDecl(c *C) { // revoked snap declaration (snap-name=="") ! headers := map[string]interface{}{ "series": "16", "snap-id": "snap-id-1", "snap-name": "", "publisher-id": s.dev1Acct.AccountID(), "revision": "1", "timestamp": time.Now().Format(time.RFC3339), } snapDecl, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") c.Assert(err, IsNil) err = s.localDB.Add(snapDecl) c.Assert(err, IsNil) digest := makeDigest(12) size := uint64(len(fakeSnap(12))) headers = map[string]interface{}{ "snap-id": "snap-id-1", "snap-sha3-384": digest, "snap-size": fmt.Sprintf("%d", size), "snap-revision": "12", "developer-id": s.dev1Acct.AccountID(), "timestamp": time.Now().Format(time.RFC3339), } snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") c.Assert(err, IsNil) err = s.localDB.Add(snapRev) c.Assert(err, IsNil) si := &snap.SideInfo{ SnapID: "snap-id-1", Revision: snap.R(12), } err = snapasserts.CrossCheck("foo", digest, size, si, s.localDB) c.Check(err, ErrorMatches, `cannot install snap "foo" with a revoked snap declaration`) }