1//  Copyright © 2021 650 Industries. All rights reserved.
2
3#import "NSDictionary+EXStructuredHeadersTests.h"
4
5NS_ASSUME_NONNULL_BEGIN
6
7@implementation NSDictionary (EXStructuredHeadersTests)
8
9- (BOOL)isEqualToTestResult:(id)object
10{
11  if ([object isKindOfClass:[NSData class]] && [@"binary" isEqualToString:self[@"__type"]]) {
12    NSData *dataToCompare = [[self class] dataFromBase32String:self[@"value"]];
13    return [object isEqualToData:dataToCompare];
14  }
15
16  if ([object isKindOfClass:[NSString class]] && [@"token" isEqualToString:self[@"__type"]]) {
17    return [object isEqualToString:self[@"value"]];
18  }
19
20  // plain isEqual implementation
21  if (self == object) {
22    return YES;
23  }
24  if (![object isKindOfClass:[NSDictionary class]]) {
25    return NO;
26  }
27  return [self isEqualToDictionary:object];
28}
29
30// https://github.com/ekscrypto/Base32/blob/77e2871b17d71891a6e56e007221d84d77e566b9/Base32/MF_Base32Additions.m
31+ (NSData *)dataFromBase32String:(NSString *)encoding
32{
33  NSData *data = nil;
34  unsigned char *decodedBytes = NULL;
35  @try {
36#define __ 255
37    static char decodingTable[256] = {
38      __,__,__,__, __,__,__,__, __,__,__,__, __,__,__,__,  // 0x00 - 0x0F
39      __,__,__,__, __,__,__,__, __,__,__,__, __,__,__,__,  // 0x10 - 0x1F
40      __,__,__,__, __,__,__,__, __,__,__,__, __,__,__,__,  // 0x20 - 0x2F
41      __,__,26,27, 28,29,30,31, __,__,__,__, __, 0,__,__,  // 0x30 - 0x3F
42      __, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,  // 0x40 - 0x4F
43      15,16,17,18, 19,20,21,22, 23,24,25,__, __,__,__,__,  // 0x50 - 0x5F
44      __, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,  // 0x60 - 0x6F
45      15,16,17,18, 19,20,21,22, 23,24,25,__, __,__,__,__,  // 0x70 - 0x7F
46      __,__,__,__, __,__,__,__, __,__,__,__, __,__,__,__,  // 0x80 - 0x8F
47      __,__,__,__, __,__,__,__, __,__,__,__, __,__,__,__,  // 0x90 - 0x9F
48      __,__,__,__, __,__,__,__, __,__,__,__, __,__,__,__,  // 0xA0 - 0xAF
49      __,__,__,__, __,__,__,__, __,__,__,__, __,__,__,__,  // 0xB0 - 0xBF
50      __,__,__,__, __,__,__,__, __,__,__,__, __,__,__,__,  // 0xC0 - 0xCF
51      __,__,__,__, __,__,__,__, __,__,__,__, __,__,__,__,  // 0xD0 - 0xDF
52      __,__,__,__, __,__,__,__, __,__,__,__, __,__,__,__,  // 0xE0 - 0xEF
53      __,__,__,__, __,__,__,__, __,__,__,__, __,__,__,__,  // 0xF0 - 0xFF
54    };
55    static NSUInteger paddingAdjustment[8] = {0,1,1,1,2,3,3,4};
56    encoding = [encoding stringByReplacingOccurrencesOfString:@"=" withString:@""];
57    NSData *encodedData = [encoding dataUsingEncoding:NSASCIIStringEncoding];
58    unsigned char *encodedBytes = (unsigned char *)[encodedData bytes];
59
60    NSUInteger encodedLength = [encodedData length];
61    if( encodedLength >= (NSUIntegerMax - 7) ) return nil; // NSUInteger overflow check
62    NSUInteger encodedBlocks = (encodedLength + 7) >> 3;
63    NSUInteger expectedDataLength = encodedBlocks * 5;
64
65    decodedBytes = calloc(expectedDataLength, 1);
66    if( decodedBytes != NULL ) {
67
68      unsigned char encodedByte1, encodedByte2, encodedByte3, encodedByte4;
69      unsigned char encodedByte5, encodedByte6, encodedByte7, encodedByte8;
70      NSUInteger encodedBytesToProcess = encodedLength;
71      NSUInteger encodedBaseIndex = 0;
72      NSUInteger decodedBaseIndex = 0;
73      unsigned char encodedBlock[8] = {0,0,0,0,0,0,0,0};
74      NSUInteger encodedBlockIndex = 0;
75      unsigned char c;
76      while( encodedBytesToProcess-- >= 1 ) {
77        c = encodedBytes[encodedBaseIndex++];
78        if( c == '=' ) break; // padding...
79
80        c = decodingTable[c];
81        if( c == __ ) continue;
82
83        encodedBlock[encodedBlockIndex++] = c;
84        if( encodedBlockIndex == 8 ) {
85          encodedByte1 = encodedBlock[0];
86          encodedByte2 = encodedBlock[1];
87          encodedByte3 = encodedBlock[2];
88          encodedByte4 = encodedBlock[3];
89          encodedByte5 = encodedBlock[4];
90          encodedByte6 = encodedBlock[5];
91          encodedByte7 = encodedBlock[6];
92          encodedByte8 = encodedBlock[7];
93          decodedBytes[decodedBaseIndex] = ((encodedByte1 << 3) & 0xF8) | ((encodedByte2 >> 2) & 0x07);
94          decodedBytes[decodedBaseIndex+1] = ((encodedByte2 << 6) & 0xC0) | ((encodedByte3 << 1) & 0x3E) | ((encodedByte4 >> 4) & 0x01);
95          decodedBytes[decodedBaseIndex+2] = ((encodedByte4 << 4) & 0xF0) | ((encodedByte5 >> 1) & 0x0F);
96          decodedBytes[decodedBaseIndex+3] = ((encodedByte5 << 7) & 0x80) | ((encodedByte6 << 2) & 0x7C) | ((encodedByte7 >> 3) & 0x03);
97          decodedBytes[decodedBaseIndex+4] = ((encodedByte7 << 5) & 0xE0) | (encodedByte8 & 0x1F);
98          decodedBaseIndex += 5;
99          encodedBlockIndex = 0;
100        }
101      }
102      encodedByte7 = 0;
103      encodedByte6 = 0;
104      encodedByte5 = 0;
105      encodedByte4 = 0;
106      encodedByte3 = 0;
107      encodedByte2 = 0;
108      switch (encodedBlockIndex) {
109        case 7:
110          encodedByte7 = encodedBlock[6];
111        case 6:
112          encodedByte6 = encodedBlock[5];
113        case 5:
114          encodedByte5 = encodedBlock[4];
115        case 4:
116          encodedByte4 = encodedBlock[3];
117        case 3:
118          encodedByte3 = encodedBlock[2];
119        case 2:
120          encodedByte2 = encodedBlock[1];
121        case 1:
122          encodedByte1 = encodedBlock[0];
123          decodedBytes[decodedBaseIndex] = ((encodedByte1 << 3) & 0xF8) | ((encodedByte2 >> 2) & 0x07);
124          decodedBytes[decodedBaseIndex+1] = ((encodedByte2 << 6) & 0xC0) | ((encodedByte3 << 1) & 0x3E) | ((encodedByte4 >> 4) & 0x01);
125          decodedBytes[decodedBaseIndex+2] = ((encodedByte4 << 4) & 0xF0) | ((encodedByte5 >> 1) & 0x0F);
126          decodedBytes[decodedBaseIndex+3] = ((encodedByte5 << 7) & 0x80) | ((encodedByte6 << 2) & 0x7C) | ((encodedByte7 >> 3) & 0x03);
127          decodedBytes[decodedBaseIndex+4] = ((encodedByte7 << 5) & 0xE0);
128      }
129      decodedBaseIndex += paddingAdjustment[encodedBlockIndex];
130      data = [[NSData alloc] initWithBytes:decodedBytes length:decodedBaseIndex];
131    }
132  }
133  @catch (NSException *exception) {
134    data = nil;
135    NSLog(@"WARNING: error occured while decoding base 32 string: %@", exception);
136  }
137  @finally {
138    if( decodedBytes != NULL ) {
139      free( decodedBytes );
140    }
141  }
142  return data;
143}
144
145@end
146
147NS_ASSUME_NONNULL_END
148
149