Hello, I'm creating a mobile application using Flutter and Dart and having issues getting the R and S values from the LiteCard.
For reference, this is the documentation for the card. https://keycard.tech/docs/apdu/sign.html
No matter which way I sublist the response from KeyCard, I can't seem to get a valid r and s value. Can anybody see anything wrong with this code? Please let me know if you need more information.
Uint8List encryptData(Uint8List data, Uint8List encryptionKey, Uint8List iv) {
var paddedData = padData(data);
final cipher = CBCBlockCipher(AESEngine());
cipher.init(true, ParametersWithIV(KeyParameter(encryptionKey), iv));
var encryptedData = _processBlocks(cipher, paddedData);
return encryptedData;
}
Uint8List _processBlocks(BlockCipher cipher, Uint8List data) {
var out = Uint8List(data.length); // Output buffer
for (var offset = 0; offset < data.length; offset += cipher.blockSize) {
cipher.processBlock(data, offset, out, offset);
}
return out;
}
Uint8List decryptData2(Uint8List encryptedData, Uint8List decryptionKey, Uint8List iv) {
final cipher = CBCBlockCipher(AESEngine());
cipher.init(false, ParametersWithIV(KeyParameter(decryptionKey), iv)); // false for decryption
var decryptedData = _processBlocks(cipher, encryptedData);
return removeCustomPadding(decryptedData);
}
Uint8List removeCustomPadding(Uint8List data) {
// Find the padding start index
var paddingStartIndex = data.lastIndexOf(0x80);
if (paddingStartIndex == -1) {
throw const FormatException("Invalid padding");
}
return Uint8List.fromList(data.sublist(0, paddingStartIndex));
}
Future<Map<Object, Object>> sign(Uint8List hashBuffer) async {
Uint8List encryptionIV = await getEncryptionIV();
Uint8List encryptionKey = await getEncryptionKey();
Uint8List macKey = await getMacKey();
Uint8List encryptedData = encryptData(hashBuffer, encryptionKey, encryptionIV);
Uint8List mac = calculateMac(0x80, 0xC0, 0x00, 0x00, encryptedData.length + 16, encryptedData, macKey);
Uint8List cmd = Uint8List.fromList([0x80, 0xC0, 0x00, 0x00, encryptedData.length + mac.length, ...mac, ...encryptedData]);
Uint8List result = await FlutterNfcKit.transceive(cmd, timeout: const Duration(seconds: 5));
Uint8List macReceived = result.sublist(0, 16);
bool verifyMacResp = verifyMac(macReceived, result.sublist(16, result.length - 2), macKey);
if (!verifyMacResp) {
// print('Verify Mac: $verifyMacResp');
throw Exception('sign method failed to verify mac!');
}
await storeEncryptionIV(macReceived);
Uint8List decryptedResponse = decryptData2(result.sublist(16, result.length - 2), encryptionKey, mac);
print(decryptedResponse);
Uint8List publicKeyBytes = decryptedResponse.sublist(5, 5 + 65);
print(hex.encode(publicKeyBytes).length);
ECDomainParameters domainParams = ECDomainParameters('secp256k1');
var publicKey = domainParams.curve.decodePoint(publicKeyBytes);
ECPublicKey ecPublicKey = ECPublicKey(
publicKey, domainParams
);
// print(ecPublicKey.Q);
Uint8List remainingBuffer = decryptedResponse.sublist(5 + 65);
int indexOfR = remainingBuffer.indexOf(0x02);
int lengthOfR = remainingBuffer[indexOfR + 1];
print('Length of R: $lengthOfR');
Uint8List rBytes = remainingBuffer.sublist(indexOfR + 2, indexOfR + 2 + lengthOfR);
Uint8List sBytes = remainingBuffer.sublist(indexOfR + 2 + lengthOfR + 2, indexOfR + 2 + lengthOfR + 2 + 32);
String rHex = hex.encode(rBytes);
String sHex = hex.encode(sBytes);
BigInt r = BigInt.parse(rHex, radix: 16);
BigInt s = BigInt.parse(sHex, radix: 16);
ECSignature signature = ECSignature(r, s);
Signer verifier = Signer('SHA-256/ECDSA');
verifier.init(false, PublicKeyParameter<ECPublicKey>(ecPublicKey));
bool isValid = verifier.verifySignature(hashBuffer, signature);
print('Signature validation: $isValid');
storeEncryptionIV(macReceived);
// Get RecoveryId
int recoveryId = -1;
for (int i = 27; i <= 28; i++) {
Uint8List recoveredBytes = ecRecover(hashBuffer, MsgSignature(r, s, i));
if (hex.encode(publicKeyBytes) == hex.encode(recoveredBytes)) {
recoveryId = i;
break;
}
}
return {
'rHex': rHex,
'sHex': sHex,
'recoveryId': recoveryId,
'publicHexKey': hex.encode(publicKeyBytes)
};
}