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 <zircon/assert.h>
15 #include <zircon/errors.h>
16 
17 // See note in //zircon/third_party/ulib/uboringssl/rules.mk
18 #define BORINGSSL_NO_CXX
19 #include <openssl/sha.h>
20 
21 #include <utility>
22 
23 namespace digest {
24 
25 // The previously opaque crypto implementation context.
26 struct Digest::Context {
Contextdigest::Digest::Context27     Context() {}
~Contextdigest::Digest::Context28     ~Context() {}
29     SHA256_CTX impl;
30 };
31 
Digest()32 Digest::Digest() : bytes_{0} {}
33 
Digest(const uint8_t * other)34 Digest::Digest(const uint8_t* other) : bytes_{0} {
35     *this = other;
36 }
37 
~Digest()38 Digest::~Digest() {
39     ZX_DEBUG_ASSERT(ref_count_ == 0);
40 }
41 
Digest(Digest && o)42 Digest::Digest(Digest&& o) {
43     ZX_DEBUG_ASSERT(o.ref_count_ == 0);
44     ctx_ = std::move(o.ctx_);
45     memcpy(bytes_, o.bytes_, kLength);
46     memset(o.bytes_, 0, kLength);
47 }
48 
operator =(Digest && o)49 Digest& Digest::operator=(Digest&& o) {
50     ZX_DEBUG_ASSERT(o.ref_count_ == 0);
51     ZX_DEBUG_ASSERT(ref_count_ == 0);
52     memcpy(bytes_, o.bytes_, kLength);
53     return *this;
54 }
55 
operator =(const uint8_t * rhs)56 Digest& Digest::operator=(const uint8_t* rhs) {
57     ZX_DEBUG_ASSERT(ref_count_ == 0);
58     memcpy(bytes_, rhs, kLength);
59     return *this;
60 }
61 
Init()62 zx_status_t Digest::Init() {
63     ZX_DEBUG_ASSERT(ref_count_ == 0);
64     fbl::AllocChecker ac;
65     ctx_.reset(new (&ac) Context());
66     if (!ac.check()) {
67         return ZX_ERR_NO_MEMORY;
68     }
69     SHA256_Init(&ctx_->impl);
70     return ZX_OK;
71 }
72 
Update(const void * buf,size_t len)73 void Digest::Update(const void* buf, size_t len) {
74     ZX_DEBUG_ASSERT(ref_count_ == 0);
75     ZX_DEBUG_ASSERT(len <= INT_MAX);
76     ZX_DEBUG_ASSERT(ctx_ != nullptr);
77     SHA256_Update(&ctx_->impl, buf, len);
78 }
79 
Final()80 const uint8_t* Digest::Final() {
81     ZX_DEBUG_ASSERT(ref_count_ == 0);
82     ZX_DEBUG_ASSERT(ctx_ != nullptr);
83     SHA256_Final(bytes_, &ctx_->impl);
84     return bytes_;
85 }
86 
Hash(const void * buf,size_t len)87 const uint8_t* Digest::Hash(const void* buf, size_t len) {
88     Init();
89     Update(buf, len);
90     return Final();
91 }
92 
Parse(const char * hex,size_t len)93 zx_status_t Digest::Parse(const char* hex, size_t len) {
94     ZX_DEBUG_ASSERT(ref_count_ == 0);
95     if (len < sizeof(bytes_) * 2) {
96         return ZX_ERR_INVALID_ARGS;
97     }
98     uint8_t c = 0;
99     size_t i = 0;
100     for (size_t j = 0; j < sizeof(bytes_) * 2; ++j) {
101         c = static_cast<uint8_t>(toupper(hex[j]) & 0xFF);
102         if (!isxdigit(c)) {
103             return ZX_ERR_INVALID_ARGS;
104         }
105         c = static_cast<uint8_t>(c < 'A' ? c - '0' : c - '7'); // '7' = 'A' - 10
106         if (j % 2 == 0) {
107             bytes_[i] = static_cast<uint8_t>(c << 4);
108         } else {
109             bytes_[i++] |= c;
110         }
111     }
112     return ZX_OK;
113 }
114 
ToString(char * out,size_t len) const115 zx_status_t Digest::ToString(char* out, size_t len) const {
116     if (len < sizeof(bytes_) * 2 + 1) {
117         return ZX_ERR_BUFFER_TOO_SMALL;
118     }
119     memset(out, 0, len);
120     char* p = out;
121     for (size_t i = 0; i < sizeof(bytes_); ++i) {
122         sprintf(p, "%02x", bytes_[i]);
123         p += 2;
124     }
125     return ZX_OK;
126 }
127 
CopyTo(uint8_t * out,size_t len) const128 zx_status_t Digest::CopyTo(uint8_t* out, size_t len) const {
129     if (len < sizeof(bytes_)) {
130         return ZX_ERR_BUFFER_TOO_SMALL;
131     }
132     memset(out, 0, len);
133     memcpy(out, bytes_, sizeof(bytes_));
134     return ZX_OK;
135 }
136 
AcquireBytes() const137 const uint8_t* Digest::AcquireBytes() const {
138     ZX_DEBUG_ASSERT(ref_count_ < SIZE_MAX);
139     ++ref_count_;
140     return bytes_;
141 }
142 
ReleaseBytes() const143 void Digest::ReleaseBytes() const {
144     ZX_DEBUG_ASSERT(ref_count_ > 0);
145     --ref_count_;
146 }
147 
operator ==(const Digest & rhs) const148 bool Digest::operator==(const Digest& rhs) const {
149     return memcmp(bytes_, rhs.bytes_, kLength) == 0;
150 }
151 
operator !=(const Digest & rhs) const152 bool Digest::operator!=(const Digest& rhs) const {
153     return !(*this == rhs);
154 }
155 
operator ==(const uint8_t * rhs) const156 bool Digest::operator==(const uint8_t* rhs) const {
157     return rhs ? memcmp(bytes_, rhs, kLength) == 0 : false;
158 }
159 
operator !=(const uint8_t * rhs) const160 bool Digest::operator!=(const uint8_t* rhs) const {
161     return !(*this == rhs);
162 }
163 
164 } // namespace digest
165 
166 using digest::Digest;
167 
168 // C-style wrapper functions
169 struct digest_t {
170     Digest obj;
171 };
172 
digest_init(digest_t ** out)173 zx_status_t digest_init(digest_t** out) {
174     fbl::AllocChecker ac;
175     fbl::unique_ptr<digest_t> uptr(new (&ac) digest_t);
176     if (!ac.check()) {
177         return ZX_ERR_NO_MEMORY;
178     }
179     uptr->obj.Init();
180     *out = uptr.release();
181     return ZX_OK;
182 }
183 
digest_update(digest_t * digest,const void * buf,size_t len)184 void digest_update(digest_t* digest, const void* buf, size_t len) {
185     digest->obj.Update(buf, len);
186 }
187 
digest_final(digest_t * digest,void * out,size_t out_len)188 zx_status_t digest_final(digest_t* digest, void* out, size_t out_len) {
189     fbl::unique_ptr<digest_t> uptr(digest);
190     uptr->obj.Final();
191     return uptr->obj.CopyTo(static_cast<uint8_t*>(out), out_len);
192 }
193 
digest_hash(const void * buf,size_t len,void * out,size_t out_len)194 zx_status_t digest_hash(const void* buf, size_t len, void* out, size_t out_len) {
195     Digest digest;
196     digest.Hash(buf, len);
197     return digest.CopyTo(static_cast<uint8_t*>(out), out_len);
198 }
199