blob: 45eb99c829aad16daea916eaa760c548ff240a71 [file] [log] [blame]
// Copyright 2017 The Fuchsia 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 "src/ledger/bin/storage/impl/object_digest.h"
#include <bitset>
#include "src/ledger/bin/encryption/primitives/hash.h"
#include "src/ledger/bin/storage/impl/constants.h"
#include "src/lib/fxl/strings/concatenate.h"
namespace storage {
namespace {
static_assert(kStorageHashSize == encryption::kHashSize,
"Unexpected kStorageHashSize value");
// The first bit is 1 for inlined values and 0 otherwise.
constexpr size_t kInlineBit = 0;
// The second bit is 0 for CHUNK and 1 for INDEX.
constexpr size_t kTypeBit = 1;
// The third bit is 1 for a tree node and 0 otherwise.
constexpr char kTreeNodeBit = 2;
// Builds an object digest by concatenating |prefix| and |data|.
ObjectDigest BuildDigest(std::bitset<8> prefix,
convert::ExtendedStringView data) {
std::string result;
result.reserve(data.size() + 1);
result.push_back(prefix.to_ulong());
result.append(data.data(), data.size());
return ObjectDigest(std::move(result));
}
} // namespace
bool IsDigestValid(convert::ExtendedStringView object_digest) {
// All object digests should have a prefix.
if (object_digest.size() == 0) {
FXL_LOG(INFO) << "Invalid object digest: empty.";
return false;
}
std::bitset<8> prefix(object_digest[0]);
// Check inline bit.
if (prefix[kInlineBit] && object_digest.size() > kStorageHashSize + 1) {
FXL_LOG(INFO) << "Invalid object digest: inline but size="
<< object_digest.size()
<< "; digest=" << convert::ToHex(object_digest);
return false;
}
if (!prefix[kInlineBit] && object_digest.size() != kStorageHashSize + 1) {
FXL_LOG(INFO) << "Invalid object digest: not inline but size="
<< object_digest.size()
<< "; digest=" << convert::ToHex(object_digest);
return false;
}
// All bits must be zero except the ones we use for ObjectDigestInfo.
prefix.reset(kInlineBit);
prefix.reset(kTypeBit);
prefix.reset(kTreeNodeBit);
return prefix.none();
}
bool IsDigestValid(const ObjectDigest& object_digest) {
return object_digest.IsValid() && IsDigestValid(object_digest.Serialize());
}
ObjectDigestInfo GetObjectDigestInfo(const ObjectDigest& object_digest) {
FXL_DCHECK(IsDigestValid(object_digest));
const std::string& digest = object_digest.Serialize();
std::bitset<8> prefix(digest[0]);
ObjectDigestInfo result;
result.object_type =
prefix[kTreeNodeBit] ? ObjectType::TREE_NODE : ObjectType::BLOB;
result.piece_type = prefix[kTypeBit] ? PieceType::INDEX : PieceType::CHUNK;
result.inlined = prefix[kInlineBit] ? InlinedPiece::YES : InlinedPiece::NO;
return result;
}
fxl::StringView ExtractObjectDigestData(const ObjectDigest& object_digest) {
FXL_DCHECK(IsDigestValid(object_digest));
fxl::StringView digest = object_digest.Serialize();
return digest.substr(1);
}
ObjectDigest ComputeObjectDigest(PieceType piece_type, ObjectType object_type,
convert::ExtendedStringView content) {
std::bitset<8> prefix;
prefix[kTypeBit] = piece_type == PieceType::INDEX;
prefix[kTreeNodeBit] = object_type == ObjectType::TREE_NODE;
if (content.size() <= kStorageHashSize) {
prefix[kInlineBit] = true;
return BuildDigest(prefix, content);
}
return BuildDigest(prefix, encryption::SHA256WithLengthHash(content));
}
} // namespace storage