blob: bc688de2967d1c3cf513a752a47163977c85bef8 [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 <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <zircon/assert.h>
#include <zircon/errors.h>
#include <iomanip>
#include <iostream>
#include <memory>
#include <utility>
#include <fbl/alloc_checker.h>
#include <fbl/string.h>
// See note in //zircon/kernel/lib/crypto/boringssl/BUILD.gn
#define BORINGSSL_NO_CXX
#include <openssl/crypto.h>
#include <openssl/mem.h>
#include <openssl/sha.h>
#include "src/lib/digest/digest.h"
namespace digest {
// The previously opaque crypto implementation context.
struct Digest::Context {
SHA256_CTX impl;
};
Digest::Digest() : bytes_{0} {}
Digest::Digest(const uint8_t (&bytes)[sizeof(bytes_)]) { *this = bytes; }
Digest::Digest(Digest&& other) { *this = std::move(other); }
Digest::~Digest() {}
Digest& Digest::operator=(const uint8_t (&bytes)[sizeof(bytes_)]) {
ctx_.reset();
memcpy(bytes_, bytes, sizeof(bytes_));
return *this;
}
Digest& Digest::operator=(Digest&& other) {
ctx_ = std::move(other.ctx_);
memcpy(bytes_, other.bytes_, sizeof(bytes_));
memset(other.bytes_, 0, sizeof(other.bytes_));
return *this;
}
Digest::Digest(const Digest& other) {
ZX_DEBUG_ASSERT(!other.ctx_); // Can only copy finalized digests.
memcpy(bytes_, other.bytes_, sizeof(bytes_));
}
Digest& Digest::operator=(const Digest& other) {
ZX_DEBUG_ASSERT(!other.ctx_); // Can only copy finalized digests.
memcpy(bytes_, other.bytes_, sizeof(bytes_));
return *this;
}
void Digest::Init() {
CRYPTO_library_init();
ctx_.reset(new Context());
SHA256_Init(&ctx_->impl);
}
void Digest::Update(const void* buf, size_t len) {
ZX_DEBUG_ASSERT(ctx_);
ZX_DEBUG_ASSERT(len <= INT_MAX);
SHA256_Update(&ctx_->impl, buf, len);
}
const uint8_t* Digest::Final() {
ZX_DEBUG_ASSERT(ctx_);
SHA256_Final(bytes_, &ctx_->impl);
ctx_.reset();
return bytes_;
}
const uint8_t* Digest::Hash(const void* buf, size_t len) {
Init();
Update(buf, len);
return Final();
}
zx_status_t Digest::Parse(const char* hex, size_t len) {
if (len != sizeof(bytes_) * 2) {
return ZX_ERR_INVALID_ARGS;
}
size_t i = 0;
for (size_t j = 0; j < sizeof(bytes_) * 2; ++j) {
int c = toupper(hex[j]);
if (!isxdigit(c)) {
return ZX_ERR_INVALID_ARGS;
}
c = c < 'A' ? c - '0' : c - '7'; // '7' = 'A' - 10
if (j % 2 == 0) {
bytes_[i] = static_cast<uint8_t>(c << 4);
} else {
bytes_[i++] |= static_cast<uint8_t>(c);
}
}
return ZX_OK;
}
fbl::String Digest::ToString() const {
char hex[kSha256HexLength + 1];
char* p = hex;
static_assert(sizeof(bytes_) * 2 < sizeof(hex));
for (size_t i = 0; i < sizeof(bytes_); ++i) {
snprintf(p, sizeof(hex) - i * 2, "%02x", bytes_[i]);
p += 2;
}
return fbl::String(hex);
}
void Digest::CopyTo(uint8_t* out, size_t len) const {
ZX_DEBUG_ASSERT(len >= sizeof(bytes_));
CopyTruncatedTo(out, len);
}
void Digest::CopyTruncatedTo(uint8_t* out, size_t len) const {
if (len == 0) {
return;
} else if (len <= sizeof(bytes_)) {
memcpy(out, bytes_, len);
} else {
memcpy(out, bytes_, sizeof(bytes_));
out += sizeof(bytes_);
len -= sizeof(bytes_);
memset(out, 0, len);
}
}
bool Digest::Equals(const uint8_t* rhs, size_t len) const {
return rhs && len == sizeof(bytes_) && CRYPTO_memcmp(bytes_, rhs, sizeof(bytes_)) == 0;
}
std::ostream& operator<<(std::ostream& stream, const Digest& digest) {
std::ios::fmtflags f(stream.flags());
for (uint8_t byte : digest.bytes_) {
stream << std::setfill('0') << std::hex << std::setw(2) << int{byte};
}
stream.flags(f);
return stream;
}
} // namespace digest