PointyCastle / pointycastle

Moved into the Bouncy Castle project: https://github.com/bcgit/pc-dart
MIT License
273 stars 74 forks source link

SHA-256/ECDSA signer gives invalid results with some inputs #96

Open mpcomplete opened 8 years ago

mpcomplete commented 8 years ago

[ migrated from https://github.com/izaera/cipher/issues/95 ]

I'm using pointycastle in a project, and I have a simple fuzz tester to verify my sign+verify code is correct. Occasionally it will fail with certain seeds to the SecureRandom number generator. Here is a sample reduced test case that fails. (You can add this code to pointycastle/test/signers/ecdsa_signer_test.dart to see it fail in cipher's test suite).

import 'dart:typed_data'; import "package:test/test.dart"; void fail() { // Bad seed: List key = [ 67, 3, 241, 75, 143, 78, 115, 99, 21, 242, 180, 43, 26, 7, 194, 20 ]; List iv = [ 87, 117, 137, 182, 2, 199, 132, 230, 120, 12, 109, 177, 34, 197, 186, 206 ]; KeyParameter keyParam = new KeyParameter(new Uint8List.fromList(key)); ParametersWithIV paramsIV = new ParametersWithIV(keyParam, new Uint8List.fromList(iv)); SecureRandom random = new SecureRandom('AES/CTR/AUTO-SEED-PRNG') ..seed(paramsIV);

// Bad test pattern: final Uint8List kTestBytes = new Uint8List.fromList([1, 2, 3]);

// Bad key pair: final ECDomainParameters ecDomain = new ECDomainParameters('prime256v1'); ECPrivateKey privateKey = new ECPrivateKey( new BigInteger("49796319969581709255674594372216982488391390668014565621687620773258125227296"), ecDomain); ECPoint Q = privateKey.parameters.G * privateKey.d; ECPublicKey publicKey = new ECPublicKey(Q, privateKey.parameters);

Signer signer = new Signer('SHA-256/ECDSA'); PrivateKeyParameter signParams = new PrivateKeyParameter(privateKey); signer.init(true, new ParametersWithRandom(signParams, random)); ECSignature signature = signer.generateSignature(kTestBytes);

signer.init(false, new PublicKeyParameter(publicKey)); bool verify = signer.verifySignature(kTestBytes, signature); expect(verify, true); }

and add this to main(): test('failing signer', () => fail());

stevenroose commented 8 years ago

Strange. I will look into this next week! (Just came back from a 3-week holiday, so I apologize for my inactivity.)

imphila commented 5 years ago

how it is going on? i have the same problem.

imphila commented 5 years ago

/ This is necessary because if a message can be signed by (r, s), it can also be signed by (r, -s (mod N)) which N being the order of the elliptic function used. In order to ensure transactions can't be tampered with (even though it would be harmless), Ethereum only accepts the signature with the lower value of s to make the signature for the message unique. More details at https://github.com/web3j/web3j/blob/master/crypto/src/main/java/org/web3j/crypto/ECDSASignature.java#L27 / if (sig.s.compareTo(_halfCurveOrder) > 0) { final canonicalisedS = _params.n - sig.s; sig = ECSignature(sig.r, canonicalisedS); }

final ECDomainParameters _params = ECCurve_secp256k1(); final BigInt _halfCurveOrder = _params.n >> 1;

above may help

andromeda911 commented 4 years ago

have any solutions?

Mavahu commented 4 years ago

Any ETA on a fix?