Skip to content

Commit

Permalink
add support for owner password
Browse files Browse the repository at this point in the history
* Allow specifying an owner password when creating the SRK
  • Loading branch information
novag committed Mar 30, 2024
1 parent 5924ae1 commit 1e813d0
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 53 deletions.
6 changes: 4 additions & 2 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var SSH_TPM_AGENT_ADD = "tpm-add-key"
type Agent struct {
mu sync.Mutex
tpm func() transport.TPMCloser
op func() ([]byte, error)
pin func(*key.Key) ([]byte, error)
listener *net.UnixListener
quit chan interface{}
Expand Down Expand Up @@ -83,7 +84,7 @@ func (a *Agent) signers() ([]ssh.Signer, error) {
}

for _, k := range a.keys {
s, err := ssh.NewSignerFromSigner(signer.NewTPMSigner(k, a.tpm, a.pin))
s, err := ssh.NewSignerFromSigner(signer.NewTPMSigner(k, a.op, a.tpm, a.pin))
if err != nil {
return nil, fmt.Errorf("failed to prepare signer: %w", err)
}
Expand Down Expand Up @@ -316,10 +317,11 @@ func LoadKeys(keyDir string) (map[string]*key.Key, error) {
return keys, err
}

func NewAgent(listener *net.UnixListener, agents []agent.ExtendedAgent, tpmFetch func() transport.TPMCloser, pin func(*key.Key) ([]byte, error)) *Agent {
func NewAgent(listener *net.UnixListener, agents []agent.ExtendedAgent, tpmFetch func() transport.TPMCloser, ownerPassword func() ([]byte, error), pin func(*key.Key) ([]byte, error)) *Agent {
a := &Agent{
agents: agents,
tpm: tpmFetch,
op: ownerPassword,
listener: listener,
pin: pin,
quit: make(chan interface{}),
Expand Down
12 changes: 5 additions & 7 deletions agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,11 @@ func TestAddKey(t *testing.T) {
ag := NewAgent(unixList,
[]agent.ExtendedAgent{},
// TPM Callback
func() transport.TPMCloser {
return tpm
},
func() transport.TPMCloser { return tpm },
// Owner password
func() ([]byte, error) { return []byte(nil), nil },
// PIN Callback
func(_ *key.Key) ([]byte, error) {
return []byte(""), nil
},
func(_ *key.Key) ([]byte, error) { return []byte(""), nil },
)
defer ag.Stop()

Expand All @@ -49,7 +47,7 @@ func TestAddKey(t *testing.T) {

client := agent.NewClient(conn)

k, err := key.CreateKey(tpm, tpm2.TPMAlgECC, 256, []byte(""), "")
k, err := key.CreateKey(tpm, tpm2.TPMAlgECC, 256, []byte(nil), []byte(""), "")
if err != nil {
t.Fatal(err)
}
Expand Down
23 changes: 18 additions & 5 deletions cmd/ssh-tpm-agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ Options:
--no-load Do not load TPM sealed keys by default.
-o, --owner-password Ask for the owner password.
-d Enable debug logging.
--install-user-units Installs systemd system units and sshd configs for using
Expand Down Expand Up @@ -98,10 +100,10 @@ func main() {
}

var (
socketPath, keyDir string
swtpmFlag, printSocketFlag bool
installUserUnits bool
system, noLoad, debugMode bool
socketPath, keyDir string
swtpmFlag, printSocketFlag bool
installUserUnits, system, noLoad bool
askOwnerPassword, debugMode bool
)

envSocketPath := func() string {
Expand All @@ -126,6 +128,8 @@ func main() {
flag.BoolVar(&installUserUnits, "install-user-units", false, "install systemd user units")
flag.BoolVar(&system, "install-system", false, "install systemd user units")
flag.BoolVar(&noLoad, "no-load", false, "don't load TPM sealed keys")
flag.BoolVar(&askOwnerPassword, "o", false, "ask for the owner password")
flag.BoolVar(&askOwnerPassword, "owner-password", false, "ask for the owner password")
flag.BoolVar(&debugMode, "d", false, "debug mode")
flag.Parse()

Expand Down Expand Up @@ -201,12 +205,21 @@ func main() {
return tpm
},

// Owner password
func() ([]byte, error) {
if askOwnerPassword {
return pinentry.GetOwnerPassword()
} else {
return []byte(nil), nil
}
},

// PIN Callback
func(key *key.Key) ([]byte, error) {
pbytes := tpm2.New2B(key.Pubkey)
keyHash := sha256.Sum256(pbytes.Bytes())
keyInfo := fmt.Sprintf("ssh-tpm-agent/%x", keyHash)
return pinentry.GetPinentry(keyInfo)
return pinentry.GetPin(keyInfo)
},
)

Expand Down
12 changes: 5 additions & 7 deletions cmd/ssh-tpm-agent/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func runSSHAuth(t *testing.T, keytype tpm2.TPMAlgID, bits int) {
}
defer tpm.Close()

k, err := key.CreateKey(tpm, keytype, bits, []byte(""), "")
k, err := key.CreateKey(tpm, keytype, bits, []byte(nil), []byte(""), "")
if err != nil {
t.Fatalf("failed creating key: %v", err)
}
Expand Down Expand Up @@ -139,13 +139,11 @@ func runSSHAuth(t *testing.T, keytype tpm2.TPMAlgID, bits int) {
ag := agent.NewAgent(unixList,
[]sshagent.ExtendedAgent{},
// TPM Callback
func() transport.TPMCloser {
return tpm
},
func() transport.TPMCloser { return tpm },
// Owner password
func() ([]byte, error) { return []byte(nil), nil },
// PIN Callback
func(_ *key.Key) ([]byte, error) {
return []byte(""), nil
},
func(_ *key.Key) ([]byte, error) { return []byte(""), nil },
)
defer ag.Stop()

Expand Down
31 changes: 28 additions & 3 deletions cmd/ssh-tpm-keygen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const usage = `Usage:
ssh-tpm-keygen
Options:
-o, --owner-password Ask for the owner password.
-C Provide a comment with the key.
-f Output keyfile.
-N PIN for the key.
Expand Down Expand Up @@ -94,12 +95,26 @@ func getPin() []byte {
}
}

func getOwnerPassword() []byte {
for {
fmt.Printf("Enter owner password: ")
password, err := term.ReadPassword(int(syscall.Stdin))
fmt.Println("")
if err != nil {
log.Fatal(err)
}

return password
}
}

func main() {
flag.Usage = func() {
fmt.Println(usage)
}

var (
askOwnerPassword bool
comment, outputFile, keyPin string
keyType, importKey string
bits int
Expand All @@ -123,6 +138,8 @@ func main() {
return user.Username + "@" + host
}()

flag.BoolVar(&askOwnerPassword, "o", false, "ask for the owner password")
flag.BoolVar(&askOwnerPassword, "owner-password", false, "ask for the owner password")
flag.StringVar(&comment, "C", defaultComment, "provide a comment, default to user@host")
flag.StringVar(&outputFile, "f", "", "output keyfile")
flag.StringVar(&keyPin, "N", "", "new pin for the key")
Expand Down Expand Up @@ -163,6 +180,14 @@ func main() {
os.Exit(0)
}

// Ask for owner password
var ownerPassword []byte
if askOwnerPassword {
ownerPassword = getOwnerPassword()
} else {
ownerPassword = []byte(nil)
}

// Generate host keys
if hostKeys {
// Mimics the `ssh-keygen -A -f ./something` behaviour
Expand All @@ -189,7 +214,7 @@ func main() {

slog.Info("Generating new host key", slog.String("algorithm", strings.ToUpper(n)))

k, err := key.CreateKey(tpm, t.alg, t.bits, []byte(""), defaultComment)
k, err := key.CreateKey(tpm, t.alg, t.bits, ownerPassword, []byte(""), defaultComment)
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -351,12 +376,12 @@ func main() {

if importKey != "" {
// TODO: Read public key for comment
k, err = key.ImportKey(tpm, toImportKey, pin, comment)
k, err = key.ImportKey(tpm, ownerPassword, toImportKey, pin, comment)
if err != nil {
log.Fatal(err)
}
} else {
k, err = key.CreateKey(tpm, tpmkeyType, bits, pin, comment)
k, err = key.CreateKey(tpm, tpmkeyType, bits, ownerPassword, pin, comment)
if err != nil {
log.Fatal(err)
}
Expand Down
24 changes: 13 additions & 11 deletions key/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,12 @@ func DecodeKey(pemBytes []byte) (*Key, error) {
}

// Creates a Storage Key, or return the loaded storage key
func CreateSRK(tpm transport.TPMCloser) (*tpm2.AuthHandle, *tpm2.TPMTPublic, error) {
func CreateSRK(tpm transport.TPMCloser, ownerPassword []byte) (*tpm2.AuthHandle, *tpm2.TPMTPublic, error) {
srk := tpm2.CreatePrimary{
PrimaryHandle: tpm2.TPMRHOwner,
PrimaryHandle: tpm2.AuthHandle{
Handle: tpm2.TPMRHOwner,
Auth: tpm2.PasswordAuth(ownerPassword),
},
InSensitive: tpm2.TPM2BSensitiveCreate{
Sensitive: &tpm2.TPMSSensitiveCreate{
UserAuth: tpm2.TPM2BAuth{
Expand Down Expand Up @@ -228,7 +231,7 @@ func createRSAKey(bits tpm2.TPMKeyBits, sha tpm2.TPMAlgID) tpm2.TPM2B[tpm2.TPMTP
})
}

func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, bits int, pin []byte, comment string) (*Key, error) {
func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, bits int, ownerPassword []byte, pin []byte, comment string) (*Key, error) {
rsaBits := []int{2048}
ecdsaBits := []int{256, 384, 521}

Expand Down Expand Up @@ -256,7 +259,7 @@ func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, bits int, pin []b
return nil, fmt.Errorf("unsupported key type")
}

srkHandle, srkPublic, err := CreateSRK(tpm)
srkHandle, srkPublic, err := CreateSRK(tpm, ownerPassword)
if err != nil {
return nil, fmt.Errorf("failed creating SRK: %v", err)
}
Expand Down Expand Up @@ -294,8 +297,7 @@ func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, bits int, pin []b
emptyAuth = false
}

var createRsp *tpm2.CreateResponse
createRsp, err = createKey.Execute(tpm,
createRsp, err := createKey.Execute(tpm,
tpm2.HMAC(tpm2.TPMAlgSHA256, 16,
tpm2.AESEncryption(128, tpm2.EncryptIn),
tpm2.Salted(srkHandle.Handle, *srkPublic)))
Expand All @@ -313,7 +315,7 @@ func CreateKey(tpm transport.TPMCloser, keytype tpm2.TPMAlgID, bits int, pin []b
return &Key{tpmkey}, nil
}

func ImportKey(tpm transport.TPMCloser, pk any, pin []byte, comment string) (*Key, error) {
func ImportKey(tpm transport.TPMCloser, ownerPassword []byte, pk any, pin []byte, comment string) (*Key, error) {
var public tpm2.TPMTPublic
var sensitive tpm2.TPMTSensitive
var unique tpm2.TPMUPublicID
Expand Down Expand Up @@ -417,7 +419,7 @@ func ImportKey(tpm transport.TPMCloser, pk any, pin []byte, comment string) (*Ke
return nil, fmt.Errorf("unsupported key type")
}

srkHandle, srkPublic, err := CreateSRK(tpm)
srkHandle, srkPublic, err := CreateSRK(tpm, ownerPassword)
if err != nil {
return nil, fmt.Errorf("failed creating SRK: %v", err)
}
Expand All @@ -432,7 +434,7 @@ func ImportKey(tpm transport.TPMCloser, pk any, pin []byte, comment string) (*Ke
emptyAuth = false
}

// We need the size calcualted in the buffer, so we do this serialization dance
// We need the size calculated in the buffer, so we do this serialization dance
l := tpm2.Marshal(tpm2.TPM2BPrivate{Buffer: tpm2.Marshal(sensitive)})

pubbytes := tpm2.New2B(public)
Expand Down Expand Up @@ -481,8 +483,8 @@ func LoadKeyWithParent(tpm transport.TPMCloser, parent tpm2.AuthHandle, key *Key
}, nil
}

func LoadKey(tpm transport.TPMCloser, key *Key) (*tpm2.AuthHandle, error) {
srkHandle, _, err := CreateSRK(tpm)
func LoadKey(tpm transport.TPMCloser, ownerPassword []byte, key *Key) (*tpm2.AuthHandle, error) {
srkHandle, _, err := CreateSRK(tpm, ownerPassword)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions key/key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,14 @@ func TestCreateKey(t *testing.T) {

for _, c := range cases {
t.Run(c.text, func(t *testing.T) {
k, err := CreateKey(tpm, c.alg, c.bits, []byte(""), "")
k, err := CreateKey(tpm, c.alg, c.bits, []byte(nil), []byte(""), "")
if err != nil {
t.Fatalf("failed key import: %v", err)
}

// Test if we can load the key
// signer/signer_test.go tests the signing of the key
handle, err := LoadKey(tpm, k)
handle, err := LoadKey(tpm, []byte(nil), k)
if err != nil {
t.Fatalf("failed loading key: %v", err)
}
Expand Down
26 changes: 22 additions & 4 deletions pinentry/pinentry.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ var (
ErrPinentryCancelled = errors.New("cancelled pinentry")
)

func GetPinentry(keyInfo string) ([]byte, error) {
func GetPinentry(keyInfo string, description string, prompt string, title string) ([]byte, error) {
// TODO: Include some additional key metadata
client, err := pinentry.NewClient(
pinentry.WithCommand("OPTION allow-external-password-cache"),
pinentry.WithCommandf("SETKEYINFO %v", keyInfo),
pinentry.WithBinaryNameFromGnuPGAgentConf(),
pinentry.WithDesc("Enter PIN for TPM key"),
pinentry.WithDesc(description),
pinentry.WithGPGTTY(),
pinentry.WithPrompt("PIN:"),
pinentry.WithTitle("ssh-tpm-agent PIN entry"),
pinentry.WithPrompt(prompt),
pinentry.WithTitle(title),
)
if err != nil {
return nil, err
Expand All @@ -37,3 +37,21 @@ func GetPinentry(keyInfo string) ([]byte, error) {
return []byte(pin), nil
}
}

func GetPin(keyInfo string) ([]byte, error) {
return GetPinentry(
keyInfo,
"Enter PIN for TPM key",
"PIN:",
"ssh-tpm-agent PIN entry",
)
}

func GetOwnerPassword() ([]byte, error) {
return GetPinentry(
"ssh-tpm-agent/owner-password",
"Enter owner password for TPM",
"Owner password:",
"ssh-tpm-agent owner password entry",
)
}
Loading

0 comments on commit 1e813d0

Please sign in to comment.