github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/asserts/snapasserts/snapasserts_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package snapasserts_test 21 22 import ( 23 "crypto" 24 "fmt" 25 "io/ioutil" 26 "path/filepath" 27 "testing" 28 "time" 29 30 "golang.org/x/crypto/sha3" 31 32 . "gopkg.in/check.v1" 33 34 "github.com/snapcore/snapd/asserts" 35 "github.com/snapcore/snapd/asserts/assertstest" 36 "github.com/snapcore/snapd/asserts/snapasserts" 37 "github.com/snapcore/snapd/snap" 38 ) 39 40 func TestSnapasserts(t *testing.T) { TestingT(t) } 41 42 type snapassertsSuite struct { 43 storeSigning *assertstest.StoreStack 44 dev1Acct *asserts.Account 45 46 localDB *asserts.Database 47 } 48 49 var _ = Suite(&snapassertsSuite{}) 50 51 func (s *snapassertsSuite) SetUpTest(c *C) { 52 s.storeSigning = assertstest.NewStoreStack("can0nical", nil) 53 54 s.dev1Acct = assertstest.NewAccount(s.storeSigning, "developer1", nil, "") 55 56 localDB, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ 57 Backstore: asserts.NewMemoryBackstore(), 58 Trusted: s.storeSigning.Trusted, 59 }) 60 c.Assert(err, IsNil) 61 62 s.localDB = localDB 63 64 // add in prereqs assertions 65 err = s.localDB.Add(s.storeSigning.StoreAccountKey("")) 66 c.Assert(err, IsNil) 67 err = s.localDB.Add(s.dev1Acct) 68 c.Assert(err, IsNil) 69 70 headers := map[string]interface{}{ 71 "series": "16", 72 "snap-id": "snap-id-1", 73 "snap-name": "foo", 74 "publisher-id": s.dev1Acct.AccountID(), 75 "timestamp": time.Now().Format(time.RFC3339), 76 } 77 snapDecl, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 78 c.Assert(err, IsNil) 79 err = s.localDB.Add(snapDecl) 80 c.Assert(err, IsNil) 81 } 82 83 func fakeSnap(rev int) []byte { 84 fake := fmt.Sprintf("hsqs________________%d", rev) 85 return []byte(fake) 86 } 87 88 func fakeHash(rev int) []byte { 89 h := sha3.Sum384(fakeSnap(rev)) 90 return h[:] 91 } 92 93 func makeDigest(rev int) string { 94 d, err := asserts.EncodeDigest(crypto.SHA3_384, fakeHash(rev)) 95 if err != nil { 96 panic(err) 97 } 98 return string(d) 99 } 100 101 func (s *snapassertsSuite) TestCrossCheckHappy(c *C) { 102 digest := makeDigest(12) 103 size := uint64(len(fakeSnap(12))) 104 headers := map[string]interface{}{ 105 "snap-id": "snap-id-1", 106 "snap-sha3-384": digest, 107 "snap-size": fmt.Sprintf("%d", size), 108 "snap-revision": "12", 109 "developer-id": s.dev1Acct.AccountID(), 110 "timestamp": time.Now().Format(time.RFC3339), 111 } 112 snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") 113 c.Assert(err, IsNil) 114 err = s.localDB.Add(snapRev) 115 c.Assert(err, IsNil) 116 117 si := &snap.SideInfo{ 118 SnapID: "snap-id-1", 119 Revision: snap.R(12), 120 } 121 122 // everything cross checks, with the regular snap name 123 err = snapasserts.CrossCheck("foo", digest, size, si, s.localDB) 124 c.Check(err, IsNil) 125 // and a snap instance name 126 err = snapasserts.CrossCheck("foo_instance", digest, size, si, s.localDB) 127 c.Check(err, IsNil) 128 } 129 130 func (s *snapassertsSuite) TestCrossCheckErrors(c *C) { 131 digest := makeDigest(12) 132 size := uint64(len(fakeSnap(12))) 133 headers := map[string]interface{}{ 134 "snap-id": "snap-id-1", 135 "snap-sha3-384": digest, 136 "snap-size": fmt.Sprintf("%d", size), 137 "snap-revision": "12", 138 "developer-id": s.dev1Acct.AccountID(), 139 "timestamp": time.Now().Format(time.RFC3339), 140 } 141 snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") 142 c.Assert(err, IsNil) 143 err = s.localDB.Add(snapRev) 144 c.Assert(err, IsNil) 145 146 si := &snap.SideInfo{ 147 SnapID: "snap-id-1", 148 Revision: snap.R(12), 149 } 150 151 // different size 152 err = snapasserts.CrossCheck("foo", digest, size+1, si, s.localDB) 153 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)) 154 err = snapasserts.CrossCheck("foo_instance", digest, size+1, si, s.localDB) 155 c.Check(err, ErrorMatches, fmt.Sprintf(`snap "foo_instance" file does not have expected size according to signatures \(download is broken or tampered\): %d != %d`, size+1, size)) 156 157 // mismatched revision vs what we got from store original info 158 err = snapasserts.CrossCheck("foo", digest, size, &snap.SideInfo{ 159 SnapID: "snap-id-1", 160 Revision: snap.R(21), 161 }, s.localDB) 162 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`) 163 err = snapasserts.CrossCheck("foo_instance", digest, size, &snap.SideInfo{ 164 SnapID: "snap-id-1", 165 Revision: snap.R(21), 166 }, s.localDB) 167 c.Check(err, ErrorMatches, `snap "foo_instance" does not have expected ID or revision according to assertions \(metadata is broken or tampered\): 21 / snap-id-1 != 12 / snap-id-1`) 168 169 // mismatched snap id vs what we got from store original info 170 err = snapasserts.CrossCheck("foo", digest, size, &snap.SideInfo{ 171 SnapID: "snap-id-other", 172 Revision: snap.R(12), 173 }, s.localDB) 174 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`) 175 err = snapasserts.CrossCheck("foo_instance", digest, size, &snap.SideInfo{ 176 SnapID: "snap-id-other", 177 Revision: snap.R(12), 178 }, s.localDB) 179 c.Check(err, ErrorMatches, `snap "foo_instance" does not have expected ID or revision according to assertions \(metadata is broken or tampered\): 12 / snap-id-other != 12 / snap-id-1`) 180 181 // changed name 182 err = snapasserts.CrossCheck("baz", digest, size, si, s.localDB) 183 c.Check(err, ErrorMatches, `cannot install "baz", snap "baz" is undergoing a rename to "foo"`) 184 err = snapasserts.CrossCheck("baz_instance", digest, size, si, s.localDB) 185 c.Check(err, ErrorMatches, `cannot install "baz_instance", snap "baz" is undergoing a rename to "foo"`) 186 187 } 188 189 func (s *snapassertsSuite) TestCrossCheckRevokedSnapDecl(c *C) { 190 // revoked snap declaration (snap-name=="") ! 191 headers := map[string]interface{}{ 192 "series": "16", 193 "snap-id": "snap-id-1", 194 "snap-name": "", 195 "publisher-id": s.dev1Acct.AccountID(), 196 "revision": "1", 197 "timestamp": time.Now().Format(time.RFC3339), 198 } 199 snapDecl, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 200 c.Assert(err, IsNil) 201 err = s.localDB.Add(snapDecl) 202 c.Assert(err, IsNil) 203 204 digest := makeDigest(12) 205 size := uint64(len(fakeSnap(12))) 206 headers = map[string]interface{}{ 207 "snap-id": "snap-id-1", 208 "snap-sha3-384": digest, 209 "snap-size": fmt.Sprintf("%d", size), 210 "snap-revision": "12", 211 "developer-id": s.dev1Acct.AccountID(), 212 "timestamp": time.Now().Format(time.RFC3339), 213 } 214 snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") 215 c.Assert(err, IsNil) 216 err = s.localDB.Add(snapRev) 217 c.Assert(err, IsNil) 218 219 si := &snap.SideInfo{ 220 SnapID: "snap-id-1", 221 Revision: snap.R(12), 222 } 223 224 err = snapasserts.CrossCheck("foo", digest, size, si, s.localDB) 225 c.Check(err, ErrorMatches, `cannot install snap "foo" with a revoked snap declaration`) 226 err = snapasserts.CrossCheck("foo_instance", digest, size, si, s.localDB) 227 c.Check(err, ErrorMatches, `cannot install snap "foo_instance" with a revoked snap declaration`) 228 } 229 230 func (s *snapassertsSuite) TestDeriveSideInfoHappy(c *C) { 231 digest := makeDigest(42) 232 size := uint64(len(fakeSnap(42))) 233 headers := map[string]interface{}{ 234 "snap-id": "snap-id-1", 235 "snap-sha3-384": digest, 236 "snap-size": fmt.Sprintf("%d", size), 237 "snap-revision": "42", 238 "developer-id": s.dev1Acct.AccountID(), 239 "timestamp": time.Now().Format(time.RFC3339), 240 } 241 snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") 242 c.Assert(err, IsNil) 243 err = s.localDB.Add(snapRev) 244 c.Assert(err, IsNil) 245 246 tempdir := c.MkDir() 247 snapPath := filepath.Join(tempdir, "anon.snap") 248 err = ioutil.WriteFile(snapPath, fakeSnap(42), 0644) 249 c.Assert(err, IsNil) 250 251 si, err := snapasserts.DeriveSideInfo(snapPath, s.localDB) 252 c.Assert(err, IsNil) 253 c.Check(si, DeepEquals, &snap.SideInfo{ 254 RealName: "foo", 255 SnapID: "snap-id-1", 256 Revision: snap.R(42), 257 Channel: "", 258 }) 259 } 260 261 func (s *snapassertsSuite) TestDeriveSideInfoNoSignatures(c *C) { 262 tempdir := c.MkDir() 263 snapPath := filepath.Join(tempdir, "anon.snap") 264 err := ioutil.WriteFile(snapPath, fakeSnap(42), 0644) 265 c.Assert(err, IsNil) 266 267 _, err = snapasserts.DeriveSideInfo(snapPath, s.localDB) 268 // cannot find signatures with metadata for snap 269 c.Assert(asserts.IsNotFound(err), Equals, true) 270 } 271 272 func (s *snapassertsSuite) TestDeriveSideInfoSizeMismatch(c *C) { 273 digest := makeDigest(42) 274 size := uint64(len(fakeSnap(42))) 275 headers := map[string]interface{}{ 276 "snap-id": "snap-id-1", 277 "snap-sha3-384": digest, 278 "snap-size": fmt.Sprintf("%d", size+5), // broken 279 "snap-revision": "42", 280 "developer-id": s.dev1Acct.AccountID(), 281 "timestamp": time.Now().Format(time.RFC3339), 282 } 283 snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") 284 c.Assert(err, IsNil) 285 err = s.localDB.Add(snapRev) 286 c.Assert(err, IsNil) 287 288 tempdir := c.MkDir() 289 snapPath := filepath.Join(tempdir, "anon.snap") 290 err = ioutil.WriteFile(snapPath, fakeSnap(42), 0644) 291 c.Assert(err, IsNil) 292 293 _, err = snapasserts.DeriveSideInfo(snapPath, s.localDB) 294 c.Check(err, ErrorMatches, fmt.Sprintf(`snap %q does not have expected size according to signatures \(broken or tampered\): %d != %d`, snapPath, size, size+5)) 295 } 296 297 func (s *snapassertsSuite) TestDeriveSideInfoRevokedSnapDecl(c *C) { 298 // revoked snap declaration (snap-name=="") ! 299 headers := map[string]interface{}{ 300 "series": "16", 301 "snap-id": "snap-id-1", 302 "snap-name": "", 303 "publisher-id": s.dev1Acct.AccountID(), 304 "revision": "1", 305 "timestamp": time.Now().Format(time.RFC3339), 306 } 307 snapDecl, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 308 c.Assert(err, IsNil) 309 err = s.localDB.Add(snapDecl) 310 c.Assert(err, IsNil) 311 312 digest := makeDigest(42) 313 size := uint64(len(fakeSnap(42))) 314 headers = map[string]interface{}{ 315 "snap-id": "snap-id-1", 316 "snap-sha3-384": digest, 317 "snap-size": fmt.Sprintf("%d", size), 318 "snap-revision": "42", 319 "developer-id": s.dev1Acct.AccountID(), 320 "timestamp": time.Now().Format(time.RFC3339), 321 } 322 snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") 323 c.Assert(err, IsNil) 324 err = s.localDB.Add(snapRev) 325 c.Assert(err, IsNil) 326 327 tempdir := c.MkDir() 328 snapPath := filepath.Join(tempdir, "anon.snap") 329 err = ioutil.WriteFile(snapPath, fakeSnap(42), 0644) 330 c.Assert(err, IsNil) 331 332 _, err = snapasserts.DeriveSideInfo(snapPath, s.localDB) 333 c.Check(err, ErrorMatches, fmt.Sprintf(`cannot install snap %q with a revoked snap declaration`, snapPath)) 334 }