diff --git a/.gitignore b/.gitignore
index 5a46d357..eb913738 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,7 @@
+examples/activate-volume/activate-volume
+examples/change-pin/change-pin
+examples/provision-status/provision-status
+examples/provision-tpm/provision-tpm
+examples/seal-key/seal-key
+examples/unseal-key/unseal-key
vendor/*/
diff --git a/examples/activate-volume/main.go b/examples/activate-volume/main.go
new file mode 100644
index 00000000..d25d9e1e
--- /dev/null
+++ b/examples/activate-volume/main.go
@@ -0,0 +1,139 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2019 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 .
+ *
+ */
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "os"
+ "strconv"
+ "strings"
+
+ "github.com/snapcore/secboot"
+)
+
+func run() int {
+ args := flag.Args()
+ if len(args) == 0 {
+ fmt.Printf("Usage: activate-volume VOLUME SOURCE-DEVICE SEALED-KEY-FILE [AUTH-FILE] [OPTIONS]\n")
+ return 0
+ }
+
+ if len(args) < 3 {
+ fmt.Fprintf(os.Stderr, "Cannot activate device: insufficient arguments\n")
+ return 1
+ }
+
+ volume := args[0]
+ sourceDevice := args[1]
+
+ var keyFilePath string
+ if args[2] != "" && args[2] != "-" && args[2] != "none" {
+ keyFilePath = args[2]
+ }
+
+ var authFilePath string
+ if len(args) >= 4 && args[3] != "" && args[3] != "-" && args[3] != "none" {
+ authFilePath = args[3]
+ }
+
+ var lock bool
+ var forceRecovery bool
+ pinTries := 1
+ recoveryTries := 1
+ var activateOptions []string
+
+ if len(args) >= 5 && args[4] != "" && args[4] != "-" && args[4] != "none" {
+ opts := strings.Split(args[4], ",")
+ for _, opt := range opts {
+ switch {
+ case opt == "lock":
+ lock = true
+ case opt == "force-recovery":
+ forceRecovery = true
+ case strings.HasPrefix(opt, "pin-tries="):
+ u, err := strconv.ParseUint(strings.TrimPrefix(opt, "pin-tries="), 10, 8)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot activate device %s: invalid value for \"recovery-tries=\"\n", sourceDevice)
+ return 1
+ }
+ pinTries = int(u)
+ case strings.HasPrefix(opt, "recovery-tries="):
+ u, err := strconv.ParseUint(strings.TrimPrefix(opt, "recovery-tries="), 10, 8)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot activate device %s: invalid value for \"recovery-tries=\"\n", sourceDevice)
+ return 1
+ }
+ recoveryTries = int(u)
+ default:
+ activateOptions = append(activateOptions, opt)
+ }
+ }
+ }
+
+ var authReader io.Reader
+ if authFilePath != "" {
+ f, err := os.Open(authFilePath)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot open auth file: %v\n", err)
+ return 1
+ }
+ defer f.Close()
+ authReader = f
+ }
+
+ if !forceRecovery {
+ tpm, err := secboot.ConnectToDefaultTPM()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot connect to TPM: %v\n", err)
+ return 1
+ }
+ defer tpm.Close()
+
+ options := secboot.ActivateWithTPMSealedKeyOptions{
+ PINTries: pinTries,
+ RecoveryKeyTries: recoveryTries,
+ ActivateOptions: activateOptions,
+ LockSealedKeyAccess: lock}
+ if success, err := secboot.ActivateVolumeWithTPMSealedKey(tpm, volume, sourceDevice, keyFilePath, authReader, &options); err != nil {
+ if !success {
+ fmt.Fprintf(os.Stderr, "Activation failed: %v\n", err)
+ return 1
+ }
+ fmt.Printf("Activation succeeded with fallback recovery key: %v\n", err)
+ }
+ } else {
+ options := secboot.ActivateWithRecoveryKeyOptions{
+ Tries: recoveryTries,
+ ActivateOptions: activateOptions}
+ if err := secboot.ActivateVolumeWithRecoveryKey(volume, sourceDevice, authReader, &options); err != nil {
+ fmt.Fprintf(os.Stderr, "Activation with recovery key failed: %v\n", err)
+ return 1
+ }
+ }
+
+ return 0
+}
+
+func main() {
+ flag.Parse()
+ os.Exit(run())
+}
diff --git a/examples/change-pin/main.go b/examples/change-pin/main.go
new file mode 100644
index 00000000..77c732ac
--- /dev/null
+++ b/examples/change-pin/main.go
@@ -0,0 +1,68 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2019 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 .
+ *
+ */
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+
+ "github.com/snapcore/secboot"
+)
+
+var keyFile string
+var currentPin string
+
+func init() {
+ flag.StringVar(¤tPin, "current-pin", "", "")
+ flag.StringVar(&keyFile, "key-file", "", "")
+}
+
+func run() int {
+ if keyFile == "" {
+ fmt.Fprintf(os.Stderr, "Cannot change PIN: missing -key-file\n")
+ return 1
+ }
+
+ args := flag.Args()
+ var pin string
+ if len(args) > 0 {
+ pin = args[0]
+ }
+
+ tpm, err := secboot.ConnectToDefaultTPM()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot connect to TPM: %v\n", err)
+ return 1
+ }
+ defer tpm.Close()
+
+ if err := secboot.ChangePIN(tpm, keyFile, currentPin, pin); err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot change PIN: %v\n", err)
+ return 1
+ }
+
+ return 0
+}
+
+func main() {
+ flag.Parse()
+ os.Exit(run())
+}
diff --git a/examples/provision-status/main.go b/examples/provision-status/main.go
new file mode 100644
index 00000000..b68c822a
--- /dev/null
+++ b/examples/provision-status/main.go
@@ -0,0 +1,84 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2019 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 .
+ *
+ */
+
+package main
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/snapcore/secboot"
+)
+
+func run() int {
+ tpm, err := secboot.ConnectToDefaultTPM()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot connect to TPM: %v\n", err)
+ return 1
+ }
+ defer tpm.Close()
+
+ status, err := secboot.ProvisionStatus(tpm)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot determine status: %v\n", err)
+ return 1
+ }
+
+ if status&secboot.AttrValidSRK > 0 {
+ fmt.Println("Valid SRK found in TPM")
+ } else {
+ fmt.Println("** ERROR: TPM does not have a valid SRK **")
+ }
+
+ if status&secboot.AttrValidEK > 0 {
+ fmt.Println("Valid EK found in TPM")
+ } else {
+ fmt.Println("** ERROR: TPM does not have a valid EK **")
+ }
+
+ if status&secboot.AttrDAParamsOK > 0 {
+ fmt.Println("TPM's DA parameters are correct")
+ } else {
+ fmt.Println("** ERROR: TPM's DA parameters are not the values set during provisioning **")
+ }
+
+ if status&secboot.AttrOwnerClearDisabled > 0 {
+ fmt.Println("TPM does not allow clearing with the lockout hierarchy authorization")
+ } else {
+ fmt.Println("** ERROR: TPM allows clearing with the lockout hierarchy authorization **")
+ }
+
+ if status&secboot.AttrLockoutAuthSet > 0 {
+ fmt.Println("The lockout hierarchy authorization is set")
+ } else {
+ fmt.Println("** ERROR: The lockout hierarchy authorization is not set **")
+ }
+
+ if status&secboot.AttrValidLockNVIndex > 0 {
+ fmt.Println("Valid lock NV index found in TPM")
+ } else {
+ fmt.Println("** ERROR: TPM does not have a valid lock NV index **")
+ }
+
+ return 0
+}
+
+func main() {
+ os.Exit(run())
+}
diff --git a/examples/provision-tpm/main.go b/examples/provision-tpm/main.go
new file mode 100644
index 00000000..2eefcdb4
--- /dev/null
+++ b/examples/provision-tpm/main.go
@@ -0,0 +1,111 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2019 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 .
+ *
+ */
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+
+ "github.com/snapcore/secboot"
+)
+
+var (
+ clear bool
+ lockoutAuth string
+ noLockoutAuth bool
+ ownerAuth string
+ endorsementAuth string
+ requestClear bool
+)
+
+func init() {
+ flag.BoolVar(&clear, "clear", false, "Attempt to clear the TPM before provisioning")
+ flag.StringVar(&lockoutAuth, "lockout-auth", "", "The current lockout hierarchy authorization value")
+ flag.BoolVar(&noLockoutAuth, "no-lockout-auth", false,
+ "Don't perform provisioning actions that require the use of the lockout hierarchy authorization")
+ flag.StringVar(&ownerAuth, "owner-auth", "", "The current storage hierarchy authorization value")
+ flag.StringVar(&endorsementAuth, "endorsement-auth", "", "The current endorsement hierarchy authorization value")
+ flag.BoolVar(&requestClear, "request-clear", false, "Request to clear the TPM via the physical presence interface")
+}
+
+func run() int {
+ args := flag.Args()
+
+ if requestClear {
+ if err := secboot.RequestTPMClearUsingPPI(); err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to request clearing the TPM via the PPI: %v\n", err)
+ os.Exit(1)
+ }
+ fmt.Println("Request to clear the TPM submitted successfully. Now perform a system restart")
+ return 0
+ }
+
+ if clear && noLockoutAuth {
+ fmt.Fprintf(os.Stderr, "-clear and -no-lockout-auth can't be used at the same time\n")
+ return 1
+ }
+
+ var newLockoutAuth string
+ if len(args) > 0 {
+ newLockoutAuth = args[0]
+ }
+
+ tpm, err := secboot.ConnectToDefaultTPM()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot connect to TPM: %v\n", err)
+ return 1
+ }
+ defer tpm.Close()
+
+ var mode secboot.ProvisionMode
+ switch {
+ case clear:
+ mode = secboot.ProvisionModeClear
+ case noLockoutAuth:
+ mode = secboot.ProvisionModeWithoutLockout
+ default:
+ mode = secboot.ProvisionModeFull
+ }
+
+ tpm.OwnerHandleContext().SetAuthValue([]byte(ownerAuth))
+ tpm.EndorsementHandleContext().SetAuthValue([]byte(endorsementAuth))
+ tpm.LockoutHandleContext().SetAuthValue([]byte(lockoutAuth))
+
+ if err := secboot.ProvisionTPM(tpm, mode, []byte(newLockoutAuth)); err != nil {
+ switch err {
+ case secboot.ErrTPMClearRequiresPPI:
+ fmt.Fprintf(os.Stderr, "Clearing requires the use of the physical presence interface. Re-run with -request-clear\n")
+ case secboot.ErrTPMLockout:
+ fmt.Fprintf(os.Stderr, "The lockout hierarchy is in dictionary attack lockout mode. Either wait for the recovery time to expire, "+
+ "or request to clear the TPM with -request-clear\n")
+ default:
+ fmt.Fprintf(os.Stderr, "Failed to provision the TPM: %v\n", err)
+ }
+ return 1
+ }
+
+ return 0
+}
+
+func main() {
+ flag.Parse()
+ os.Exit(run())
+}
diff --git a/examples/seal-key/main.go b/examples/seal-key/main.go
new file mode 100644
index 00000000..7f0ac11c
--- /dev/null
+++ b/examples/seal-key/main.go
@@ -0,0 +1,174 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2019 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 .
+ *
+ */
+
+package main
+
+import (
+ "bytes"
+ "encoding/binary"
+ "encoding/hex"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+
+ "github.com/canonical/go-tpm2"
+ "github.com/snapcore/secboot"
+)
+
+type pathList []string
+
+func (l *pathList) String() string {
+ var builder bytes.Buffer
+ for i, path := range *l {
+ if i > 0 {
+ builder.WriteString(", ")
+ }
+ builder.WriteString(path)
+ }
+ return builder.String()
+}
+
+func (l *pathList) Set(value string) error {
+ *l = append(*l, value)
+ return nil
+}
+
+var create bool
+var clearKeyFile string
+var keyFile string
+var policyUpdateDataFile string
+var pinIndex string
+var ownerAuth string
+var kernels pathList
+var grubs pathList
+var shims pathList
+
+func init() {
+ flag.BoolVar(&create, "new", false, "Create a new key file using the SealKeyToTPM API")
+ flag.StringVar(&clearKeyFile, "clear-key-file", "", "Path of the file containing the cleartext key to seal (with -new)")
+ flag.StringVar(&keyFile, "key-file", "", "Path of the sealed key data file to create (with -new) or to update (without -new)")
+ flag.StringVar(&policyUpdateDataFile, "policy-update-data-file", "",
+ "Path of the file containing data required for updating policy, to create (with -new) or to use (without -new)")
+ flag.StringVar(&pinIndex, "pin-index", "", "Handle to use for the PIN NV index (with -new)")
+ flag.StringVar(&ownerAuth, "auth", "", "Authorization value for the storage hierarchy (with -new)")
+ flag.Var(&kernels, "with-kernel", "")
+ flag.Var(&grubs, "with-grub", "")
+ flag.Var(&shims, "with-shim", "")
+}
+
+func run() int {
+ if keyFile == "" {
+ fmt.Fprintf(os.Stderr, "Missing -key-file\n")
+ return 1
+ }
+
+ tpm, err := secboot.ConnectToDefaultTPM()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot connect to TPM: %v\n", err)
+ return 1
+ }
+ defer tpm.Close()
+
+ pcrProfile := secboot.NewPCRProtectionProfile()
+ if len(shims) == 0 && len(grubs) == 0 && len(kernels) == 0 {
+ pcrProfile.AddPCRValueFromTPM(tpm2.HashAlgorithmSHA256, 7)
+ } else {
+ secureBootParams := &secboot.EFISecureBootPolicyProfileParams{PCRAlgorithm: tpm2.HashAlgorithmSHA256}
+ for _, shim := range shims {
+ s := &secboot.EFIImageLoadEvent{Source: secboot.Firmware, Image: secboot.FileEFIImage(shim)}
+ for _, grub := range grubs {
+ g := &secboot.EFIImageLoadEvent{Source: secboot.Shim, Image: secboot.FileEFIImage(grub)}
+ for _, kernel := range kernels {
+ k := &secboot.EFIImageLoadEvent{Source: secboot.Shim, Image: secboot.FileEFIImage(kernel)}
+ g.Next = append(g.Next, k)
+ }
+ s.Next = append(s.Next, g)
+ }
+ secureBootParams.LoadSequences = append(secureBootParams.LoadSequences, s)
+ }
+ if err := secboot.AddEFISecureBootPolicyProfile(pcrProfile, secureBootParams); err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot add EFI secure boot policy profile to PCR profile: %v\n", err)
+ return 1
+ }
+ }
+
+ if create {
+ if clearKeyFile == "" {
+ fmt.Fprintf(os.Stderr, "Missing -master-key-file\n")
+ return 1
+ }
+ if pinIndex == "" {
+ fmt.Fprintf(os.Stderr, "Missing -pin-index\n")
+ return 1
+ }
+
+ var in *os.File
+ if clearKeyFile == "-" {
+ in = os.Stdin
+ } else {
+ f, err := os.Open(clearKeyFile)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot open key file: %v\n", err)
+ return 1
+ }
+ in = f
+ defer in.Close()
+ }
+
+ var pinHandle tpm2.Handle
+ if h, err := hex.DecodeString(pinIndex); err != nil {
+ fmt.Fprintf(os.Stderr, "Invalid -pin-index: %v\n", err)
+ return 1
+ } else {
+ pinHandle = tpm2.Handle(binary.BigEndian.Uint32(h))
+ }
+
+ key, err := ioutil.ReadAll(in)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot read key: %v\n", err)
+ return 1
+ }
+
+ createParams := secboot.KeyCreationParams{PCRProfile: pcrProfile, PINHandle: pinHandle}
+ tpm.OwnerHandleContext().SetAuthValue([]byte(ownerAuth))
+
+ if err := secboot.SealKeyToTPM(tpm, key, keyFile, policyUpdateDataFile, &createParams); err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot seal key to TPM: %v\n", err)
+ return 1
+ }
+ } else {
+ if policyUpdateDataFile == "" {
+ fmt.Fprintf(os.Stderr, "Missing -policy-update-data-file\n")
+ return 1
+ }
+
+ if err := secboot.UpdateKeyPCRProtectionPolicy(tpm, keyFile, policyUpdateDataFile, pcrProfile); err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot update key PCR protection policy: %v\n", err)
+ return 1
+ }
+ }
+
+ return 0
+}
+
+func main() {
+ flag.Parse()
+ os.Exit(run())
+}
diff --git a/examples/unseal-key/main.go b/examples/unseal-key/main.go
new file mode 100644
index 00000000..9268b872
--- /dev/null
+++ b/examples/unseal-key/main.go
@@ -0,0 +1,94 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2019 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 .
+ *
+ */
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+
+ "github.com/snapcore/secboot"
+)
+
+var keyFile string
+var outFile string
+var pin string
+
+func init() {
+ flag.StringVar(&keyFile, "key-file", "", "Path of the sealed key data file")
+ flag.StringVar(&outFile, "out-file", "", "Path of the file to store the cleartext key in")
+ flag.StringVar(&pin, "pin", "", "")
+}
+
+func run() int {
+ if keyFile == "" {
+ fmt.Fprintf(os.Stderr, "Missing -key-file\n")
+ return 1
+ }
+ if outFile == "" {
+ fmt.Fprintf(os.Stderr, "Missing -out-file\n")
+ return 1
+ }
+
+ var out *os.File
+ if outFile == "-" {
+ out = os.Stdout
+ } else {
+ f, err := os.OpenFile(outFile, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot open output file: %v\n", err)
+ return 1
+ }
+ out = f
+ defer out.Close()
+ }
+
+ tpm, err := secboot.ConnectToDefaultTPM()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot connect to TPM: %v\n", err)
+ return 1
+ }
+ defer tpm.Close()
+
+ k, err := secboot.ReadSealedKeyObject(keyFile)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot load sealed key object: %v\n", err)
+ return 1
+ }
+
+ key, err := k.UnsealFromTPM(tpm, pin)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot unseal key: %v\n", err)
+ return 1
+ }
+
+ _, err = out.Write(key)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot write unsealed key: %v\n", err)
+ return 1
+ }
+
+ return 0
+}
+
+func main() {
+ flag.Parse()
+ os.Exit(run())
+}