U2fResponseConverter.cpp [plain text]
#include "config.h"
#include "U2fResponseConverter.h"
#if ENABLE(WEB_AUTHN)
#include "CommonCryptoDERUtilities.h"
#include "FidoConstants.h"
#include "WebAuthenticationConstants.h"
#include "WebAuthenticationUtils.h"
namespace fido {
using namespace WebCore;
namespace {
const uint8_t uncompressedKey = 0x04;
const uint8_t makeCredentialFlags = 0b01000001; const uint8_t minSignatureLength = 71;
const uint8_t maxSignatureLength = 73;
const size_t flagIndex = 0;
const size_t counterIndex = 1;
const size_t signatureIndex = 5;
static Vector<uint8_t> extractECPublicKeyFromU2fRegistrationResponse(const Vector<uint8_t>& u2fData)
{
size_t pos = kReservedLength;
if (u2fData.size() <= pos || u2fData[pos] != uncompressedKey)
return { };
pos++;
if (u2fData.size() < pos + 2 * ES256FieldElementLength)
return { };
Vector<uint8_t> x;
x.append(u2fData.data() + pos, ES256FieldElementLength);
pos += ES256FieldElementLength;
Vector<uint8_t> y;
y.append(u2fData.data() + pos, ES256FieldElementLength);
return encodeES256PublicKeyAsCBOR(WTFMove(x), WTFMove(y));
}
static Vector<uint8_t> extractCredentialIdFromU2fRegistrationResponse(const Vector<uint8_t>& u2fData)
{
size_t pos = kU2fKeyHandleLengthOffset;
if (u2fData.size() <= pos)
return { };
size_t credentialIdLength = u2fData[pos];
pos++;
if (u2fData.size() < pos + credentialIdLength)
return { };
Vector<uint8_t> credentialId;
credentialId.append(u2fData.data() + pos, credentialIdLength);
return credentialId;
}
static Vector<uint8_t> createAttestedCredentialDataFromU2fRegisterResponse(const Vector<uint8_t>& u2fData, const Vector<uint8_t>& publicKey)
{
auto credentialId = extractCredentialIdFromU2fRegistrationResponse(u2fData);
if (credentialId.isEmpty())
return { };
return buildAttestedCredentialData(Vector<uint8_t>(aaguidLength, 0), credentialId, publicKey);
}
static size_t parseX509Length(const Vector<uint8_t>& u2fData, size_t offset)
{
if (u2fData.size() <= offset || u2fData[offset] != SequenceMark)
return 0;
offset++;
if (u2fData.size() <= offset)
return 0;
const auto sequenceLengthLength = bytesUsedToEncodedLength(u2fData[offset]);
if (sequenceLengthLength > sizeof(size_t) || (u2fData.size() < offset + sequenceLengthLength))
return 0;
size_t sequenceLength = sequenceLengthLength == 1 ? u2fData[offset] : 0;
offset++;
for (auto i = sequenceLengthLength - 1; i; i--, offset++)
sequenceLength += u2fData[offset] << (i - 1) * 8;
return sequenceLength + sequenceLengthLength + sizeof(SequenceMark);
}
static cbor::CBORValue::MapValue createFidoAttestationStatementFromU2fRegisterResponse(const Vector<uint8_t>& u2fData, size_t offset)
{
auto x509Length = parseX509Length(u2fData, offset);
if (!x509Length || u2fData.size() < offset + x509Length)
return { };
Vector<uint8_t> x509;
x509.append(u2fData.data() + offset, x509Length);
offset += x509Length;
Vector<uint8_t> signature;
signature.append(u2fData.data() + offset, u2fData.size() - offset);
if (signature.size() < minSignatureLength || signature.size() > maxSignatureLength)
return { };
cbor::CBORValue::MapValue attestationStatementMap;
attestationStatementMap[cbor::CBORValue("sig")] = cbor::CBORValue(WTFMove(signature));
Vector<cbor::CBORValue> cborArray;
cborArray.append(cbor::CBORValue(WTFMove(x509)));
attestationStatementMap[cbor::CBORValue("x5c")] = cbor::CBORValue(WTFMove(cborArray));
return attestationStatementMap;
}
}
Optional<PublicKeyCredentialData> readU2fRegisterResponse(const String& rpId, const Vector<uint8_t>& u2fData)
{
auto publicKey = extractECPublicKeyFromU2fRegistrationResponse(u2fData);
if (publicKey.isEmpty())
return WTF::nullopt;
auto attestedCredentialData = createAttestedCredentialDataFromU2fRegisterResponse(u2fData, publicKey);
if (attestedCredentialData.isEmpty())
return WTF::nullopt;
auto credentialId = extractCredentialIdFromU2fRegistrationResponse(u2fData);
ASSERT(!credentialId.isEmpty());
auto authData = buildAuthData(rpId, makeCredentialFlags, 0, attestedCredentialData);
auto fidoAttestationStatement = createFidoAttestationStatementFromU2fRegisterResponse(u2fData, kU2fKeyHandleOffset + credentialId.size());
if (fidoAttestationStatement.empty())
return WTF::nullopt;
auto attestationObject = buildAttestationObject(WTFMove(authData), "fido-u2f", WTFMove(fidoAttestationStatement));
return PublicKeyCredentialData { ArrayBuffer::create(credentialId.data(), credentialId.size()), true, nullptr, ArrayBuffer::create(attestationObject.data(), attestationObject.size()), nullptr, nullptr, nullptr };
}
Optional<PublicKeyCredentialData> readU2fSignResponse(const String& rpId, const Vector<uint8_t>& keyHandle, const Vector<uint8_t>& u2fData)
{
if (keyHandle.isEmpty() || u2fData.size() <= signatureIndex)
return WTF::nullopt;
auto flags = u2fData[flagIndex];
uint32_t counter = u2fData[counterIndex] << 24;
counter += u2fData[counterIndex + 1] << 16;
counter += u2fData[counterIndex + 2] << 8;
counter += u2fData[counterIndex + 3];
auto authData = buildAuthData(rpId, flags, counter, { });
return PublicKeyCredentialData { ArrayBuffer::create(keyHandle.data(), keyHandle.size()), false, nullptr, nullptr, ArrayBuffer::create(authData.data(), authData.size()), ArrayBuffer::create(u2fData.data() + signatureIndex, u2fData.size() - signatureIndex), nullptr };
}
}
#endif // ENABLE(WEB_AUTHN)