Skip to content

Commit 300d6ec

Browse files
committed
SHA256 support (SHA1 --> Digest<SHA,Size>)
It would be easy to add SHA384 and/or SHA512 too. Or other digest types if we add crypto code implementing them.
1 parent dbd6602 commit 300d6ec

File tree

2 files changed

+132
-54
lines changed

2 files changed

+132
-54
lines changed

Crypto/SecureDigest.cc

Lines changed: 72 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,69 +17,117 @@
1717
#pragma clang diagnostic push
1818
#pragma clang diagnostic ignored "-Wdocumentation-deprecated-sync"
1919
#include "mbedtls/sha1.h"
20+
#include "mbedtls/sha256.h"
2021
#pragma clang diagnostic pop
2122

2223
#ifdef __APPLE__
23-
#define USE_COMMON_CRYPTO
24+
# define USE_COMMON_CRYPTO
2425
#endif
2526

2627
#ifdef USE_COMMON_CRYPTO
2728
#include <CommonCrypto/CommonDigest.h>
28-
#define _CONTEXT ((CC_SHA1_CTX*)_context)
29-
#else
30-
#define _CONTEXT ((mbedtls_sha1_context*)_context)
3129
#endif
3230

3331
namespace litecore {
3432

35-
void SHA1::computeFrom(fleece::slice s) {
36-
(SHA1Builder() << s).finish(&bytes, sizeof(bytes));
37-
}
38-
3933

40-
bool SHA1::setDigest(fleece::slice s) {
41-
if (s.size != sizeof(bytes))
34+
template <DigestType TYPE, size_t SIZE>
35+
bool Digest<TYPE,SIZE>::setDigest(fleece::slice s) {
36+
if (s.size != _bytes.size())
4237
return false;
43-
memcpy(bytes, s.buf, sizeof(bytes));
38+
s.copyTo(_bytes.data());
4439
return true;
4540
}
4641

4742

48-
std::string SHA1::asBase64() const {
43+
template <DigestType TYPE, size_t SIZE>
44+
std::string Digest<TYPE,SIZE>::asBase64() const {
4945
return fleece::base64::encode(asSlice());
5046
}
5147

5248

53-
SHA1Builder::SHA1Builder() {
49+
#pragma mark - SHA1:
50+
51+
52+
template <>
53+
Digest<SHA,1>::Builder::Builder() {
5454
static_assert(sizeof(_context) >= sizeof(mbedtls_sha1_context));
5555
#ifdef USE_COMMON_CRYPTO
5656
static_assert(sizeof(_context) >= sizeof(CC_SHA1_CTX));
57-
CC_SHA1_Init(_CONTEXT);
57+
CC_SHA1_Init((CC_SHA1_CTX*)_context);
5858
#else
59-
mbedtls_sha1_init(_CONTEXT);
60-
mbedtls_sha1_starts(_CONTEXT);
59+
mbedtls_sha1_init((mbedtls_sha1_context*)_context);
60+
mbedtls_sha1_starts((mbedtls_sha1_context*)_context);
6161
#endif
6262
}
6363

6464

65-
SHA1Builder& SHA1Builder::operator<< (fleece::slice s) {
65+
template <>
66+
Digest<SHA,1>::Builder& Digest<SHA,1>::Builder::operator<< (fleece::slice s) {
6667
#ifdef USE_COMMON_CRYPTO
67-
CC_SHA1_Update(_CONTEXT, s.buf, (CC_LONG)s.size);
68+
CC_SHA1_Update((CC_SHA1_CTX*)_context, s.buf, (CC_LONG)s.size);
6869
#else
69-
mbedtls_sha1_update(_CONTEXT, (unsigned char*)s.buf, s.size);
70+
mbedtls_sha1_update((mbedtls_sha1_context*)_context, (unsigned char*)s.buf, s.size);
7071
#endif
7172
return *this;
7273
}
7374

7475

75-
void SHA1Builder::finish(void *result, size_t resultSize) {
76-
DebugAssert(resultSize == sizeof(SHA1::bytes));
76+
template <>
77+
void Digest<SHA,1>::Builder::finish(void *result, size_t resultSize) {
78+
Assert(resultSize == kSizeInBytes);
7779
#ifdef USE_COMMON_CRYPTO
78-
CC_SHA1_Final((uint8_t*)result, _CONTEXT);
80+
CC_SHA1_Final((uint8_t*)result, (CC_SHA1_CTX*)_context);
7981
#else
80-
mbedtls_sha1_finish(_CONTEXT, (uint8_t*)result);
81-
mbedtls_sha1_free(_CONTEXT);
82+
mbedtls_sha1_finish((mbedtls_sha1_context*)_context, (uint8_t*)result);
83+
mbedtls_sha1_free((mbedtls_sha1_context*)_context);
8284
#endif
8385
}
8486

87+
// Force the non-specialized methods to be instantiated:
88+
template class Digest<SHA,1>;
89+
90+
91+
#pragma mark - SHA256:
92+
93+
94+
template <>
95+
Digest<SHA,256>::Builder::Builder() {
96+
static_assert(sizeof(_context) >= sizeof(mbedtls_sha256_context));
97+
#ifdef USE_COMMON_CRYPTO
98+
static_assert(sizeof(_context) >= sizeof(CC_SHA256_CTX));
99+
CC_SHA256_Init((CC_SHA256_CTX*)_context);
100+
#else
101+
mbedtls_sha256_init((mbedtls_sha256_context*)_context);
102+
mbedtls_sha256_starts((mbedtls_sha256_context*)_context, 0);
103+
#endif
104+
}
105+
106+
107+
template <>
108+
Digest<SHA,256>::Builder& Digest<SHA,256>::Builder::operator<< (fleece::slice s) {
109+
#ifdef USE_COMMON_CRYPTO
110+
CC_SHA256_Update((CC_SHA256_CTX*)_context, s.buf, (CC_LONG)s.size);
111+
#else
112+
mbedtls_sha256_update((mbedtls_sha256_context*)_context, (unsigned char*)s.buf, s.size);
113+
#endif
114+
return *this;
115+
}
116+
117+
118+
template <>
119+
void Digest<SHA,256>::Builder::finish(void *result, size_t resultSize) {
120+
Assert(resultSize == kSizeInBytes);
121+
#ifdef USE_COMMON_CRYPTO
122+
CC_SHA256_Final((uint8_t*)result, (CC_SHA256_CTX*)_context);
123+
#else
124+
mbedtls_sha256_finish((mbedtls_sha256_context*)_context, (uint8_t*)result);
125+
mbedtls_sha256_free((mbedtls_sha256_context*)_context);
126+
#endif
127+
}
128+
129+
130+
// Force the non-specialized methods to be instantiated:
131+
template class Digest<SHA,256>;
132+
85133
}

Crypto/SecureDigest.hh

Lines changed: 60 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,66 +12,96 @@
1212

1313
#pragma once
1414
#include "fleece/slice.hh"
15+
#include <array>
1516
#include <string>
1617

1718
namespace litecore {
1819

19-
/// A SHA-1 digest.
20-
class SHA1 {
20+
enum DigestType {
21+
SHA,
22+
};
23+
24+
25+
/// A cryptographic digest. Available instantiations are <SHA,1> and <SHA,256>.
26+
/// (SHA384 and SHA512 could be added by doing some copy-and-pasting in the .cc file.)
27+
template <DigestType TYPE, size_t SIZE>
28+
class Digest {
2129
public:
22-
SHA1() { memset(bytes, 0, sizeof(bytes)); }
30+
class Builder;
31+
32+
Digest() {_bytes.fill(std::byte{0});}
33+
34+
/// Constructs instance with a digest of the data in `s`.
35+
explicit Digest(fleece::slice s) {computeFrom(s);}
2336

24-
/// Constructs instance with a SHA-1 digest of the data in `s`
25-
explicit SHA1(fleece::slice s) {computeFrom(s);}
37+
inline Digest(Builder&&);
2638

27-
/// Computes a SHA-1 digest of the data
28-
void computeFrom(fleece::slice);
39+
/// Computes a digest of the data.
40+
void computeFrom(fleece::slice data);
2941

30-
/// Stores a digest; returns false if slice is the wrong size
31-
bool setDigest(fleece::slice);
42+
/// Stores a digest; returns false if slice is the wrong size.
43+
bool setDigest(fleece::slice digestData);
3244

33-
/// The digest as a slice
34-
fleece::slice asSlice() const {return {bytes, sizeof(bytes)};}
45+
/// The digest as a slice.
46+
fleece::slice asSlice() const {return {_bytes.data(), _bytes.size()};}
3547
operator fleece::slice() const {return asSlice();}
3648

49+
/// The digest encoded in Base64.
3750
std::string asBase64() const;
3851

39-
bool operator==(const SHA1 &x) const {return memcmp(&bytes, &x.bytes, sizeof(bytes)) == 0;}
40-
bool operator!= (const SHA1 &x) const {return !(*this == x);}
52+
bool operator==(const Digest &x) const {return _bytes == x._bytes;}
53+
bool operator!=(const Digest &x) const {return _bytes != x._bytes;}
4154

4255
private:
43-
char bytes[20];
56+
static constexpr size_t kSizeInBytes = ((TYPE == SHA && SIZE == 1) ? 160 : SIZE) / 8;
4457

45-
friend class SHA1Builder;
58+
std::array<std::byte,kSizeInBytes> _bytes;
4659
};
4760

4861

49-
/// Builder for creating SHA-1 digests from piece-by-piece data.
50-
class SHA1Builder {
62+
/// Builder for creating digests incrementally from piece-by-piece data.
63+
template <DigestType TYPE, size_t SIZE>
64+
class Digest<TYPE, SIZE>::Builder {
5165
public:
52-
SHA1Builder();
66+
Builder();
5367

54-
/// Add a single byte
55-
SHA1Builder& operator<< (fleece::slice s);
68+
/// Adds data.
69+
Builder& operator<< (fleece::slice s);
5670

57-
/// Add data
58-
SHA1Builder& operator<< (uint8_t b) {return *this << fleece::slice(&b, 1);}
71+
/// Adds a single byte.
72+
Builder& operator<< (uint8_t b) {return *this << fleece::slice(&b, 1);}
5973

60-
/// Finish and write the digest to `result`. (Don't reuse the builder.)
74+
/// Finishes and writes the digest.
75+
/// @warning Don't reuse this builder.
76+
/// @param result The address to write the digest to.
77+
/// @param resultSize Must be equal to the size of a digest.
6178
void finish(void *result, size_t resultSize);
6279

63-
/// Finish and return the digest as a SHA1 object. (Don't reuse the builder.)
64-
SHA1 finish() {
65-
SHA1 result;
66-
finish(&result.bytes, sizeof(result.bytes));
67-
return result;
68-
}
80+
/// Finishes and returns the digest as a new object.
81+
/// @warning Don't reuse this builder.
82+
Digest finish() {return Digest(std::move(*this));}
6983

7084
private:
71-
uint8_t _context[100]; // big enough to hold any platform's context struct
85+
std::byte _context[110]; // big enough to hold any platform's context struct
7286
};
7387

7488

89+
template<DigestType TYPE, size_t SIZE>
90+
Digest<TYPE, SIZE>::Digest(Builder &&builder) {
91+
builder.finish(_bytes.data(), _bytes.size());
92+
}
93+
94+
template <DigestType TYPE, size_t SIZE>
95+
void Digest<TYPE,SIZE>::computeFrom(fleece::slice s) {
96+
(Builder() << s).finish(_bytes.data(), _bytes.size());
97+
}
98+
99+
100+
// Shorthand names:
101+
using SHA1 = Digest<SHA,1>;
102+
using SHA1Builder = Digest<SHA,1>::Builder;
103+
104+
using SHA256 = Digest<SHA,256>;
75105
}
76106

77107

0 commit comments

Comments
 (0)