AusweisApp2/src/card/base/pace/ec/EcdhKeyAgreement.cpp

206 lines
6.2 KiB
C++

/*!
* \copyright Copyright (c) 2014 Governikus GmbH & Co. KG
*/
#include "asn1/KnownOIDs.h"
#include "asn1/PACEInfo.h"
#include "Commands.h"
#include "pace/CipherMac.h"
#include "pace/ec/EcdhGenericMapping.h"
#include "pace/ec/EcdhKeyAgreement.h"
#include "pace/ec/EcUtil.h"
#include "pace/ec/EllipticCurveFactory.h"
#include <QLoggingCategory>
using namespace governikus;
Q_DECLARE_LOGGING_CATEGORY(card)
Q_DECLARE_LOGGING_CATEGORY(secure)
QByteArray EcdhKeyAgreement::encodeUncompressedPublicKey(const QSharedPointer<const PACEInfo>& pPaceInfo, const QSharedPointer<const EC_GROUP>& pCurve, const QSharedPointer<const EC_POINT>& pPoint)
{
QByteArray pointBytes = EcUtil::point2oct(pCurve, pPoint.data());
QByteArray publicKeyData;
publicKeyData += char(0x06);
const auto& protocolBytes = pPaceInfo->getProtocolValueBytes();
if (protocolBytes.size() > 0xFF)
{
qCCritical(card) << "Protocol value bytes size > 0xFF not supported";
Q_ASSERT(protocolBytes.size() <= 0xFF);
return QByteArray();
}
publicKeyData += static_cast<char>(protocolBytes.size());
publicKeyData += protocolBytes;
publicKeyData += char(0x86);
if (pointBytes.size() > 0xFF)
{
qCCritical(card) << "Point bytes size > 0xFF not supported";
Q_ASSERT(pointBytes.size() <= 0xFF);
return QByteArray();
}
publicKeyData += static_cast<char>(pointBytes.size());
publicKeyData += pointBytes;
QByteArray publicKey;
publicKey += QByteArray::fromHex("7F49");
if (publicKeyData.size() > 0xFF)
{
qCCritical(card) << "Public key bytes size > 0xFF not supported";
Q_ASSERT(publicKeyData.size() <= 0xFF);
return QByteArray();
}
publicKey += static_cast<char>(publicKeyData.size());
publicKey += publicKeyData;
return publicKey;
}
QByteArray EcdhKeyAgreement::encodeCompressedPublicKey(const QSharedPointer<const EC_GROUP>& pCurve, const QSharedPointer<const EC_POINT>& pPoint)
{
QByteArray uncompressedPointBytes = EcUtil::point2oct(pCurve, pPoint.data());
return uncompressedPointBytes.mid(1, (uncompressedPointBytes.size() - 1) / 2);
}
EcdhKeyAgreement::EcdhKeyAgreement(const QSharedPointer<const PACEInfo>& pPaceInfo, const QSharedPointer<CardConnectionWorker>& pCardConnectionWorker)
: KeyAgreement(pPaceInfo, pCardConnectionWorker)
, mMapping()
, mEphemeralCurve()
, mTerminalPublicKey()
, mCardPublicKey()
{
}
QSharedPointer<KeyAgreement> EcdhKeyAgreement::create(const QSharedPointer<const PACEInfo>& pPaceInfo,
const QSharedPointer<CardConnectionWorker>& pCardConnectionWorker)
{
QSharedPointer<EcdhKeyAgreement> keyAgreement(new EcdhKeyAgreement(pPaceInfo, pCardConnectionWorker));
QSharedPointer<EC_GROUP> ecGroup = EllipticCurveFactory::create(pPaceInfo);
if (ecGroup == nullptr)
{
qCCritical(card) << "Creation of elliptic curve failed";
return QSharedPointer<EcdhKeyAgreement>();
}
if (pPaceInfo->getMappingType() != MappingType::GM)
{
qCCritical(card) << "Currently only generic mapping supported";
return QSharedPointer<EcdhKeyAgreement>();
}
keyAgreement->mMapping.reset(new EcdhGenericMapping(ecGroup));
return keyAgreement;
}
EcdhKeyAgreement::~EcdhKeyAgreement()
{
}
QByteArray EcdhKeyAgreement::determineSharedSecret(const QByteArray& pNonce)
{
mEphemeralCurve = determineEphemeralDomainParameters(pNonce);
if (!mEphemeralCurve)
{
return QByteArray();
}
QSharedPointer<EC_POINT> mutualPoint = performKeyExchange(mEphemeralCurve);
if (!mutualPoint)
{
return QByteArray();
}
QByteArray sharedSecret = EcUtil::point2oct(mEphemeralCurve, mutualPoint.data());
sharedSecret = sharedSecret.mid(1, (sharedSecret.size() - 1) / 2);
return sharedSecret;
}
QSharedPointer<EC_GROUP> EcdhKeyAgreement::determineEphemeralDomainParameters(const QByteArray& pNonce)
{
QByteArray terminalMappingData = mMapping->generateTerminalMappingData();
QByteArray cardMappingData = transmitGAMappingData(terminalMappingData);
if (cardMappingData.isNull())
{
return QSharedPointer<EC_GROUP>();
}
return mMapping->generateEphemeralDomainParameters(cardMappingData, pNonce);
}
QSharedPointer<EC_POINT> EcdhKeyAgreement::performKeyExchange(const QSharedPointer<const EC_GROUP>& pCurve)
{
QSharedPointer<EC_KEY> terminalEphemeralKey = EcUtil::create(EC_KEY_new());
if (!EC_KEY_set_group(terminalEphemeralKey.data(), pCurve.data()))
{
qCCritical(card) << "Error EC_KEY_set_group";
return QSharedPointer<EC_POINT>();
}
if (!EC_KEY_generate_key(terminalEphemeralKey.data()))
{
qCCritical(card) << "Error EC_KEY_generate_key";
return QSharedPointer<EC_POINT>();
}
// Make a copy of the terminal public key for later mutual authentication.
mTerminalPublicKey = EcUtil::create(EC_POINT_dup(EC_KEY_get0_public_key(terminalEphemeralKey.data()), pCurve.data()));
QByteArray terminalEphemeralPublicKeyBytes = EcUtil::point2oct(pCurve, mTerminalPublicKey.data());
const BIGNUM* terminalPrivateKey = EC_KEY_get0_private_key(terminalEphemeralKey.data());
QByteArray cardEphemeralPublicKeyBytes = transmitGAEphemeralPublicKey(terminalEphemeralPublicKeyBytes);
if (cardEphemeralPublicKeyBytes.isNull())
{
return QSharedPointer<EC_POINT>();
}
qCDebug(secure) << "uncompressedCardEphemeralPublicKey: " << cardEphemeralPublicKeyBytes.toHex();
mCardPublicKey = EcUtil::oct2point(pCurve, cardEphemeralPublicKeyBytes);
if (!mCardPublicKey)
{
qCCritical(card) << "Cannot encode card ephemeral public key";
return QSharedPointer<EC_POINT>();
}
if (!EC_POINT_cmp(pCurve.data(), mTerminalPublicKey.data(), mCardPublicKey.data(), nullptr))
{
qCCritical(card) << "The exchanged public keys are equal";
return QSharedPointer<EC_POINT>();
}
QSharedPointer<EC_POINT> mutualPoint = EcUtil::create(EC_POINT_new(pCurve.data()));
if (!EC_POINT_mul(pCurve.data(), mutualPoint.data(), nullptr, mCardPublicKey.data(), terminalPrivateKey, nullptr))
{
qCCritical(card) << "Calculation of elliptic curve point H failed";
return QSharedPointer<EC_POINT>();
}
return mutualPoint;
}
QByteArray EcdhKeyAgreement::getUncompressedTerminalPublicKey()
{
return encodeUncompressedPublicKey(mPaceInfo, mEphemeralCurve, mTerminalPublicKey);
}
QByteArray EcdhKeyAgreement::getUncompressedCardPublicKey()
{
return encodeUncompressedPublicKey(mPaceInfo, mEphemeralCurve, mCardPublicKey);
}
QByteArray EcdhKeyAgreement::getCompressedCardPublicKey()
{
return encodeCompressedPublicKey(mEphemeralCurve, mCardPublicKey);
}