Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EthPrivateKey random creation with signature inconsistencies #104

Open
yangricardo opened this issue May 17, 2023 · 2 comments
Open

EthPrivateKey random creation with signature inconsistencies #104

yangricardo opened this issue May 17, 2023 · 2 comments

Comments

@yangricardo
Copy link

Hi,
I am developing a digital signature app based on ECDSA with Ethereum.
After several tests i noted that the random keys creation may be inconsistent by validating the signatures generated.
As a fix to my implementation i only return the EthPrivateKey after verify that some signature is valid. The main error i faced during validation belongs to this assert line

image

Uint8List unsignedIntToBytes(BigInt number) {

@xclud
Copy link
Owner

xclud commented May 18, 2023

Hi,
Thanks or reporting.

What do you mean by "inconsistent"? Do you get a signature not matching the public/private key?

@yangricardo
Copy link
Author

Hi, thanks for look this issue.
Yes, i have implemented a flow of bytes signature. Sometimes, i generate a key pair and when try to verify a signauture from it using by calling ecRecover(messageHash, signature) the assert call from unsignedIntToBytes throws a error...

To avoid this i only return key pairs that i can validate effectivelly its signature, but i am not sure if the library error is on random EhtPrivateKey.createRandom or at ecRecover...

Here some snippets:

import 'dart:convert';
import 'dart:typed_data';
import 'dart:math'; 
import 'package:web3dart/crypto.dart';
import 'package:web3dart/web3dart.dart';
import 'package:flutter/material.dart';
// ignore: implementation_imports
import 'package:web3dart/src/utils/typed_data.dart';

Wallet createRandomWallet(String password) {
    try {
      final Random random = Random.secure();
      final EthPrivateKey privateKey = EthPrivateKey.createRandom(random);
      final Wallet wallet = Wallet.createNew(privateKey, password, random);
      debugPrint(
          "Wallet Private Key ${bytesToHex(wallet.privateKey.privateKey, include0x: true)}");
      debugPrint("Wallet Public Address ${wallet.privateKey.address.hex}");
      final signatureTestBytes =
          sign(wallet.privateKey, privateKey.address.hex);
      debugPrint(
          "Signature Test ${bytesToHex(signatureTestBytes, include0x: true)}");
      final signatureValidation = isValidHexSignatureFromRawMessage(
          bytesToHex(signatureTestBytes, include0x: true),
          privateKey.address.hex,
          privateKey.address.hex);
      if (!signatureValidation) {
        throw Exception("Signature Validation Failed");
      }
      return wallet;
    } catch (e) {
      return createRandomWallet(password);
    }
  }

  Wallet openWalletFromJSON(String encodedJsonWallet, String password) {
    return Wallet.fromJson(encodedJsonWallet, password);
  }

  EthPrivateKey fromPrivateKeyHex(String privateKeyHex) {
    final privateKeyBytes = hexToBytes(strip0x(privateKeyHex));
    final privateKey = EthPrivateKey.fromHex(bytesToHex(privateKeyBytes));
    debugPrint(
        "Private Key ${bytesToHex(privateKey.privateKey, include0x: true)}");
    debugPrint("Public Address ${privateKey.address.hex}");
    return privateKey;
  }

  Uint8List strToBytes(String message) {
    return Uint8List.fromList(message.codeUnits);
  }

  String bytesToStr(Uint8List message) {
    return String.fromCharCodes(message);
  }

  Uint8List sign(EthPrivateKey privateKey, String message) {
    try {
      final messageBytes = strToBytes(message);
      final signature = privateKey.signPersonalMessageToUint8List(messageBytes);
      final signatureHex = bytesToHex(signature, include0x: true);
      debugPrint("signature: $signatureHex");
      return signature;
    } catch (e) {
      return sign(privateKey, message);
    }
  }

  Uint8List buildPrefixedMessage(String message) {
    final Uint8List payload = strToBytes(message);
    const messagePrefix = '\u0019Ethereum Signed Message:\n';
    final prefix = messagePrefix + payload.length.toString();
    final prefixBytes = ascii.encode(prefix);

    // will be a Uint8List, see the documentation of Uint8List.+
    final prefixedMessage = uint8ListFromList(prefixBytes + payload);
    debugPrint(
        "prefixedMessage: ${bytesToHex(prefixedMessage, include0x: true)}");
    return prefixedMessage;
  }

  Uint8List buildPrefixedMessageHash(String message) {
    final Uint8List prefixedMessage = buildPrefixedMessage(message);
    final Uint8List messageHash = keccak256(prefixedMessage);
    debugPrint("messageHash: ${bytesToHex(messageHash, include0x: true)}");
    return messageHash;
  }

  MsgSignature hexToMsgSignature(String signatureHex) {
    final signatureBytes = hexToBytes(strip0x(signatureHex));
    final r = Uint8List.sublistView(signatureBytes, 0, 32);
    final s = Uint8List.sublistView(signatureBytes, 32, 64);
    final v = signatureBytes[64];
    debugPrint(
        "r: ${bytesToHex(r, include0x: true)} s: ${bytesToHex(s, include0x: true)} v: ${v.toRadixString(16)}");
    return MsgSignature(bytesToInt(r.toList()), bytesToInt(s.toList()), v);
  }

  MsgSignature signatureBytesToMsgSignature(Uint8List signatureBytes) {
    return hexToMsgSignature(bytesToHex(signatureBytes, include0x: true));
  }

  EthereumAddress recoverSignatureAddress(
      MsgSignature signature, Uint8List messageHash) {
    final recoveredPublicKey = ecRecover(messageHash, signature);
    EthereumAddress recoveredAddress =
        EthereumAddress.fromPublicKey(recoveredPublicKey);
    debugPrint("recoveredAddress: ${recoveredAddress.hex}");
    return recoveredAddress;
  }

  bool isValidHexSignatureFromRawMessage(
      String hexSignature, String message, String hexAddress) {
    final signature = hexToMsgSignature(hexSignature);
    final messageHash = buildPrefixedMessageHash(message);
    final recoveredAddress = recoverSignatureAddress(signature, messageHash);

    final comparedToAddess = EthereumAddress.fromHex(hexAddress);
    final isValid = recoveredAddress == comparedToAddess;
    debugPrint(
        "isValid: $isValid recoveredAddress: ${recoveredAddress.hex} comparedToAddess: ${comparedToAddess.hex}");
    return isValid;
  }

void main(List<String> args) {
    createRandomWallet("test-password");
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants