blob: 7434bf259ed6a5bb3cb2b6ba30b2bc4573a61af6 [file] [log] [blame]
// Copyright 2013 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cryptohome/lockbox.h"
#include <arpa/inet.h>
#include <limits.h>
#include <openssl/sha.h>
#include <stdint.h>
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
#include <base/check.h>
#include <base/check_op.h>
#include <base/files/file_path.h>
#include <base/logging.h>
#include <base/notreached.h>
#include <base/strings/string_split.h>
#include <base/sys_byteorder.h>
#include <base/threading/platform_thread.h>
#include <base/time/time.h>
#include <brillo/secure_blob.h>
#include <libhwsec/status.h>
#include <libhwsec-foundation/crypto/secure_blob_util.h>
#include <libhwsec-foundation/crypto/sha.h>
#include <libstorage/platform/platform.h>
using base::FilePath;
using brillo::SecureBlob;
using hwsec::TPMErrorBase;
using hwsec_foundation::CreateSecureRandomBlob;
using hwsec_foundation::SecureBlobToHex;
using hwsec_foundation::Sha256;
namespace cryptohome {
std::ostream& operator<<(std::ostream& out, LockboxError error) {
return out << static_cast<int>(error);
}
Lockbox::Lockbox(const hwsec::CryptohomeFrontend* hwsec, hwsec::Space space)
: hwsec_(hwsec), space_(space) {
CHECK(hwsec_);
}
Lockbox::~Lockbox() {}
bool Lockbox::Reset(LockboxError* error) {
uint32_t nvram_bytes = LockboxContents::kNvramSize;
if (hwsec::Status status = hwsec_->PrepareSpace(space_, nvram_bytes);
!status.ok()) {
LOG(ERROR) << "Failed to prepare lockbox space: " << status;
*error = LockboxError::kTpmError;
return false;
}
return true;
}
bool Lockbox::Store(const brillo::Blob& blob, LockboxError* error) {
// Construct a LockboxContents instance.
std::unique_ptr<LockboxContents> contents = LockboxContents::New();
// Create the random key material.
brillo::SecureBlob key_material =
CreateSecureRandomBlob(contents->key_material_size());
brillo::SecureBlob nvram_blob;
if (!contents->SetKeyMaterial(key_material) || !contents->Protect(blob) ||
!contents->Encode(&nvram_blob)) {
LOG(ERROR) << "Failed to set up lockbox contents.";
*error = LockboxError::kNvramInvalid;
return false;
}
// Write the hash to nvram
if (hwsec::Status status = hwsec_->StoreSpace(
space_, brillo::Blob(nvram_blob.begin(), nvram_blob.end()));
!status.ok()) {
LOG(ERROR) << "Failed to write lockbox space: " << status;
*error = LockboxError::kTpmError;
return false;
}
return true;
}
// static
std::unique_ptr<LockboxContents> LockboxContents::New() {
std::unique_ptr<LockboxContents> result(new LockboxContents());
result->key_material_.resize(kKeyMaterialSize);
return result;
}
bool LockboxContents::Decode(const brillo::SecureBlob& nvram_data) {
// Reject data of incorrect size.
if (nvram_data.size() != kNvramSize) {
return false;
}
brillo::SecureBlob::const_iterator cursor = nvram_data.begin();
// Extract the expected data size from the NVRAM. For historic reasons, this
// is encoded in reverse host byte order (!).
uint32_t reversed_size;
uint8_t* reversed_size_ptr = reinterpret_cast<uint8_t*>(&reversed_size);
std::copy(cursor, cursor + sizeof(reversed_size), reversed_size_ptr);
cursor += sizeof(reversed_size);
size_ = base::ByteSwap(reversed_size);
// Grab the flags.
flags_ = *cursor++;
// Grab the key material.
key_material_.assign(cursor, cursor + key_material_size());
cursor += key_material_size();
// Grab the hash.
std::copy(cursor, cursor + sizeof(hash_), hash_);
cursor += sizeof(hash_);
// Per the checks at function entry we should have exactly reached the end.
CHECK(cursor == nvram_data.end());
return true;
}
bool LockboxContents::Encode(brillo::SecureBlob* blob) {
// Encode the data size. For historic reasons, this is encoded in reverse host
// byte order (!).
uint32_t reversed_size = base::ByteSwap(size_);
uint8_t* reversed_size_ptr = reinterpret_cast<uint8_t*>(&reversed_size);
blob->insert(blob->end(), reversed_size_ptr,
reversed_size_ptr + sizeof(reversed_size));
// Append the flags byte.
blob->push_back(flags_);
// Append the key material.
blob->insert(blob->end(), std::begin(key_material_), std::end(key_material_));
// Append the hash.
blob->insert(blob->end(), std::begin(hash_), std::end(hash_));
return true;
}
bool LockboxContents::SetKeyMaterial(const brillo::SecureBlob& key_material) {
if (key_material.size() != key_material_size()) {
return false;
}
key_material_ = key_material;
return true;
}
bool LockboxContents::Protect(const brillo::Blob& blob) {
brillo::SecureBlob salty_blob(blob);
salty_blob.insert(salty_blob.end(), key_material_.begin(),
key_material_.end());
SecureBlob salty_blob_hash = Sha256(salty_blob);
CHECK_EQ(sizeof(hash_), salty_blob_hash.size());
std::copy(salty_blob_hash.begin(), salty_blob_hash.end(), std::begin(hash_));
size_ = blob.size();
return true;
}
LockboxContents::VerificationResult LockboxContents::Verify(
const brillo::Blob& blob) {
// Make sure that the file size matches what was stored in nvram.
if (blob.size() != size_) {
LOG(ERROR) << "Verify() expected " << size_ << " , but received "
<< blob.size() << " bytes.";
return VerificationResult::kSizeMismatch;
}
// Compute the hash of the blob to verify.
brillo::SecureBlob salty_blob(blob);
salty_blob.insert(salty_blob.end(), key_material_.begin(),
key_material_.end());
SecureBlob salty_blob_hash = Sha256(salty_blob);
// Validate the blob hash versus the stored hash.
if (sizeof(hash_) != salty_blob_hash.size() ||
brillo::SecureMemcmp(hash_, salty_blob_hash.data(), sizeof(hash_))) {
LOG(ERROR) << "Verify() hash mismatch!";
return VerificationResult::kHashMismatch;
}
return VerificationResult::kValid;
}
} // namespace cryptohome