mirror of
https://github.com/veracrypt/VeraCrypt.git
synced 2026-10-22 06:12:35 +00:00
Security tokens: wipe APDU buffers throughout their lifetime
EMV keyfile cleanup did not cover APDU response storage, temporary copies, or transport buffers on exceptions. Wipe responses on clear, replacement, destruction and growth, avoid redundant response and PAN copies, and install transport cleanup guards before use. Add standalone checks for erasure before release, aliasing, allocation failures, exception unwinding and const TLV input.
This commit is contained in:
+20
-22
@@ -151,7 +151,6 @@ namespace VeraCrypt
|
||||
bool hasBeenReset = false;
|
||||
CommandAPDU command;
|
||||
ResponseAPDU response;
|
||||
vector<uint8> responseData;
|
||||
shared_ptr<TLVNode> rootNode;
|
||||
shared_ptr<TLVNode> fciNode;
|
||||
shared_ptr<TLVNode> dfNameNode;
|
||||
@@ -218,8 +217,7 @@ namespace VeraCrypt
|
||||
}
|
||||
if (response.getSW() == SW_NO_ERROR && response.getData().size() > 0)
|
||||
{
|
||||
responseData = response.getData();
|
||||
rootNode = TLVParser::TLV_Parse(responseData.data(), responseData.size());
|
||||
rootNode = TLVParser::TLV_Parse(response.getData().data(), response.getData().size());
|
||||
fciNode = TLVParser::TLV_Find(rootNode, EMV_FCI_TAG);
|
||||
if (fciNode && fciNode->Subs->size() >= 2)
|
||||
{
|
||||
@@ -261,11 +259,9 @@ namespace VeraCrypt
|
||||
m_reader->ApduProcessData(command, response);
|
||||
if (response.getSW() == SW_NO_ERROR && response.getData().size() > 0)
|
||||
{
|
||||
responseData = response.getData();
|
||||
|
||||
try
|
||||
{
|
||||
templateNode = TLVParser::TLV_Parse(responseData.data(), responseData.size());
|
||||
templateNode = TLVParser::TLV_Parse(response.getData().data(), response.getData().size());
|
||||
if (templateNode && templateNode->Tag == EMV_TEMPLATE_TAG && templateNode->Subs->size() >= 1)
|
||||
{
|
||||
for (size_t i = 0; i < templateNode->Subs->size(); i++)
|
||||
@@ -342,10 +338,15 @@ namespace VeraCrypt
|
||||
shared_ptr<TLVNode> issuerPublicKeyCertNode;
|
||||
CommandAPDU command;
|
||||
ResponseAPDU response;
|
||||
vector<uint8> responseData;
|
||||
|
||||
if (!iccCert.empty())
|
||||
burn(iccCert.data(), iccCert.size());
|
||||
iccCert.clear();
|
||||
if (!issuerCert.empty())
|
||||
burn(issuerCert.data(), issuerCert.size());
|
||||
issuerCert.clear();
|
||||
if (!cplcData.empty())
|
||||
burn(cplcData.data(), cplcData.size());
|
||||
cplcData.clear();
|
||||
|
||||
if (m_iccCert.size() && m_issuerCert.size() && m_cplcData.size())
|
||||
@@ -395,11 +396,9 @@ namespace VeraCrypt
|
||||
m_reader->ApduProcessData(command, response);
|
||||
if (response.getSW() == SW_NO_ERROR && response.getData().size() > 0)
|
||||
{
|
||||
responseData = response.getData();
|
||||
|
||||
try
|
||||
{
|
||||
rootNode = TLVParser::TLV_Parse(responseData.data(), responseData.size());
|
||||
rootNode = TLVParser::TLV_Parse(response.getData().data(), response.getData().size());
|
||||
}
|
||||
catch(TLVException)
|
||||
{
|
||||
@@ -410,6 +409,8 @@ namespace VeraCrypt
|
||||
if (iccPublicKeyCertNode && iccPublicKeyCertNode->Value->size() > 0)
|
||||
{
|
||||
iccFound = true;
|
||||
if (!iccCert.empty())
|
||||
burn(iccCert.data(), iccCert.size());
|
||||
iccCert = *iccPublicKeyCertNode->Value.get();
|
||||
}
|
||||
|
||||
@@ -417,6 +418,8 @@ namespace VeraCrypt
|
||||
if (issuerPublicKeyCertNode && issuerPublicKeyCertNode->Value->size() > 0)
|
||||
{
|
||||
issuerFound = true;
|
||||
if (!issuerCert.empty())
|
||||
burn(issuerCert.data(), issuerCert.size());
|
||||
issuerCert = *issuerPublicKeyCertNode->Value.get();
|
||||
}
|
||||
}
|
||||
@@ -456,12 +459,12 @@ namespace VeraCrypt
|
||||
bool panFound = false;
|
||||
bool aidSelected = false;
|
||||
vector<uint8> EMVCardAid;
|
||||
vector<uint8> panData;
|
||||
uint8 panData[2] = { 0 };
|
||||
finally_do_arg (uint8 *, panData, { burn (finally_arg, 2); });
|
||||
shared_ptr<TLVNode> rootNode;
|
||||
shared_ptr<TLVNode> panNode;
|
||||
CommandAPDU command;
|
||||
ResponseAPDU response;
|
||||
vector<uint8> responseData;
|
||||
|
||||
lastPANDigits = L"";
|
||||
|
||||
@@ -501,11 +504,9 @@ namespace VeraCrypt
|
||||
m_reader->ApduProcessData(command, response);
|
||||
if (response.getSW() == SW_NO_ERROR && response.getData().size() > 0)
|
||||
{
|
||||
responseData = response.getData();
|
||||
|
||||
try
|
||||
{
|
||||
rootNode = TLVParser::TLV_Parse(responseData.data(), responseData.size());
|
||||
rootNode = TLVParser::TLV_Parse(response.getData().data(), response.getData().size());
|
||||
}
|
||||
catch(TLVException)
|
||||
{
|
||||
@@ -516,10 +517,10 @@ namespace VeraCrypt
|
||||
if (panNode && panNode->Value->size() >= 8)
|
||||
{
|
||||
panFound = true;
|
||||
panData = *panNode->Value.get();
|
||||
panData = vector<uint8>(panData.rbegin(), panData.rbegin() + 2); // only interested in last digits
|
||||
std::swap(panData[0], panData[1]);
|
||||
lastPANDigits = ArrayToHexWideString(panData.data(), (int) panData.size());
|
||||
// Only copy the last digits, leaving the full PAN in the wiping TLV node.
|
||||
panData[0] = (*panNode->Value)[panNode->Value->size() - 2];
|
||||
panData[1] = panNode->Value->back();
|
||||
lastPANDigits = ArrayToHexWideString(panData, (int) sizeof(panData));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -528,9 +529,6 @@ namespace VeraCrypt
|
||||
}
|
||||
}
|
||||
|
||||
if (panData.size())
|
||||
burn(panData.data(), panData.size());
|
||||
|
||||
if (!aidSelected)
|
||||
throw EMVSelectAIDFailed();
|
||||
|
||||
|
||||
+68
-37
@@ -1,33 +1,18 @@
|
||||
#include "ResponseAPDU.h"
|
||||
#include "Tcdefs.h"
|
||||
#include <stdexcept>
|
||||
#include <string.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace VeraCrypt
|
||||
{
|
||||
uint16 BytesToUInt16(const vector<uint8>& buff)
|
||||
{
|
||||
uint16 value = 0;
|
||||
for (uint16 i = 0; i < buff.size(); i++)
|
||||
{
|
||||
value <<= 8;
|
||||
value |= (uint16)buff.at(i);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void AppendData (vector<uint8>& buffer, const uint8* pbData, size_t cbData)
|
||||
{
|
||||
size_t orgSize = buffer.size ();
|
||||
buffer.resize (orgSize + cbData);
|
||||
memcpy (buffer.data () + orgSize, pbData, cbData);
|
||||
}
|
||||
|
||||
/*********************************************************************************/
|
||||
|
||||
void ResponseAPDU::clear()
|
||||
{
|
||||
if (!m_data.empty())
|
||||
burn(m_data.data(), m_data.size());
|
||||
m_data.clear();
|
||||
m_SW = 0;
|
||||
}
|
||||
@@ -36,10 +21,28 @@ namespace VeraCrypt
|
||||
{
|
||||
}
|
||||
|
||||
ResponseAPDU::ResponseAPDU(const vector<uint8>& data, uint16 SW)
|
||||
ResponseAPDU::ResponseAPDU(const vector<uint8>& data, uint16 SW) : m_data(data), m_SW(SW)
|
||||
{
|
||||
m_data = data;
|
||||
m_SW = SW;
|
||||
}
|
||||
|
||||
ResponseAPDU::ResponseAPDU(const ResponseAPDU& other) : m_data(other.m_data), m_SW(other.m_SW)
|
||||
{
|
||||
}
|
||||
|
||||
ResponseAPDU& ResponseAPDU::operator=(const ResponseAPDU& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
ResponseAPDU replacement(other);
|
||||
m_data.swap(replacement.m_data);
|
||||
m_SW = other.m_SW;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ResponseAPDU::~ResponseAPDU()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
uint32 ResponseAPDU::getNr()
|
||||
@@ -47,7 +50,7 @@ namespace VeraCrypt
|
||||
return (uint32)m_data.size();
|
||||
}
|
||||
|
||||
const vector<uint8> ResponseAPDU::getData()
|
||||
const vector<uint8>& ResponseAPDU::getData() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
@@ -67,13 +70,17 @@ namespace VeraCrypt
|
||||
return m_SW;
|
||||
}
|
||||
|
||||
const vector<uint8> ResponseAPDU::getBytes()
|
||||
vector<uint8> ResponseAPDU::getBytes()
|
||||
{
|
||||
vector<uint8> apdu;
|
||||
|
||||
AppendData(apdu, m_data.data(), m_data.size());
|
||||
apdu.push_back((uint8)getSW1());
|
||||
apdu.push_back((uint8)getSW2());
|
||||
if (m_data.size() > apdu.max_size() - 2)
|
||||
throw length_error("APDU response too large");
|
||||
// Allocate the complete output before copying sensitive data.
|
||||
apdu.resize(m_data.size() + 2);
|
||||
if (!m_data.empty())
|
||||
memcpy(apdu.data(), m_data.data(), m_data.size());
|
||||
apdu[m_data.size()] = (uint8)getSW1();
|
||||
apdu[m_data.size() + 1] = (uint8)getSW2();
|
||||
|
||||
return apdu;
|
||||
}
|
||||
@@ -85,7 +92,34 @@ namespace VeraCrypt
|
||||
|
||||
void ResponseAPDU::appendData(const uint8* data, size_t dataLen)
|
||||
{
|
||||
AppendData(m_data, data, dataLen);
|
||||
if (dataLen == 0)
|
||||
return;
|
||||
if (dataLen > m_data.max_size() - m_data.size())
|
||||
throw length_error("APDU response too large");
|
||||
|
||||
size_t oldSize = m_data.size();
|
||||
size_t newSize = oldSize + dataLen;
|
||||
if (newSize > m_data.capacity())
|
||||
{
|
||||
// vector growth would otherwise release the previous contents without wiping.
|
||||
vector<uint8> replacement;
|
||||
size_t capacity = m_data.capacity();
|
||||
if (capacity <= m_data.max_size() / 2 && capacity * 2 > newSize)
|
||||
replacement.reserve(capacity * 2);
|
||||
replacement.resize(newSize);
|
||||
if (oldSize > 0)
|
||||
memcpy(replacement.data(), m_data.data(), oldSize);
|
||||
memcpy(replacement.data() + oldSize, data, dataLen);
|
||||
// Copy the appended bytes before erasing, as data may refer to m_data.
|
||||
if (oldSize > 0)
|
||||
burn(m_data.data(), oldSize);
|
||||
m_data.swap(replacement);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_data.resize(newSize);
|
||||
memcpy(m_data.data() + oldSize, data, dataLen);
|
||||
}
|
||||
}
|
||||
|
||||
void ResponseAPDU::setSW(uint16 SW)
|
||||
@@ -95,17 +129,14 @@ namespace VeraCrypt
|
||||
|
||||
void ResponseAPDU::setBytes(const vector<uint8>& bytes)
|
||||
{
|
||||
clear();
|
||||
ResponseAPDU replacement;
|
||||
if (bytes.size() >= 2)
|
||||
{
|
||||
vector<uint8> SWBytes;
|
||||
m_data.resize(bytes.size() - 2);
|
||||
SWBytes.resize(2);
|
||||
|
||||
memcpy(m_data.data(), bytes.data(), bytes.size() - 2);
|
||||
memcpy(SWBytes.data(), bytes.data() + bytes.size() - 2, 2);
|
||||
m_SW = BytesToUInt16(SWBytes);
|
||||
replacement.appendData(bytes.data(), bytes.size() - 2);
|
||||
replacement.m_SW = (static_cast<uint16>(bytes[bytes.size() - 2]) << 8) | bytes.back();
|
||||
}
|
||||
m_data.swap(replacement.m_data);
|
||||
m_SW = replacement.m_SW;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,10 +20,14 @@ namespace VeraCrypt
|
||||
ResponseAPDU();
|
||||
|
||||
ResponseAPDU(const vector<uint8>& data, uint16 SW);
|
||||
ResponseAPDU(const ResponseAPDU& other);
|
||||
ResponseAPDU& operator=(const ResponseAPDU& other);
|
||||
~ResponseAPDU();
|
||||
|
||||
uint32 getNr();
|
||||
|
||||
const vector<uint8> getData();
|
||||
// The returned data remains owned by this response and is invalidated by mutation.
|
||||
const vector<uint8>& getData() const;
|
||||
|
||||
uint8 getSW1();
|
||||
|
||||
@@ -31,7 +35,8 @@ namespace VeraCrypt
|
||||
|
||||
uint16 getSW();
|
||||
|
||||
const vector<uint8> getBytes();
|
||||
// The caller owns this serialized copy and is responsible for erasing it.
|
||||
vector<uint8> getBytes();
|
||||
|
||||
void setSW(uint16 SW);
|
||||
void setBytes(const vector<uint8>& bytes);
|
||||
@@ -41,4 +46,4 @@ namespace VeraCrypt
|
||||
};
|
||||
};
|
||||
|
||||
#endif // TC_HEADER_Common_ResponseAPDU
|
||||
#endif // TC_HEADER_Common_ResponseAPDU
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "SCardReader.h"
|
||||
#include "PCSCException.h"
|
||||
#include "Platform/Finally.h"
|
||||
|
||||
#include <locale>
|
||||
|
||||
@@ -399,7 +400,9 @@ namespace VeraCrypt
|
||||
size_t indexOfLcData = 0;
|
||||
|
||||
vector<uint8> pbSendBuffer;
|
||||
finally_do_arg (vector<uint8> *, &pbSendBuffer, { if (!finally_arg->empty()) burn (finally_arg->data(), finally_arg->size()); });
|
||||
vector<uint8> pbRecvBuffer;
|
||||
finally_do_arg (vector<uint8> *, &pbRecvBuffer, { if (!finally_arg->empty()) burn (finally_arg->data(), finally_arg->size()); });
|
||||
DWORD cbSendLength = 0;
|
||||
DWORD cbRecvLength = 0;
|
||||
|
||||
@@ -639,9 +642,6 @@ namespace VeraCrypt
|
||||
|
||||
end:
|
||||
|
||||
burn(pbSendBuffer.data(), pbSendBuffer.size());
|
||||
burn(pbRecvBuffer.data(), pbRecvBuffer.size());
|
||||
|
||||
if (lRet != SCARD_S_SUCCESS)
|
||||
throw PCSCException(lRet);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace VeraCrypt
|
||||
}
|
||||
|
||||
/* Parsing one TLV node */
|
||||
shared_ptr<TLVNode> TLVParser::TLV_Parse_One(uint8* buf, size_t size)
|
||||
shared_ptr<TLVNode> TLVParser::TLV_Parse_One(const uint8* buf, size_t size)
|
||||
{
|
||||
size_t index = 0;
|
||||
size_t i = 0;
|
||||
@@ -205,7 +205,7 @@ namespace VeraCrypt
|
||||
}
|
||||
|
||||
/* Parsing TLV from a buffer and constructing TLV structure */
|
||||
shared_ptr<TLVNode> TLVParser::TLV_Parse(uint8* buf, size_t size)
|
||||
shared_ptr<TLVNode> TLVParser::TLV_Parse(const uint8* buf, size_t size)
|
||||
{
|
||||
shared_ptr<TLVNode> node = TLV_Parse_One(buf, size);
|
||||
TLV_Parse_Sub(node);
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace VeraCrypt
|
||||
static uint16 CheckBit(uint8 value, int bit);
|
||||
|
||||
/* Parsing one TLV node */
|
||||
static shared_ptr<TLVNode> TLV_Parse_One(uint8* buf, size_t size);
|
||||
static shared_ptr<TLVNode> TLV_Parse_One(const uint8* buf, size_t size);
|
||||
|
||||
/* Parsing all TLV nodes */
|
||||
static int TLV_Parse_SubNodes(shared_ptr<TLVNode> parent);
|
||||
@@ -54,7 +54,7 @@ namespace VeraCrypt
|
||||
public:
|
||||
|
||||
/* Parsing TLV from a buffer and constructing TLV structure */
|
||||
static shared_ptr<TLVNode> TLV_Parse(uint8* buf, size_t size);
|
||||
static shared_ptr<TLVNode> TLV_Parse(const uint8* buf, size_t size);
|
||||
|
||||
/* Finding a TLV node with a particular tag */
|
||||
static shared_ptr<TLVNode> TLV_Find(shared_ptr<TLVNode> node, uint16 tag);
|
||||
|
||||
Reference in New Issue
Block a user