Skip to content

Instantly share code, notes, and snippets.

@anonymouse64
Last active February 12, 2021 14:59
Show Gist options
  • Save anonymouse64/f644d32f8fc24b98399cf2cb36891931 to your computer and use it in GitHub Desktop.
Save anonymouse64/f644d32f8fc24b98399cf2cb36891931 to your computer and use it in GitHub Desktop.
Generate a leaf, non-self-signed account-key assertion with associated private key for use with snapd
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2021 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package main
import (
"bytes"
"crypto/rsa"
"fmt"
"log"
"time"
"github.com/snapcore/snapd/asserts"
"github.com/snapcore/snapd/asserts/assertstest"
"github.com/snapcore/snapd/asserts/systestkeys"
"golang.org/x/crypto/openpgp/armor"
"golang.org/x/crypto/openpgp/packet"
)
var (
v1FixedTimestamp = time.Date(2016, time.January, 1, 0, 0, 0, 0, time.UTC)
)
// from pedronis @ https://gist.github.com/pedronis/d4ba5849e40814f1fa5ee22d8b525471
func dumpRSAKey(rsaKey *rsa.PrivateKey) string {
buf := &bytes.Buffer{}
pkt := packet.NewRSAPrivateKey(v1FixedTimestamp, rsaKey)
arm, err := armor.Encode(buf, "PGP PRIVATE KEY", nil)
if err != nil {
panic(err)
}
err = pkt.Serialize(arm)
if err != nil {
panic(err)
}
err = arm.Close()
if err != nil {
panic(err)
}
blob := buf.String()
fmt.Println(blob)
return blob
}
func main() {
// generate a key
repairRootKey, repairRootRSAKey := assertstest.GenerateKey(4096)
db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
KeypairManager: asserts.NewMemoryKeypairManager(),
Backstore: asserts.NewMemoryBackstore(),
Trusted: systestkeys.Trusted,
})
if err != nil {
log.Fatalf("error opening database: %v", err)
}
// for signing import the testrootorg key
rootSigningKey, _ := assertstest.ReadPrivKey(systestkeys.TestRootPrivKey)
if err := db.ImportKey(rootSigningKey); err != nil {
log.Fatalf("error importing private signing key: %v", err)
}
headers := map[string]interface{}{
// account-id and authority-id must be the same for root of trust
// assertions like this
"account-id": "testrootrepair",
"authority-id": "testrootrepair",
"name": "test-root-repair",
"since": time.Now().Format(time.RFC3339),
"public-key-sha3-384": repairRootKey.PublicKey().ID(),
}
// the public key is the body of the account key assertion
body, err := asserts.EncodePublicKey(repairRootKey.PublicKey())
if err != nil {
log.Fatalf("error encoding public key of private key: %v", err)
}
a, err := db.Sign(asserts.AccountKeyType, headers, body, rootSigningKey.PublicKey().ID())
if err != nil {
log.Fatalf("error signing account-key: %v", err)
}
// output the account-key assertion itself
fmt.Println(string(asserts.Encode(a)))
// also export the private key in ASCII
dumpRSAKey(repairRootRSAKey)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment