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