/macgyver

A Chrome extension which duct tapes an SSH agent to the platformKey API

Primary LanguageGoMIT LicenseMIT

This project is deprecated and is no longer being actively maintained.

MacGyver

Agent MacGyver

MacGyver is a Chrome extension for enterprise-managed Chromebooks. It duct tapes an SSH agent to the new chrome.platformKeys API (which provides access to X.509 certificates stored in a Chromebook's TPM), exposing it to the Chrome Secure Shell extension.

Background

For some time, Chrome OS has included the ability to store X.509 certificates in its TPM. This allows certificates to be stored in such a way that the private keys can not be extracted. Chrome 45 added the new chrome.platformKeys extension API, which allows extensions to use those certificates (subject to some permissions constraints) for signing data.

Separately, the Secure Shell extension for Chrome (which is an OpenSSH compiled for NaCl) supports using an external extension as a stand-in for an SSH agent.

MacGyver duct tapes these two developments together, allowing the Secure Shell extension to utilize TPM-stored certificates (or, really, the public key in the certificates) and private keys via the SSH agent protocol and the chrome.platformKeys API.

Usage

After installing, you can pass --ssh-agent=extensionid in the "relay options" field (not the "SSH Arguments"!) of the Secure Shell extension.

Building

The extension is written in Go, and compiled to JavaScript using GopherJS. Using Go lets us take advantage of packages like x/crypto, which already has an SSH agent implementation.

You can compile the extension by running the following:

  • go get -u github.com/gopherjs/gopherjs
  • cd go && gopherjs build

Permissions

Unfortunately, the new chrome.platformKeys API doesn't make it very easy to get at certificates.

It's only possible to access certificates that were generated using the chrome.enterprise.platformKeys API. In order to use MacGyver, this means that you must have an enterprise-enrolled Chromebook that uses an administrator-provisioned extension to generate and load certificates. This also means that MacGyver will never see certificates that are (e.g.) imported using the Certificate manager or generated via <keygen> tags.

Additionally, even if an extension has the platformKeys permission, it can only access certificates created by chrome.enterprise.platformKeys if it's been explicitly whitelisted for that access. That whitelisting happens via the KeyPermissions policy, which must be set to {"extensionid": {"allowCorporateKeyUsage": true}}.

Unfortunately, this is tricky, since the KeyPermissions policy is not yet exposed from the Google Apps control panel. As of this writing, the only way to set KeyPermissions is to enter developer mode (the process differs by model), disabling rootfs verification, and manually creating a Linux policy file in (for example) /etc/opt/chrome/policies/managed/macgyver.json. The contents of the file should look something like this:

{
  "KeyPermissions": {
    "monnheglpedplnifignjahmadpadlmgj": {
      "allowCorporateKeyUsage": true
    }
  }
}

Once the file is in place, the system policies can be reloaded by going to chrome://policy and clicking "Reload policies". If the configuration worked, the KeyPermissions policy should immediately show up in the list of active policies.

Chrome SSH Agent Protocol

The Secure Shell extension for Chrome has supported relaying the SSH agent protocol to another extension since November 2014. The protocol is fairly straightforward, but undocumented.

The SSH agent protocol is based on a simple length-prefixed framing protocol. Each message is prefixed with a 4-byte network-encoded length. Messages are sent over a UNIX socket.

By contrast, the Secure Shell agent protocol uses Chrome cross-extension messaging, connecting to the agent extension with chrome.runtime.connect. Each frame of the SSH agent protocol is assembled, stripped of its length prefix, and sent as an array of numbers (not, say, an ArrayBuffer) in the "data" field of an object via postMessage.

Here's an example message, representing the SSH2_AGENTC_REQUEST_IDENTITIES request (to list keys):

{
  "type": "auth-agent@openssh.com",
  "data": [11]
}

SSH agents are expected to respond in the same format.

macgyver.AgentPort

Because x/crypto's SSH agent implementation expects an io.ReadWriter that implements the standard (length-prefixed) protocol, MacGyver implements a wrapper around a chrome.runtime.Port that between Secure Shell's protocol and the native protocol (stripping or adding the length prefix and JSON object wrapper as necessary).

Hacking

If you want to hack on this not on a Chromebook, there's a rough localStorage backend for keys that you can use instead of Chrome PlatformKeys.

  • Create a localStorage item for the extension with key privateKey. An easy way to do this is to open the console and run localStorage.privateKey = "-----BEGIN RSA PRIVATE KEY-----\nkey\nwith\nliteral\nnewlines\n-----END RSA PRIVATE KEY-----"
  • Edit main.go and change the branch to false.

Contributors

  • Evan Broder
  • Dan Benamy