github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/asserts/gpgkeypairmgr_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 asserts_test 21 22 import ( 23 "bytes" 24 "crypto" 25 "crypto/rand" 26 "crypto/rsa" 27 "fmt" 28 "os" 29 "time" 30 31 . "gopkg.in/check.v1" 32 33 "golang.org/x/crypto/openpgp/packet" 34 35 "github.com/snapcore/snapd/asserts" 36 "github.com/snapcore/snapd/asserts/assertstest" 37 "github.com/snapcore/snapd/osutil" 38 ) 39 40 type gpgKeypairMgrSuite struct { 41 homedir string 42 keypairMgr asserts.KeypairManager 43 } 44 45 var _ = Suite(&gpgKeypairMgrSuite{}) 46 47 func (gkms *gpgKeypairMgrSuite) SetUpSuite(c *C) { 48 if !osutil.FileExists("/usr/bin/gpg1") && !osutil.FileExists("/usr/bin/gpg") { 49 c.Skip("gpg not installed") 50 } 51 } 52 53 func (gkms *gpgKeypairMgrSuite) importKey(key string) { 54 assertstest.GPGImportKey(gkms.homedir, key) 55 } 56 57 func (gkms *gpgKeypairMgrSuite) SetUpTest(c *C) { 58 gkms.homedir = c.MkDir() 59 os.Setenv("SNAP_GNUPG_HOME", gkms.homedir) 60 gkms.keypairMgr = asserts.NewGPGKeypairManager() 61 // import test key 62 gkms.importKey(assertstest.DevKey) 63 } 64 65 func (gkms *gpgKeypairMgrSuite) TearDownTest(c *C) { 66 os.Unsetenv("SNAP_GNUPG_HOME") 67 } 68 69 func (gkms *gpgKeypairMgrSuite) TestGetPublicKeyLooksGood(c *C) { 70 got, err := gkms.keypairMgr.Get(assertstest.DevKeyID) 71 c.Assert(err, IsNil) 72 keyID := got.PublicKey().ID() 73 c.Check(keyID, Equals, assertstest.DevKeyID) 74 } 75 76 func (gkms *gpgKeypairMgrSuite) TestGetNotFound(c *C) { 77 got, err := gkms.keypairMgr.Get("ffffffffffffffff") 78 c.Check(err, ErrorMatches, `cannot find key "ffffffffffffffff" in GPG keyring`) 79 c.Check(got, IsNil) 80 } 81 82 func (gkms *gpgKeypairMgrSuite) TestUseInSigning(c *C) { 83 store := assertstest.NewStoreStack("trusted", nil) 84 85 devKey, err := gkms.keypairMgr.Get(assertstest.DevKeyID) 86 c.Assert(err, IsNil) 87 88 devAcct := assertstest.NewAccount(store, "devel1", map[string]interface{}{ 89 "account-id": "dev1-id", 90 }, "") 91 devAccKey := assertstest.NewAccountKey(store, devAcct, nil, devKey.PublicKey(), "") 92 93 signDB, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ 94 KeypairManager: gkms.keypairMgr, 95 }) 96 c.Assert(err, IsNil) 97 98 checkDB, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ 99 Backstore: asserts.NewMemoryBackstore(), 100 Trusted: store.Trusted, 101 }) 102 c.Assert(err, IsNil) 103 // add store key 104 err = checkDB.Add(store.StoreAccountKey("")) 105 c.Assert(err, IsNil) 106 // enable devel key 107 err = checkDB.Add(devAcct) 108 c.Assert(err, IsNil) 109 err = checkDB.Add(devAccKey) 110 c.Assert(err, IsNil) 111 112 headers := map[string]interface{}{ 113 "authority-id": "dev1-id", 114 "snap-sha3-384": blobSHA3_384, 115 "snap-id": "snap-id-1", 116 "grade": "devel", 117 "snap-size": "1025", 118 "timestamp": time.Now().Format(time.RFC3339), 119 } 120 snapBuild, err := signDB.Sign(asserts.SnapBuildType, headers, nil, assertstest.DevKeyID) 121 c.Assert(err, IsNil) 122 123 err = checkDB.Check(snapBuild) 124 c.Check(err, IsNil) 125 } 126 127 func (gkms *gpgKeypairMgrSuite) TestGetNotUnique(c *C) { 128 mockGPG := func(prev asserts.GPGRunner, input []byte, args ...string) ([]byte, error) { 129 if args[1] == "--list-secret-keys" { 130 return prev(input, args...) 131 } 132 c.Assert(args[1], Equals, "--export") 133 134 pk1, err := rsa.GenerateKey(rand.Reader, 512) 135 c.Assert(err, IsNil) 136 pk2, err := rsa.GenerateKey(rand.Reader, 512) 137 c.Assert(err, IsNil) 138 139 buf := new(bytes.Buffer) 140 err = packet.NewRSAPublicKey(time.Now(), &pk1.PublicKey).Serialize(buf) 141 c.Assert(err, IsNil) 142 err = packet.NewRSAPublicKey(time.Now(), &pk2.PublicKey).Serialize(buf) 143 c.Assert(err, IsNil) 144 145 return buf.Bytes(), nil 146 } 147 restore := asserts.MockRunGPG(mockGPG) 148 defer restore() 149 150 _, err := gkms.keypairMgr.Get(assertstest.DevKeyID) 151 c.Check(err, ErrorMatches, `cannot load GPG public key with fingerprint "[A-F0-9]+": cannot select exported public key, found many`) 152 } 153 154 func (gkms *gpgKeypairMgrSuite) TestUseInSigningBrokenSignature(c *C) { 155 _, rsaPrivKey := assertstest.ReadPrivKey(assertstest.DevKey) 156 pgpPrivKey := packet.NewRSAPrivateKey(time.Unix(1, 0), rsaPrivKey) 157 158 var breakSig func(sig *packet.Signature, cont []byte) []byte 159 160 mockGPG := func(prev asserts.GPGRunner, input []byte, args ...string) ([]byte, error) { 161 if args[1] == "--list-secret-keys" || args[1] == "--export" { 162 return prev(input, args...) 163 } 164 n := len(args) 165 c.Assert(args[n-1], Equals, "--detach-sign") 166 167 sig := new(packet.Signature) 168 sig.PubKeyAlgo = packet.PubKeyAlgoRSA 169 sig.Hash = crypto.SHA512 170 sig.CreationTime = time.Now() 171 172 // poking to break the signature 173 cont := breakSig(sig, input) 174 175 h := sig.Hash.New() 176 h.Write([]byte(cont)) 177 178 err := sig.Sign(h, pgpPrivKey, nil) 179 c.Assert(err, IsNil) 180 181 buf := new(bytes.Buffer) 182 sig.Serialize(buf) 183 return buf.Bytes(), nil 184 } 185 restore := asserts.MockRunGPG(mockGPG) 186 defer restore() 187 188 signDB, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ 189 KeypairManager: gkms.keypairMgr, 190 }) 191 c.Assert(err, IsNil) 192 193 headers := map[string]interface{}{ 194 "authority-id": "dev1-id", 195 "snap-sha3-384": blobSHA3_384, 196 "snap-id": "snap-id-1", 197 "grade": "devel", 198 "snap-size": "1025", 199 "timestamp": time.Now().Format(time.RFC3339), 200 } 201 202 tests := []struct { 203 breakSig func(*packet.Signature, []byte) []byte 204 expectedErr string 205 }{ 206 {func(sig *packet.Signature, cont []byte) []byte { 207 sig.Hash = crypto.SHA1 208 return cont 209 }, "cannot sign assertion: bad GPG produced signature: expected SHA512 digest"}, 210 {func(sig *packet.Signature, cont []byte) []byte { 211 return cont[:5] 212 }, "cannot sign assertion: bad GPG produced signature: it does not verify:.*"}, 213 } 214 215 for _, t := range tests { 216 breakSig = t.breakSig 217 218 _, err = signDB.Sign(asserts.SnapBuildType, headers, nil, assertstest.DevKeyID) 219 c.Check(err, ErrorMatches, t.expectedErr) 220 } 221 222 } 223 224 func (gkms *gpgKeypairMgrSuite) TestUseInSigningFailure(c *C) { 225 mockGPG := func(prev asserts.GPGRunner, input []byte, args ...string) ([]byte, error) { 226 if args[1] == "--list-secret-keys" || args[1] == "--export" { 227 return prev(input, args...) 228 } 229 n := len(args) 230 c.Assert(args[n-1], Equals, "--detach-sign") 231 return nil, fmt.Errorf("boom") 232 } 233 restore := asserts.MockRunGPG(mockGPG) 234 defer restore() 235 236 signDB, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ 237 KeypairManager: gkms.keypairMgr, 238 }) 239 c.Assert(err, IsNil) 240 241 headers := map[string]interface{}{ 242 "authority-id": "dev1-id", 243 "snap-sha3-384": blobSHA3_384, 244 "snap-id": "snap-id-1", 245 "grade": "devel", 246 "snap-size": "1025", 247 "timestamp": time.Now().Format(time.RFC3339), 248 } 249 250 _, err = signDB.Sign(asserts.SnapBuildType, headers, nil, assertstest.DevKeyID) 251 c.Check(err, ErrorMatches, "cannot sign assertion: cannot sign using GPG: boom") 252 } 253 254 const shortPrivKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- 255 Version: GnuPG v1 256 257 lQOYBFdGO7MBCADltsXglnDQdfBw0yOVpKZdkuvSnJKKn1H72PapgAr7ucLqNBCA 258 js0kltDTa2LQP4vljiTyoMzOMnex4kXwRPlF+poZIEBHDLT0i/6sJ6mDukss1HBR 259 GgNpU3y49WTXc8qxFY4clhbuqgQmy6bUmaVoo3Z4z7cqbsCepWfx5y+vJwMYqlo3 260 Nb4q2+hTKS/o3yLiYB7/hkEhMZrFrOPR5SM7Tz5y7cpF6ObY+JZIp/MK+LsLWLji 261 fEX/pcOtSjFdQqbcnhJJscXRERlFQDbc+gNmZYZ2RqdH5o46OliHkGhVDVTiW25A 262 SqhGfnodypbZ9QAPSRvhLrN64AqEsvRb3I13ABEBAAEAB/9cQKg8Nz6sQUkkDm9C 263 iCK1/qyNYwro9+3VXj9FOCJxEJuqMemUr4TMVnMcDQrchkC5GnpVJGXLw3HVcwFS 264 amjPhUKAp7aYsg40DcrjuXP27oiFQvWuZGuNT5WNtCNg8WQr9POjIFWqWIYdTHk9 265 9Ux79vW7s/Oj62GY9OWHPSilxpq1MjDKo9CSMbLeWxW+gbDxaD7cK7H/ONcz8bZ7 266 pRfEhNIx3mEbWaZpWRrf+dSUx2OJbPGRkeFFMbCNapqftse173BZCwUKsW7RTp2S 267 w8Vpo2Ky63Jlpz1DpoMDBz2vSH7pzaqAdnziI2r0IKiidajXFfpXJpJ3ICo/QhWj 268 x1eRBADrI4I99zHeyy+12QMpkDrOu+ahF6/emdsm1FIy88TqeBmLkeXCXKZIpU3c 269 USnxzm0nPNbOl7Nvf2VdAyeAftyag7t38Cud5MXldv/iY0e6oTKzxgha37yr6oRv 270 PZ6VGwbkBvWti1HL4yx1QnkHFS6ailR9WiiHr3HaWAklZAsC0QQA+hgOi0V9fMZZ 271 Y4/iFVRI9k1NK3pl0mP7pVTzbcjVYspLdIPQxPDsHJW0z48g23KOt0vL3yZvxdBx 272 cfYGqIonAX19aMD5D4bNLx616pZs78DKGlOz6iXDcaib+n/uCNWxd5R/0m/zugrB 273 qklpyIC/uxx+SmkJqqq378ytfvBMzccD/3Y6m3PM0ZnrIkr4Q7cKi9ao9rvM+J7o 274 ziMgfnKWedNDxNa4tIVYYGPiXsjxY/ASUyxVjUPbkyCy3ubZrew0zQ9+kQbO/6vB 275 WAg9ffT9M92QbSDjuxgUiC5GfvlCoDgJtuLRHd0YLDgUCS5nwb+teEsOpiNWEGXc 276 Tr+5HZO+g6wxT6W0BiAoeHh4KYkBOAQTAQIAIgUCV0Y7swIbLwYLCQgHAwIGFQgC 277 CQoLBBYCAwECHgECF4AACgkQEYacUJMr9p/i5wf/XbEiAe1+Y/ZNMO8PYnq1Nktk 278 CbZEfQo+QH/9gJpt4p78YseWeUp14gsULLks3xRojlKNzYkqBpJcP7Ex+hQ3LEp7 279 9IVbept5md4uuZcU0GFF42WAYXExd2cuxPv3lmWHOPuN63a/xpp0M2vYDfpt63qi 280 Tly5/P4+NgpD6vAh8zwRHuBV/0mno/QX6cUCLVxq2v1aOqC9zq9B5sdYKQKjsQBP 281 NOXCt1wPaINkqiW/8w2KhUl6mL6vhO0Onqu/F7M/YNXitv6Z2NFdFUVBh58UZW3C 282 2jrc8JeRQ4Qlr1oeHh2loYOdZfxFPxRjhsRTnNKY8UHWLfbeI6lMqxR5G3DS+g== 283 =kQRo 284 -----END PGP PRIVATE KEY BLOCK----- 285 ` 286 287 func (gkms *gpgKeypairMgrSuite) TestUseInSigningKeyTooShort(c *C) { 288 gkms.importKey(shortPrivKey) 289 privk, _ := assertstest.ReadPrivKey(shortPrivKey) 290 291 signDB, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ 292 KeypairManager: gkms.keypairMgr, 293 }) 294 c.Assert(err, IsNil) 295 296 headers := map[string]interface{}{ 297 "authority-id": "dev1-id", 298 "snap-sha3-384": blobSHA3_384, 299 "snap-id": "snap-id-1", 300 "grade": "devel", 301 "snap-size": "1025", 302 "timestamp": time.Now().Format(time.RFC3339), 303 } 304 305 _, err = signDB.Sign(asserts.SnapBuildType, headers, nil, privk.PublicKey().ID()) 306 c.Check(err, ErrorMatches, `cannot sign assertion: signing needs at least a 4096 bits key, got 2048`) 307 } 308 309 func (gkms *gpgKeypairMgrSuite) TestParametersForGenerate(c *C) { 310 gpgKeypairMgr := gkms.keypairMgr.(*asserts.GPGKeypairManager) 311 baseParameters := ` 312 Key-Type: RSA 313 Key-Length: 4096 314 Name-Real: test-key 315 Creation-Date: seconds=1451606400 316 Preferences: SHA512 317 ` 318 319 tests := []struct { 320 passphrase string 321 extraParameters string 322 }{ 323 {"", ""}, 324 {"secret", "Passphrase: secret\n"}, 325 } 326 327 for _, test := range tests { 328 parameters := gpgKeypairMgr.ParametersForGenerate(test.passphrase, "test-key") 329 c.Check(parameters, Equals, baseParameters+test.extraParameters) 330 } 331 }