pc-dart icon indicating copy to clipboard operation
pc-dart copied to clipboard

ECDSA Signature validation with base64 strings

Open KODA-Christian opened this issue 2 years ago • 4 comments

Hi! I need help verifying an EC signature which is a base64 string with the public key that is also a base64 string. Something like:

Signature: MEUCIBYmubxYx/Q5CWiKwbfxgZs8KXaB+KEW59izRU8IPbROAiEA3YPVuD2TxpJeQ8gZLUfFfNgrr96orTjTwEXy4mQQqqI=

Public Key: -----BEGIN PUBLIC KEY----- MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEN3ISIC6bUzVqAfbHnAl7iIO3le9UAqYM wymvYSQTxQQWr8mgc498BRjZqtEfnpRn95DpCxMJwcvPkwm4PFo/jg== -----END PUBLIC KEY-----

Thanks in advance

KODA-Christian avatar Apr 18 '22 23:04 KODA-Christian

The signature looks like DER-encoded ASN1. It is an ASN1 Sequence of the two ASN1 Integers r and s, which constitute an ECSignature.

  var sdecoded = base64Decode(signature);
  var bytes = Uint8List.fromList(sdecoded);
  var p = ASN1Parser(bytes);
  var seq = p.nextObject() as ASN1Sequence;
  var ar = seq.elements?[0] as ASN1Integer;
  var as = seq.elements?[1] as ASN1Integer;

  var r = ar.integer!;
  var s = as.integer!;

  var ecSignature = ECSignature(r, s);

The Key is a SubjectPublicKeyInfo ASN1-Structure. Decoding this for an ECPublicKey is way more complicated. On the top-level it is an ASN1 Sequence consisting of an AlgorithmIdentifier (with its own ASN1-Substructure) and an ASN1 Bitstring , which contains a DER-encoded representation of the public key (with a structure depending on the AlgorithmIdentifier, as a RSA public key is very different from an EC public key). BouncyCastle has a decoder for this. You may want to look at the bc implementation and port it to dart but that is really a lot of work, even if one restrict it to decoding a SubjectPublicKeyInfo containing an ECPublicKey.

Or you may use the dart-packages x509 and crypto_keys like this:

import 'package:crypto_keys/crypto_keys.dart' as crypto;
import 'package:x509/x509.dart' as x509;

ECDomainParameters createCurveParameters(crypto.Identifier curve) {
  var name = curve.name.split('/').last;
  switch (name) {
    case 'P-256':
      return ECDomainParameters('secp256r1');
    case 'P-256K':
      return ECDomainParameters('secp256k1');
    case 'P-384':
      return ECDomainParameters('secp384r1');
    case 'P-521':
      return ECDomainParameters('secp521r1');
  }
  throw ArgumentError('Unknwon curve type $name');
}

ECPublicKey getPublicKey(String publicKeyInfo) {
  var keyInfo = x509.parsePem(publicKeyInfo).first as x509.SubjectPublicKeyInfo;
  var key = keyInfo.subjectPublicKey as crypto.EcPublicKey;
  var d = createCurveParameters(key.curve);

  var ecPublickey =
      ECPublicKey(d.curve.createPoint(key.xCoordinate, key.yCoordinate), d);
  return ecPublickey;
}

Unfortunately this limits you to the curves supported by x509 and crypto_keys, which are just the NIST-curves.

Once you have both the ECSignature and the ECPublicKey - and the data signed as an Uint8List of bytes, verification is as follows:

var verifier = Signer('SHA-256/ECDSA')..init(false, PublicKeyParameters(ecPublicKey));
var result = verifier.verify(data, ecSignature);

Instead of 'SHA-256/..' use the digest-designator for the digest algorithm used to sign.

tallinn1960 avatar Apr 21 '22 19:04 tallinn1960

For decoding the public key I found a less limiting helper package 'basic_utils' on pub.dev. It features a function

ECPublicKey ecPublicKeyFromPem(String pem);

and seems to support all curves of pointycastle.

tallinn1960 avatar Apr 21 '22 20:04 tallinn1960

First of all thanks to you @tallinn1960,

My biggest problem was with converting to ECSignature, ECPublicKey,...

Looks great, I'm checking that out and will give feedback very soon.

Again, I really appreciate your help.

Thanks and best wishes.

KODA-Christian avatar Apr 22 '22 09:04 KODA-Christian

@KODA-Christian @tallinn1960 Converting a EC Signature to base64 and verifying a base64 EC signature is now available with the package basic_utils 4.3.0.

Take a look at the implementation :

  ///
  /// Verifying the given [signedData] with the given [publicKey] and the given [signature] in base64.
  /// Will return **true** if the given [signature] matches the [signedData].
  ///
  /// The default [algorithm] used is **SHA-1/ECDSA**. All supported algorihms are :
  ///
  /// * SHA-1/ECDSA
  /// * SHA-224/ECDSA
  /// * SHA-256/ECDSA
  /// * SHA-384/ECDSA
  /// * SHA-512/ECDSA
  /// * SHA-1/DET-ECDSA
  /// * SHA-224/DET-ECDSA
  /// * SHA-256/DET-ECDSA
  /// * SHA-384/DET-ECDSA
  /// * SHA-512/DET-ECDSA
  ///
  static bool ecVerifyBase64(
      ECPublicKey publicKey, Uint8List origData, String signature,
      {String algorithm = 'SHA-1/ECDSA'}) {
    var ecSignature = ecSignatureFromBase64(signature);

    return ecVerify(publicKey, origData, ecSignature, algorithm: algorithm);
  }

There is also an example on how to use the package for this case :

https://github.com/Ephenodrom/Dart-Basic-Utils/blob/master/example/elliptic_curve.dart

Ephenodrom avatar Jul 14 '22 10:07 Ephenodrom