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

addition: ES256 Signer #5

Open
shrugs opened this issue Dec 26, 2018 · 2 comments
Open

addition: ES256 Signer #5

shrugs opened this issue Dec 26, 2018 · 2 comments

Comments

@shrugs
Copy link

shrugs commented Dec 26, 2018

I've written an ES256 signer that I'm using in a personal project. Unfortunately I don't have the time to write a full pull request with tests, but I'd like to make the code available for searchers or a future implementor.

The signature encode/decode comes from crypto_keys, which might have additional LICENSE restrictions.

import 'dart:typed_data';

import "package:pointycastle/pointycastle.dart";
import 'package:corsac_jwt/corsac_jwt.dart';

class ES256Signer implements JWTSigner {
  String get algorithm => 'ES256';

  final AsymmetricKeyPair<PublicKey, PrivateKey> pair;

  ES256Signer({
    this.pair,
  });

  @override
  List<int> sign(List<int> data) {
    final pcSigner = Signer("SHA-256/DET-ECDSA");
    pcSigner.init(
      true,
      PrivateKeyParameter(pair.privateKey),
    );
    final ECSignature sig = pcSigner.generateSignature(data);
    var length = 32;
    var bytes = Uint8List(length * 2);
    bytes.setRange(0, length, _bigIntToBytes(sig.r, length).toList().reversed);
    bytes.setRange(
        length, length * 2, _bigIntToBytes(sig.s, length).toList().reversed);
    return bytes;
  }

  @override
  bool verify(List<int> data, List<int> signature) {
    final verifier = Signer("SHA-256/DET-ECDSA");
    verifier.init(false, PublicKeyParameter(pair.publicKey));
    final sig = ECSignature(
      _bigIntFromBytes(signature.take(32)),
      _bigIntFromBytes(signature.skip(32)),
    );
    return verifier.verifySignature(data, sig);
  }
}
®c
final _b256 = BigInt.from(256);

Iterable<int> _bigIntToBytes(BigInt v, int length) sync* {
  for (var i = 0; i < length; i++) {
    yield (v % _b256).toInt();
    v = v ~/ _b256;
  }
}

BigInt _bigIntFromBytes(Iterable<int> bytes) {
  return bytes.fold(BigInt.zero, (a, b) => a * _b256 + BigInt.from(b));
}
@pulyaevskiy
Copy link
Contributor

Thanks for the example!

Just wondering which package/tool are you using to read/decode public and private keys in your project? I used rsa_pkcs but it's not maintained anymore, unfortunately.

@shrugs
Copy link
Author

shrugs commented Dec 30, 2018

😅 I just wrote my own super hackily; private keys are just bigints, so .toString/.parse and for public keys I encode them as uncompressed octet strings like

const String kUncompressedPrefix = "4";

/// serializes public key as uncompressed octect string
String serializePublicKey(ECPublicKey publicKey) {
  return kUncompressedPrefix +
      bigIntToHexString(publicKey.Q.x.toBigInteger(), 64) +
      bigIntToHexString(publicKey.Q.y.toBigInteger(), 64);
}

String bigIntToHexString(BigInt n, int length) => n.toRadixString(16).padLeft(length, '0');

which is all I need for my app (using them as identifiers for auth). But I was very annoyed at the lack of an obvious mechanism of encoding them for use with other libraries; I had to dig into the ruby openssl source docs to see what it expected and reverse engineer it from there.

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