// 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 #include #include #include #include #include #include #include #include // See note in //zircon/third_party/ulib/uboringssl/rules.mk #define BORINGSSL_NO_CXX #include #include namespace digest { // The previously opaque crypto implementation context. struct Digest::Context { Context() {} ~Context() {} SHA256_CTX impl; }; Digest::Digest() : bytes_{0} {} Digest::Digest(const uint8_t* other) : bytes_{0} { *this = other; } Digest::~Digest() { ZX_DEBUG_ASSERT(ref_count_ == 0); } Digest::Digest(Digest&& o) { ZX_DEBUG_ASSERT(o.ref_count_ == 0); ctx_ = std::move(o.ctx_); memcpy(bytes_, o.bytes_, kLength); memset(o.bytes_, 0, kLength); } Digest& Digest::operator=(Digest&& o) { ZX_DEBUG_ASSERT(o.ref_count_ == 0); ZX_DEBUG_ASSERT(ref_count_ == 0); memcpy(bytes_, o.bytes_, kLength); return *this; } Digest& Digest::operator=(const uint8_t* rhs) { ZX_DEBUG_ASSERT(ref_count_ == 0); memcpy(bytes_, rhs, kLength); return *this; } zx_status_t Digest::Init() { ZX_DEBUG_ASSERT(ref_count_ == 0); fbl::AllocChecker ac; ctx_.reset(new (&ac) Context()); if (!ac.check()) { return ZX_ERR_NO_MEMORY; } SHA256_Init(&ctx_->impl); return ZX_OK; } void Digest::Update(const void* buf, size_t len) { ZX_DEBUG_ASSERT(ref_count_ == 0); ZX_DEBUG_ASSERT(len <= INT_MAX); ZX_DEBUG_ASSERT(ctx_ != nullptr); SHA256_Update(&ctx_->impl, buf, len); } const uint8_t* Digest::Final() { ZX_DEBUG_ASSERT(ref_count_ == 0); ZX_DEBUG_ASSERT(ctx_ != nullptr); SHA256_Final(bytes_, &ctx_->impl); 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) { ZX_DEBUG_ASSERT(ref_count_ == 0); if (len < sizeof(bytes_) * 2) { return ZX_ERR_INVALID_ARGS; } uint8_t c = 0; size_t i = 0; for (size_t j = 0; j < sizeof(bytes_) * 2; ++j) { c = static_cast(toupper(hex[j]) & 0xFF); if (!isxdigit(c)) { return ZX_ERR_INVALID_ARGS; } c = static_cast(c < 'A' ? c - '0' : c - '7'); // '7' = 'A' - 10 if (j % 2 == 0) { bytes_[i] = static_cast(c << 4); } else { bytes_[i++] |= c; } } return ZX_OK; } zx_status_t Digest::ToString(char* out, size_t len) const { if (len < sizeof(bytes_) * 2 + 1) { return ZX_ERR_BUFFER_TOO_SMALL; } memset(out, 0, len); char* p = out; for (size_t i = 0; i < sizeof(bytes_); ++i) { sprintf(p, "%02x", bytes_[i]); p += 2; } return ZX_OK; } zx_status_t Digest::CopyTo(uint8_t* out, size_t len) const { if (len < sizeof(bytes_)) { return ZX_ERR_BUFFER_TOO_SMALL; } memset(out, 0, len); memcpy(out, bytes_, sizeof(bytes_)); return ZX_OK; } const uint8_t* Digest::AcquireBytes() const { ZX_DEBUG_ASSERT(ref_count_ < SIZE_MAX); ++ref_count_; return bytes_; } void Digest::ReleaseBytes() const { ZX_DEBUG_ASSERT(ref_count_ > 0); --ref_count_; } bool Digest::operator==(const Digest& rhs) const { return memcmp(bytes_, rhs.bytes_, kLength) == 0; } bool Digest::operator!=(const Digest& rhs) const { return !(*this == rhs); } bool Digest::operator==(const uint8_t* rhs) const { return rhs ? memcmp(bytes_, rhs, kLength) == 0 : false; } bool Digest::operator!=(const uint8_t* rhs) const { return !(*this == rhs); } } // namespace digest using digest::Digest; // C-style wrapper functions struct digest_t { Digest obj; }; zx_status_t digest_init(digest_t** out) { fbl::AllocChecker ac; fbl::unique_ptr uptr(new (&ac) digest_t); if (!ac.check()) { return ZX_ERR_NO_MEMORY; } uptr->obj.Init(); *out = uptr.release(); return ZX_OK; } void digest_update(digest_t* digest, const void* buf, size_t len) { digest->obj.Update(buf, len); } zx_status_t digest_final(digest_t* digest, void* out, size_t out_len) { fbl::unique_ptr uptr(digest); uptr->obj.Final(); return uptr->obj.CopyTo(static_cast(out), out_len); } zx_status_t digest_hash(const void* buf, size_t len, void* out, size_t out_len) { Digest digest; digest.Hash(buf, len); return digest.CopyTo(static_cast(out), out_len); }