Skip to content

Commit 5e17f14

Browse files
committed
Added PublicKey/PrivateKey methods for signatures
1 parent 300d6ec commit 5e17f14

File tree

4 files changed

+106
-0
lines changed

4 files changed

+106
-0
lines changed

Crypto/PublicKey.cc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "mbedtls/config.h"
1414
#include "PublicKey.hh"
15+
#include "SecureRandomize.hh"
1516
#include "TLSContext.hh"
1617
#include "Logging.hh"
1718
#include "StringUtil.hh"
@@ -78,11 +79,36 @@ namespace litecore { namespace crypto {
7879
}
7980

8081

82+
#pragma mark - PUBLIC KEY:
83+
84+
8185
PublicKey::PublicKey(slice data) {
8286
parsePEMorDER(data, "public key", context(), &mbedtls_pk_parse_public_key);
8387
}
8488

8589

90+
static int rngFunction(void *ctx, unsigned char *dst, size_t size) {
91+
SecureRandomize({dst, size});
92+
return 0;
93+
}
94+
95+
96+
bool PublicKey::verifySignature(const SHA256 &inputDigest, slice signature) {
97+
int result = mbedtls_pk_verify(context(),
98+
MBEDTLS_MD_SHA256, // declares that input is a SHA256 digest.
99+
(const uint8_t*)inputDigest.asSlice().buf,
100+
inputDigest.asSlice().size,
101+
(const uint8_t*)signature.buf, signature.size);
102+
if (result == MBEDTLS_ERR_RSA_VERIFY_FAILED)
103+
return false;
104+
TRY(result); // other error codes throw exceptions
105+
return true;
106+
}
107+
108+
109+
#pragma mark - PRIVATE KEY:
110+
111+
86112
PrivateKey::PrivateKey(slice data, slice password) {
87113
if (password.size == 0)
88114
password = nullslice; // interpret empty password as 'no password'
@@ -123,7 +149,19 @@ namespace litecore { namespace crypto {
123149
default:
124150
Assert(false, "Invalid key format received (%d)", format);
125151
}
152+
}
153+
126154

155+
alloc_slice PrivateKey::sign(const SHA256 &inputDigest) {
156+
alloc_slice signature(MBEDTLS_PK_SIGNATURE_MAX_SIZE);
157+
size_t sigLen = 0;
158+
TRY(mbedtls_pk_sign(context(),
159+
MBEDTLS_MD_SHA256, // declares that input is a SHA256 digest.
160+
(const uint8_t*)inputDigest.asSlice().buf, inputDigest.asSlice().size,
161+
(uint8_t*)signature.buf, &sigLen,
162+
rngFunction, nullptr));
163+
signature.shorten(sigLen);
164+
return signature;
127165
}
128166

129167

Crypto/PublicKey.hh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,16 @@ namespace litecore { namespace crypto {
7575

7676
virtual bool isPrivate() override {return false;}
7777

78+
/** Checks whether `signature` is a valid signature, created by my matching private key,
79+
of the given SHA256 digest. */
80+
virtual bool verifySignature(const SHA256 &inputDigest, fleece::slice signature);
81+
82+
/** Checks whether `signature` is a valid signature, created by my matching private key,
83+
of a SHA256 digest of the input data. */
84+
bool verifySignature(fleece::slice inputData, fleece::slice signature) {
85+
return verifySignature(SHA256(inputData), signature);
86+
}
87+
7888
protected:
7989
friend class CertBase;
8090

@@ -114,6 +124,12 @@ namespace litecore { namespace crypto {
114124
return new PublicKey(publicKeyData(KeyFormat::Raw));
115125
}
116126

127+
/** Generates a signature of a SHA256 digest. */
128+
virtual fleece::alloc_slice sign(const SHA256 &inputDigest);
129+
130+
/** Generates a signature of a SHA256 digest of the input data. */
131+
fleece::alloc_slice sign(fleece::slice inputData) {return sign(SHA256(inputData));}
132+
117133
protected:
118134
PrivateKey() =default;
119135
};

Crypto/SignatureTest.cc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//
2+
// SignatureTest.cc
3+
//
4+
// Copyright 2022-Present Couchbase, Inc.
5+
//
6+
// Use of this software is governed by the Business Source License included
7+
// in the file licenses/BSL-Couchbase.txt. As of the Change Date specified
8+
// in that file, in accordance with the Business Source License, use of this
9+
// software will be governed by the Apache License, Version 2.0, included in
10+
// the file licenses/APL2.txt.
11+
//
12+
13+
#include "PublicKey.hh"
14+
#include "Base64.hh"
15+
#include "Error.hh"
16+
#include "LiteCoreTest.hh"
17+
#include <iostream>
18+
19+
20+
using namespace litecore;
21+
using namespace litecore::crypto;
22+
using namespace std;
23+
using namespace fleece;
24+
25+
26+
TEST_CASE("RSA Signatures", "[Signatures]") {
27+
static constexpr slice kDataToSign = "The only thing we learn from history"
28+
" is that people do not learn from history. --Hegel";
29+
Retained<PrivateKey> key = PrivateKey::generateTemporaryRSA(2048);
30+
alloc_slice signature = key->sign(kDataToSign);
31+
cout << "Signature is " << signature.size << " bytes: " << base64::encode(signature) << endl;
32+
33+
// Verify:
34+
CHECK(key->publicKey()->verifySignature(kDataToSign, signature));
35+
36+
// Verification fails with wrong public key:
37+
auto key2 = PrivateKey::generateTemporaryRSA(2048);
38+
CHECK(!key2->publicKey()->verifySignature(kDataToSign, signature));
39+
40+
// Verification fails with incorrect digest:
41+
auto badDigest = SHA256(kDataToSign);
42+
((uint8_t*)&badDigest)[10]++;
43+
CHECK(!key->publicKey()->verifySignature(badDigest, signature));
44+
45+
// Verification fails with altered signature:
46+
((uint8_t&)signature[100])++;
47+
CHECK(!key->publicKey()->verifySignature(kDataToSign, signature));
48+
}

Xcode/LiteCore.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@
160160
27469D08233D719800A1EE1A /* PublicKey+Apple.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2762A02022EF8C4E00F9AB18 /* PublicKey+Apple.mm */; };
161161
27469D09233D719800A1EE1A /* mbedUtils.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2762A01C22EB933100F9AB18 /* mbedUtils.cc */; };
162162
2746C8E62639E88700A3B2CC /* ThreadUtil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2746C8E52639E88700A3B2CC /* ThreadUtil.cc */; };
163+
2747A1BE2798847300F286AF /* SignatureTest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2747A1BD2798847300F286AF /* SignatureTest.cc */; };
163164
27480E37253A5D9C0091CF37 /* VectorRecordTest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 27480E36253A5D9C0091CF37 /* VectorRecordTest.cc */; };
164165
2749B9871EB298360068DBF9 /* RESTListener+Handlers.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2749B9861EB298360068DBF9 /* RESTListener+Handlers.cc */; };
165166
274B36D225B271F7001FC28D /* Version.cc in Sources */ = {isa = PBXBuildFile; fileRef = 274B36D125B271F7001FC28D /* Version.cc */; };
@@ -1048,6 +1049,7 @@
10481049
2747664520190841007B39D1 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; };
10491050
2747664720190841007B39D1 /* sqlite3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sqlite3.c; sourceTree = "<group>"; };
10501051
2747664820190841007B39D1 /* sqlite3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sqlite3.h; sourceTree = "<group>"; };
1052+
2747A1BD2798847300F286AF /* SignatureTest.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SignatureTest.cc; sourceTree = "<group>"; };
10511053
27480E36253A5D9C0091CF37 /* VectorRecordTest.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = VectorRecordTest.cc; sourceTree = "<group>"; };
10521054
27491C9A1E7B1001001DC54B /* c4Socket.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = c4Socket.h; sourceTree = "<group>"; };
10531055
27491C9E1E7B2532001DC54B /* c4Socket.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = c4Socket.cc; sourceTree = "<group>"; };
@@ -2324,6 +2326,7 @@
23242326
isa = PBXGroup;
23252327
children = (
23262328
2762A01422EB7CC800F9AB18 /* CertificateTest.cc */,
2329+
2747A1BD2798847300F286AF /* SignatureTest.cc */,
23272330
);
23282331
name = tests;
23292332
sourceTree = "<group>";
@@ -3949,6 +3952,7 @@
39493952
27E19D662316EDEA00E031F8 /* RESTClientTest.cc in Sources */,
39503953
27B9669723284F2900B2897F /* RESTListenerTest.cc in Sources */,
39513954
27AFF3BA2303758E00B4D6C4 /* ReplicatorAPITest.cc in Sources */,
3955+
2747A1BE2798847300F286AF /* SignatureTest.cc in Sources */,
39523956
277071D5230B682100F7EB95 /* SyncListenerTest.cc in Sources */,
39533957
27AFF3BB2303759400B4D6C4 /* ReplicatorSGTest.cc in Sources */,
39543958
27480E37253A5D9C0091CF37 /* VectorRecordTest.cc in Sources */,

0 commit comments

Comments
 (0)