Skip to content

Commit

Permalink
Move RSA signing code from PublicKey to Signing
Browse files Browse the repository at this point in the history
Backs out the changes made to PublicKey.
  • Loading branch information
snej committed Jan 31, 2022
1 parent 741a9b2 commit aaa23c5
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 104 deletions.
38 changes: 0 additions & 38 deletions Crypto/PublicKey.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

#include "mbedtls/config.h"
#include "PublicKey.hh"
#include "SecureRandomize.hh"
#include "TLSContext.hh"
#include "Logging.hh"
#include "StringUtil.hh"
Expand Down Expand Up @@ -79,36 +78,11 @@ namespace litecore { namespace crypto {
}


#pragma mark - PUBLIC KEY:


PublicKey::PublicKey(slice data) {
parsePEMorDER(data, "public key", context(), &mbedtls_pk_parse_public_key);
}


static int rngFunction(void *ctx, unsigned char *dst, size_t size) {
SecureRandomize({dst, size});
return 0;
}


bool PublicKey::verifySignature(const SHA256 &inputDigest, slice signature) {
int result = mbedtls_pk_verify(context(),
MBEDTLS_MD_SHA256, // declares that input is a SHA256 digest.
(const uint8_t*)inputDigest.asSlice().buf,
inputDigest.asSlice().size,
(const uint8_t*)signature.buf, signature.size);
if (result == MBEDTLS_ERR_RSA_VERIFY_FAILED)
return false;
TRY(result); // other error codes throw exceptions
return true;
}


#pragma mark - PRIVATE KEY:


PrivateKey::PrivateKey(slice data, slice password) {
if (password.size == 0)
password = nullslice; // interpret empty password as 'no password'
Expand Down Expand Up @@ -149,19 +123,7 @@ namespace litecore { namespace crypto {
default:
Assert(false, "Invalid key format received (%d)", (int)format);
}
}


alloc_slice PrivateKey::sign(const SHA256 &inputDigest) {
alloc_slice signature(MBEDTLS_PK_SIGNATURE_MAX_SIZE);
size_t sigLen = 0;
TRY(mbedtls_pk_sign(context(),
MBEDTLS_MD_SHA256, // declares that input is a SHA256 digest.
(const uint8_t*)inputDigest.asSlice().buf, inputDigest.asSlice().size,
(uint8_t*)signature.buf, &sigLen,
rngFunction, nullptr));
signature.shorten(sigLen);
return signature;
}


Expand Down
16 changes: 0 additions & 16 deletions Crypto/PublicKey.hh
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,6 @@ namespace litecore { namespace crypto {

virtual bool isPrivate() override {return false;}

/** Checks whether `signature` is a valid signature, created by my matching private key,
of the given SHA256 digest. */
virtual bool verifySignature(const SHA256 &inputDigest, fleece::slice signature);

/** Checks whether `signature` is a valid signature, created by my matching private key,
of a SHA256 digest of the input data. */
bool verifySignature(fleece::slice inputData, fleece::slice signature) {
return verifySignature(SHA256(inputData), signature);
}

protected:
friend class CertBase;

Expand Down Expand Up @@ -124,12 +114,6 @@ namespace litecore { namespace crypto {
return new PublicKey(publicKeyData(KeyFormat::Raw));
}

/** Generates a signature of a SHA256 digest. */
virtual fleece::alloc_slice sign(const SHA256 &inputDigest);

/** Generates a signature of a SHA256 digest of the input data. */
fleece::alloc_slice sign(fleece::slice inputData) {return sign(SHA256(inputData));}

protected:
PrivateKey() =default;
};
Expand Down
40 changes: 23 additions & 17 deletions Crypto/SignatureTest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,54 +25,60 @@ using namespace std;
using namespace fleece;


TEST_CASE("RSA Signatures", "[Signatures]") {
TEST_CASE("Signatures", "[Signatures]") {
static constexpr slice kDataToSign = "The only thing we learn from history"
" is that people do not learn from history. --Hegel";
Retained<PrivateKey> key = PrivateKey::generateTemporaryRSA(2048);
alloc_slice signature = key->sign(kDataToSign);

const char *alg = GENERATE(kRSAAlgorithmName, kEd25519AlgorithmName);
cerr << "\t---- " << alg << endl;

auto signingKey = SigningKey::generate(alg);
alloc_slice signature = signingKey->sign(kDataToSign);
cout << "Signature is " << signature.size << " bytes: " << base64::encode(signature) << endl;

// Verify:
CHECK(key->publicKey()->verifySignature(kDataToSign, signature));
auto verifyingKey = signingKey->verifyingKey();
CHECK(verifyingKey->verifySignature(kDataToSign, signature));

// Verification fails with wrong public key:
auto key2 = PrivateKey::generateTemporaryRSA(2048);
CHECK(!key2->publicKey()->verifySignature(kDataToSign, signature));
auto key2 = SigningKey::generate(kRSAAlgorithmName);
CHECK(!key2->verifyingKey()->verifySignature(kDataToSign, signature));

// Verification fails with incorrect digest:
auto badDigest = SHA256(kDataToSign);
((uint8_t*)&badDigest)[10]++;
CHECK(!key->publicKey()->verifySignature(badDigest, signature));
CHECK(!verifyingKey->verifySignature(badDigest, signature));

// Verification fails with altered signature:
((uint8_t&)signature[100])++;
CHECK(!key->publicKey()->verifySignature(kDataToSign, signature));
((uint8_t&)signature[30])++;
CHECK(!verifyingKey->verifySignature(kDataToSign, signature));
}


TEST_CASE("Signed Document", "[Signatures]") {
const char *algorithm = GENERATE(kRSAAlgorithmName, kEd25519AlgorithmName);
bool embedKey = GENERATE(false, true);
cout << "---- Embed key in signature = " << embedKey << endl;
cerr << "\t---- " << algorithm << "; embed key in signature = " << embedKey << endl;

// Create a signed doc and convert to JSON:
alloc_slice publicKeyData;
string json;
{
auto priv = Ed25519SigningKey::generate();
auto pub = priv.publicKey();
publicKeyData = pub.data();
auto priv = SigningKey::generate(algorithm);
auto pub = priv->verifyingKey();
publicKeyData = pub->data();

MutableDict doc = MutableDict::newDict();
doc["name"] = "Oliver Bolliver Butz";
doc["age"] = 6;
cout << "Document: " << doc.toJSONString() << endl;

MutableDict sig = makeSignature(doc, priv, 5 /*minutes*/, embedKey);
MutableDict sig = makeSignature(doc, *priv, 5 /*minutes*/, embedKey);
REQUIRE(sig);
string sigJson = sig.toJSONString();
cout << "Signature, " << sigJson.size() << " bytes: " << sigJson << endl;

CHECK(verifySignature(doc, sig, &pub) == VerifyResult::Valid);
CHECK(verifySignature(doc, sig, pub.get()) == VerifyResult::Valid);

doc["(sig)"] = sig; // <-- add signature to doc, in "(sig)" property
json = doc.toJSONString();
Expand All @@ -86,13 +92,13 @@ TEST_CASE("Signed Document", "[Signatures]") {
Dict sig = doc["(sig)"].asDict();
REQUIRE(sig);

auto parsedKey = getSignaturePublicKey(sig, "Ed25519");
auto parsedKey = getSignaturePublicKey(sig, algorithm);
if (embedKey) {
REQUIRE(parsedKey);
CHECK(parsedKey->data() == publicKeyData);
} else {
CHECK(!parsedKey);
parsedKey = make_unique<Ed25519VerifyingKey>(publicKeyData);
parsedKey = VerifyingKey::instantiate(publicKeyData, algorithm);
}

MutableDict unsignedDoc = doc.mutableCopy();
Expand Down
76 changes: 73 additions & 3 deletions Crypto/Signing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,30 @@
#include "Signing.hh"
#include "Error.hh"
#include "SecureRandomize.hh"
#include "mbedUtils.hh"
#include "monocypher.h"
#include "monocypher-ed25519.h"

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation-deprecated-sync"
#include "mbedtls/pk.h"
#pragma clang diagnostic pop

namespace litecore::crypto {
using namespace std;


std::unique_ptr<SigningKey> SigningKey::generate(const char *algorithm) {
if (0 == strcmp(algorithm, kRSAAlgorithmName)) {
return make_unique<RSASigningKey>(PrivateKey::generateTemporaryRSA(2048));
} else if (0 == strcmp(algorithm, kEd25519AlgorithmName)) {
return make_unique<Ed25519SigningKey>();
} else {
error::_throw(error::CryptoError, "Unknown signature algorithm '%s'", algorithm);
}
}


unique_ptr<VerifyingKey> VerifyingKey::instantiate(slice data, const char *algorithm) {
if (0 == strcmp(algorithm, kRSAAlgorithmName)) {
return make_unique<RSAVerifyingKey>(data);
Expand All @@ -37,9 +54,54 @@ namespace litecore::crypto {
}


#pragma mark - RSA:


static int rngFunction(void *ctx, unsigned char *dst, size_t size) {
SecureRandomize({dst, size});
return 0;
}


alloc_slice RSASigningKey::sign(slice data) const {
SHA256 inputDigest(data);
alloc_slice signature(MBEDTLS_PK_SIGNATURE_MAX_SIZE);
size_t sigLen = 0;
TRY(mbedtls_pk_sign(_key->context(),
MBEDTLS_MD_SHA256, // declares that input is a SHA256 digest.
(const uint8_t*)inputDigest.asSlice().buf, inputDigest.asSlice().size,
(uint8_t*)signature.buf, &sigLen,
rngFunction, nullptr));
signature.shorten(sigLen);
return signature;
}


unique_ptr<VerifyingKey> RSASigningKey::verifyingKey() const {
return make_unique<RSAVerifyingKey>(_key->publicKey());
}


bool RSAVerifyingKey::verifySignature(slice data, slice signature) const {
SHA256 inputDigest(data);
int result = mbedtls_pk_verify(_key->context(),
MBEDTLS_MD_SHA256, // declares that input is a SHA256 digest.
(const uint8_t*)inputDigest.asSlice().buf,
inputDigest.asSlice().size,
(const uint8_t*)signature.buf, signature.size);
if (result == MBEDTLS_ERR_RSA_VERIFY_FAILED)
return false;
TRY(result); // other error codes throw exceptions
return true;
}


#pragma mark - Ed25519:


Ed25519Base::Ed25519Base(slice bytes) {
if (bytes.size != sizeof(_bytes))
error::_throw(error::CryptoError, "Invalid data for Ed25519 key");
error::_throw(error::CryptoError, "Invalid data size for Ed25519 key");
bytes.copyTo(_bytes.data());
}

Expand All @@ -56,13 +118,20 @@ namespace litecore::crypto {
}


unique_ptr<VerifyingKey> Ed25519SigningKey::verifyingKey() const {
unique_ptr<Ed25519VerifyingKey> pub(new Ed25519VerifyingKey());
crypto_ed25519_public_key(pub->_bytes.data(), _bytes.data());
return pub;
}


alloc_slice Ed25519SigningKey::verifyingKeyData() const {
return publicKey().data();
}


alloc_slice Ed25519SigningKey::sign(slice data) const {
alloc_slice signature(64);
alloc_slice signature(kSignatureSize);
crypto_ed25519_sign((uint8_t*)signature.buf,
(const uint8_t*)_bytes.data(), nullptr,
(const uint8_t*)data.buf, data.size);
Expand All @@ -76,7 +145,8 @@ namespace litecore::crypto {
}

bool Ed25519VerifyingKey::verifySignature(slice inputData, slice signature) const {
return 0 == crypto_ed25519_check((const uint8_t*)signature.buf,
return signature.size == kSignatureSize
&& 0 == crypto_ed25519_check((const uint8_t*)signature.buf,
(const uint8_t*)_bytes.data(),
(const uint8_t*)inputData.buf,
inputData.size);
Expand Down
Loading

0 comments on commit aaa23c5

Please sign in to comment.