iOS17.4 - iPhone Pro / ProMax - SHA256

Hi,

An existing app on AppStore have issue with SHA256 Hash only since iOS17.4. And only Pro and Pro Max seems affected by this issue.

NSData is read from downloaded file.

Hash is calculated with unsigned char *CC_SHA256(const void *data, CC_LONG len, unsigned char *md)

And compared with original HASH

It appears different only on iOS17.4 and only Pro and Pro Max.

But i can't reproduce this issue. Do you have an idea ? Thank you

Replies

Common Crypto is tricky to call correctly because it’s entirely based on unsafe pointers. If you get it wrong, you end up relying on undefined behaviour, and the exact results can then vary from machine-to-machine, version-to-version of the OS, and so on. So, as a first step, I recommend that you review how you’re calling CC_SHA256.

It sounds like you’re calling it on an NSData value. Can you post the code you’re using for that?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hello,

Here the code "flatted" - executed in main thread.


    NSString* pfn  = /* path file name */
    NSData* data = [NSData dataWithContentsOfFile:pfn];

.... 

    if( data.length > 0 )
    {
        CC_LONG hashLen = CC_SHA256_DIGEST_LENGTH;
        unsigned char hash[ hashLen ];

        CC_SHA256( data.bytes, (CC_LONG)data.length, hash );

        NSMutableString *output = [NSMutableString stringWithCapacity:hashLen * 2];
        for (int i = 0 ; i < hashLen ; i++ )
            [output appendFormat:@"%02x", hash[i]];
        return output;
    }

    return nil;

and then, NSString is compared to the required Hash.

What we know with some tests elsewhere :

  • required hash length > 0
  • file exists, file have the good size (about 4400 bytes)
  • data.length > 0
  • the code above return not nil (output)

but comparaison return not equal for a small part of all devices, and only from iOS17.4

Here the code "flatted"

Thanks. I can’t see anything wrong with that code.

for a small part of all devices

Can you clarify that? Do you mean that it only affects a small number of the specific models you mentioned?

Also, upthread you wrote:

only Pro and Pro Max

but it’s not clear what particular iteration of iPhone Pro [Max] you’re talking about. Just the iPhone 15 Pro [Max]? Or all iPhone N Pro [Max] models?

But i can't reproduce this issue.

So, you’ve tested this with iOS 17.4 on a model that matches what one of your affected users has?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

The more time passes, the more samples can help to find common points. At this time :

  • Only 17.4 concerned
  • Pro and Pro Max are mainly concerned but not all and not specific iPhone number (13/14/15)
  • One user with iPhone 14 MPW73CH/A contact me for the first time (!?)

So, you’ve tested this with iOS 17.4 on a model that matches what one of your affected users has?

I do not have a pro version. I ask some users but they don't have this issue. Others devices tested do not have the issue.

Thank you

So if a user has this problem will they consistently have this problem? Such that you could send them a TestFlight build with some debugging code in it?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi @eskimo

Yes, the issue seems happend always for devices which have the issue one time. I have 2 apps with this same code, when the issue happens on a device the 2 apps have the issue,

I ask to restart the device, uninstall/install the app, check 4G/Wifi/VPN*, but nothing change.

I can't ask those users more action, TestFlight is too involving.

I have tested on simulator : no issue.

I just buy a 15 Pro for testing (!) and unfortunately i can't reproduce the issue.

Thanks for any other info.

(*) first i suspect network problem with NSURLSessionDownloadTask.

AMAZING ! look at this : How reproduce the issue - not only in iPhone PRO

NSData+Tools.h add function

@interface NSData (Tools)
- (NSString *)sha256; // see the code above with CC_SHA256
// ...
@end

In a UIView with a WKWebView :

- (void)viewDidLoad
{
        NSURL *url = [[NSBundle mainBundle] URLForResource:@"index" withExtension:@"html" subdirectory:@"www"];
        NSURLRequest* urlReq = [NSURLRequest requestWithURL:url];

        unsigned char toto[] = "titi";
        NSData* data = [NSData dataWithBytes:toto length:4];
        
        id hash;
        
        hash = [data sha256];
        DLog( @"1 NSData SHA256 %@", hash );
        
        
        [self.myWebView loadRequest:urlReq];

        hash = [data sha256];
        DLog( @"2 NSData SHA256 %@", hash );
}

log :

1 NSData SHA256 cce66316b4c1c59df94a35afb80cecd82d1a8d91b554022557e115f5c275f515
2 NSData SHA256 {length = 32, bytes = 0xcce66316 b4c1c59d f94a35af b80cecd8 ... 57e115f5 c275f515 }

My function sha256 was replaced when calling loadRequest !

  • Note: in the existing app if the user didn't go in a part with a WKWebView, the SHA256 function stay mine. If he navigate in the app with a call to WKWebView, the function NSData sha256 is replaced.

    i use WKWebView only for static html in ressources (not external loading for network)

Add a Comment

AMAZING << 2 !

I ask a user to send me a screen record, and I see he have a "accessibility shortcut" activated. Let's try :

  • Add "Accessibility > Accessibility shortcut > Full Keyboard Access"
  • Select "Full Keyboard Access" in menu

Then, when I launch my app, from the start of the first screen (without WebView call) , my [NSData sha256] function is remplaced like the previous case with the WebView.

--

So, i can reproduce by this way :

=> Settings / Accessibility / Keyboards / Full keyboard access : ON / OFF

  • ON : NSData have sha256 function replaced by the system
  • OFF : NSData keep my sha256 function and the app works fine

Oh gosh, you’re doing this from a category on NSData!

So, in Objective-C [1] the methods on a class live in a single flat namespace. This causes all sorts of problems, but it’s especially bad for categories. If two subsystems within a process each use a category to add a method to a class and happen to choose the same method name, the results are undefined [2].

Given that, standard practice is to add prefixes to any method names that you add to common classes. In your case you could change your method name to -myFabulousApp_sha256.

WebKit should be adding be using a prefix as well. I’d appreciate you filing a bug against it. Please post your bug number, just for the record.

Taking a step back, my general advice is that you avoid adding categories on classes you don’t ‘own’. I find that folks overuse categories for this sort of thing, and it’s just easier to using a standalone function:

extern NSString * sha256ForData(NSData * data);

Or, if you have an aversion to such functions, do something like this:

@interface MFAUtils: NSObject

+ (NSString *)sha256ForData:(NSData *)data;

@end

Note that you need a prefix on the class name (MFA stands for My Fabulous App, obviously :-) because Objective-C classes also live in a single namespace.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] This isn’t true for Swift.

[2] Well, the process will eventually stabilise on using one or the other implementation, but a) that can change over time, and b) which one you eventually stabilise on is undefined.

  • Hi @eskimo

    The bug report id : FB13690599

    Category was used for long time ago, in old project, in legacy code, all can't be updated each time the system is updated. I knew about the namespace and all stuff in objc/runtime.h but i can't imagine that system's component can play with that over the app without caution.

Add a Comment

In fact, if we make DIFF between all methods of "NSData* data" before and after the call of loadRequest :

unsigned char toto[] = "titi";
NSData* data = [NSData dataWithBytes:toto length:4];

NSLog(@"%@", [data performSelector:@selector(_methodDescription)]);
        
[self.myWebView loadRequest:urlReq];

NSLog(@"%@", [data performSelector:@selector(_methodDescription)]);

it may be scary if you have one of this methods added :

	- (unsigned long) u64BE:(unsigned long)arg1; (0x23856f0c0)
	- (unsigned long) u64LE:(unsigned long)arg1; (0x23856f094)
	- (unsigned short) u16BE:(unsigned long)arg1; (0x23856efd4)
	- (unsigned short) u16LE:(unsigned long)arg1; (0x23856efa8)
	- (unsigned int) u32BE:(unsigned long)arg1; (0x23856f034)
	- (unsigned int) u32LE:(unsigned long)arg1; (0x23856f008)
	- (id) sha1; (0x238565bd0)
	- (id) sha256; (0x238565c64)
	- (id) sha384; (0x238565cf8)
	- (id) asHexString; (0x23856ed34)
	+ (id) dataWithHexString:(id)arg1; (0x23856edf0)
	+ (id) randomData:(unsigned long)arg1; (0x238566b98)
	+ (id) withU16BE:(unsigned short)arg1; (0x23856f0f0)
	- (BOOL) isAllZero; (0x23857012c)
	- (BOOL) isEqualToHexCString:(const char*)arg1; (0x238570218)

... etc ( I don't put all )

What I do not understand,

  • on my XS Max previous methods are added on WebView loadRequest

  • on my 15 Pro Max methods are already added in Application delegate didFinishLaunching...

Same code runing in same time from same Xcode

But it explain why some category of devices have the issue from start, and other not or only with some actions in the app

WebKit should be adding be using a prefix as well. I’d appreciate you filing a bug against it. Please post your bug number, just for the record.

It looks like the culprit is something called libSESShared.dylib, according to this helpful bit of information someone has put together:

https://github.com/MTACS/iOS-17-Runtime-Headers/blob/main/Frameworks/CoreFoundation.framework/NSData.h#L1503

  • @Scott Thank you !

Add a Comment

Ok,

on Feedback, Apple reply there are currently no plans to address this issue. The best practice is to prefix all methods in a category (as @eskimo said)

--

What I do not understand …

That’s just the nature of undefined behaviour. Sometimes things work, sometimes things don’t, and it’s hard to work out the exact cause.

I suspect that there some sort of timing issue in play but, honestly, it doesn’t really matter. The path forward is clear, and I don’t see much benefit to looking backwards.

The best practice is to prefix all methods in a category

The best practice is Swift (-:

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

  • "The path forward is clear, and I don’t see much benefit to looking backwards." - It's always better to struggle rather than let yourself sink. Especially when there is no alternative. Sometime legacy stuff can't be update, I'll go bury it and dig my hole. Anyway, thank you for your replies.

Add a Comment