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!

Interested in getting your voice heard by members of the Developer Marketing team at Oracle? Check out this post for AppDev or this post for AI focus group information.

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
170 views