blob: 63600fcf7276b3566ae5a9c10528652432b9ee14 [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 <stddef.h>
#include <stdint.h>
#include <string.h>
#include <crypto/bytes.h>
#include <explicit-memory/bytes.h>
#include <fbl/algorithm.h>
#include <fbl/macros.h>
#include <fbl/unique_ptr.h>
#include <fdio/debug.h>
#include <openssl/mem.h>
#include <zircon/errors.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#define MXDEBUG 0
namespace crypto {
// Public methods
Bytes::Bytes() : buf_(nullptr), len_(0) {}
Bytes::~Bytes() {
Reset();
}
void Bytes::Adopt(fbl::unique_ptr<uint8_t[]> buf, size_t len) {
Reset();
buf_ = fbl::move(buf);
len_ = len;
}
zx_status_t Bytes::Init(size_t size, uint8_t fill) {
Reset();
return Resize(size, fill);
}
zx_status_t Bytes::Resize(size_t size, uint8_t fill) {
// Early exit if truncating to zero.
if (size == 0) {
Reset();
return ZX_OK;
}
// Allocate new memory
fbl::AllocChecker ac;
fbl::unique_ptr<uint8_t[]> tmp(new (&ac) uint8_t[size]);
if (!ac.check()) {
xprintf("%s: allocation failed: %zu bytes\n", __PRETTY_FUNCTION__, size);
return ZX_ERR_NO_MEMORY;
}
// Fill it with old data and pad as needed
if (len_ == 0) {
memset(tmp.get(), fill, size);
} else if (len_ < size) {
memcpy(tmp.get(), buf_.get(), len_);
memset(tmp.get() + len_, fill, size - len_);
} else {
memcpy(tmp.get(), buf_.get(), size);
}
Reset();
len_ = size;
buf_ = fbl::move(tmp);
return ZX_OK;
}
zx_status_t Bytes::Copy(const void* buf, size_t len, zx_off_t off) {
zx_status_t rc;
if (len == 0) {
return ZX_OK;
}
if (!buf) {
xprintf("%s: null buffer\n", __PRETTY_FUNCTION__);
return ZX_ERR_INVALID_ARGS;
}
size_t size = off + len;
ZX_ASSERT(size >= len); // overflow
if (len_ < size && (rc = Resize(size)) != ZX_OK) {
return rc;
}
memcpy(buf_.get() + off, buf, len);
return ZX_OK;
}
zx_status_t Bytes::Randomize(size_t size) {
zx_status_t rc;
if ((rc = Resize(size)) != ZX_OK) {
return rc;
}
uint8_t* p = buf_.get();
size_t actual;
while (size != 0) {
size_t n = fbl::min(size, static_cast<size_t>(ZX_CPRNG_DRAW_MAX_LEN));
if ((rc = zx_cprng_draw(p, n, &actual)) != ZX_OK) {
xprintf("%s: zx_cprng_draw(%p, %zu, %p) failed: %s", __PRETTY_FUNCTION__, p, n, &actual,
zx_status_get_string(rc));
return rc;
}
p += actual;
size -= actual;
}
return ZX_OK;
}
zx_status_t Bytes::Increment() {
bool overflow = true;
size_t i = len_;
// This is intentionally branchless to be as close to constant time as possible. Although
// unlikely, it's conceivable that differences in timing on incrementing leak information about
// the contents.
while(i != 0) {
--i;
uint8_t n = overflow ? 1 : 0;
buf_[i] = static_cast<uint8_t>(buf_[i] + n);
overflow &= (buf_[i] == 0);
}
return overflow ? ZX_ERR_OUT_OF_RANGE : ZX_OK;
}
fbl::unique_ptr<uint8_t[]> Bytes::Release(size_t* len) {
if (len) {
*len = len_;
}
len_ = 0;
return fbl::move(buf_);
}
void Bytes::Reset() {
if (buf_) {
mandatory_memset(buf_.get(), 0, len_);
}
buf_.reset();
len_ = 0;
}
const uint8_t& Bytes::operator[](zx_off_t off) const {
ZX_ASSERT(off < len_);
return buf_[off];
}
uint8_t& Bytes::operator[](zx_off_t off) {
ZX_ASSERT(off < len_);
return buf_[off];
}
bool Bytes::operator==(const Bytes& other) const {
if (len_ != other.len_) {
return false;
} else if (len_ == 0) {
return true;
} else {
ZX_DEBUG_ASSERT(buf_.get());
ZX_DEBUG_ASSERT(other.buf_.get());
return CRYPTO_memcmp(buf_.get(), other.buf_.get(), len_) == 0;
}
}
} // namespace crypto