AESDecrypt method

List<int> AESDecrypt(
  1. List<int> cipherText,
  2. SVPrivateKey recipientPrivateKey
)

Perform an ECIES decryption using AES for the symmetric cipher.

cipherText - The buffer to decrypt. Note that the buffer in this instance has a very specific encoding format called "BIE1" or "Electrum ECIES". It is in essence a serialization format with a built-in checksum.

  • bytes 0 - 4 : Magic value. Literally "BIE1".
  • bytes 4 - 37 : Compressed Public Key
  • bytes 37 - (length - 32) : Actual cipherText
  • bytes length - 32 : (last 32 bytes) Checksum valu

recipientPrivateKey - Private Key of the receiving party

Implementation

List<int> AESDecrypt(List<int> cipherText, SVPrivateKey recipientPrivateKey){

  //AES Cipher is calculated as
  //1) S = recipientPrivateKey o senderPublicKey
  //2) cipher = S.x

  if (cipherText.length < 37){
    throw Exception('Buffer is too small ');
  }

  final magic = utf8.decode(cipherText.sublist(0, 4));

  if ( magic != 'BIE1'){
    throw Exception('Not a BIE1-encoded buffer');
  }

  final senderPubkeyBuffer = cipherText.sublist(4, 37);
  final senderPublicKey = SVPublicKey.fromHex(HEX.encode(senderPubkeyBuffer));

  //calculate S = recipientPrivateKey o senderPublicKey
  final S = (senderPublicKey.point * recipientPrivateKey.privateKey)!; //point multiplication
  final cipher = S.x;

  if (cipherText.length - tagLength <= 37 ){
    throw Exception('Invalid Checksum detected. Combined sum of Checksum and Message makes no sense');
  }

  //validate the checksum bytes
  final pubkeyS = SVPublicKey.fromXY(S.x!.toBigInteger()!, S.y!.toBigInteger()!);
  final pubkeyBuffer = HEX.decode(pubkeyS.getEncoded(true));
  final pubkeyHash = SHA512Digest().process(pubkeyBuffer as Uint8List);

  //initialization vector parameters
  final iv = pubkeyHash.sublist(0, 16);
  final kE = pubkeyHash.sublist(16, 32);
  final kM = pubkeyHash.sublist(32, 64);
  final message = Uint8List.fromList(cipherText.sublist(0, cipherText.length - tagLength));

  final hmac = _calculateHmac(kM, message);
  final messageChecksum = cipherText.sublist(cipherText.length - tagLength, cipherText.length);

  if (!ListEquality().equals(messageChecksum, hmac)){
    throw Exception('HMAC checksum failed to validate');
  }

  //decrypt!
  CipherParameters params = PaddedBlockCipherParameters(ParametersWithIV(KeyParameter(kE), iv), null);
  BlockCipher decryptionCipher = PaddedBlockCipher("AES/CBC/PKCS7");
  decryptionCipher.init(false, params);

  final decrypted = decryptionCipher.process(cipherText.sublist(37, cipherText.length - tagLength) as Uint8List);
  return decrypted;
}