/*! * \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 using namespace governikus; Q_DECLARE_LOGGING_CATEGORY(card) Q_DECLARE_LOGGING_CATEGORY(secure) QByteArray EcdhKeyAgreement::encodeUncompressedPublicKey(const QSharedPointer& pPaceInfo, const QSharedPointer& pCurve, const QSharedPointer& 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(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(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(publicKeyData.size()); publicKey += publicKeyData; return publicKey; } QByteArray EcdhKeyAgreement::encodeCompressedPublicKey(const QSharedPointer& pCurve, const QSharedPointer& pPoint) { QByteArray uncompressedPointBytes = EcUtil::point2oct(pCurve, pPoint.data()); return uncompressedPointBytes.mid(1, (uncompressedPointBytes.size() - 1) / 2); } EcdhKeyAgreement::EcdhKeyAgreement(const QSharedPointer& pPaceInfo, const QSharedPointer& pCardConnectionWorker) : KeyAgreement(pPaceInfo, pCardConnectionWorker) , mMapping() , mEphemeralCurve() , mTerminalPublicKey() , mCardPublicKey() { } QSharedPointer EcdhKeyAgreement::create(const QSharedPointer& pPaceInfo, const QSharedPointer& pCardConnectionWorker) { QSharedPointer keyAgreement(new EcdhKeyAgreement(pPaceInfo, pCardConnectionWorker)); QSharedPointer ecGroup = EllipticCurveFactory::create(pPaceInfo); if (ecGroup == nullptr) { qCCritical(card) << "Creation of elliptic curve failed"; return QSharedPointer(); } if (pPaceInfo->getMappingType() != MappingType::GM) { qCCritical(card) << "Currently only generic mapping supported"; return QSharedPointer(); } keyAgreement->mMapping.reset(new EcdhGenericMapping(ecGroup)); return keyAgreement; } EcdhKeyAgreement::~EcdhKeyAgreement() { } QByteArray EcdhKeyAgreement::determineSharedSecret(const QByteArray& pNonce) { mEphemeralCurve = determineEphemeralDomainParameters(pNonce); if (!mEphemeralCurve) { return QByteArray(); } QSharedPointer 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 EcdhKeyAgreement::determineEphemeralDomainParameters(const QByteArray& pNonce) { QByteArray terminalMappingData = mMapping->generateTerminalMappingData(); QByteArray cardMappingData = transmitGAMappingData(terminalMappingData); if (cardMappingData.isNull()) { return QSharedPointer(); } return mMapping->generateEphemeralDomainParameters(cardMappingData, pNonce); } QSharedPointer EcdhKeyAgreement::performKeyExchange(const QSharedPointer& pCurve) { QSharedPointer terminalEphemeralKey = EcUtil::create(EC_KEY_new()); if (!EC_KEY_set_group(terminalEphemeralKey.data(), pCurve.data())) { qCCritical(card) << "Error EC_KEY_set_group"; return QSharedPointer(); } if (!EC_KEY_generate_key(terminalEphemeralKey.data())) { qCCritical(card) << "Error EC_KEY_generate_key"; return QSharedPointer(); } // 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(); } qCDebug(secure) << "uncompressedCardEphemeralPublicKey: " << cardEphemeralPublicKeyBytes.toHex(); mCardPublicKey = EcUtil::oct2point(pCurve, cardEphemeralPublicKeyBytes); if (!mCardPublicKey) { qCCritical(card) << "Cannot encode card ephemeral public key"; return QSharedPointer(); } if (!EC_POINT_cmp(pCurve.data(), mTerminalPublicKey.data(), mCardPublicKey.data(), nullptr)) { qCCritical(card) << "The exchanged public keys are equal"; return QSharedPointer(); } QSharedPointer 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(); } 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); }