-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Implement code signing for client ios SDK #974
Implement code signing for client ios SDK #974
Conversation
Update JWT library to 3.0.0-beta4 Implement signature verification mechanism
|
@ruslan-bikkinin, |
ios/CodePush/CodePushConfig.m
Outdated
| NSString *deploymentKey = [infoDictionary objectForKey:@"CodePushDeploymentKey"]; | ||
| NSString *serverURL = [infoDictionary objectForKey:@"CodePushServerURL"]; | ||
|
|
||
| NSString *publicKey = [infoDictionary objectForKey:@"PublicKey"]; |
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.
As the value is taken from plist file that can be shared between different libraries does it make sense to name it CodePushPublicKey to avoid confusion about the reference of this property? (the same as for deploymentKey/ServerUrl)
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.
@max-mironov makes sense, I'll do it, tnx!
ios/CodePush/CodePushPackage.m
Outdated
| error:(NSError **)error | ||
| { | ||
| CPLog(@"The update contents failed the data integrity check."); | ||
| if (!error || !*error) { |
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.
Not sure if !*error is needed here, I believe if (!error) should be enough.
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.
Also don't understand why do we need at all err variable, seems that it is not used anywhere at all
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.
Also don't understand why do we need at all err variable, seems that it is not used anywhere at all
this is a part of callback function signature
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.
Not sure if !*error is needed here, I believe if (!error) should be enough.
Okay, i will refactor it to simplify conditions
ios/CodePush/CodePushPackage.m
Outdated
| failCallback(error); | ||
| if(isDiffUpdate){ | ||
| CPLog(@"Applying diff update."); | ||
| }else{ |
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.
Just style issue to use spaces between }else{ or probably use Ternary operator here
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.
@max-mironov got it!
ios/CodePush/CodePushPackage.m
Outdated
| BOOL isSignatureValid = [CodePushUpdateUtils verifySignatureFor:newUpdateFolderPath | ||
| withPublicKey:publicKey | ||
| error:&error]; | ||
| if(!isSignatureValid){ |
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.
Same style issue, please use spaces (and please check all the places below)
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.
@max-mironov got it!
ios/CodePush/CodePushUpdateUtils.m
Outdated
| } | ||
|
|
||
| @end | ||
| + (NSString *)cleanPublicKey:(NSString *)publicKeyString |
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.
From my point of view Clean is not a good name here, probably preparePublicKeyForDecoding or something else could fit better
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.
@max-mironov I'll fix it, thx!
ios/CodePush/CodePushUpdateUtils.m
Outdated
| return nil; | ||
| } | ||
|
|
||
| + (NSDictionary *) verifyJWT:(NSString *) signature |
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 can see that other verify methods here return BOOL value (that make sense!) but this one is not. Maybe parseJWT should better reflect the purpose of it.
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.
@max-mironov I'll tried to make code on android and ios looks the same as possible, so that's why I call this function that way. If it is crucial, I can change it on both platforms, what do you think?
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.
yep, looks like this can be modified for both SDK's
ios/CodePush/CodePushUpdateUtils.m
Outdated
| NSString *signature = [self getSignatureFor: folderPath | ||
| error: error]; | ||
| if(signature == nil) { | ||
| if(error && *error){ |
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 verify if we ever need this error && *error?
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.
@max-mironov Okay, thx!
| return false; | ||
| } | ||
|
|
||
| if(![envelopedPayload objectForKey:@"contentHash"]){ |
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.
Probably could move @contentHash to constants
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.
@max-mironov not so important for now, i think. May be later, if payload structure will become more complicated.
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.
Agreed!
| if ([[NSFileManager defaultManager] fileExistsAtPath:signatureFilePath]) { | ||
| return [NSString stringWithContentsOfFile:signatureFilePath encoding:NSUTF8StringEncoding error:error]; | ||
| }else{ | ||
| *error = [CodePushErrorUtils errorWithMessage:[NSString stringWithFormat: @"Cannot find signature at %@", signatureFilePath]]; |
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.
Do we need to check for null here before assigning variable (e.g. if (error) {...})?
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.
@max-mironov No, we don't, because we just send Error to method caller in order to it be able to process error. This is method's caller responsibility to provide initialized pointer for NSError*.
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.
Well, that should be in most cases, but if someone will not do it we'll get an exception here. Although we do not expect that someone else should call it who knows how it would be changed in future? I'd prefer to stick to the arguments provided e.g. in this answer. But that's only my opinion, no pressure from my side :)
c1ecd34 to
9e5c8ca
Compare
ios/CodePush/CodePush.h
Outdated
| expectedHash:(NSString *)expectedHash | ||
| error:(NSError **)error; | ||
|
|
||
| + (NSString *)preparePublicKeyForDecoding:(NSString *)publicKeyString; |
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 we should try to find the better name for preparePublicKeyForDecoding method, probably something like this:
preparePublicKeyForDecoding -> GetKeyValueFromPublicKeyString // remove BEGIN / END tags and line breaks from public key string
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.
@sergey-akhalkov thanks for suggestion, I'll do it!
ios/CodePush/CodePushPackage.m
Outdated
| [self handleFailedDataIntegrityCheck:failCallback error:&error]; | ||
| return; | ||
| } else { | ||
| CPLog(@"The update contents succueded the data integrity check."); |
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.
Typo here - succueded
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.
@sergey-akhalkov, I'll fix it.
ios/CodePush/CodePushPackage.m
Outdated
| [self handleFailedDataIntegrityCheck:failCallback error:&error]; | ||
| return; | ||
| } else { | ||
| CPLog(@"The update contents succueded the data integrity check."); |
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.
Typo here - succueded
ios/CodePush/CodePushPackage.m
Outdated
| CPLog(@"Applying diff update."); | ||
| } else { | ||
| CPLog(@"Applying full update."); | ||
| } |
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 would be more elegant to use ?: ternary operator here due to this block of code is used for logging purposes only:
CPLog((isDiffUpdate) ? @"Applying diff update." : @"Applying full update.")
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.
@sergey-akhalkov thx, I'll do it.
ios/CodePush/CodePushPackage.m
Outdated
|
|
||
| failCallback(*error); | ||
| 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.
Should not we refactor it like this?
+ (void) handleFailedDataIntegrityCheck:(void (^)(NSError *err))failCallback
error:(NSError **)error
{
NSString * const errorMessage = @"The update contents failed the data integrity check.";
CPLog(errorMessage);
if (!error) {
*error = [CodePushErrorUtils errorWithMessage:errorMessage];
}
failCallback(*error);
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.
Please also double check that you are operating with pointers and addresses of error parameter correctly while checking for nil and below.
ios/CodePush/CodePushUpdateUtils.m
Outdated
| NSError *signatureVerificationError; | ||
| NSString *signature = [self getSignatureFor: folderPath | ||
| error: &signatureVerificationError]; | ||
| if(signatureVerificationError) { |
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.
Code style issue here and below - should be if ( instead of if(
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.
@sergey-akhalkov I'll fix it
ios/CodePush/CodePushPackage.m
Outdated
| withPublicKey:publicKey | ||
| error:&error]; | ||
| if (!isSignatureValid) { | ||
| [self handleFailedDataIntegrityCheck:failCallback error:&error]; |
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 would prefer to avoid of using handleFailedDataIntegrityCheck method at all and implement error handling like this:
if (!error) {
error = [CodePushErrorUtils errorWithMessage:@"Code Signing integrity check error..."];
}
CPLog(error);
failCallback(*error);
return;This will allow us to have different error messages for code signing integrity check and for general integrity check and determine in future where the issue occurs. Also it makes code cleaner due to we have no method where we pass error as parameter ones again.
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.
@sergey-akhalkov nice proposition, I'll do it!
ios/CodePush/CodePushPackage.m
Outdated
| expectedHash:newUpdateHash | ||
| error:&error]) { | ||
|
|
||
| [self handleFailedDataIntegrityCheck:failCallback error:&error]; |
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.
The same as above is here:
if (!error) {
error = [CodePushErrorUtils errorWithMessage:@"General integrity check error..."];
}
CPLog(error);
failCallback(*error);
return;Add additional checking for specific update situations Fix bugs Refactor method names
6121753 to
2bdaa43
Compare
Fix log messages
Basic support for bundle's signature verification for ios applications has been implemented.
By default, feature is disabled, so no need for clients not interested in code signing to worry about changing code-push sdk initialization code after update.
To use this feature, developers need to add
PublicKeystring row inInfo.plistwith content of public key they want to use to verify bundles signature.