blob: 174acc126941e437f0aab3e6ba2394f0ebc3b995 [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "crypto/tpm_parser.h"
#include <stdint.h>
#include <optional>
#include <utility>
#include <vector>
#include "base/containers/span.h"
#include "base/containers/span_rust.h"
#include "base/containers/to_vector.h"
#include "base/types/expected.h"
#include "base/types/expected_macros.h"
#include "crypto/ecdsa_utils.h"
#include "crypto/hash.h"
#include "crypto/keypair.h"
#include "crypto/sign.h"
#include "crypto/tpm.rs.h"
namespace crypto::tpm {
namespace {
SignatureErrorOr<void> MapSignatureParseResult(SignatureParseResult result) {
switch (result) {
case SignatureParseResult::Ok:
return base::ok();
case SignatureParseResult::BufferTooSmall:
return base::unexpected(SignatureError::kBufferTooSmall);
case SignatureParseResult::TrailingBytes:
return base::unexpected(SignatureError::kTrailingBytes);
case SignatureParseResult::UnsupportedSignatureAlgorithm:
return base::unexpected(SignatureError::kUnsupportedSignatureAlgorithm);
}
NOTREACHED();
}
TpmParseErrorOr<void> MapResponseStatus(const ResponseStatus& status) {
switch (status.result) {
case ParseResult::Ok:
return base::ok();
case ParseResult::BufferTooSmall:
return base::unexpected(
TpmParseError(TpmParseError::Type::kBufferTooSmall));
case ParseResult::TrailingBytes:
return base::unexpected(
TpmParseError(TpmParseError::Type::kTrailingBytes));
case ParseResult::TpmErrorResponse:
return base::unexpected(TpmParseError(
TpmParseError::Type::kTpmErrorResponse, status.tpm_response_code));
case ParseResult::WrongType:
return base::unexpected(TpmParseError(TpmParseError::Type::kWrongType));
}
NOTREACHED();
}
SignatureErrorOr<hash::HashKind> MapHashAlgorithm(TpmAlgHash hash_alg) {
switch (hash_alg) {
case TPM_ALG_SHA256:
return hash::kSha256;
case TPM_ALG_SHA1:
return hash::kSha1;
default:
return base::unexpected(SignatureError::kUnsupportedHashAlgorithm);
}
}
constexpr TpmAlgHash ToTpmAlgHash(hash::HashKind hash_kind) {
switch (hash_kind) {
case hash::kSha1:
return TPM_ALG_SHA1;
case hash::kSha256:
return TPM_ALG_SHA256;
case hash::kSha384:
return TPM_ALG_SHA384;
case hash::kSha512:
return TPM_ALG_SHA512;
}
NOTREACHED();
}
sign::SignatureKind ToSignatureKind(TpmAlgSigScheme alg, hash::HashKind hash) {
switch (alg) {
case TPM_ALG_NULL:
NOTREACHED();
case TPM_ALG_RSASSA:
switch (hash) {
case hash::kSha1:
return sign::RSA_PKCS1_SHA1;
case hash::kSha256:
return sign::RSA_PKCS1_SHA256;
case hash::kSha384:
return sign::RSA_PKCS1_SHA384;
case hash::kSha512:
return sign::RSA_PKCS1_SHA512;
}
case TPM_ALG_RSAPSS:
switch (hash) {
case hash::kSha1:
NOTREACHED();
case hash::kSha256:
return sign::RSA_PSS_SHA256;
case hash::kSha384:
return sign::RSA_PSS_SHA384;
case hash::kSha512:
return sign::RSA_PSS_SHA512;
}
case TPM_ALG_ECDSA:
switch (hash) {
case hash::kSha1:
return sign::ECDSA_SHA1;
case hash::kSha256:
return sign::ECDSA_SHA256;
case hash::kSha384:
return sign::ECDSA_SHA384;
case hash::kSha512:
return sign::ECDSA_SHA512;
}
}
NOTREACHED();
}
SignatureErrorOr<void> VerifyRsaSignature(const keypair::PublicKey& public_key,
base::span<const uint8_t> statement,
hash::HashKind hash_kind,
base::span<const uint8_t> rsa_sig) {
bool verified = sign::Verify(ToSignatureKind(TPM_ALG_RSASSA, hash_kind),
public_key, statement, rsa_sig);
if (!verified) {
return base::unexpected(SignatureError::kInvalidSignature);
}
return base::ok();
}
SignatureErrorOr<void> VerifyEcdsaSignature(
const keypair::PublicKey& public_key,
base::span<const uint8_t> statement,
hash::HashKind hash_kind,
base::span<const uint8_t> ecdsa_r,
base::span<const uint8_t> ecdsa_s) {
std::optional<std::vector<uint8_t>> der_sig =
ConvertEcdsaRawComponentsToDer(ecdsa_r, ecdsa_s);
bool verified = der_sig.has_value() &&
sign::Verify(ToSignatureKind(TPM_ALG_ECDSA, hash_kind),
public_key, statement, *der_sig);
if (!verified) {
return base::unexpected(SignatureError::kInvalidSignature);
}
return base::ok();
}
std::optional<SignatureAlgorithms> ToSignatureAlgorithms(
sign::SignatureKind kind) {
switch (kind) {
case sign::SignatureKind::RSA_PKCS1_SHA256:
return SignatureAlgorithms{.sig_alg = TPM_ALG_RSASSA,
.hash_alg = TPM_ALG_SHA256};
case sign::SignatureKind::RSA_PKCS1_SHA384:
return SignatureAlgorithms{.sig_alg = TPM_ALG_RSASSA,
.hash_alg = TPM_ALG_SHA384};
case sign::SignatureKind::RSA_PKCS1_SHA512:
return SignatureAlgorithms{.sig_alg = TPM_ALG_RSASSA,
.hash_alg = TPM_ALG_SHA512};
case sign::SignatureKind::RSA_PSS_SHA256:
return SignatureAlgorithms{.sig_alg = TPM_ALG_RSAPSS,
.hash_alg = TPM_ALG_SHA256};
case sign::SignatureKind::RSA_PSS_SHA384:
return SignatureAlgorithms{.sig_alg = TPM_ALG_RSAPSS,
.hash_alg = TPM_ALG_SHA384};
case sign::SignatureKind::RSA_PSS_SHA512:
return SignatureAlgorithms{.sig_alg = TPM_ALG_RSAPSS,
.hash_alg = TPM_ALG_SHA512};
case sign::SignatureKind::ECDSA_SHA256:
return SignatureAlgorithms{.sig_alg = TPM_ALG_ECDSA,
.hash_alg = TPM_ALG_SHA256};
case sign::SignatureKind::ECDSA_SHA384:
return SignatureAlgorithms{.sig_alg = TPM_ALG_ECDSA,
.hash_alg = TPM_ALG_SHA384};
case sign::SignatureKind::ECDSA_SHA512:
return SignatureAlgorithms{.sig_alg = TPM_ALG_ECDSA,
.hash_alg = TPM_ALG_SHA512};
default:
return std::nullopt;
}
}
} // namespace
// A nonexistent handle is reported in more than one way: response codes are
// stored unmasked, so TPM_RC_HANDLE may carry the handle-index qualifier
// TPM_RC_1, and a handle that is absent from the resource manager is reported
// as the warning TPM_RC_REFERENCE_H0 instead (TPM 2.0 Part 2, Table 17).
bool IsHandleError(const TpmParseError& error) {
// `tpm_error_code` is only set for `kTpmErrorResponse`, and a TPM error
// response never carries code 0, so the fallback below cannot match.
switch (error.tpm_error_code.value_or(0)) {
case 0x08B: // TPM_RC_HANDLE
case 0x18B: // TPM_RC_HANDLE | TPM_RC_1
case 0x910: // TPM_RC_REFERENCE_H0
return true;
default:
return false;
}
}
std::optional<std::vector<uint8_t>> BuildCreateAikCommand(
uint32_t parent_handle,
sign::SignatureKind kind) {
return ToSignatureAlgorithms(kind).transform(
[parent_handle](const auto& algs) {
return base::ToVector(build_create_aik_command(
parent_handle, algs.sig_alg, algs.hash_alg));
});
}
TpmParseErrorOr<CreateResponse> ParseCreateResponse(
base::span<const uint8_t> response_blob) {
RawCreateResponse raw_response =
parse_create_response(base::SpanToRustSlice(response_blob));
return MapResponseStatus(raw_response.status).transform([&] {
return CreateResponse{
.out_private = base::ToVector(raw_response.out_private),
.out_public = base::ToVector(raw_response.out_public),
};
});
}
std::vector<uint8_t> BuildCreatePrimaryEccSrkCommand() {
return base::ToVector(build_create_primary_ecc_srk_command());
}
TpmParseErrorOr<CreatePrimaryResponse> ParseCreatePrimaryResponse(
base::span<const uint8_t> response_blob) {
RawCreatePrimaryResponse raw_response =
parse_create_primary_response(base::SpanToRustSlice(response_blob));
return MapResponseStatus(raw_response.status).transform([&] {
return CreatePrimaryResponse{
.object_handle = raw_response.object_handle,
};
});
}
std::vector<uint8_t> BuildFlushContextCommand(uint32_t handle) {
return base::ToVector(build_flush_context_command(handle));
}
TpmParseErrorOr<FlushContextResponse> ParseFlushContextResponse(
base::span<const uint8_t> response_blob) {
ResponseStatus status =
parse_flush_context_response(base::SpanToRustSlice(response_blob));
return MapResponseStatus(status).transform(
[] { return FlushContextResponse{}; });
}
std::vector<uint8_t> BuildHashCommand(base::span<const uint8_t> data,
hash::HashKind hash_kind) {
return base::ToVector(
build_hash_command(base::SpanToRustSlice(data), ToTpmAlgHash(hash_kind)));
}
TpmParseErrorOr<HashResponse> ParseHashResponse(
base::span<const uint8_t> response_blob) {
RawHashResponse raw_response =
parse_hash_response(base::SpanToRustSlice(response_blob));
return MapResponseStatus(raw_response.status).transform([&] {
return HashResponse{
.digest = base::ToVector(raw_response.digest),
.validation_ticket = base::ToVector(raw_response.validation_ticket),
};
});
}
std::vector<uint8_t> BuildHashSequenceStartCommand(hash::HashKind hash_kind) {
return base::ToVector(
build_hash_sequence_start_command(ToTpmAlgHash(hash_kind)));
}
TpmParseErrorOr<HashSequenceStartResponse> ParseHashSequenceStartResponse(
base::span<const uint8_t> response_blob) {
RawHashSequenceStartResponse raw_response =
parse_hash_sequence_start_response(base::SpanToRustSlice(response_blob));
return MapResponseStatus(raw_response.status).transform([&] {
return HashSequenceStartResponse{
.sequence_handle = raw_response.sequence_handle,
};
});
}
std::vector<uint8_t> BuildSequenceCompleteCommand(
uint32_t sequence_handle,
base::span<const uint8_t> data) {
return base::ToVector(build_sequence_complete_command(
sequence_handle, base::SpanToRustSlice(data)));
}
TpmParseErrorOr<SequenceCompleteResponse> ParseSequenceCompleteResponse(
base::span<const uint8_t> response_blob) {
RawHashResponse raw_response =
parse_sequence_complete_response(base::SpanToRustSlice(response_blob));
return MapResponseStatus(raw_response.status).transform([&] {
return SequenceCompleteResponse{
.digest = base::ToVector(raw_response.digest),
.validation_ticket = base::ToVector(raw_response.validation_ticket),
};
});
}
std::vector<uint8_t> BuildSequenceUpdateCommand(
uint32_t sequence_handle,
base::span<const uint8_t> data) {
return base::ToVector(build_sequence_update_command(
sequence_handle, base::SpanToRustSlice(data)));
}
TpmParseErrorOr<SequenceUpdateResponse> ParseSequenceUpdateResponse(
base::span<const uint8_t> response_blob) {
ResponseStatus status =
parse_sequence_update_response(base::SpanToRustSlice(response_blob));
return MapResponseStatus(status).transform(
[] { return SequenceUpdateResponse{}; });
}
std::vector<uint8_t> BuildSignCommand(
uint32_t key_handle,
base::span<const uint8_t> digest,
base::span<const uint8_t> validation_ticket) {
return base::ToVector(
build_sign_command(key_handle, base::SpanToRustSlice(digest),
base::SpanToRustSlice(validation_ticket)));
}
TpmParseErrorOr<SignResponse> ParseSignResponse(
base::span<const uint8_t> response_blob) {
RawSignResponse raw_response =
parse_sign_response(base::SpanToRustSlice(response_blob));
return MapResponseStatus(raw_response.status).transform([&] {
return SignResponse{
.signature = base::ToVector(raw_response.signature),
};
});
}
std::optional<std::vector<uint8_t>> ParseTpmSignature(
base::span<const uint8_t> signature_blob) {
RawSignatureComponents raw_sig =
parse_tpm_signature(base::SpanToRustSlice(signature_blob));
if (raw_sig.status != SignatureParseResult::Ok) {
return std::nullopt;
}
switch (raw_sig.sig_alg) {
case TPM_ALG_RSASSA:
return base::ToVector(raw_sig.rsa_sig);
case TPM_ALG_ECDSA:
return ConvertEcdsaRawComponentsToDer(raw_sig.ecdsa_r, raw_sig.ecdsa_s);
default:
return std::nullopt;
}
}
SignatureErrorOr<SignatureAlgorithms> GetSignatureAlgorithms(
base::span<const uint8_t> signature_blob) {
RawSignatureComponents raw_sig =
parse_tpm_signature(base::SpanToRustSlice(signature_blob));
RETURN_IF_ERROR(MapSignatureParseResult(raw_sig.status));
return SignatureAlgorithms{
.sig_alg = raw_sig.sig_alg,
.hash_alg = raw_sig.hash_alg,
};
}
SignatureErrorOr<void> VerifySignature(
base::span<const uint8_t> spki,
base::span<const uint8_t> statement,
base::span<const uint8_t> signature_blob) {
// 1. Parse the signature using Rust FFI
RawSignatureComponents raw_sig =
parse_tpm_signature(base::SpanToRustSlice(signature_blob));
RETURN_IF_ERROR(MapSignatureParseResult(raw_sig.status));
ASSIGN_OR_RETURN(hash::HashKind hash_kind,
MapHashAlgorithm(raw_sig.hash_alg));
// 2. Import public key
ASSIGN_OR_RETURN(auto public_key,
keypair::PublicKey::FromSubjectPublicKeyInfo(spki),
[] { return SignatureError::kInvalidPublicKey; });
// 3. Verify signature
switch (raw_sig.sig_alg) {
case TPM_ALG_RSASSA:
return VerifyRsaSignature(public_key, statement, hash_kind,
raw_sig.rsa_sig);
case TPM_ALG_ECDSA:
return VerifyEcdsaSignature(public_key, statement, hash_kind,
raw_sig.ecdsa_r, raw_sig.ecdsa_s);
default:
return base::unexpected(SignatureError::kUnsupportedSignatureAlgorithm);
}
}
} // namespace crypto::tpm