blob: f802afb4efa3f1ae0068b250f1038d0bb01f518f [file] [log] [blame]
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <base/sys_byteorder.h>
#include "u2fd/tpm_vendor_cmd.h"
namespace {
// All TPM extension commands use this struct for input and output. Any other
// data follows immediately after. All values are big-endian over the wire.
struct TpmCmdHeader {
uint16_t tag; // TPM_ST_NO_SESSIONS
uint32_t size; // including this header
uint32_t code; // Command out, Response back.
uint16_t subcommand_code; // Additional command/response codes
} __attribute__((packed));
// TPMv2 Spec mandates that vendor-specific command codes have bit 29 set,
// while bits 15-0 indicate the command. All other bits should be zero. We
// define one of those 16-bit command values for Cr50 purposes, and use the
// subcommand_code in struct TpmCmdHeader to further distinguish the desired
// operation.
const uint32_t kTpmCcVendorBit = 0x20000000;
// Vendor-specific command codes
const uint32_t kTpmCcVendorCr50 = 0x0000;
// Cr50 vendor-specific subcommand codes. 16 bits available.
const uint16_t kVendorCcU2fApdu = 27;
} // namespace
namespace u2f {
TpmVendorCommandProxy::TpmVendorCommandProxy() {}
TpmVendorCommandProxy::~TpmVendorCommandProxy() {}
uint32_t TpmVendorCommandProxy::VendorCommand(uint16_t cc,
const std::string& input,
std::string* output) {
// Pack up the header and the input
TpmCmdHeader header;
header.tag = base::HostToNet16(trunks::TPM_ST_NO_SESSIONS);
header.size = base::HostToNet32(sizeof(header) + input.size());
header.code = base::HostToNet32(kTpmCcVendorBit | kTpmCcVendorCr50);
header.subcommand_code = base::HostToNet16(cc);
std::string command(reinterpret_cast<char*>(&header), sizeof(header));
command += input;
// Send the command, get the response
VLOG(2) << "Out(" << command.size()
<< "): " << base::HexEncode(command.data(), command.size());
std::string response = SendCommandAndWait(command);
VLOG(2) << "In(" << response.size()
<< "): " << base::HexEncode(response.data(), response.size());
if (response.size() < sizeof(header)) {
LOG(ERROR) << "TPM response was too short!";
return -1;
}
// Unpack the response header and any output
memcpy(&header, response.data(), sizeof(header));
header.size = base::NetToHost32(header.size);
header.code = base::NetToHost32(header.code);
// Error of some sort?
if (header.code) {
if ((header.code & kVendorRcErr) == kVendorRcErr) {
LOG(WARNING) << "TPM error code 0x" << std::hex << header.code;
}
}
// Pass back any reply beyond the header
*output = response.substr(sizeof(header));
return header.code;
}
int TpmVendorCommandProxy::SendU2fApdu(const std::string& req,
std::string* resp_out) {
return VendorCommand(kVendorCcU2fApdu, req, resp_out);
}
int TpmVendorCommandProxy::GetU2fVersion(std::string* version_out) {
std::string ping(8, 0);
std::string ver;
// build the command U2F_VERSION:
// CLA INS P1 P2 Le
// 00 03 00 00 00
ping[1] = 0x03;
int rc = SendU2fApdu(ping, &ver);
if (!rc) {
// remove the 16-bit status code at the end
*version_out = ver.substr(0, ver.length() - sizeof(uint16_t));
VLOG(1) << "version " << *version_out;
}
return rc;
}
void TpmVendorCommandProxy::LogIndividualCertificate() {
// ISO7816-4:2005 APDU header size.
constexpr uint8_t kApduHeaderSize = 5;
// U2F_REGISTER command instruction index.
constexpr uint8_t kCmdU2fRegister = 0x01;
// U2F_REGISTER flag in the APDU P1 field.
constexpr uint8_t kG2fAttest = 0x80;
// U2F_REGISTER payload is the nonce and appid (usually 2x SHA-256 = 64B).
constexpr uint8_t kRegisterPayloadSize = 2 * 32;
// ECDSA P256 uses 256-bit integers.
constexpr size_t kP256NBytes = 256 / 8;
// ASN.1 DER constants we are using for certificate parsing.
constexpr uint8_t kAsn1ClassStructured = 0x20;
constexpr uint8_t kAsn1TagSequence = 0x10;
constexpr uint8_t kAsn1LengthLong = 0x80;
// Size of the Type and Length prefix for a Sequence with a 2-byte size.
constexpr size_t kAsn1SequenceTagSize = 2 + sizeof(uint16_t);
// build the U2F_REGISTER command:
// CLA INS P1 P2 Lc Payload
// 00 01 80 00 00 (dummy) nonce and appid
std::string cmd(kApduHeaderSize + kRegisterPayloadSize, 0);
cmd[1] = kCmdU2fRegister;
cmd[2] = kG2fAttest;
cmd[4] = kRegisterPayloadSize;
std::string resp;
if (SendU2fApdu(cmd, &resp) != 0) {
VLOG(1) << "Cannot retrieve individual attestation certificate";
return;
}
// The response is:
// A reserved byte [1 byte], which for legacy reasons has the value 0x05.
// A user public key [65 bytes]. This is the (uncompressed) x,y-representation
// of a curve point on the P-256 elliptic curve.
// A key handle length byte [1 byte], which specifies the length of the key
// handle (see below).
// The value is unsigned (range 0-255).
// A key handle [length, see previous field]. This a handle that
// allows the U2F token to identify the
// generated key pair. U2F tokens may wrap
// the generated private key and the
// application id it was generated for,
// and output that as the key handle.
// An attestation certificate [variable length]. This is a certificate in
// X.509 DER format.
// A signature. This is a ECDSA signature (on P-256).
const int pkey_offset = 1;
const size_t pkey_size = 1 + 2 * kP256NBytes;
const int handle_len_offset = pkey_offset + pkey_size;
const int handle_offset = handle_len_offset + 1;
// Validate the length up to the certificate prefix.
if (resp.size() < handle_offset)
return;
uint8_t handle_len = resp[handle_len_offset];
const int cert_offset = handle_offset + handle_len;
if (resp.size() < cert_offset + kAsn1SequenceTagSize)
return;
// parse the first tag of the certificate ASN.1 data to know its length.
std::string cert_seq_tag = resp.substr(cert_offset, kAsn1SequenceTagSize);
// Should be a Constructed Sequence ASN.1 tag else all bets are off,
// with the size taking 2 bytes (the certificate size is somewhere between
// 256B and 2KB).
if ((static_cast<uint8_t>(cert_seq_tag[0]) !=
(kAsn1ClassStructured | kAsn1TagSequence)) ||
(static_cast<uint8_t>(cert_seq_tag[1]) !=
(kAsn1LengthLong | sizeof(uint16_t)))) {
VLOG(1) << "Cannot parse certificate";
return;
}
uint16_t length_tag;
memcpy(&length_tag, cert_seq_tag.c_str() + 2, sizeof(length_tag));
size_t cert_size = base::NetToHost16(length_tag) + cert_seq_tag.size();
// Validate the length of the certificate.
if (resp.size() < cert_offset + cert_size)
return;
VLOG(1) << "Certificate: "
<< base::HexEncode(resp.data() + cert_offset, cert_size);
}
int TpmVendorCommandProxy::SetU2fVendorMode(uint8_t mode) {
std::string vendor_mode(5, 0);
std::string rmode;
const uint8_t kCmdU2fVendorMode = 0xbf;
const uint8_t kP1SetMode = 0x1;
const uint8_t kU2fExtended = 3;
// build the command U2F_VENDOR_MODE:
// CLA INS P1 P2 Le
// 00 bf 01 md 00
vendor_mode[1] = kCmdU2fVendorMode;
vendor_mode[2] = kP1SetMode;
vendor_mode[3] = mode;
int rc = SendU2fApdu(vendor_mode, &rmode);
if (!rc) {
// remove the 16-bit status code at the end
VLOG(1) << "current mode " << static_cast<int>(rmode[0]);
// record the individual attestation certificate if the extension is on.
if (rmode[0] == kU2fExtended && VLOG_IS_ON(1))
LogIndividualCertificate();
}
return rc;
}
void TpmVendorCommandProxy::GetVendorSysInfo(std::string* sysinfo_out) {
std::string sysinfo_apdu(2, 0);
std::string info_blob;
constexpr uint8_t kCmdG2fSysInfo = 0x11;
constexpr int kG2fSysInfoVersionOffset = 0;
constexpr int kG2fSysInfoVersionLen = 3;
sysinfo_apdu[1] = kCmdG2fSysInfo;
int rc = SendU2fApdu(sysinfo_apdu, &info_blob);
if (rc ||
info_blob.length() < kG2fSysInfoVersionOffset + kG2fSysInfoVersionLen) {
VLOG(1) << "No system info available from the firmware";
return;
}
LOG(INFO) << "System info: FW version "
<< static_cast<int>(info_blob[kG2fSysInfoVersionOffset]) << "."
<< static_cast<int>(info_blob[kG2fSysInfoVersionOffset + 1]) << "."
<< static_cast<int>(info_blob[kG2fSysInfoVersionOffset + 2]);
// Vendor system information blob definition.
constexpr int kVendorSysInfoLen = 43;
constexpr int kVendorSysInfoIdOffset = 0;
constexpr char kVendorPlatformId[] = "hg_int00";
constexpr int kVendorSysInfoFwEpochOffset = 8;
constexpr uint8_t kVendorFwEpoch = 0x03;
constexpr int kVendorSysInfoAppletVersionOffset = 11;
std::string sysinfo(kVendorSysInfoLen, 0);
sysinfo.replace(kVendorSysInfoIdOffset, sizeof(kVendorPlatformId) - 1,
kVendorPlatformId);
sysinfo.replace(
kVendorSysInfoFwEpochOffset, kG2fSysInfoVersionLen,
info_blob.substr(kG2fSysInfoVersionOffset, kG2fSysInfoVersionLen));
sysinfo.replace(
kVendorSysInfoAppletVersionOffset, kG2fSysInfoVersionLen,
info_blob.substr(kG2fSysInfoVersionOffset, kG2fSysInfoVersionLen));
sysinfo[kVendorSysInfoFwEpochOffset] = kVendorFwEpoch;
*sysinfo_out = sysinfo;
}
} // namespace u2f