This commit is contained in:
howard-stearns 2017-11-22 16:29:56 -08:00
parent 899999a4c6
commit abf41300dd
4 changed files with 23 additions and 92 deletions

View file

@ -2505,28 +2505,25 @@ QByteArray EntityItemProperties::getStaticCertificateHash() const {
return QCryptographicHash::hash(getStaticCertificateJSON(), QCryptographicHash::Sha256); return QCryptographicHash::hash(getStaticCertificateJSON(), QCryptographicHash::Sha256);
} }
bool EntityItemProperties::verifyStaticCertificateProperties() { // FIXME: This is largely copied from EntityItemProperties::verifyStaticCertificateProperties, which should be refactored to use this.
// True IIF a non-empty certificateID matches the static certificate json. // I also don't like the nested-if style, but for this step I'm deliberately preserving the similarity.
// I.e., if we can verify that the certificateID was produced by High Fidelity signing the static certificate hash. bool EntityItemProperties::verifySignature(const QString& publicKey, const QByteArray& digestByteArray, const QByteArray& signatureByteArray) {
if (getCertificateID().isEmpty()) { if (digestByteArray.isEmpty()) {
return false; return false;
} }
const QByteArray marketplacePublicKeyByteArray = EntityItem::_marketplacePublicKey.toUtf8(); const unsigned char* key = reinterpret_cast<const unsigned char*>(publicKey.toUtf8().constData());
const unsigned char* marketplacePublicKey = reinterpret_cast<const unsigned char*>(marketplacePublicKeyByteArray.constData()); int keyLength = publicKey.length();
int marketplacePublicKeyLength = marketplacePublicKeyByteArray.length();
BIO *bio = BIO_new_mem_buf((void*)marketplacePublicKey, marketplacePublicKeyLength); BIO *bio = BIO_new_mem_buf((void*)key, keyLength);
EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
if (evp_key) { if (evp_key) {
EC_KEY* ec = EVP_PKEY_get1_EC_KEY(evp_key); EC_KEY* ec = EVP_PKEY_get1_EC_KEY(evp_key);
if (ec) { if (ec) {
const QByteArray digestByteArray = getStaticCertificateHash();
const unsigned char* digest = reinterpret_cast<const unsigned char*>(digestByteArray.constData()); const unsigned char* digest = reinterpret_cast<const unsigned char*>(digestByteArray.constData());
int digestLength = digestByteArray.length(); int digestLength = digestByteArray.length();
const QByteArray signatureByteArray = QByteArray::fromBase64(getCertificateID().toUtf8());
const unsigned char* signature = reinterpret_cast<const unsigned char*>(signatureByteArray.constData()); const unsigned char* signature = reinterpret_cast<const unsigned char*>(signatureByteArray.constData());
int signatureLength = signatureByteArray.length(); int signatureLength = signatureByteArray.length();
@ -2543,9 +2540,8 @@ bool EntityItemProperties::verifyStaticCertificateProperties() {
long error = ERR_get_error(); long error = ERR_get_error();
if (error != 0) { if (error != 0) {
const char* error_str = ERR_error_string(error, NULL); const char* error_str = ERR_error_string(error, NULL);
qCWarning(entities) << "ERROR while verifying static certificate properties! EC error:" << error_str qCWarning(entities) << "ERROR while verifying signature! EC error:" << error_str
<< "\nStatic Cert JSON:" << getStaticCertificateJSON() << "\nKey:" << publicKey << "\nutf8 Key Length:" << keyLength
<< "\nKey:" << EntityItem::_marketplacePublicKey << "\nKey Length:" << marketplacePublicKeyLength
<< "\nDigest:" << digest << "\nDigest Length:" << digestLength << "\nDigest:" << digest << "\nDigest Length:" << digestLength
<< "\nSignature:" << signature << "\nSignature Length:" << signatureLength; << "\nSignature:" << signature << "\nSignature Length:" << signatureLength;
} }
@ -2557,7 +2553,8 @@ bool EntityItemProperties::verifyStaticCertificateProperties() {
EVP_PKEY_free(evp_key); EVP_PKEY_free(evp_key);
} }
return answer; return answer;
} else { }
else {
if (bio) { if (bio) {
BIO_free(bio); BIO_free(bio);
} }
@ -2566,16 +2563,23 @@ bool EntityItemProperties::verifyStaticCertificateProperties() {
} }
long error = ERR_get_error(); long error = ERR_get_error();
const char* error_str = ERR_error_string(error, NULL); const char* error_str = ERR_error_string(error, NULL);
qCWarning(entities) << "Failed to verify static certificate properties! EC error:" << error_str; qCWarning(entities) << "Failed to verify signature! key" << publicKey << " EC key error:" << error_str;
return false; return false;
} }
} else { }
else {
if (bio) { if (bio) {
BIO_free(bio); BIO_free(bio);
} }
long error = ERR_get_error(); long error = ERR_get_error();
const char* error_str = ERR_error_string(error, NULL); const char* error_str = ERR_error_string(error, NULL);
qCWarning(entities) << "Failed to verify static certificate properties! EC error:" << error_str; qCWarning(entities) << "Failed to verify signature! key" << publicKey << " EC PEM error:" << error_str;
return false; return false;
} }
} }
bool EntityItemProperties::verifyStaticCertificateProperties() {
// True IIF a non-empty certificateID matches the static certificate json.
// I.e., if we can verify that the certificateID was produced by High Fidelity signing the static certificate hash.
return verifySignature(EntityItem::_marketplacePublicKey, getStaticCertificateHash(), QByteArray::fromBase64(getCertificateID().toUtf8()));
}

View file

@ -339,6 +339,7 @@ public:
QByteArray getStaticCertificateJSON() const; QByteArray getStaticCertificateJSON() const;
QByteArray getStaticCertificateHash() const; QByteArray getStaticCertificateHash() const;
bool verifyStaticCertificateProperties(); bool verifyStaticCertificateProperties();
static bool verifySignature(const QString& key, const QByteArray& text, const QByteArray& signature);
protected: protected:
QString getCollisionMaskAsString() const; QString getCollisionMaskAsString() const;

View file

@ -1190,7 +1190,7 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, Entity
} }
QString annotatedKey = "-----BEGIN PUBLIC KEY-----\n" + key + "\n-----END PUBLIC KEY-----"; QString annotatedKey = "-----BEGIN PUBLIC KEY-----\n" + key + "\n-----END PUBLIC KEY-----";
bool verificationSuccess = verifySignature(annotatedKey.toUtf8(), actualNonce.toUtf8(), nonce.toUtf8()); bool verificationSuccess = EntityItemProperties::verifySignature(annotatedKey.toUtf8(), actualNonce.toUtf8(), nonce.toUtf8());
if (verificationSuccess) { if (verificationSuccess) {
qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded."; qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded.";
@ -1201,79 +1201,6 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, Entity
return verificationSuccess; return verificationSuccess;
} }
// FIXME: This is largely copied from EntityItemProperties::verifyStaticCertificateProperties, which should be refactored to use this.
// I also don't like the nested-if style, but for this step I'm deliberately preserving the similarity.
bool EntityTree::verifySignature(const QString& publicKey, const QByteArray& digestByteArray, const QByteArray& signatureByteArray) {
if (digestByteArray.isEmpty()) {
return false;
}
const unsigned char* key = reinterpret_cast<const unsigned char*>(publicKey.toUtf8().constData());
int keyLength = publicKey.length();
BIO *bio = BIO_new_mem_buf((void*)key, keyLength);
EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
if (evp_key) {
EC_KEY* ec = EVP_PKEY_get1_EC_KEY(evp_key);
if (ec) {
const unsigned char* digest = reinterpret_cast<const unsigned char*>(digestByteArray.constData());
int digestLength = digestByteArray.length();
const unsigned char* signature = reinterpret_cast<const unsigned char*>(signatureByteArray.constData());
int signatureLength = signatureByteArray.length();
ERR_clear_error();
// ECSDA verification prototype: note that type is currently ignored
// int ECDSA_verify(int type, const unsigned char *dgst, int dgstlen,
// const unsigned char *sig, int siglen, EC_KEY *eckey);
bool answer = ECDSA_verify(0,
digest,
digestLength,
signature,
signatureLength,
ec);
long error = ERR_get_error();
if (error != 0) {
const char* error_str = ERR_error_string(error, NULL);
qCWarning(entities) << "ERROR while verifying signature! EC error:" << error_str
<< "\nKey:" << publicKey << "\nutf8 Key Length:" << keyLength
<< "\nDigest:" << digest << "\nDigest Length:" << digestLength
<< "\nSignature:" << signature << "\nSignature Length:" << signatureLength;
}
EC_KEY_free(ec);
if (bio) {
BIO_free(bio);
}
if (evp_key) {
EVP_PKEY_free(evp_key);
}
return answer;
}
else {
if (bio) {
BIO_free(bio);
}
if (evp_key) {
EVP_PKEY_free(evp_key);
}
long error = ERR_get_error();
const char* error_str = ERR_error_string(error, NULL);
qCWarning(entities) << "Failed to verify signature! key" << publicKey << " EC key error:" << error_str;
return false;
}
}
else {
if (bio) {
BIO_free(bio);
}
long error = ERR_get_error();
const char* error_str = ERR_error_string(error, NULL);
qCWarning(entities) << "Failed to verify signature! key" << publicKey << " EC PEM error:" << error_str;
return false;
}
}
void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
int certIDByteArraySize; int certIDByteArraySize;
int textByteArraySize; int textByteArraySize;

View file

@ -277,7 +277,6 @@ public:
QByteArray computeNonce(const QString& certID, const QString ownerKey); QByteArray computeNonce(const QString& certID, const QString ownerKey);
bool verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id); bool verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id);
static bool verifySignature(const QString& key, const QByteArray& text, const QByteArray& signature);
void setMyAvatar(std::shared_ptr<AvatarData> myAvatar) { _myAvatar = myAvatar; } void setMyAvatar(std::shared_ptr<AvatarData> myAvatar) { _myAvatar = myAvatar; }