-
Notifications
You must be signed in to change notification settings - Fork 28
Conversation
} | ||
|
||
|
||
NSDictionary *genericSignature(BTCKey *keypair, NSData *hash, BOOL lowS) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@christianlundkvist @mirceanis This code occasionally creates an invalid signature. It's something like 1 in a 1000 (unscientifically).
Can you guys please look at the maths if there is something wrong I'm doing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should just comment that the previous code was seemingly worse than this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pelle do you have an example of invalid signature?
#include <openssl/obj_mac.h> | ||
|
||
static int BTCRegenerateKey(EC_KEY *eckey, BIGNUM *priv_key) { | ||
BN_CTX *ctx = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest using 4-space indentation (like all other iOS code), not 2.
@"recoveryParam": @(recoveryParam) | ||
}; | ||
} | ||
return @{}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant.
Just saving work. This still doesn't fix it
I've gone through and compared it with other implementations. There still seems to be a few very occasional cases where it breaks.
b8389c0
to
f1c240d
Compare
Signatures seem to be valid now
#include <openssl/obj_mac.h> | ||
|
||
NSMutableData *compressedPublicKey(EC_KEY *key) { | ||
if (!key) return nil; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please indent all changed/added code by 4 spaces.
|
||
NSDictionary *signatureDictionary = @{ @"v": @(base + [sig[@"recoveryParam"] intValue]), | ||
@"r": [rData base64EncodedStringWithOptions:0], | ||
@"s":[sData base64EncodedStringWithOptions:0]}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add spaces before & after the :
and before the closing }
=>
NSDictionary *signatureDictionary = @{ @"v" : @(base + [sig[@"recoveryParam"] intValue]),
@"r" : [rData base64EncodedStringWithOptions:0],
@"s" : [sData base64EncodedStringWithOptions:0] };
// K was infinity and does not work, redo it | ||
if ([K isInfinity]) return NULL; | ||
BTCMutableBigNumber* Kx = [K.x mutableCopy]; | ||
[Kx mod: n]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove space before n
=> [Kx mod:n];
BTCBigNumber* hashBN = [[BTCBigNumber alloc] initWithUnsignedBigEndian:hash]; | ||
|
||
// Compute s = (k^-1)*(h + Kx*privkey) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming above comment belongs to line below: Remove empty line.
@"r": rData, | ||
@"s": sData, | ||
@"recoveryParam": @(recoveryParam) | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Format as other literal =>
return @{ @"r" : rData,
@"s" : sData,
@"recoveryParam" : @(recoveryParam) };
Would also fit on one line, but it's nice/more-clear like this.
|
||
// Compute s = (k^-1)*(h + Kx*privkey) | ||
|
||
BTCMutableBigNumber* signatureBN = [[[[privkeyBN mutableCopy] multiply:Kx mod:n] add:hashBN mod: n] multiply:[k inverseMod:n] mod:n]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove space between :
and n
=> [[[[privkeyBN mutableCopy] multiply:Kx mod:n] add:hashBN mod:n] multiply:[k inverseMod:n] mod:n];
[K clear]; | ||
[Kx clear]; | ||
|
||
BTCBigNumber *twiceS = [[signatureBN mutableCopy] add: signatureBN]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove space after :
=> [[signatureBN mutableCopy] add:signatureBN];
BTCBigNumber *s; | ||
if (lowS && BN_cmp(twiceS.BIGNUM, n.BIGNUM) > 0) { | ||
// enforce low S values, by negating the value (modulo the order) if above order/2. | ||
s = [[n mutableCopy] subtract: signatureBN]; // Maybe this should be swapped |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove space after :
memset(sigBytes, 0, 64); | ||
|
||
memcpy(sigBytes, rData.bytes, 32); | ||
memcpy(sigBytes+32, sData.bytes, 32); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add spaces around +
int n = 0; | ||
int i = recid / 2; | ||
|
||
const EC_GROUP *group = EC_KEY_get0_group(eckey); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In lines below, add spaces around =
's
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll leave this bit as is, because it's copied verbatim from openssl source. So it's easier to update. Actually, while it's ugly I'll comment it out. It's not needed here, but may be needed by the sdk.
if (signature) { | ||
result(signature, nil); | ||
} else { | ||
NSError *signingError = [[NSError alloc] initWithDomain:@"UPTError" code:UPTSignerErrorCodeLevelSigningFailed.integerValue userInfo:@{@"message": @"signing failed due to invalid values"}]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's good to keep lines shorter than 120 characters. Please break up this line, and use same dictionary literal format as commented on above =>
NSError *signingError;
signingError = [[NSError alloc] initWithDomain:@"UPTError"
code:UPTSignerErrorCodeLevelSigningFailed.integerValue
userInfo:@{ @"message" : @"signing failed due to invalid values" }];
if (signature) { | ||
result(signature, nil); | ||
} else { | ||
NSError *signingError = [[NSError alloc] initWithDomain:@"UPTError" code:UPTSignerErrorCodeLevelSigningFailed.integerValue userInfo:@{@"message": @"signing failed due to invalid values"}]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Break up in 4 lines, as above, to keep lines < 120 chars.
And again use same dictionary literal format => @{ @"message" : @"signing failed due to invalid values" }
callback(signature, nil); | ||
} else { | ||
NSError *signingError = [[NSError alloc] initWithDomain:@"UPTHDSignerError" code:UPTHDSignerErrorCodeLevelSigningFailed.integerValue userInfo:@{@"message": [NSString stringWithFormat:@"signing failed due to invalid values for eth address: signJWT %@", rootAddress]}]; | ||
callback( nil, signingError); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove space after (
@@ -0,0 +1,25 @@ | |||
// Copyright (C) 2018 ConsenSys AG |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add file name to header, to stick with iOS/Xcode standard =>
//
// EthereumSigner.h
// uPort Mobile App
//
// Copyright (C) 2018 ConsenSys AG
@@ -0,0 +1,25 @@ | |||
// Copyright (C) 2018 ConsenSys AG | |||
// | |||
// This file is part of uPort Mobile App. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be deleted because it's now mentioned above?
// | ||
// This file is part of uPort Mobile App. | ||
// | ||
// uPort Mobile App is free software: you can redistribute it and/or modify |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it correct to call this uPort Mobile App
, since it's not an app but SDK instead, and I'd expect iOS
in the project/module name.
// | ||
// This file is part of uPort Mobile App. | ||
// | ||
// uPort Mobile App is free software: you can redistribute it and/or modify |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to indent all lines below as the standard Xcode header, so adding one space before each line.
@@ -0,0 +1,203 @@ | |||
// Copyright (C) 2018 ConsenSys AG |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update file header; see .h
comment above.
#include <openssl/obj_mac.h> | ||
|
||
NSMutableData *compressedPublicKey(EC_KEY *key) { | ||
if (!key) return nil; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it's good practice to always have the if
/else
/while
/... code blocks as a real block between { }
. Placing code on the same line makes that they can be missed easily (because it's a second way to format these statements; best is to have as little ways to format code as possible making it easier to scan by a human).
return data; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove line?
return signatureDictionary; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove line?
if (!sig) return NULL; | ||
NSData *rData = (NSData *)sig[@"r"]; | ||
NSData *sData = (NSData *)sig[@"s"]; | ||
/////// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete
|
||
NSData *simpleSignature(BTCKey *keypair, NSData *hash) { | ||
NSDictionary *sig = genericSignature(keypair, hash, NO); | ||
if (!sig) return NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although it's correct in C, what you're actually testing if sig
is nil
. So to keep close to that meaning, I think it's 'better' to write if (sig == nil)
Meaning matters ;-)
int i = recid / 2; | ||
|
||
const EC_GROUP *group = EC_KEY_get0_group(eckey); | ||
if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may help remove some visual clutter here to use a good old CPP macro:
#define CHECK(condition, error) { if (!(condition)) { ret = (error); goto err; }
CHECK(EC_GROUP_get_order(group, order, ctx)), -1)
x = BN_CTX_get(ctx);
CHECK(BN_copy(x, order)), -1)
CHECK(BN_mul_word(x, i)), -1)
Just an idea eh; probably best to leave code as it is.
result(signature, nil); | ||
} else { | ||
NSError *signingError = [[NSError alloc] initWithDomain:@"UPTError" code:UPTSignerErrorCodeLevelSigningFailed.integerValue userInfo:@{@"message": @"signing failed due to invalid values"}]; | ||
result( nil, signingError); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove space before nil
.
result(signature, nil); | ||
} else { | ||
NSError *signingError = [[NSError alloc] initWithDomain:@"UPTError" code:UPTSignerErrorCodeLevelSigningFailed.integerValue userInfo:@{@"message": @"signing failed due to invalid values"}]; | ||
result( nil, signingError); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove space before nil
.
if (signature) { | ||
callback(signature, nil); | ||
} else { | ||
NSError *signingError = [[NSError alloc] initWithDomain:@"UPTHDSignerError" code:UPTHDSignerErrorCodeLevelSigningFailed.integerValue userInfo:@{@"message": [NSString stringWithFormat:@"signing failed due to invalid values for eth address: signJWT %@", rootAddress]}]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, please break up for line < 120
if (signature) { | ||
callback(signature, nil); | ||
} else { | ||
NSError *signingError = [[NSError alloc] initWithDomain:@"UPTHDSignerError" code:UPTHDSignerErrorCodeLevelSigningFailed.integerValue userInfo:@{@"message": [NSString stringWithFormat:@"signing failed due to invalid values for eth address: signJWT %@", rootAddress]}]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Break up in multiple lines to keep line length below 120; possibly by introducing local variable(s).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All I could do (at the moment, without knowing yet how to run this) is comments about formatting. I'm glad you managed to get it working. I'm curious what changes fixed things.
|
||
NSDictionary *signatureDictionary = @{ @"v" : @(base + [sig[@"recoveryParam"] intValue]), | ||
@"r" : [rData base64EncodedStringWithOptions:0], | ||
@"s" :[sData base64EncodedStringWithOptions:0] }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Space missing after :
=> @"s" : [sData base64EncodedStringWithOptions:0] };
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great!
(One space missing somewhere.)
I've rewritten and refactored the signing code.
There is a testing function in
hubSaga.js
that can be enabled by uncommenting line 300This now works 100% of the time. At least I've tested it up to 10,000 signatures.