Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS Support #2

Open
FawadNL opened this issue Jan 23, 2019 · 18 comments
Open

iOS Support #2

FawadNL opened this issue Jan 23, 2019 · 18 comments
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@FawadNL
Copy link

FawadNL commented Jan 23, 2019

Hi,

It is not working with iOS. The files are getting encrypted properly, but when I run the app via Xcode, the app gets stuck at splashscreen.

@PeterHdd
Copy link
Owner

Hello,

Are you getting any error? Also what are you getting in the log?

@PeterHdd PeterHdd added bug Something isn't working help wanted Extra attention is needed labels Jan 23, 2019
@FawadNL
Copy link
Author

FawadNL commented Jan 24, 2019

Hi,

I am not getting any error in Xcode log, but will see the Safari developer console for JS error. Did the plugin work at your end?

@PeterHdd
Copy link
Owner

Nope, I'm gonna try and fix it this week.

@FawadNL
Copy link
Author

FawadNL commented Jan 24, 2019

I only see this error in my Safari inspect window -
SyntaxError: No identifiers allowed directly after numeric literal

@an-rahulpandey
Copy link

@PeterHdd I am also facing the same issue.

@PeterHdd
Copy link
Owner

yes IOS part is not working, only android part until now. Still checking the ios part.

@an-rahulpandey
Copy link

Ok, let me know if you need any help in debugging.

@PeterHdd
Copy link
Owner

If you are able to figure it out also, please commit it, thank you!

@PeterHdd
Copy link
Owner

OK, so I have been testing and trying to make it work on IOS, but its not working. So, I need either of you to help me(@FawadNL @an-rahulpandey ) as my knowledge in iOS or objective C is null actually.

So here is the thing, the iOS part has not been updated for 4 years, so basically I need to fix everything.
To explain how to make it work:

First since ionic webview version 2.0+ works on localhost:8080 server, then the plugin crypto-file needs to be also on localhost:8080 to work.

Therefore in the javascript part, I did the following if it is platform iOS (it works):

        if (platform == 'ios') {
            var pluginDir;
            console.log(platformInfo);
            var iosProject = platformInfo.locations.xcodeCordovaProj;
            pluginDir        = path.join(iosProject, 'Plugins',context.opts.plugin.id);
            replaceCryptKey_ios(pluginDir, key, iv);

            var cfg   = new ConfigParser(platformInfo.projectConfig.path);
            var port = cfg.getGlobalPreference("cryptoPort");
            if( port == '')
            {
                cfg.doc.getroot().getchildren().filter(function(child, idx, arr) {
                    return (child.tag == 'content');
                }).forEach(function(child) {
                    child.attrib.src = 'http://localhost:8080/' + child.attrib.src;
                });
            }
            else
            {
                cfg.doc.getroot().getchildren().filter(function(child, idx, arr) {
                    return (child.tag == 'content');
                }).forEach(function(child) {
                    child.attrib.src = 'http://localhost:' + port + '/' + child.attrib.src;
                });
            }
            cfg.write();
       }

cfg.write() will send http://localhost:8080/index.html and/or http://localhost:8080/cordova.js...etc... to the objective c code. So, basically http://localhost:8080/ is the www in the ionic app and it will send all the decrypted files to the objective c code. (Therefore the javascript part is correct).

Now, the file that needs to be modified is CDVCryptURLProtocol.m, here is what I did:

#import "CDVCryptURLProtocol.h"

#import <MobileCoreServices/MobileCoreServices.h>
#import <CommonCrypto/CommonCryptor.h>
#import <CommonCrypto/CommonDigest.h>


static NSString* const kCryptKey = @" ";
static NSString* const kCryptIv = @" ";

static int const kIncludeFileLength = 1;
static int const kExcludeFileLength = 0;
static NSString* const kIncludeFiles[] = { @"\\.(htm|html|js|css)$" };
static NSString* const kExcludeFiles[] = {  };
NSString *retrievePath;
NSString *wwwPath;
NSString *checkPath;


@implementation CDVCryptURLProtocol

+ (BOOL)canInitWithRequest:(NSURLRequest*)theRequest
{

    NSLog(@"the url inside the init request: %@",theRequest);
    if ([self checkCryptFile:theRequest.URL]) {
        return YES;
    }
    
    return [super canInitWithRequest:theRequest];
}

- (void)startLoading
{
    NSURL* url  = self.request.URL;

    wwwPath = [[NSBundle mainBundle].resourcePath stringByAppendingString:@"/www"];
    NSString *urling    = [@"file://"stringByAppendingString:wwwPath];
    NSString *urlings   = [urling stringByAppendingString:checkPath];
    NSString *finalUrl  = [urlings stringByReplacingOccurrencesOfString:@" " withString:@"%20"];

    NSURL *urls = [[NSURL alloc] initWithString:finalUrl];
    url         = urls;

    //    if ([[self class] checkCryptFile:url]) {
    NSString *mimeType = [self getMimeType:url];
    NSError* error;
    NSString* content  = [[NSString alloc] initWithContentsOfFile:url.path encoding:NSUTF8StringEncoding error:&error];
    if(error)
    {
        NSLog(@"the error inside is: %@", error);
    }
    if (!error) {
        NSLog(@"Decrypt: %@",url);
        NSData* data = [self decryptAES256WithKey:kCryptKey iv:kCryptIv data:content];
        [self sendResponseWithResponseCode:200 data:data mimeType:mimeType];
    }

    
    [super startLoading];
}


+ (BOOL)checkCryptFile:(NSURL *)url {

    checkPath = [url.path stringByReplacingOccurrencesOfString:@"http://localhost:8080/" withString:@""];//index.html
    NSLog(@"the check path: %@", checkPath); //index.html
    
    if (![self hasMatch:checkPath regexArr:kIncludeFiles length:kIncludeFileLength]) {
        return NO;
    }
    if ([self hasMatch:checkPath regexArr:kExcludeFiles length:kExcludeFileLength]) {
        return NO;
    }
    
    return YES;
}

+ (BOOL)hasMatch:(NSString *)text regexArr:(NSString* const [])regexArr length:(int)length {
    for (int i = 0; i < length; i++) {
        NSString* const regex = regexArr[i];
        if ([self isMatch:text pattern:regex]) {
            return YES;
        }
    }
    return NO;
}

+ (BOOL)isMatch:(NSString *)text pattern:(NSString *)pattern {
    NSError *error = nil;
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
    if (error) {
        return NO;
    }
    if ([regex firstMatchInString:text options:0 range:NSMakeRange(0, text.length)]) {
        return YES;
    }
    return NO;
}

- (NSString*)getMimeType:(NSURL *)url
{
    NSString *fullPath = url.path;
    NSString *mimeType = nil;
    
    if (fullPath) {
        CFStringRef typeId = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[fullPath pathExtension], NULL);
        if (typeId) {
            mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass(typeId, kUTTagClassMIMEType);
            if (!mimeType) {
                // special case for m4a
                if ([(__bridge NSString*)typeId rangeOfString : @"m4a-audio"].location != NSNotFound) {
                    mimeType = @"audio/mp4";
                } else if ([[fullPath pathExtension] rangeOfString:@"wav"].location != NSNotFound) {
                    mimeType = @"audio/wav";
                } else if ([[fullPath pathExtension] rangeOfString:@"css"].location != NSNotFound) {
                    mimeType = @"text/css";
                }
            }
            CFRelease(typeId);
        }
    }
    return mimeType;
}

- (NSData *)decryptAES256WithKey:(NSString *)key iv:(NSString *)iv data:(NSString *)base64String {
    NSLog(@"inside decrypt");
    
    NSData *data = [[NSData alloc] initWithBase64EncodedString:base64String options:0];
    
    size_t bufferSize = [data length] + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    size_t numBytesDecrypted = 0;
    
    NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
    NSData *ivData = [iv dataUsingEncoding:NSUTF8StringEncoding];
    
    CCCryptorStatus status = CCCrypt(kCCDecrypt,
                                     kCCAlgorithmAES128,
                                     kCCOptionPKCS7Padding,
                                     keyData.bytes,
                                     kCCKeySizeAES256,
                                     ivData.bytes,
                                     data.bytes,
                                     data.length,
                                     buffer,
                                     bufferSize,
                                     &numBytesDecrypted);
    
    if (status == kCCSuccess) {
        return [NSData dataWithBytes:buffer length:numBytesDecrypted];
    }
    free(buffer);
    
    return nil;
}

- (NSString*)getMimeTypeFromPath:(NSString*)fullPath
{
    NSString* mimeType = nil;
    
    if (fullPath) {
        CFStringRef typeId = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[fullPath pathExtension], NULL);
        if (typeId) {
            mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass(typeId, kUTTagClassMIMEType);
            if (!mimeType) {
                // special case for m4a
                if ([(__bridge NSString*)typeId rangeOfString : @"m4a-audio"].location != NSNotFound) {
                    mimeType = @"audio/mp4";
                } else if ([[fullPath pathExtension] rangeOfString:@"wav"].location != NSNotFound) {
                    mimeType = @"audio/wav";
                } else if ([[fullPath pathExtension] rangeOfString:@"css"].location != NSNotFound) {
                    mimeType = @"text/css";
                }
            }
            CFRelease(typeId);
        }
    }
    return mimeType;
}

- (void)sendResponseWithResponseCode:(NSInteger)statusCode data:(NSData*)data mimeType:(NSString*)mimeType
{
    NSLog(@"inside response");
    if (mimeType == nil) {
        mimeType = @"text/plain";
    }
    
    NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] initWithURL:[[self request] URL] statusCode:statusCode HTTPVersion:@"HTTP/1.1" headerFields:@{@"Content-Type" : mimeType}];
    
    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    if (data != nil) {
        [[self client] URLProtocol:self didLoadData:data];
    }
    [[self client] URLProtocolDidFinishLoading:self];
}

+ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)request
{
    NSLog(@"%@ received %@", self, NSStringFromSelector(_cmd));
    return request;
}

@end

To explain this, the canInitWithRequest will receive the url "http://localhost:8080/index.html`, then the checkCryptFile method will retrieve the extension example "html" and call the hasMatch method.

Then in the startLoading method, I change this url to a valid file directory to be able to decrypt it, or you will get an error file not found. So, the good thing is that it decrypts the files, and in the logs I get:

2019-01-26 20:06:42.976473+0200 myApp[3848:75777] Finished load of: http://localhost:8080/index.html

But the problem is I keep getting a white screen with no data and nothing just a white screen.

So, it is decrypting and you can see in the log when you run it, but in the simulator (iPhone XR), I keep getting white screen after its loaded.

Note:
Also in the config.xml, you need to add <allow-navigation href="http://localhost:8080/*" /> to be able to use localhost:8080.

Also im testing this without cordova-ionic-webview plugin, as crypto-file needs to work first, then we add ionic-webview and debug to check.

Summary:

Good Part:

Javascript part of cordova-crypto-file is done, files are getting decrypted. The app is being served on localhost:8080.

Bad Part:

After finishing loading, only white screen is appearing in the app, sometimes the design and the data appear in the screen though.

@an-rahulpandey
Copy link

@PeterHdd Thanks for trying. I am also looking it now.

@an-rahulpandey
Copy link

an-rahulpandey commented Jan 30, 2019

@PeterHdd The plugin works properly, the problem is with the decryption time. If you open the safari inspect window and press reload button you will see that it will work one in five times. The plugins JS files sometimes doesn't get decrypted properly which also gives error.

Also I have noticed that sometimes the files contents are overwritten by other files. See the Resources tab in inspect window and try to see some js files. For e.g device plugin js content will be also shown in splashscreen js file, main.js file content shown in vendor.js file, etc..

@FawadNL
Copy link
Author

FawadNL commented Feb 4, 2019

@PeterHdd I am seeing same issue with decryption time, however my knowledge of objective-C is also null so I can't help you unfortunately. Hope you find a fix soon.

@victor1342
Copy link

According to my tests using cordova-plugin-ionic-webview, canInitWithRequest isn't even being instantiated. Something to do with CDVURLProtocol not working with WkWebView I suppose.

@parvageshahariar
Copy link

parvageshahariar commented May 5, 2019

Hello,
It is not working with iOS. The files are getting encrypted properly, but when I run the app via Xcode, the app gets stuck at splash screen.
It shows encrypted content on a white screen. Ios version 12.2

@parvageshahariar
Copy link

@PeterHdd where is the update? Still ios not working. Nothing there after January

@tarekMohamed8
Copy link

hello any updates ?????????????????????????

@givethanks1
Copy link

If there is an ios developer who is willing to fix this, please let me know (I can help pay for your time and efforts, God willing). Thank you.

@PeterHdd
Copy link
Owner

@givethanks1 check the forks, maybe someone fixed it there

givethanks1 added a commit to givethanks1/cordova-plugin-crypto-file that referenced this issue Jun 2, 2021
Updates to include @PeterHdd code from PeterHdd#2
givethanks1 added a commit to givethanks1/cordova-plugin-crypto-file that referenced this issue Jun 2, 2021
More updates from PeterHDD code PeterHdd#2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

7 participants