From 10e3131d5b431182c40d10bf46957bb712fa902a Mon Sep 17 00:00:00 2001 From: Boris Rybalkin Date: Mon, 6 Jul 2026 09:22:17 +0100 Subject: [PATCH 1/2] asserts: trust second syncloud root account-key (rotation, not yet used) Add a second syncloud root account-key (public-key-sha3-384 NX7IJUak...) to the trusted assertion set, alongside the existing one. This is Phase 1 of signing-key rotation: devices that pick up this snapd will trust assertions signed by EITHER the old or the new key. The store still signs with the old key, so nothing changes yet. Once the fleet trusts both keys, the store can be switched to the new key, and the old key dropped from the trusted set in a later change. The new key pair was generated offline; its private half is kept out of source (staged only as a store CI secret). The account-key's self-signature was verified independently (raw openpgp) before embedding. --- asserts/sysdb/trusted.go | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/asserts/sysdb/trusted.go b/asserts/sysdb/trusted.go index d61b395514d..6abdc128571 100644 --- a/asserts/sysdb/trusted.go +++ b/asserts/sysdb/trusted.go @@ -159,6 +159,38 @@ EVxgC/cuxMGcS40Co3O8wG3H/WIWOqcRQfolQTexmyzQljYt9WyWJdXmtPtaMzQGbOqE/dIjOK9j xvrghVU4kX6fJFwPi+azMrluHV+WGSVxPCuLW8o2aipjOd1/bUQCL5OwRuaEWuLCiV01J8H/JjWV hL4gGVqEM2KEPIDwY2yqX36jE7uN9O+mIPnS4Tdj0JQ5ZD1qh34wv+4QvhgNeyP120nuS1ykO9X0 A806uPC5QK1+cgRMUz8zJ0afDNwE/DvpBQvE5CIi9A== +` + + encodedSyncloudRootAccountKeyV2 = `type: account-key +authority-id: syncloud +public-key-sha3-384: NX7IJUakITdsfLFyqWHkdS-YTp77S-v2-vckEZ1cVBwElYYld8_34m6Ni9JZ1aQL +account-id: syncloud +name: root-2 +since: 2016-04-01T00:00:00Z +body-length: 717 +sign-key-sha3-384: NX7IJUakITdsfLFyqWHkdS-YTp77S-v2-vckEZ1cVBwElYYld8_34m6Ni9JZ1aQL + +AcbBTQRWhcGAARAAuC+dQxstBjYPgbKRfQMRd9TwJWDf8xRiFZEce14EJaJ7rIOll/2JOf62PgDe +SYG1q+JaaOEghdMKBNdK0VSmib/tlTX0SN2JyTUXL+r6qi6AoEDBZZ+13Bq9NfNiuZ6+T1uYy7v7 +uLGTL5UPnnYTQmoect0JPnxR4iFLrBNFEm5JWHR+jzdpMJbnPnWp42eAAGEYuCAzHoTdHnUnnYoe +8caGDRdJtVBSqiudkLPYkmt7/SxiLw2xekam3zFFrnqcI5ARthrGRMimqvUchTIl1WFjetboCbnQ +Pdc2aWw8+AHIcvpBDwf0xoX5Q+OL09MsUAo5NLLCT0zCwm/ELGYSTNBzzIj79jyGT4H+SNKnHGoW +DcuTXPwFbE0DK7qe2pkOXk9AwwmMbyi7azMXNc6i/NFSMAB4PK+IMwilxoBsJgcoikAb4frktunt +EDQ5pcE7T2i3cHJy4dm592zDOGnXpgeSMUaMMbgF6iyJlY1PIp6Rdq9jqb1/KDPRKEZ8ITB1hUrh +13mhphatsSFqhB/jLYWvDdA/YwfJQ1e8/taQER4XQUVSdnonhDDE5mxq6jPKEdzQnMXv5LvIE6G5 +F88NlJZxUnfx3oTCs4QXGfP6iX93ZK8A6qEqrbYLevCLFU8bEHUKUFLOmz/kPZACvVQcMB/xE0EL +rCXDgGD4QcFyOUkAEQEAAQ== + +AcLBUgQAAQoABgUCakthPAAAPJUQAEJbJQAcAEfEUenb4HdWB17jqgcTWrbbx1aEiqN7eBAtbnuD +ZNIqHwWY+XXetX0jJkDJeNh+krg3oAaUgWp3bTqilUnLx2XGUZL2rnG1pGo5dDm3Ck0vVZPcUWjk +yRTn0byOsHvQiSGAGHbsta70jT0IJX3upI99CnAvghLA19mTAyjLh8ocKpvk4I7beJGsBFqIyxKh +pRv3fxdoWEW080P6UsUo92GGyLRKjgvJoAerP8yvW7Dknf1mOgmmbnoAjJVSdMqntQ4GNTLkcIEo +zhnn3GBv87/ksg6W8zBYLR+QNB696VGWl5fKmxmvtoe2E7QAlEzkG+OtXur4ms7/5EHuzHKvw83r +S+4EAFV9rjnmA9u+e4l+u0MEslT6JTjyTGK0HwtK7YEARqpJyrQfpoYrrWGbxkpPbdauOcZZrYEu +gmcUSjSoyZMJEIjuxYSlSD2+++aKc6193qUh+JcENL67I+Ig9oOuOczgnhLMI0NmG1nmAJQO6Vzc +5gjffQX+IKT6WM9IPbZ7zMLVT5ZAIdLyum6KghCvKy6J+2OVpm3h9Grxw8YoJr1cVRNuY+++tzoX +3EhOU9t13uR6pIagE4vmsWBbzZHO1Dt22tbZXwsuIFy6TsRYWU31xHCmLL26wToHeYnMQ3Y59q4+ +iNHhqhayLg6ZscHc+Al/MaaUOI7d ` ) @@ -185,7 +217,11 @@ func init() { if err != nil { panic(fmt.Sprintf("cannot decode trusted assertion: %v", err)) } - trustedAssertions = []asserts.Assertion{canonicalAccount, canonicalRootAccountKey, syncloudAccount, syncloudRootAccountKey} + syncloudRootAccountKeyV2, err := asserts.Decode([]byte(encodedSyncloudRootAccountKeyV2)) + if err != nil { + panic(fmt.Sprintf("cannot decode trusted assertion: %v", err)) + } + trustedAssertions = []asserts.Assertion{canonicalAccount, canonicalRootAccountKey, syncloudAccount, syncloudRootAccountKey, syncloudRootAccountKeyV2} } // Trusted returns a copy of the current set of trusted assertions as used by Open. From 083da66184e0abefdc41ab6fe9cc01c70808fff1 Mon Sep 17 00:00:00 2001 From: Boris Rybalkin Date: Tue, 7 Jul 2026 09:15:19 +0100 Subject: [PATCH 2/2] asserts: restore real signature verification (remove Syncloud hack) Both SignatureCheck and CheckSignature swallowed signature-verification failures and returned nil, so snapd accepted any assertion regardless of its cryptographic signature. Restore the real error return and drop the associated debug prints and now-unused logger imports. Safe to do now that the store emits canonical, verifying assertions (syncloud/store: canonical signer). Validated end-to-end by the store e2e installing a snap from the store with this snapd. --- asserts/asserts.go | 9 +-------- asserts/database.go | 11 +---------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/asserts/asserts.go b/asserts/asserts.go index 612e51d7504..2b241d52963 100644 --- a/asserts/asserts.go +++ b/asserts/asserts.go @@ -30,7 +30,6 @@ import ( "strings" "unicode/utf8" - "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/osutil" "github.com/snapcore/snapd/snap/naming" ) @@ -1330,13 +1329,7 @@ func SignatureCheck(assert Assertion, pubKey PublicKey) error { } err = pubKey.verify(content, sig) if err != nil { - //return fmt.Errorf("failed signature verification: %v", err) - fmt.Println("content:") - fmt.Println(string(content)) - fmt.Println("encoded signature:") - fmt.Println(string(encodedSig)) - logger.Noticef("Syncloud hack: failed signature verification: %v", err) - return nil + return fmt.Errorf("failed signature verification: %v", err) } return nil } diff --git a/asserts/database.go b/asserts/database.go index 9dd7c3ae634..54fe5883158 100644 --- a/asserts/database.go +++ b/asserts/database.go @@ -24,7 +24,6 @@ package asserts import ( "errors" "fmt" - "github.com/snapcore/snapd/logger" "regexp" "time" ) @@ -773,7 +772,6 @@ func CheckSignature(assert Assertion, signingKey *AccountKey, roDB RODatabase, c var pubKey PublicKey if signingKey != nil { pubKey = signingKey.publicKey() - fmt.Println("signing key public key id: ", pubKey.ID()) if assert.AuthorityID() != signingKey.AccountID() { return fmt.Errorf("assertion authority %q does not match public key from %q", assert.AuthorityID(), signingKey.AccountID()) } @@ -783,7 +781,6 @@ func CheckSignature(assert Assertion, signingKey *AccountKey, roDB RODatabase, c return fmt.Errorf("cannot check no-authority assertion type %q", assert.Type().Name) } pubKey = custom.signKey() - fmt.Println("custom signer key public key id: ", pubKey.ID()) } content, encSig := assert.Signature() signature, err := decodeSignature(encSig) @@ -792,13 +789,7 @@ func CheckSignature(assert Assertion, signingKey *AccountKey, roDB RODatabase, c } err = pubKey.verify(content, signature) if err != nil { - //return fmt.Errorf("failed signature verification: %v", err) - //fmt.Println("content:") - //fmt.Println(string(content)) - //fmt.Println("encoded signature:") - //fmt.Println(string(encSig)) - logger.Noticef("Syncloud hack: failed signature verification: %v", err) - return nil + return fmt.Errorf("failed signature verification: %v", err) } return nil }