Skip to Main Content

Embedded Technologies

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Help Extracting R and S values from KeyCard Sign in Dart

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)
	};
}
Comments
Post Details
Added on Feb 7 2024
0 comments
553 views