Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for owner password #37

Merged
merged 1 commit into from
May 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ $ export SSH_AUTH_SOCK="$(ssh-tpm-agent --print-socket)"
$ ssh git@github.com
```

**Note:** For `ssh-tpm-agent` you can specify the TPM owner password using the command line flags `-o` or `--owner-password`, which are preferred. Alternatively, you can use the environment variable `SSH_TPM_AGENT_OWNER_PASSWORD`.

### Import existing key

Useful if you want to back up the key to a remote secure storage while using the key day-to-day from the TPM.
Expand Down
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 @@ -343,10 +344,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 },
// 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(""), []byte(""), "")
if err != nil {
t.Fatal(err)
}
Expand Down
25 changes: 20 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,23 @@ func main() {
return tpm
},

// Owner password
func() ([]byte, error) {
if askOwnerPassword {
return pinentry.GetOwnerPassword()
} else {
ownerPassword := os.Getenv("SSH_TPM_AGENT_OWNER_PASSWORD")

return []byte(ownerPassword), 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
6 changes: 3 additions & 3 deletions cmd/ssh-tpm-agent/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ func runSSHAuth(t *testing.T, keytype tpm2.TPMAlgID, bits int, pin []byte, keyfn
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 },
// PIN Callback
func(_ *key.Key) ([]byte, error) {
return pin, nil
Expand Down
31 changes: 27 additions & 4 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,24 @@ func getPin() []byte {
}
}

func getOwnerPassword() []byte {
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 +136,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 @@ -164,6 +179,14 @@ func main() {
os.Exit(0)
}

// Ask for owner password
var ownerPassword []byte
if askOwnerPassword {
ownerPassword = getOwnerPassword()
Foxboron marked this conversation as resolved.
Show resolved Hide resolved
} else {
ownerPassword = []byte("")
}

// Generate host keys
if hostKeys {
// Mimics the `ssh-keygen -A -f ./something` behaviour
Expand All @@ -190,7 +213,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 @@ -275,7 +298,7 @@ func main() {
}
fmt.Println()

newkey, err := key.ChangeAuth(tpm, k, oldPin, newPin)
newkey, err := key.ChangeAuth(tpm, ownerPassword, k, oldPin, newPin)
if err != nil {
log.Fatal("Failed changing pin on the key.")
}
Expand Down Expand Up @@ -409,12 +432,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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.22.0
require (
github.com/foxboron/go-tpm-keyfiles v0.0.0-20240225134915-950e719db3d9
github.com/foxboron/swtpm_test v0.0.0-20230726224112-46aaafdf7006
github.com/google/go-tpm v0.9.1-0.20240411180339-1fb84445f623
github.com/google/go-tpm v0.9.1-0.20240514145214-58e3e47cd434
github.com/twpayne/go-pinentry v0.3.0
golang.org/x/crypto v0.21.0
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ github.com/google/go-sev-guest v0.6.1 h1:NajHkAaLqN9/aW7bCFSUplUMtDgk2+HcN7jC2bt
github.com/google/go-sev-guest v0.6.1/go.mod h1:UEi9uwoPbLdKGl1QHaq1G8pfCbQ4QP0swWX4J0k6r+Q=
github.com/google/go-tpm v0.9.1-0.20240411180339-1fb84445f623 h1:LGYp08nFCGgxM/pRoE4etWElLB2WsrhJiBG4jK04MPE=
github.com/google/go-tpm v0.9.1-0.20240411180339-1fb84445f623/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/go-tpm v0.9.1-0.20240514145214-58e3e47cd434 h1:uPadaCeI0VnloLvthGLalr0Io0IDoI1VEQ95APzVAiw=
github.com/google/go-tpm v0.9.1-0.20240514145214-58e3e47cd434/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba h1:qJEJcuLzH5KDR0gKc0zcktin6KSAwL7+jWKBYceddTc=
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba/go.mod h1:EFYHy8/1y2KfgTAsx7Luu7NGhoxtuVHnNo8jE7FikKc=
github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ=
Expand Down
4 changes: 2 additions & 2 deletions internal/keytest/keytest.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func MkECDSA(t *testing.T, a elliptic.Curve) ecdsa.PrivateKey {
// Test helper for CreateKey
func MkKey(t *testing.T, tpm transport.TPMCloser, keytype tpm2.TPMAlgID, bits int, pin []byte, comment string) (*key.Key, error) {
t.Helper()
return key.CreateKey(tpm, keytype, bits, pin, comment)
return key.CreateKey(tpm, keytype, bits, []byte(""), pin, comment)
}

// Helper to make an importable key
Expand All @@ -56,7 +56,7 @@ func MkImportableKey(t *testing.T, tpm transport.TPMCloser, keytype tpm2.TPMAlgI
case tpm2.TPMAlgRSA:
pk = MkRSA(t, bits)
}
return key.ImportKey(tpm, pk, pin, comment)
return key.ImportKey(tpm, []byte(""), pk, pin, comment)
}

// Give us some random bytes
Expand Down
34 changes: 18 additions & 16 deletions key/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,16 @@ 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{
Buffer: []byte(nil),
Buffer: []byte(""),
},
},
},
Expand Down Expand Up @@ -230,7 +233,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 @@ -258,7 +261,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 @@ -296,8 +299,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 @@ -315,7 +317,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 @@ -419,7 +421,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 @@ -434,7 +436,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 @@ -483,8 +485,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 Expand Up @@ -571,7 +573,7 @@ func newRSASigScheme(digest tpm2.TPMAlgID) tpm2.TPMTSigScheme {
}
}

func Sign(tpm transport.TPMCloser, key *Key, digest []byte, auth []byte, digestalg tpm2.TPMAlgID) ([]byte, error) {
func Sign(tpm transport.TPMCloser, ownerPassword []byte, key *Key, digest []byte, auth []byte, digestalg tpm2.TPMAlgID) ([]byte, error) {
var digestlength int

switch digestalg {
Expand All @@ -587,7 +589,7 @@ func Sign(tpm transport.TPMCloser, key *Key, digest []byte, auth []byte, digesta
return nil, fmt.Errorf("incorrect checksum length. expected %v got %v", digestlength, len(digest))
}

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 @@ -649,10 +651,10 @@ func Sign(tpm transport.TPMCloser, key *Key, digest []byte, auth []byte, digesta

// ChangeAuth changes the object authn header to something else
// notice this changes the private blob inside the key in-place.
func ChangeAuth(tpm transport.TPMCloser, key *Key, oldpin, newpin []byte) (*Key, error) {
func ChangeAuth(tpm transport.TPMCloser, ownerPassword []byte, key *Key, oldpin, newpin []byte) (*Key, error) {
var err error

srkHandle, _, err := CreateSRK(tpm)
srkHandle, _, err := CreateSRK(tpm, ownerPassword)
if err != nil {
return nil, fmt.Errorf("failed creating SRK: %v", err)
}
Expand Down
Loading
Loading