Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Conversation

@ruslan-bikkinin
Copy link
Contributor

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 PublicKey string row inInfo.plist with content of public key they want to use to verify bundles signature.

@msftclas
Copy link

@ruslan-bikkinin,
Thanks for having already signed the Contribution License Agreement. Your agreement was validated by Microsoft. We will now review your pull request.
Thanks,
Microsoft Pull Request Bot

NSString *deploymentKey = [infoDictionary objectForKey:@"CodePushDeploymentKey"];
NSString *serverURL = [infoDictionary objectForKey:@"CodePushServerURL"];

NSString *publicKey = [infoDictionary objectForKey:@"PublicKey"];
Copy link
Contributor

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)

Copy link
Contributor Author

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!

error:(NSError **)error
{
CPLog(@"The update contents failed the data integrity check.");
if (!error || !*error) {
Copy link
Contributor

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.

Copy link
Contributor

@max-mironov max-mironov Aug 25, 2017

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

Copy link
Contributor Author

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

Copy link
Contributor Author

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

failCallback(error);
if(isDiffUpdate){
CPLog(@"Applying diff update.");
}else{
Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@max-mironov got it!

BOOL isSignatureValid = [CodePushUpdateUtils verifySignatureFor:newUpdateFolderPath
withPublicKey:publicKey
error:&error];
if(!isSignatureValid){
Copy link
Contributor

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)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@max-mironov got it!

}

@end
+ (NSString *)cleanPublicKey:(NSString *)publicKeyString
Copy link
Contributor

@max-mironov max-mironov Aug 25, 2017

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

Copy link
Contributor Author

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!

return nil;
}

+ (NSDictionary *) verifyJWT:(NSString *) signature
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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

NSString *signature = [self getSignatureFor: folderPath
error: error];
if(signature == nil) {
if(error && *error){
Copy link
Contributor

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?

Copy link
Contributor Author

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"]){
Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor

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]];
Copy link
Contributor

@max-mironov max-mironov Aug 25, 2017

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) {...})?

Copy link
Contributor Author

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*.

Copy link
Contributor

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 :)

expectedHash:(NSString *)expectedHash
error:(NSError **)error;

+ (NSString *)preparePublicKeyForDecoding:(NSString *)publicKeyString;
Copy link
Contributor

@sergey-akhalkov sergey-akhalkov Aug 28, 2017

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

Copy link
Contributor Author

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!

[self handleFailedDataIntegrityCheck:failCallback error:&error];
return;
} else {
CPLog(@"The update contents succueded the data integrity check.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo here - succueded

Copy link
Contributor Author

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.

[self handleFailedDataIntegrityCheck:failCallback error:&error];
return;
} else {
CPLog(@"The update contents succueded the data integrity check.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo here - succueded

CPLog(@"Applying diff update.");
} else {
CPLog(@"Applying full update.");
}
Copy link
Contributor

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.")

Copy link
Contributor Author

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.


failCallback(*error);
return;
}
Copy link
Contributor

@sergey-akhalkov sergey-akhalkov Aug 28, 2017

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;
}

Copy link
Contributor

@sergey-akhalkov sergey-akhalkov Aug 28, 2017

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.

NSError *signatureVerificationError;
NSString *signature = [self getSignatureFor: folderPath
error: &signatureVerificationError];
if(signatureVerificationError) {
Copy link
Contributor

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(

Copy link
Contributor Author

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

withPublicKey:publicKey
error:&error];
if (!isSignatureValid) {
[self handleFailedDataIntegrityCheck:failCallback error:&error];
Copy link
Contributor

@sergey-akhalkov sergey-akhalkov Aug 28, 2017

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.

Copy link
Contributor Author

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!

expectedHash:newUpdateHash
error:&error]) {

[self handleFailedDataIntegrityCheck:failCallback error:&error];
Copy link
Contributor

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
Fix log messages
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants