diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d53cd06..1ba66a63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Changelog * Fixed RSA block length and offset checks in RSAEngine.processBlock. * Fixed RSASigner.verifySignature to return false when signature is bad. * Add HKDF support (IETF RFC 5869) +* Add optional `normalize` boolean on `generateSignature` and `normalize` function on `ECSignature` to convert an ecdsa signature to lower-s form #### Version 1.0.2 (2019-11-15) diff --git a/lib/ecc/api.dart b/lib/ecc/api.dart index c58c5ff8..33e38385 100644 --- a/lib/ecc/api.dart +++ b/lib/ecc/api.dart @@ -155,10 +155,25 @@ class ECPublicKey extends ECAsymmetricKey implements PublicKey { /// A [Signature] created with ECC. class ECSignature implements Signature { final BigInt r; - final BigInt s; + BigInt s; ECSignature(this.r, this.s); + /** + * 'normalize' this signature by converting its s to lower-s form if necessary + * This is required to validate this signature with some libraries such as libsecp256k1 + * which enforce lower-s form for all signatures to combat ecdsa signature malleability + * + * Returns false if the signature was already normalized, or true if it changed + */ + bool normalize(ECDomainParameters curveParams) { + if (s.compareTo(curveParams.n >> 1) > 0) { + s = curveParams.n - s; + return true; + } + return false; + } + String toString() => "(${r.toString()},${s.toString()})"; bool operator ==(other) { diff --git a/lib/signers/ecdsa_signer.dart b/lib/signers/ecdsa_signer.dart index e7c0534e..85daca5c 100644 --- a/lib/signers/ecdsa_signer.dart +++ b/lib/signers/ecdsa_signer.dart @@ -87,7 +87,7 @@ class ECDSASigner implements Signer { } } - Signature generateSignature(Uint8List message) { + Signature generateSignature(Uint8List message, {bool normalize = false}) { message = _hashMessageIfNeeded(message); var n = _pvkey.parameters.n; @@ -124,7 +124,9 @@ class ECDSASigner implements Signer { s = (k.modInverse(n) * (e + (d * r))) % n; } while (s == BigInt.zero); - return new ECSignature(r, s); + var signature = new ECSignature(r, s); + if (normalize) signature.normalize(_pvkey.parameters); + return signature; } bool verifySignature(Uint8List message, covariant ECSignature signature) { diff --git a/lib/signers/rsa_signer.dart b/lib/signers/rsa_signer.dart index 9cd9ebc6..1ce7d893 100644 --- a/lib/signers/rsa_signer.dart +++ b/lib/signers/rsa_signer.dart @@ -78,7 +78,7 @@ class RSASigner implements Signer { _rsa.init(forSigning, params); } - RSASignature generateSignature(Uint8List message) { + RSASignature generateSignature(Uint8List message, {bool normalize = false}) { if (!_forSigning) { throw new StateError( "Signer was not initialised for signature generation"); diff --git a/lib/src/api/signer.dart b/lib/src/api/signer.dart index 6cd4a0cb..180e7e20 100644 --- a/lib/src/api/signer.dart +++ b/lib/src/api/signer.dart @@ -20,7 +20,7 @@ abstract class Signer extends Algorithm { void init(bool forSigning, CipherParameters params); /// Sign the passed in [message] (usually the output of a hash function) - Signature generateSignature(Uint8List message); + Signature generateSignature(Uint8List message, {bool normalize = false}); /// Verify the [message] against the [signature]. bool verifySignature(Uint8List message, Signature signature); diff --git a/test/signers/ecdsa_signer_test.dart b/test/signers/ecdsa_signer_test.dart index 3bd97871..3615e340 100644 --- a/test/signers/ecdsa_signer_test.dart +++ b/test/signers/ecdsa_signer_test.dart @@ -41,6 +41,15 @@ void main() { _newSignature("4087581495017442027693712553398765118791696551913571321320", "4593990646726045634082084213208629584972116888758459298644"), ]); + + runSignerTests(new Signer("SHA-1/DET-ECDSA"), signParams, verifyParams, [ + "Lorem ipsum dolor sit amet, consectetur adipiscing elit ........", + _newSignature("6052012072724008730564193612572794050491696411960275629627", + "2161019278549597185578307509265728228343111084484752661213"), + "En un lugar de La Mancha, de cuyo nombre no quiero acordarme ...", + _newSignature("4087581495017442027693712553398765118791696551913571321320", + "1683111088660635129753705209967429428795077884424382985437"), + ], normalize: true); } ECSignature _newSignature(String r, String s) => diff --git a/test/test/signer_tests.dart b/test/test/signer_tests.dart index 43f0afae..33d45a34 100644 --- a/test/test/signer_tests.dart +++ b/test/test/signer_tests.dart @@ -8,7 +8,7 @@ import "package:pointycastle/pointycastle.dart"; import "./src/helpers.dart"; void runSignerTests(Signer signer, CipherParameters signParams(), - CipherParameters verifyParams(), List messageSignaturePairs) { + CipherParameters verifyParams(), List messageSignaturePairs, {bool normalize = false}) { group("${signer.algorithmName}:", () { group("generateSignature:", () { for (var i = 0; i < messageSignaturePairs.length; i += 2) { @@ -18,7 +18,7 @@ void runSignerTests(Signer signer, CipherParameters signParams(), test( "${formatAsTruncated(message)}", () => _runGenerateSignatureTest( - signer, signParams, message, signature)); + signer, signParams, message, signature, normalize: normalize)); } }); @@ -37,11 +37,11 @@ void runSignerTests(Signer signer, CipherParameters signParams(), } void _runGenerateSignatureTest(Signer signer, CipherParameters params(), - String message, Signature expectedSignature) { + String message, Signature expectedSignature, {bool normalize = false}) { signer.reset(); signer.init(true, params()); - var signature = signer.generateSignature(createUint8ListFromString(message)); + var signature = signer.generateSignature(createUint8ListFromString(message), normalize: normalize); expect(signature, expectedSignature); }