| // Copyright 2019 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/lib/digest/hash-list.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <zircon/assert.h> |
| #include <zircon/errors.h> |
| #include <zircon/types.h> |
| |
| #include <algorithm> |
| |
| #include <fbl/algorithm.h> |
| |
| #include "src/lib/digest/digest.h" |
| #include "src/lib/digest/node-digest.h" |
| |
| namespace digest { |
| namespace internal { |
| |
| // HashListBase<T> |
| |
| zx_status_t HashListBase::Align(size_t *data_off, size_t *buf_len) const { |
| size_t buf_end; |
| if (add_overflow(*data_off, *buf_len, &buf_end) || buf_end > data_len_) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| *data_off = node_digest_.PrevAligned(*data_off); |
| *buf_len = std::min(node_digest_.NextAligned(buf_end), data_len_) - *data_off; |
| return ZX_OK; |
| } |
| |
| zx_status_t HashListBase::SetDataLength(size_t data_len) { |
| if (data_len > node_digest_.MaxAligned()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| data_off_ = 0; |
| data_len_ = data_len; |
| list_off_ = 0; |
| list_len_ = 0; |
| if (data_len_ == 0) { |
| return node_digest_.Reset(data_off_, data_len_); |
| } |
| return ZX_OK; |
| } |
| |
| size_t HashListBase::GetListOffset(size_t data_off) const { |
| return node_digest_.ToNode(data_off) * GetDigestSize(); |
| } |
| |
| size_t HashListBase::GetListLength() const { |
| return std::max(GetListOffset(node_digest_.NextAligned(data_len_)), GetDigestSize()); |
| } |
| |
| bool HashListBase::IsValidRange(size_t data_off, size_t buf_len) { |
| size_t buf_end; |
| return !add_overflow(data_off, buf_len, &buf_end) && buf_end <= data_len_; |
| } |
| |
| zx_status_t HashListBase::ProcessData(const uint8_t *buf, size_t buf_len, size_t data_off) { |
| if (list_len_ == 0) { |
| return ZX_ERR_BAD_STATE; |
| } |
| if (!IsValidRange(data_off, buf_len)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| zx_status_t rc; |
| data_off_ = data_off; |
| list_off_ = GetListOffset(data_off); |
| size_t data_len_with_padding = |
| pad_data_to_node_size_ ? fbl::round_up(data_len_, GetNodeSize()) : data_len_; |
| while (buf_len != 0) { |
| if (node_digest_.IsAligned(data_off_) && |
| (rc = node_digest_.Reset(data_off_, data_len_with_padding)) != ZX_OK) { |
| return rc; |
| } |
| size_t chunk = node_digest_.Append(buf, buf_len); |
| buf += chunk; |
| buf_len -= chunk; |
| data_off_ += chunk; |
| bool at_end_of_data = data_off_ == data_len_; |
| if (at_end_of_data && pad_data_to_node_size_) { |
| node_digest_.PadWithZeros(); |
| } |
| if (node_digest_.IsAligned(data_off_) || at_end_of_data) { |
| HandleOne(); |
| } |
| } |
| return ZX_OK; |
| } |
| |
| void HashListBase::HandleOne() { |
| HandleOne(node_digest_.get()); |
| list_off_ += GetDigestSize(); |
| } |
| |
| // HashList<T> |
| |
| template <typename T> |
| zx_status_t HashList<T>::SetList(T *list, size_t list_len) { |
| if (list == nullptr) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (list_len < GetListLength()) { |
| return ZX_ERR_BUFFER_TOO_SMALL; |
| } |
| // Reset the state of the list. |
| SetDataLength(data_len()); |
| list_ = list; |
| set_list_len(list_len); |
| if (data_len() == 0) { |
| HandleOne(); |
| } |
| return ZX_OK; |
| } |
| |
| } // namespace internal |
| |
| // HashListCreator |
| |
| // Forward template declaration |
| template zx_status_t internal::HashList<uint8_t>::SetList(uint8_t *list, size_t list_len); |
| |
| zx_status_t HashListCreator::Append(const void *buf, size_t buf_len) { |
| return this->ProcessData(static_cast<const uint8_t *>(buf), buf_len, this->data_off()); |
| } |
| |
| void HashListCreator::HandleOne(const Digest &digest) { |
| digest.CopyTo(list() + list_off(), GetDigestSize()); |
| } |
| |
| // HashListVerifier |
| |
| // Forward template declaration |
| template zx_status_t internal::HashList<const uint8_t>::SetList(const uint8_t *list, |
| size_t list_len); |
| |
| zx_status_t HashListVerifier::Verify(const void *buf, size_t buf_len, size_t data_off) { |
| zx_status_t rc; |
| verified_ = true; |
| if (data_len() == 0) { |
| rc = SetList(list(), list_len()); |
| } else { |
| rc = this->ProcessData(static_cast<const uint8_t *>(buf), buf_len, data_off); |
| } |
| if (rc != ZX_OK) { |
| return rc; |
| } |
| return verified_ ? ZX_OK : ZX_ERR_IO_DATA_INTEGRITY; |
| } |
| |
| bool HashListVerifier::IsValidRange(size_t data_off, size_t buf_len) { |
| size_t buf_end; |
| if (data_off == data_len() && buf_len == 0) { |
| return true; |
| } else if (!IsAligned(data_off) || add_overflow(data_off, buf_len, &buf_end)) { |
| return false; |
| } else if (buf_end < data_len()) { |
| return IsAligned(buf_end); |
| } else { |
| return buf_end == data_len(); |
| } |
| } |
| |
| void HashListVerifier::HandleOne(const Digest &digest) { |
| verified_ &= (digest.Equals(list() + list_off(), GetDigestSize())); |
| } |
| |
| size_t CalculateHashListSize(size_t data_size, size_t node_size) { |
| NodeDigest node_digest; |
| ZX_ASSERT_MSG(node_digest.SetNodeSize(node_size) == ZX_OK, "node_size=%lu", node_size); |
| size_t digest_size = node_digest.len(); |
| return std::max(node_digest.ToNode(node_digest.NextAligned(data_size)) * digest_size, |
| digest_size); |
| } |
| |
| } // namespace digest |