verifyEvmSignature function

VerifyResult verifyEvmSignature(
  1. VerifyEvmSignatureArgs args
)

"Did the device sign exactly what I sent, with the key I expected?" keccak digest + public-key recovery; the recovered address must equal the request's. Run it before broadcasting.

Implementation

VerifyResult verifyEvmSignature(VerifyEvmSignatureArgs args) {
  if (args.signature.length < 65) {
    return failed('signature is shorter than 65 bytes');
  }
  final reEncoded = args.reEncodedSignData;
  if (reEncoded != null && !equalBytes(reEncoded, args.signData)) {
    return failed(
        'the transaction to broadcast is not the one the device signed');
  }

  final Uint8List digest;
  switch (args.dataType) {
    case EvmDataType.transaction:
    case EvmDataType.typedTransaction:
      digest = keccak256(args.signData);
    case EvmDataType.personalMessage:
      digest = _personalSignDigest(args.signData);
    case EvmDataType.typedData:
      return unverifiable(
        'EIP-712: the digest is the hash of the structure, computed only on the device',
      );
    default:
      return failed('unknown dataType ${args.dataType}');
  }

  var vBig = BigInt.zero;
  for (final b in args.signature.sublist(64)) {
    vBig = (vBig << 8) | BigInt.from(b);
  }
  final recoveryId = foldRecoveryId(vBig);
  if (recoveryId != 0 && recoveryId != 1) {
    return failed('implausible recovery value');
  }

  Uint8List recovered;
  try {
    final compressed =
        Secp256k1.recover(args.signature.sublist(0, 64), digest, recoveryId);
    final point = Secp256k1.parsePublicKey(compressed);
    final uncompressed = Uint8List.fromList(point.getEncoded(false));
    recovered = keccak256(uncompressed.sublist(1)).sublist(12);
  } on ArgumentError catch (e) {
    return failed('signature could not be checked: ${e.message}');
  }

  final expected = args.address is Uint8List
      ? args.address as Uint8List
      : hexToBytes(args.address as String);
  if (expected.length != 20) {
    return failed('expected address must be 20 bytes, got ${expected.length}');
  }
  if (!equalBytes(recovered, expected)) {
    return failed(
      'the signature does not belong to this account (recovered 0x${bytesToHex(recovered)})',
    );
  }
  return verified;
}