1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <digest/digest.h>
6
7#include <ctype.h>
8#include <limits.h>
9#include <stdio.h>
10#include <string.h>
11
12#include <fbl/alloc_checker.h>
13#include <fbl/unique_ptr.h>
14#include <openssl/sha.h>
15#include <zircon/assert.h>
16#include <zircon/errors.h>
17
18namespace digest {
19
20// The previously opaque crypto implementation context.
21struct Digest::Context {
22    Context() {}
23    ~Context() {}
24    SHA256_CTX impl;
25};
26
27Digest::Digest() : ctx_{nullptr}, bytes_{0}, ref_count_(0) {}
28
29Digest::Digest(const uint8_t* other) : ctx_{nullptr}, bytes_{0}, ref_count_(0) {
30    *this = other;
31}
32
33Digest::~Digest() {
34    ZX_DEBUG_ASSERT(ref_count_ == 0);
35}
36
37Digest::Digest(Digest&& o) {
38    ZX_DEBUG_ASSERT(o.ref_count_ == 0);
39    memcpy(bytes_, o.bytes_, kLength);
40}
41
42Digest& Digest::operator=(Digest&& o) {
43    ZX_DEBUG_ASSERT(o.ref_count_ == 0);
44    ZX_DEBUG_ASSERT(ref_count_ == 0);
45    memcpy(bytes_, o.bytes_, kLength);
46    return *this;
47}
48
49Digest& Digest::operator=(const uint8_t* rhs) {
50    ZX_DEBUG_ASSERT(ref_count_ == 0);
51    memcpy(bytes_, rhs, kLength);
52    return *this;
53}
54
55zx_status_t Digest::Init() {
56    ZX_DEBUG_ASSERT(ref_count_ == 0);
57    fbl::AllocChecker ac;
58    ctx_.reset(new (&ac) Context());
59    if (!ac.check()) {
60        return ZX_ERR_NO_MEMORY;
61    }
62    SHA256_Init(&ctx_->impl);
63    return ZX_OK;
64}
65
66void Digest::Update(const void* buf, size_t len) {
67    ZX_DEBUG_ASSERT(ref_count_ == 0);
68    ZX_DEBUG_ASSERT(len <= INT_MAX);
69    SHA256_Update(&ctx_->impl, buf, len);
70}
71
72const uint8_t* Digest::Final() {
73    ZX_DEBUG_ASSERT(ref_count_ == 0);
74    SHA256_Final(bytes_, &ctx_->impl);
75    return bytes_;
76}
77
78const uint8_t* Digest::Hash(const void* buf, size_t len) {
79    Init();
80    Update(buf, len);
81    return Final();
82}
83
84zx_status_t Digest::Parse(const char* hex, size_t len) {
85    ZX_DEBUG_ASSERT(ref_count_ == 0);
86    if (len < sizeof(bytes_) * 2) {
87        return ZX_ERR_INVALID_ARGS;
88    }
89    uint8_t c = 0;
90    size_t i = 0;
91    for (size_t j = 0; j < sizeof(bytes_) * 2; ++j) {
92        c = static_cast<uint8_t>(toupper(hex[j]) & 0xFF);
93        if (!isxdigit(c)) {
94            return ZX_ERR_INVALID_ARGS;
95        }
96        c = static_cast<uint8_t>(c < 'A' ? c - '0' : c - '7'); // '7' = 'A' - 10
97        if (j % 2 == 0) {
98            bytes_[i] = static_cast<uint8_t>(c << 4);
99        } else {
100            bytes_[i++] |= c;
101        }
102    }
103    return ZX_OK;
104}
105
106zx_status_t Digest::ToString(char* out, size_t len) const {
107    if (len < sizeof(bytes_) * 2 + 1) {
108        return ZX_ERR_BUFFER_TOO_SMALL;
109    }
110    memset(out, 0, len);
111    char* p = out;
112    for (size_t i = 0; i < sizeof(bytes_); ++i) {
113        sprintf(p, "%02x", bytes_[i]);
114        p += 2;
115    }
116    return ZX_OK;
117}
118
119zx_status_t Digest::CopyTo(uint8_t* out, size_t len) const {
120    if (len < sizeof(bytes_)) {
121        return ZX_ERR_BUFFER_TOO_SMALL;
122    }
123    memset(out, 0, len);
124    memcpy(out, bytes_, sizeof(bytes_));
125    return ZX_OK;
126}
127
128const uint8_t* Digest::AcquireBytes() const {
129    ZX_DEBUG_ASSERT(ref_count_ < SIZE_MAX);
130    ++ref_count_;
131    return bytes_;
132}
133
134void Digest::ReleaseBytes() const {
135    ZX_DEBUG_ASSERT(ref_count_ > 0);
136    --ref_count_;
137}
138
139bool Digest::operator==(const Digest& rhs) const {
140    return memcmp(bytes_, rhs.bytes_, kLength) == 0;
141}
142
143bool Digest::operator!=(const Digest& rhs) const {
144    return !(*this == rhs);
145}
146
147bool Digest::operator==(const uint8_t* rhs) const {
148    return rhs ? memcmp(bytes_, rhs, kLength) == 0 : false;
149}
150
151bool Digest::operator!=(const uint8_t* rhs) const {
152    return !(*this == rhs);
153}
154
155} // namespace digest
156
157using digest::Digest;
158
159// C-style wrapper functions
160struct digest_t {
161    Digest obj;
162};
163
164zx_status_t digest_init(digest_t** out) {
165    fbl::AllocChecker ac;
166    fbl::unique_ptr<digest_t> uptr(new (&ac) digest_t);
167    if (!ac.check()) {
168        return ZX_ERR_NO_MEMORY;
169    }
170    uptr->obj.Init();
171    *out = uptr.release();
172    return ZX_OK;
173}
174
175void digest_update(digest_t* digest, const void* buf, size_t len) {
176    digest->obj.Update(buf, len);
177}
178
179zx_status_t digest_final(digest_t* digest, void* out, size_t out_len) {
180    fbl::unique_ptr<digest_t> uptr(digest);
181    uptr->obj.Final();
182    return uptr->obj.CopyTo(static_cast<uint8_t*>(out), out_len);
183}
184
185zx_status_t digest_hash(const void* buf, size_t len, void* out, size_t out_len) {
186    Digest digest;
187    digest.Hash(buf, len);
188    return digest.CopyTo(static_cast<uint8_t*>(out), out_len);
189}
190