Ephenodrom / Dart-Basic-Utils

A dart package for many helper methods fitting common situations
MIT License
364 stars 77 forks source link

ECDSA Sign/Verifier #39

Closed azim92 closed 3 years ago

azim92 commented 3 years ago

Hi, was testing out your library since one of it's dependency is PointyCastle. Just would like to know if there is an ECDSA Sign/Verifier included in your CryptoUtils, or in another dart file?

Ephenodrom commented 3 years ago

@azim92 As far as i remember, the ECDSA Signing was not yet available when implementing the CryptoUtils. I will take a look on the current stage of the Pointycastle project and check if this is still true or not.

azim92 commented 3 years ago

@Ephenodrom

Thank you for your response. If you don't mind, could you have a look at my ECDSA verify/signer with SHA256? I'm not a knowledgeable individual for crypto related, so would like to inquire for ECDSA signer, which one is the standardize for generating an ECDSA signature? A Probabilistic Signature (Non-Static Signature) or Deterministic Signature (Static Signature)?

Static here indicates the same signature output per signature generation.

  import 'dart:convert';
  import 'dart:math';
  import 'dart:typed_data';

  import 'package:basic_utils/basic_utils.dart';
  import 'package:pointycastle/api.dart';
  import 'package:pointycastle/asn1.dart';
  import 'package:pointycastle/ecc/api.dart';
  import 'package:pointycastle/export.dart';
  import 'package:pointycastle/src/utils.dart' as pcUtils;
  import 'package:asn1lib/asn1lib.dart' as asnLib;

  static Uint8List ecSign(ECPrivateKey privateKey, Uint8List plainDataByte) {
    var signer = Signer('SHA-256/ECDSA');
    var privateParams = PrivateKeyParameter<ECPrivateKey>(privateKey);

    // probabilistic signature
    var secureRandom = FortunaRandom();
    var random = Random.secure();
    var seeds = List<int>.generate(32, (_) => random.nextInt(255));
    secureRandom.seed(KeyParameter(Uint8List.fromList(seeds)));
    var signatureParams = ParametersWithRandom(privateParams, secureRandom);
    signer.init(true, signatureParams);

    // deterministic signature
    // var signer = ECDSASigner(SHA256Digest(), HMac(SHA256Digest(), 64));
    // signer.init(true, PrivateKeyParameter<ECPrivateKey>(privateKey));

    return ecdsaSignatureToASN1Byte(signer.generateSignature(plainDataByte));
  }

   static bool ecVerify(
      ECPublicKey publicKey, Uint8List plainDataByte, Uint8List signature) {
    final verifier = ECDSASigner(SHA256Digest(), HMac(SHA256Digest(), 64));
    verifier.init(false, PublicKeyParameter<ECPublicKey>(publicKey));
    return verifier.verifySignature(
        plainDataByte, ecdsaSignatureFromASN1Byte(signature));
  }

  static Uint8List ecdsaSignatureToASN1Byte(ECSignature signature) {
    var algorithmSeq = new asnLib.ASN1Sequence();
    var r = new asnLib.ASN1Integer(signature.r);
    var s = new asnLib.ASN1Integer(signature.s);
    algorithmSeq.add(r);
    algorithmSeq.add(s);

    return algorithmSeq.encodedBytes;
  }

  static ECSignature ecdsaSignatureFromASN1Byte(Uint8List signatureByte) {
    var parser = ASN1Parser(signatureByte);
    var object = parser.nextObject() as ASN1Sequence;
    var r = pcUtils.decodeBigInt(object.elements.elementAt(0).valueBytes);
    var s = pcUtils.decodeBigInt(object.elements.elementAt(1).valueBytes);
    return ECSignature(r, s);
  }

for ecdsaSignatureToASN1Byte(...) function, I had to use ASN1 from asn1lib: ^0.8.1 package as the one provided in pointycastle: ^2.0.1 gives me an empty ASN1Sequence after adding ASN1Integer to it:

var algorithmSeq = new asnLib.ASN1Sequence();
var r = new asnLib.ASN1Integer(signature.r);
var s = new asnLib.ASN1Integer(signature.s);
algorithmSeq.add(r);
algorithmSeq.add(s);
Ephenodrom commented 3 years ago

@azim92 Please upgrade to version 3.0.0 and you will find two new methods for EC Sign and EC Verify. Please try it out and give some feedback.

PS: The documented list of supported algorithm is not complete, I will have to check again which algorithm are supported and update the list with the next release.

azim92 commented 3 years ago

Using basic_utils: ^3.0.0-nullsafety.0

My Test Cases:

import 'dart:typed_data';

import 'package:basic_utils/basic_utils.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pointycastle/ecc/api.dart';

void main() {
  var keyPair = CryptoUtils.generateEcKeyPair();
  Uint8List signDataByte = Uint8List.fromList('Hello'.codeUnits);

  test('TEST SHA-1/ECDSA Algorithm', (){
    var sig = CryptoUtils.ecSign(keyPair.privateKey as ECPrivateKey, signDataByte, algorithmName: 'SHA-1/ECDSA');
    expect(CryptoUtils.ecVerify(keyPair.publicKey as ECPublicKey, signDataByte, sig, algorithm: 'SHA-1/ECDSA'), true);
  });

  test('TEST SHA-3/ECDSA Algorithm', (){
    var sig = CryptoUtils.ecSign(keyPair.privateKey as ECPrivateKey, signDataByte, algorithmName: 'SHA-3/ECDSA');
    expect(CryptoUtils.ecVerify(keyPair.publicKey as ECPublicKey, signDataByte, sig, algorithm: 'SHA-3/ECDSA'), true);
  });

  test('TEST SHA-224/ECDSA Algorithm', (){
    var sig = CryptoUtils.ecSign(keyPair.privateKey as ECPrivateKey, signDataByte, algorithmName: 'SHA-224/ECDSA');
    expect(CryptoUtils.ecVerify(keyPair.publicKey as ECPublicKey, signDataByte, sig, algorithm: 'SHA-224/ECDSA'), true);
  });

  test('TEST SHA-256/ECDSA Algorithm', (){
    var sig = CryptoUtils.ecSign(keyPair.privateKey as ECPrivateKey, signDataByte, algorithmName: 'SHA-256/ECDSA');
    expect(CryptoUtils.ecVerify(keyPair.publicKey as ECPublicKey, signDataByte, sig, algorithm: 'SHA-256/ECDSA'), true);
  });

  test('TEST SHA-384/ECDSA Algorithm', (){
    var sig = CryptoUtils.ecSign(keyPair.privateKey as ECPrivateKey, signDataByte, algorithmName: 'SHA-384/ECDSA');
    expect(CryptoUtils.ecVerify(keyPair.publicKey as ECPublicKey, signDataByte, sig, algorithm: 'SHA-384/ECDSA'), true);
  });

  test('TEST SHA-512/ECDSA Algorithm', (){
    var sig = CryptoUtils.ecSign(keyPair.privateKey as ECPrivateKey, signDataByte, algorithmName: 'SHA-512/ECDSA');
    expect(CryptoUtils.ecVerify(keyPair.publicKey as ECPublicKey, signDataByte, sig, algorithm: 'SHA-512/ECDSA'), true);
  });

  test('TEST SHA-1/DET-ECDSA Algorithm', (){
    var sig = CryptoUtils.ecSign(keyPair.privateKey as ECPrivateKey, signDataByte, algorithmName: 'SHA-1/DET-ECDSA');
    expect(CryptoUtils.ecVerify(keyPair.publicKey as ECPublicKey, signDataByte, sig, algorithm: 'SHA-1/DET-ECDSA'), true);
  });

  test('TEST SHA-3/DET-ECDSA Algorithm', (){
    var sig = CryptoUtils.ecSign(keyPair.privateKey as ECPrivateKey, signDataByte, algorithmName: 'SHA-3/DET-ECDSA');
    expect(CryptoUtils.ecVerify(keyPair.publicKey as ECPublicKey, signDataByte, sig, algorithm: 'SHA-3/DET-ECDSA'), true);
  });

  test('TEST SHA-224/DET-ECDSA Algorithm', (){
    var sig = CryptoUtils.ecSign(keyPair.privateKey as ECPrivateKey, signDataByte, algorithmName: 'SHA-224/DET-ECDSA');
    expect(CryptoUtils.ecVerify(keyPair.publicKey as ECPublicKey, signDataByte, sig, algorithm: 'SHA-224/DET-ECDSA'), true);
  });

  test('TEST SHA-256/DET-ECDSA Algorithm', (){
    var sig = CryptoUtils.ecSign(keyPair.privateKey as ECPrivateKey, signDataByte, algorithmName: 'SHA-256/DET-ECDSA');
    expect(CryptoUtils.ecVerify(keyPair.publicKey as ECPublicKey, signDataByte, sig, algorithm: 'SHA-256/DET-ECDSA'), true);
  });

  test('TEST SHA-384/DET-ECDSA Algorithm', (){
    var sig = CryptoUtils.ecSign(keyPair.privateKey as ECPrivateKey, signDataByte, algorithmName: 'SHA-384/DET-ECDSA');
    expect(CryptoUtils.ecVerify(keyPair.publicKey as ECPublicKey, signDataByte, sig, algorithm: 'SHA-384/DET-ECDSA'), true);
  });

  test('TEST SHA-512/DET-ECDSA Algorithm', (){
    var sig = CryptoUtils.ecSign(keyPair.privateKey as ECPrivateKey, signDataByte, algorithmName: 'SHA-512/DET-ECDSA');
    expect(CryptoUtils.ecVerify(keyPair.publicKey as ECPublicKey, signDataByte, sig, algorithm: 'SHA-512/DET-ECDSA'), true);
  });

}

Results: image

Not too sure what happened with SHA-3/ECDSA and SHA-3/DET-ECDSA.

Your EC Sign will support only probabilistic signature I assume?

Ephenodrom commented 3 years ago

This seems to be more a problem of "Pointycastle". It seems the package does not support SHA-3/ECDSA and SHA-3/DET-ECDSA.

I would recommend to open an issue here : https://github.com/bcgit/pc-dart

Then this is possible I just have to update the pointycastle version.

Ephenodrom commented 3 years ago

The code documentation has now all working algorithm documented.