Improved U2F APDU Message error handling.
This commit is contained in:
9
APDU.hpp
9
APDU.hpp
@@ -9,10 +9,9 @@ enum APDU : uint8_t
|
|||||||
|
|
||||||
enum APDU_STATUS : uint16_t
|
enum APDU_STATUS : uint16_t
|
||||||
{
|
{
|
||||||
SW_NO_ERROR = 0x9000,
|
SW_NO_ERROR = 0x9000,
|
||||||
|
SW_WRONG_DATA = 0x6A80,
|
||||||
SW_CONDITIONS_NOT_SATISFIED = 0x6985,
|
SW_CONDITIONS_NOT_SATISFIED = 0x6985,
|
||||||
SW_WRONG_DATA = 0x6A80,
|
SW_COMMAND_NOT_ALLOWED = 0x6986,
|
||||||
SW_WRONG_LENGTH = 0x6700,
|
SW_INS_NOT_SUPPORTED = 0x6D00
|
||||||
SW_CLA_NOT_SUPPORTED = 0x6E00,
|
|
||||||
SW_INS_NOT_SUPPORTED = 0x6D00
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,8 +14,16 @@ using namespace std;
|
|||||||
U2F_Authenticate_APDU::U2F_Authenticate_APDU(const U2F_Msg_CMD &msg, const vector<uint8_t> &data)
|
U2F_Authenticate_APDU::U2F_Authenticate_APDU(const U2F_Msg_CMD &msg, const vector<uint8_t> &data)
|
||||||
: U2F_Msg_CMD{ msg }
|
: U2F_Msg_CMD{ msg }
|
||||||
{
|
{
|
||||||
if (data.size() < 66)
|
if (p2 != 0)
|
||||||
throw runtime_error{ "Invalid authentication request" };
|
{
|
||||||
|
//Invalid U2F (APDU) parameter detected
|
||||||
|
throw APDU_STATUS::SW_COMMAND_NOT_ALLOWED;
|
||||||
|
}
|
||||||
|
else if (data.size() < 66)
|
||||||
|
{
|
||||||
|
//Invalid authentication request
|
||||||
|
throw APDU_STATUS::SW_WRONG_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
copy(data.begin() + 0, data.begin() + 32, challengeP.begin());
|
copy(data.begin() + 0, data.begin() + 32, challengeP.begin());
|
||||||
copy(data.begin() + 32, data.begin() + 64, appParam.begin());
|
copy(data.begin() + 32, data.begin() + 64, appParam.begin());
|
||||||
@@ -27,20 +35,11 @@ U2F_Authenticate_APDU::U2F_Authenticate_APDU(const U2F_Msg_CMD &msg, const vecto
|
|||||||
|
|
||||||
void U2F_Authenticate_APDU::respond(const uint32_t channelID) const
|
void U2F_Authenticate_APDU::respond(const uint32_t channelID) const
|
||||||
{
|
{
|
||||||
U2FMessage msg{};
|
|
||||||
msg.cid = channelID;
|
|
||||||
msg.cmd = U2FHID_MSG;
|
|
||||||
auto statusCode = APDU_STATUS::SW_NO_ERROR;
|
|
||||||
|
|
||||||
auto &response = msg.data;
|
|
||||||
|
|
||||||
if (keyH.size() != sizeof(Storage::KeyHandle))
|
if (keyH.size() != sizeof(Storage::KeyHandle))
|
||||||
{
|
{
|
||||||
//Respond with error code - key handle is of wrong size
|
//Respond with error code - key handle is of wrong size
|
||||||
cerr << "Invalid key handle length" << endl;
|
cerr << "Invalid key handle length" << endl;
|
||||||
statusCode = APDU_STATUS::SW_WRONG_DATA;
|
this->error(channelID, APDU_STATUS::SW_WRONG_DATA);
|
||||||
response.insert(response.end(), FIELD_BE(statusCode));
|
|
||||||
msg.write();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,14 +49,19 @@ void U2F_Authenticate_APDU::respond(const uint32_t channelID) const
|
|||||||
{
|
{
|
||||||
//Respond with error code - key handle doesn't exist in storage
|
//Respond with error code - key handle doesn't exist in storage
|
||||||
cerr << "Invalid key handle" << endl;
|
cerr << "Invalid key handle" << endl;
|
||||||
statusCode = APDU_STATUS::SW_WRONG_DATA;
|
this->error(channelID, SW_WRONG_DATA);
|
||||||
response.insert(response.end(), FIELD_BE(statusCode));
|
|
||||||
msg.write();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto appMatches = (Storage::appParams.at(keyHB) == appParam);
|
auto appMatches = (Storage::appParams.at(keyHB) == appParam);
|
||||||
|
|
||||||
|
U2FMessage msg{};
|
||||||
|
msg.cid = channelID;
|
||||||
|
msg.cmd = U2FHID_MSG;
|
||||||
|
|
||||||
|
auto &response = msg.data;
|
||||||
|
APDU_STATUS statusCode = APDU_STATUS::SW_NO_ERROR;
|
||||||
|
|
||||||
switch (p1)
|
switch (p1)
|
||||||
{
|
{
|
||||||
case ControlCode::CheckOnly:
|
case ControlCode::CheckOnly:
|
||||||
@@ -76,10 +80,8 @@ void U2F_Authenticate_APDU::respond(const uint32_t channelID) const
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
cerr << "Unknown APDU command" << endl;
|
cerr << "Unknown APDU authentication command" << endl;
|
||||||
statusCode = APDU_STATUS::SW_WRONG_DATA;
|
this->error(channelID, APDU_STATUS::SW_COMMAND_NOT_ALLOWED);
|
||||||
response.insert(response.end(), FIELD_BE(statusCode));
|
|
||||||
msg.write();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,14 @@ shared_ptr<U2F_CMD> U2F_CMD::get(const shared_ptr<U2FMessage> uMsg)
|
|||||||
switch (uMsg->cmd)
|
switch (uMsg->cmd)
|
||||||
{
|
{
|
||||||
case U2FHID_MSG:
|
case U2FHID_MSG:
|
||||||
return U2F_Msg_CMD::generate(uMsg);
|
try
|
||||||
|
{
|
||||||
|
return U2F_Msg_CMD::generate(uMsg);
|
||||||
|
}
|
||||||
|
catch (runtime_error)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
case U2FHID_INIT:
|
case U2FHID_INIT:
|
||||||
return make_shared<U2F_Init_CMD>(uMsg);
|
return make_shared<U2F_Init_CMD>(uMsg);
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -47,12 +47,22 @@ shared_ptr<U2F_Msg_CMD> U2F_Msg_CMD::generate(const shared_ptr<U2FMessage> uMsg)
|
|||||||
if (uMsg->cmd != U2FHID_MSG)
|
if (uMsg->cmd != U2FHID_MSG)
|
||||||
throw runtime_error{ "Failed to get U2F Msg uMsg" };
|
throw runtime_error{ "Failed to get U2F Msg uMsg" };
|
||||||
else if (uMsg->data.size() < 4)
|
else if (uMsg->data.size() < 4)
|
||||||
|
{
|
||||||
|
U2F_Msg_CMD::error(uMsg->cid, APDU_STATUS::SW_WRONG_LENGTH);
|
||||||
throw runtime_error{ "Msg data is incorrect size" };
|
throw runtime_error{ "Msg data is incorrect size" };
|
||||||
|
}
|
||||||
|
|
||||||
U2F_Msg_CMD cmd;
|
U2F_Msg_CMD cmd;
|
||||||
auto &dat = uMsg->data;
|
auto &dat = uMsg->data;
|
||||||
|
|
||||||
cmd.cla = dat[0];
|
cmd.cla = dat[0];
|
||||||
|
|
||||||
|
if (cmd.cla != 0)
|
||||||
|
{
|
||||||
|
U2F_Msg_CMD::error(uMsg->cid, APDU_STATUS::SW_COMMAND_NOT_ALLOWED);
|
||||||
|
throw runtime_error{ "Invalid CLA value in U2F Message" };
|
||||||
|
}
|
||||||
|
|
||||||
cmd.ins = dat[1];
|
cmd.ins = dat[1];
|
||||||
cmd.p1 = dat[2];
|
cmd.p1 = dat[2];
|
||||||
cmd.p2 = dat[3];
|
cmd.p2 = dat[3];
|
||||||
@@ -64,7 +74,10 @@ shared_ptr<U2F_Msg_CMD> U2F_Msg_CMD::generate(const shared_ptr<U2FMessage> uMsg)
|
|||||||
if (usesData.at(cmd.ins) || data.size() > 3)
|
if (usesData.at(cmd.ins) || data.size() > 3)
|
||||||
{
|
{
|
||||||
if (cBCount == 0)
|
if (cBCount == 0)
|
||||||
|
{
|
||||||
|
U2F_Msg_CMD::error(uMsg->cid, APDU_STATUS::SW_WRONG_LENGTH);
|
||||||
throw runtime_error{ "Invalid command - should have attached data" };
|
throw runtime_error{ "Invalid command - should have attached data" };
|
||||||
|
}
|
||||||
|
|
||||||
if (data[0] != 0) //1 byte length
|
if (data[0] != 0) //1 byte length
|
||||||
{
|
{
|
||||||
@@ -79,14 +92,30 @@ shared_ptr<U2F_Msg_CMD> U2F_Msg_CMD::generate(const shared_ptr<U2FMessage> uMsg)
|
|||||||
|
|
||||||
endPtr = startPtr + cmd.lc;
|
endPtr = startPtr + cmd.lc;
|
||||||
|
|
||||||
cmd.le = getLe(data.end() - endPtr, vector<uint8_t>(endPtr, data.end()));
|
try
|
||||||
|
{
|
||||||
|
cmd.le = getLe(data.end() - endPtr, vector<uint8_t>(endPtr, data.end()));
|
||||||
|
}
|
||||||
|
catch (runtime_error)
|
||||||
|
{
|
||||||
|
U2F_Msg_CMD::error(uMsg->cid, APDU_STATUS::SW_WRONG_LENGTH);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cmd.lc = 0;
|
cmd.lc = 0;
|
||||||
endPtr = startPtr;
|
endPtr = startPtr;
|
||||||
|
|
||||||
cmd.le = getLe(cBCount, data);
|
try
|
||||||
|
{
|
||||||
|
cmd.le = getLe(cBCount, data);
|
||||||
|
}
|
||||||
|
catch (runtime_error)
|
||||||
|
{
|
||||||
|
U2F_Msg_CMD::error(uMsg->cid, APDU_STATUS::SW_WRONG_LENGTH);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto dBytes = vector<uint8_t>(startPtr, endPtr);
|
const auto dBytes = vector<uint8_t>(startPtr, endPtr);
|
||||||
@@ -124,16 +153,25 @@ shared_ptr<U2F_Msg_CMD> U2F_Msg_CMD::generate(const shared_ptr<U2FMessage> uMsg)
|
|||||||
"\t\t</table>\n"
|
"\t\t</table>\n"
|
||||||
"\t\t<br />", cmd.le);
|
"\t\t<br />", cmd.le);
|
||||||
|
|
||||||
switch (cmd.ins)
|
try
|
||||||
{
|
{
|
||||||
case APDU::U2F_REG:
|
switch (cmd.ins)
|
||||||
return make_shared<U2F_Register_APDU>(cmd, dBytes);
|
{
|
||||||
case APDU::U2F_AUTH:
|
case APDU::U2F_REG:
|
||||||
return make_shared<U2F_Authenticate_APDU>(cmd, dBytes);
|
return make_shared<U2F_Register_APDU>(cmd, dBytes);
|
||||||
case APDU::U2F_VER:
|
case APDU::U2F_AUTH:
|
||||||
return make_shared<U2F_Version_APDU>(cmd);
|
return make_shared<U2F_Authenticate_APDU>(cmd, dBytes);
|
||||||
default:
|
case APDU::U2F_VER:
|
||||||
throw runtime_error{ "Unknown APDU command issued" };
|
return make_shared<U2F_Version_APDU>(cmd);
|
||||||
|
default:
|
||||||
|
cerr << "Invalid command used" << endl;
|
||||||
|
throw APDU_STATUS::SW_INS_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const APDU_STATUS e)
|
||||||
|
{
|
||||||
|
U2F_Msg_CMD::error(uMsg->cid, e);
|
||||||
|
throw runtime_error{ "APDU construction error" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,14 @@ U2F_Register_APDU::U2F_Register_APDU(const U2F_Msg_CMD &msg, const vector<uint8_
|
|||||||
: U2F_Msg_CMD{ msg }
|
: U2F_Msg_CMD{ msg }
|
||||||
{
|
{
|
||||||
if (data.size() != 64)
|
if (data.size() != 64)
|
||||||
throw runtime_error{ "Incorrect registration size" };
|
{
|
||||||
|
//Incorrect registration size
|
||||||
|
throw APDU_STATUS::SW_WRONG_LENGTH;
|
||||||
|
}
|
||||||
else if (p1 != 0x00 || p2 != 0x00)
|
else if (p1 != 0x00 || p2 != 0x00)
|
||||||
{
|
{
|
||||||
cerr << "Ins: " << static_cast<uint32_t>(ins) << ", p1: " << static_cast<uint32_t>(p1) << ", p2: " << static_cast<uint32_t>(p2) << endl;
|
//Invalid U2F Message (APDU) parameters detected
|
||||||
cerr << "Invalid APDU parameters detected" << endl;
|
throw APDU_STATUS::SW_COMMAND_NOT_ALLOWED;
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(data.data() + 0, data.data() + 32, challengeP.begin());
|
copy(data.data() + 0, data.data() + 32, challengeP.begin());
|
||||||
@@ -30,7 +33,7 @@ U2F_Register_APDU::U2F_Register_APDU(const U2F_Msg_CMD &msg, const vector<uint8_
|
|||||||
Storage::PrivKey privKey{};
|
Storage::PrivKey privKey{};
|
||||||
Storage::PubKey pubKey{};
|
Storage::PubKey pubKey{};
|
||||||
|
|
||||||
//Unsure if necessary
|
//First byte must be 0x04 for some reason
|
||||||
pubKey[0] = 0x04;
|
pubKey[0] = 0x04;
|
||||||
|
|
||||||
uECC_make_key(pubKey.data() + 1, privKey.data(), uECC_secp256r1());
|
uECC_make_key(pubKey.data() + 1, privKey.data(), uECC_secp256r1());
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ using namespace std;
|
|||||||
|
|
||||||
U2F_Version_APDU::U2F_Version_APDU(const U2F_Msg_CMD &msg)
|
U2F_Version_APDU::U2F_Version_APDU(const U2F_Msg_CMD &msg)
|
||||||
{
|
{
|
||||||
//Don't actually respond yet
|
//Don't actually respond yet unless invalid
|
||||||
|
if (msg.p1 != 0 || msg.p2 != 0)
|
||||||
|
throw APDU_STATUS::SW_COMMAND_NOT_ALLOWED;
|
||||||
|
else if (msg.data.size() != 0)
|
||||||
|
throw APDU_STATUS::SW_WRONG_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
void U2F_Version_APDU::respond(const uint32_t channelID) const
|
void U2F_Version_APDU::respond(const uint32_t channelID) const
|
||||||
|
|||||||
Reference in New Issue
Block a user