├── .gitignore ├── BAHardCodeEncoder.py ├── Pipfile ├── README.md ├── Service ├── BAExceptionHelper.py ├── BAFileDecoder.py ├── BAFileEncoder.py └── __init__.py ├── Utils ├── BAEncryptUtil.py ├── BAErrorUtil.py ├── BAFileUtil.py └── __init__.py └── oc-class ├── GTM ├── GTMBase64.h ├── GTMBase64.m └── GTMDefines.h ├── NSString+BAHCCategory.h └── NSString+BAHCCategory.m /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | ## Build generated 5 | build/ 6 | DerivedData 7 | 8 | ## Various settings 9 | *.pbxuser 10 | !default.pbxuser 11 | *.mode1v3 12 | !default.mode1v3 13 | *.mode2v3 14 | !default.mode2v3 15 | *.perspectivev3 16 | !default.perspectivev3 17 | xcuserdata 18 | 19 | ## Other 20 | *.xccheckout 21 | *.moved-aside 22 | *.xcuserstate 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | 29 | ## AppCode 30 | *.idea 31 | 32 | ## python 33 | *.pyc 34 | Pipfile.lock 35 | .venv/ 36 | 37 | ##vscode 38 | /.vscode -------------------------------------------------------------------------------- /BAHardCodeEncoder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF-8 -*- 3 | 4 | instructions = ''' 5 | Encode all hard code string in objective-c project. 6 | 7 | How to use: 8 | 1. put files in /oc-class into your project 9 | 2. edit key & iv for encrypt and decrypt action in this script and NSString+BAHCCategory.h 10 | 3. edit exception settings in this script: file name, file prefix, file suffix, folder name, folder prefix, folder suffix, string format specifiers 11 | 4. pipenv install 12 | 5. start encode/decode action by command: python param1 --encode/--decode param2 13 | param1: path of this script 14 | param2: root path of project 15 | 6. import NSString+BAHCCategory.h and BAHCDefenitions.h globally 16 | 17 | PS: 18 | 1. length of Key and iv for encrypt action must be a multiple of 16 19 | 2. you must skip these files: NSString+BAHCCategory.h, NSString+BAHCCategory.m, BAHCDefenitions.h, GTMBase64.h, GTMBase64.m, GTMDefines.h 20 | 3. use option --encrypt/--decrypt to encrypt/decrypt individual content 21 | ''' 22 | #**************** Settings for defenitions & log file *************** 23 | 24 | DefenitionFileName = 'BAHCDefenitions.h' 25 | EncodeLogFileName = 'BAHCEncodeLog.json' 26 | 27 | #********************************************************************** 28 | 29 | #**************** Settings for encrypt & decrypt ******************** 30 | 31 | Key_salt = 'abcdef' 32 | AES_key = '9Jvae2bFOYL$JoTt' 33 | AES_iv = 'yg@t2lLZXmP8&J7r' 34 | 35 | #********************************************************************** 36 | 37 | 38 | #******************** Settings for exception ************************ 39 | 40 | Exception_File_Names = ['NSString+BAHCCategory.h', 41 | 'NSString+BAHCCategory.m', 42 | 'BAHCDefenitions.h', 43 | 'GTMBase64.h', 44 | 'GTMBase64.m', 45 | 'GTMDefines.h'] 46 | Exception_File_Prefix = [] 47 | Exception_File_Suffix = [r'\.a', r'\.framework'] 48 | 49 | Exception_Folder_Names = ['node_modules', 50 | '.idea', 51 | '.git', 52 | 'Pods'] 53 | Exception_Folder_Prefix = [] 54 | Exception_Folder_Suffix = [] 55 | 56 | Exception_String_Format_Specifiers = ['%@', '%%', '%d', '%D', '%u', '%U', '%x', '%X', 57 | '%o', '%O', '%f', '%e', '%E', '%g', '%G', '%c', 58 | '%C', '%s', '%S', '%p', '%a', '%A', '%F', '%zd'] 59 | 60 | Encode_Escape_Characters_Key = ['\\\n', '\\n', '\\a', '\\b', '\\f', '\\r', '\\t', '\\v', '\\"', '\\0', '\\\\'] 61 | Encode_Escape_Characters_Value = ['', '\n', '\a', '\b', '\f', '\r', '\t', '\v', '\"', '', '\\'] 62 | 63 | #********************************************************************** 64 | 65 | 66 | import sys, os, hashlib, json 67 | from Service.BAFileDecoder import BAFileDecoder 68 | from Service.BAFileEncoder import BAFileEncoder 69 | from Service.BAExceptionHelper import BAExceptionHelper 70 | from Utils.BAFileUtil import BAFileUtil 71 | from Utils.BAErrorUtil import BAErrorUtil, BAErrorGrade 72 | from Utils.BAEncryptUtil import BAEncryptUtil 73 | from BAAlgorithmUtils.SBOMUtil import SBOMUtil 74 | 75 | def __decodeAction(rootName, rootDir, stringSearchUtil, replaceDic, exceptionHelper): 76 | if stringSearchUtil == None: 77 | return 78 | if replaceDic == None or len(replaceDic) == 0: 79 | return 80 | if exceptionHelper.shouldSkipFolder(rootName, rootDir) == True: 81 | return 82 | 83 | for fileName in os.listdir(rootDir): 84 | filePath = os.path.join(rootDir, fileName) 85 | if (os.path.isdir(filePath)): 86 | __decodeAction(fileName, filePath, stringSearchUtil, replaceDic, exceptionHelper) 87 | else: 88 | if exceptionHelper.shouldSkipFile(fileName, filePath) == True: 89 | BAErrorUtil.printError(BAErrorGrade.normal, 'Skip file: ' + filePath) 90 | continue 91 | fileHandler = open(filePath, 'r') 92 | newFileContent = fileHandler.read() 93 | fileHandler.close() 94 | searchResult = stringSearchUtil.search(newFileContent) 95 | if searchResult == None or len(searchResult) == 0: 96 | BAErrorUtil.printError(BAErrorGrade.normal, 'Skip file: ' + filePath) 97 | continue 98 | needRewrite = False 99 | for key, indexs in searchResult.items(): 100 | if indexs != None and len(indexs) > 0 and key in replaceDic: 101 | newFileContent = newFileContent.replace(key, '@"' + replaceDic[key] + '"') 102 | needRewrite = True 103 | else: 104 | BAErrorUtil.printError(BAErrorGrade.normal, 'Skip file: ' + filePath) 105 | if needRewrite == True: 106 | BAErrorUtil.printError(BAErrorGrade.success, 'Decoded: ' + filePath) 107 | newFileHandler = open(filePath, 'w') 108 | newFileHandler.seek(0) 109 | newFileHandler.truncate() 110 | newFileHandler.write(newFileContent) 111 | newFileHandler.close() 112 | 113 | def __decode(rootPath): 114 | BAErrorUtil.printError(BAErrorGrade.normal, '👉 Decode action, here we go!') 115 | 116 | if rootPath == None: 117 | BAErrorUtil.printError(BAErrorGrade.error, 'ERROR: Project root path None!') 118 | return 119 | 120 | tmpRootPath = rootPath + '/' 121 | tmpRootPath = tmpRootPath.replace("//", "/") 122 | 123 | #check key and iv length 124 | if AES_key == None or AES_iv == None: 125 | BAErrorUtil.printError(BAErrorGrade.error, "ERROR: Key and iv for encrypt action can't be null!") 126 | return 127 | if len(AES_key) % 16 != 0 or len(AES_iv) % 16 != 0: 128 | BAErrorUtil.printError(BAErrorGrade.error, 'ERROR: Length of key and iv for encrypt action must be a multiple of 16!') 129 | return 130 | 131 | #find encode log file 132 | possiblePaths = BAFileUtil.findTargetPaths(EncodeLogFileName, False, tmpRootPath) 133 | if possiblePaths == None or len(possiblePaths) == 0: 134 | BAErrorUtil.printError(BAErrorGrade.error, "ERROR: Can't find BAHCDefenitions.h !") 135 | return 136 | encodeLogFilePath = possiblePaths[0] 137 | 138 | #read contents in encode log file 139 | encodeLogFileFileHandler = open(encodeLogFilePath, 'r') 140 | if encodeLogFileFileHandler == None: 141 | BAErrorUtil.printError(BAErrorGrade.error, "ERROR: Can't read BAHCDefenitions.h !") 142 | return 143 | encodeLogContent = encodeLogFileFileHandler.read() 144 | encodeLogFileFileHandler.close() 145 | encodeLog = json.loads(encodeLogContent) 146 | 147 | replaceDic = {} 148 | sbomUtil = SBOMUtil() 149 | for logItem in encodeLog: 150 | key = logItem['key'] 151 | sbomUtil.train(key) 152 | replaceDic[key] = logItem['oldUnCleanContent'] 153 | sbomUtil.prepare() 154 | 155 | exceptionHelper = BAExceptionHelper() 156 | exceptionHelper.excFolderNames = Exception_Folder_Names 157 | exceptionHelper.excFolderPrefixes = Exception_Folder_Prefix 158 | exceptionHelper.excFolderSuffixes = Exception_Folder_Suffix 159 | exceptionHelper.excFileNames = Exception_File_Names 160 | exceptionHelper.excFilePrefixes = Exception_File_Prefix 161 | exceptionHelper.excFileSuffixes = Exception_File_Suffix 162 | 163 | #start decode 164 | __decodeAction('', tmpRootPath, sbomUtil, replaceDic, exceptionHelper) 165 | 166 | BAErrorUtil.printError(BAErrorGrade.normal, '👌 Finished!') 167 | 168 | def __convertEscapeCharacterForEncode(source): 169 | result = source 170 | for i in range(len(Encode_Escape_Characters_Key)): 171 | result = result.replace(Encode_Escape_Characters_Key[i], Encode_Escape_Characters_Value[i]) 172 | return result 173 | 174 | def __encryptFunc(content, unCleanContent, filePath, line, column): 175 | if content == None or len(content) == 0: 176 | return None, None 177 | key = 'BAHCKey' + hashlib.md5((Key_salt + 'NEW_NAME_FOR_' + content + '_OF_' + filePath + '_AT_' + str(line) + ':' + str(column)).encode(encoding='UTF-8')).hexdigest() 178 | newContent = '[@"' + BAEncryptUtil.AESEncrypt(__convertEscapeCharacterForEncode(content), AES_key, AES_iv) + '" BAHC_Decrypt]' 179 | return key, newContent 180 | 181 | def __encodeAction(rootName, rootDir, outputFileHandler, logFileHandler, encoder, exceptionHelper): 182 | if exceptionHelper.shouldSkipFolder(rootName, rootDir) == True: 183 | return 184 | 185 | for fileName in os.listdir(rootDir): 186 | filePath = os.path.join(rootDir, fileName) 187 | if (os.path.isdir(filePath)): 188 | __encodeAction(fileName, filePath, outputFileHandler, logFileHandler, encoder, exceptionHelper) 189 | else: 190 | if exceptionHelper.shouldSkipFile(fileName, filePath) == True: 191 | BAErrorUtil.printError(BAErrorGrade.normal, 'Skip file: ' + filePath) 192 | continue 193 | logs, newContent, error = encoder.encode(fileName, filePath) 194 | if error != None: 195 | BAErrorUtil.printErrorModel(error) 196 | elif len(logs) > 0: 197 | tmpFileHandler = open(filePath, 'w') 198 | tmpFileHandler.seek(0) 199 | tmpFileHandler.truncate() 200 | tmpFileHandler.write(newContent) 201 | tmpFileHandler.close() 202 | 203 | if outputFileHandler: 204 | for logItem in logs: 205 | outputFileHandler.write('#define ' + logItem['key'] + ' ' + logItem['newContent'] + '\n') 206 | 207 | if logFileHandler: 208 | logString = json.dumps(logs) 209 | logFileHandler.write(logString[1: len(logString) - 1] + ',') 210 | 211 | BAErrorUtil.printError(BAErrorGrade.success, 'Encoded: ' + filePath) 212 | else: 213 | BAErrorUtil.printError(BAErrorGrade.normal, 'Skip file: '+filePath) 214 | 215 | def __encode(rootPath): 216 | BAErrorUtil.printError(BAErrorGrade.normal, '👉 Encode action, Here we go!') 217 | 218 | if rootPath == None: 219 | BAErrorUtil.printError(BAErrorGrade.error, 'ERROR: Project root path None!') 220 | return 221 | 222 | rootPathTmp = rootPath.replace("//", "/") 223 | 224 | #check key and iv length 225 | if AES_key == None or AES_iv == None: 226 | BAErrorUtil.printError(BAErrorGrade.error, "ERROR: Key and iv for encrypt action can't be null!") 227 | return 228 | if len(AES_key) % 16 != 0 or len(AES_iv) % 16 != 0: 229 | BAErrorUtil.printError(BAErrorGrade.error, "ERROR: Length of key and iv for encrypt action must be a multiple of 16!") 230 | return 231 | 232 | #check key & value of escape characters 233 | if isinstance(Encode_Escape_Characters_Key, list) == False or isinstance(Encode_Escape_Characters_Value, list) == False: 234 | BAErrorUtil.printError(BAErrorGrade.error, "ERROR: List escape characters key or value can't be None!") 235 | return 236 | if len(Encode_Escape_Characters_Key) != len(Encode_Escape_Characters_Value): 237 | BAErrorUtil.printError(BAErrorGrade.error, "ERROR: Length of escape characters key and value list must be equal!") 238 | return 239 | 240 | #creat defenition file 241 | defenitionFilePath = rootPathTmp + '/' + DefenitionFileName 242 | defenitionFilePath = defenitionFilePath.replace("//", "/") 243 | if os.path.exists(defenitionFilePath): 244 | os.remove(defenitionFilePath) 245 | 246 | #creat log file 247 | logFilePath = rootPathTmp + '/' + EncodeLogFileName 248 | logFilePath = logFilePath.replace("//", "/") 249 | if os.path.exists(logFilePath): 250 | os.remove(logFilePath) 251 | 252 | #start analyze 253 | encoder = BAFileEncoder() 254 | encoder.excChars = Exception_String_Format_Specifiers 255 | encoder.encryptFunc = __encryptFunc 256 | 257 | exceptionHelper = BAExceptionHelper() 258 | exceptionHelper.excFolderNames = Exception_Folder_Names 259 | exceptionHelper.excFolderPrefixes = Exception_Folder_Prefix 260 | exceptionHelper.excFolderSuffixes = Exception_Folder_Suffix 261 | exceptionHelper.excFileNames = Exception_File_Names 262 | exceptionHelper.excFilePrefixes = Exception_File_Prefix 263 | exceptionHelper.excFileSuffixes = Exception_File_Suffix 264 | 265 | defenitionFilePathHandler = open(defenitionFilePath, 'w+') 266 | logFilePathHandler = open(logFilePath, 'w+') 267 | logFilePathHandler.write('[') 268 | __encodeAction('', rootPathTmp, defenitionFilePathHandler, logFilePathHandler, encoder, exceptionHelper) 269 | logFilePathHandler.seek(logFilePathHandler.tell()-1, 0) 270 | logFilePathHandler.truncate() 271 | logFilePathHandler.write(']') 272 | logFilePathHandler.close() 273 | defenitionFilePathHandler.close() 274 | 275 | BAErrorUtil.printError(BAErrorGrade.normal, '👌 Finished!') 276 | 277 | if __name__ == '__main__': 278 | if len(sys.argv) < 2: 279 | quit() 280 | firstParam = sys.argv[1] 281 | 282 | if firstParam == '--encrypt': 283 | content = input('\033[1;32mContent: \033[0m') 284 | key = input('\033[1;32mKey: \033[0m') 285 | iv = input('\033[1;32mIV: \033[0m') 286 | print(BAEncryptUtil.AESEncrypt(content, key, iv)) 287 | quit() 288 | 289 | if firstParam == '--decrypt': 290 | content = input('\033[1;32mContent: \033[0m') 291 | key = input('\033[1;32mKey: \033[0m') 292 | iv = input('\033[1;32mIV: \033[0m') 293 | print(BAEncryptUtil.AESDecrypt(content, key, iv)) 294 | quit() 295 | 296 | if len(sys.argv) >= 3: 297 | if firstParam == '--decode': 298 | __decode(sys.argv[2]) 299 | pass 300 | elif firstParam == '--encode': 301 | __encode(sys.argv[2]) -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | pycryptodome = "*" 10 | baclangutils = "*" 11 | baalgorithmutils = "*" 12 | 13 | [requires] 14 | python_version = "3.7" 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BAHardCodeEncoder 2 | 3 | Encode all hard code string in objective-c project. Of course, you can decode the changes too. 4 | 5 | ## 1. Install and use 6 | 7 | - (1) put files in /oc-class into your project 8 | - (2) pipenv install 9 | - (3) start encode/decode action by command: 10 | > python param1 --encode/--decode param2 11 | > 12 | > param1: path of this script 13 | > 14 | > param2: root path of project 15 | - (4) import `NSString+BAHCCategory.h` and `BAHCDefenitions.h` globally 16 | 17 | Also, you can use option --encrypt/--decrypt to encrypt/decrypt individual content 18 | 19 | ## 2. Set rules of exception 20 | You can set rules of exception by change these vlaues in `BAHardCodeEncoder.py` 21 | 22 | - `Exception_File_Names`: file name 23 | - `Exception_File_Prefix`: file name prefix 24 | - `Exception_File_Suffix`: file name suffix 25 | - `Exception_Folder_Names`: folder name 26 | - `Exception_Folder_Prefix`: folder name prefix 27 | - `Exception_Folder_Suffix`: folder name suffix 28 | - `Exception_String_Format_Specifiers`: string format specifiers like `%@`. If string contain one of these, then shall skip it 29 | 30 | **You must skip these files: `NSString+BAHCCategory.h`, `NSString+BAHCCategory.m`, `BAHCDefenitions.h`, `GTMBase64.h`, `GTMBase64.m`, `GTMDefines.h`** 31 | 32 | ## 3. Keep encoded content different 33 | You should change `Key_salt` in `BAHardCodeEncoder.py` every time, to keep encoded content on code file alaways different 34 | 35 | ## 4. Self define encode logic 36 | The default encode logic is AES, you can just change key and iv of it, or rewrite encode logic 37 | 38 | ### 4.1 Change key and iv of AES 39 | Change `AES_key` and `AES_iv` in `BAHardCodeEncoder.py` 40 | 41 | **Length of Key and iv for AES must be a multiple of 16** 42 | 43 | ### 4.2 Rewrite encode logic 44 | You can make it by rewrite function `__encryptFunc` in `BAHardCodeEncoder.py` 45 | 46 | GitHub Link: [https://github.com/BenArvin/BAHardCodeEncoder](https://github.com/BenArvin/BAHardCodeEncoder) -------------------------------------------------------------------------------- /Service/BAExceptionHelper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF-8 -*- 3 | 4 | import sys, os, re 5 | 6 | class BAExceptionHelper(object): 7 | 8 | def __init__(self): 9 | super(BAExceptionHelper, self).__init__() 10 | 11 | self.excFolderNames = None 12 | self.excFileNames = None 13 | 14 | self.__excChars = None 15 | self.__excCharsRegSpec = None 16 | 17 | self.__excFolderPrefixes = None 18 | self.__excFolderPrefixesRegSpec = None 19 | 20 | self.__excFolderSuffixes = None 21 | self.__excFolderSuffixesRegSpec = None 22 | 23 | self.__excFilePrefixes = None 24 | self.__excFilePrefixesRegSpec = None 25 | 26 | self.__excFileSuffixes = None 27 | self.__excFileSuffixesRegSpec = None 28 | 29 | @property 30 | def excChars(self): 31 | return self.__excChars 32 | 33 | @excChars.setter 34 | def excChars(self, value): 35 | if self.__excChars == value: 36 | return 37 | self.__excChars = value 38 | 39 | if self.__excChars == None: 40 | self.__excCharsRegSpec = None 41 | return 42 | 43 | charsCount = len(self.__excChars) 44 | if charsCount == 0: 45 | self.__excCharsRegSpec = None 46 | return 47 | 48 | self.__excCharsRegSpec = '(.)*(' 49 | for i in range(charsCount): 50 | self.__excCharsRegSpec = self.__excCharsRegSpec + self.__excChars[i] 51 | if i != charsCount - 1: 52 | self.__excCharsRegSpec = self.__excCharsRegSpec + '|' 53 | self.__excCharsRegSpec = self.__excCharsRegSpec + ')(.)*' 54 | 55 | @property 56 | def excFolderPrefixes(self): 57 | return self.__excFolderPrefixes 58 | 59 | @excFolderPrefixes.setter 60 | def excFolderPrefixes(self, value): 61 | if self.__excFolderPrefixes == value: 62 | return 63 | self.__excFolderPrefixes = value 64 | 65 | if self.__excFolderPrefixes == None: 66 | self.__excFolderPrefixesRegSpec = None 67 | return 68 | 69 | count = len(self.__excFolderPrefixes) 70 | if count == 0: 71 | self.__excFolderPrefixesRegSpec = None 72 | return 73 | 74 | self.__excFolderPrefixesRegSpec = '^(' 75 | for i in range(count): 76 | self.__excFolderPrefixesRegSpec = self.__excFolderPrefixesRegSpec + self.__excFolderPrefixes[i] 77 | if i != count - 1: 78 | self.__excFolderPrefixesRegSpec = self.__excFolderPrefixesRegSpec + '|' 79 | self.__excFolderPrefixesRegSpec = self.__excFolderPrefixesRegSpec + ')(.)*$' 80 | 81 | @property 82 | def excFolderPrefixes(self): 83 | return self.__excFolderPrefixes 84 | 85 | @excFolderPrefixes.setter 86 | def excFolderPrefixes(self, value): 87 | if self.__excFolderPrefixes == value: 88 | return 89 | self.__excFolderPrefixes = value 90 | 91 | if self.__excFolderPrefixes == None: 92 | self.__excFolderPrefixesRegSpec = None 93 | return 94 | 95 | count = len(self.__excFolderPrefixes) 96 | if count == 0: 97 | self.__excFolderPrefixesRegSpec = None 98 | return 99 | 100 | self.__excFolderPrefixesRegSpec = '^(' 101 | for i in range(count): 102 | self.__excFolderPrefixesRegSpec = self.__excFolderPrefixesRegSpec + self.__excFolderPrefixes[i] 103 | if i != count - 1: 104 | self.__excFolderPrefixesRegSpec = self.__excFolderPrefixesRegSpec + '|' 105 | self.__excFolderPrefixesRegSpec = self.__excFolderPrefixesRegSpec + ')(.)*$' 106 | 107 | @property 108 | def excFolderSuffixes(self): 109 | return self.__excFolderSuffixes 110 | 111 | @excFolderSuffixes.setter 112 | def excFolderSuffixes(self, value): 113 | if self.__excFolderSuffixes == value: 114 | return 115 | self.__excFolderSuffixes = value 116 | 117 | if self.__excFolderSuffixes == None: 118 | self.__excFolderSuffixesRegSpec = None 119 | return 120 | 121 | count = len(self.__excFolderSuffixes) 122 | if count == 0: 123 | self.__excFolderSuffixesRegSpec = None 124 | return 125 | 126 | self.__excFolderSuffixesRegSpec = '^(.)*(' 127 | for i in range(count): 128 | self.__excFolderSuffixesRegSpec = self.__excFolderSuffixesRegSpec + self.__excFolderSuffixes[i] 129 | if i != count - 1: 130 | self.__excFolderSuffixesRegSpec = self.__excFolderSuffixesRegSpec + '|' 131 | self.__excFolderSuffixesRegSpec = self.__excFolderSuffixesRegSpec + ')$' 132 | 133 | @property 134 | def excFilePrefixes(self): 135 | return self.__excFilePrefixes 136 | 137 | @excFilePrefixes.setter 138 | def excFilePrefixes(self, value): 139 | if self.__excFilePrefixes == value: 140 | return 141 | self.__excFilePrefixes = value 142 | 143 | if self.__excFilePrefixes == None: 144 | self.__excFilePrefixesRegSpec = None 145 | return 146 | 147 | count = len(self.__excFilePrefixes) 148 | if count == 0: 149 | self.__excFilePrefixesRegSpec = None 150 | return 151 | 152 | self.__excFilePrefixesRegSpec = '^(' 153 | for i in range(count): 154 | self.__excFilePrefixesRegSpec = self.__excFilePrefixesRegSpec + self.__excFilePrefixes[i] 155 | if i != count - 1: 156 | self.__excFilePrefixesRegSpec = self.__excFilePrefixesRegSpec + '|' 157 | self.__excFilePrefixesRegSpec = self.__excFilePrefixesRegSpec + ')(.)*$' 158 | 159 | @property 160 | def excFileSuffixes(self): 161 | return self.__excFileSuffixes 162 | 163 | @excFileSuffixes.setter 164 | def excFileSuffixes(self, value): 165 | if self.__excFileSuffixes == value: 166 | return 167 | self.__excFileSuffixes = value 168 | 169 | if self.__excFileSuffixes == None: 170 | self.__excFileSuffixesRegSpec = None 171 | return 172 | 173 | count = len(self.__excFileSuffixes) 174 | if count == 0: 175 | self.__excFileSuffixesRegSpec = None 176 | return 177 | 178 | self.__excFileSuffixesRegSpec = '^(.)*(' 179 | for i in range(count): 180 | self.__excFileSuffixesRegSpec = self.__excFileSuffixesRegSpec + self.__excFileSuffixes[i] 181 | if i != count - 1: 182 | self.__excFileSuffixesRegSpec = self.__excFileSuffixesRegSpec + '|' 183 | self.__excFileSuffixesRegSpec = self.__excFileSuffixesRegSpec + ')$' 184 | 185 | def shouldSkipFolder(self, name, path): 186 | if os.path.exists(path) == False or os.path.isdir(path) == False: 187 | return True 188 | if self.excFolderNames != None and (name in self.excFolderNames): 189 | return True 190 | if self.__excFolderPrefixesRegSpec != None and re.match(self.__excFolderPrefixesRegSpec, name, re.S) != None: 191 | return True 192 | if self.__excFolderSuffixesRegSpec != None and re.match(self.__excFolderSuffixesRegSpec, name, re.S) != None: 193 | return True 194 | return False 195 | 196 | def shouldSkipFile(self, name, path): 197 | if os.path.exists(path) == False or os.path.isdir(path) == True: 198 | return True 199 | if self.excFileNames != None and (name in self.excFileNames): 200 | return True 201 | if re.match('^(.)+\\.(h|m|mm|pch)$', name, re.S) == None: 202 | return True 203 | if self.__excFilePrefixesRegSpec != None and re.match(self.__excFilePrefixesRegSpec, name, re.S) != None: 204 | return True 205 | if self.__excFileSuffixesRegSpec != None and re.match(self.__excFileSuffixesRegSpec, name, re.S) != None: 206 | return True 207 | return False 208 | 209 | def shouldSkipContent(self, content): 210 | if content == None or len(content) == 0: 211 | return True 212 | if self.__excCharsRegSpec != None and re.match(self.__excCharsRegSpec, content, re.S) != None: 213 | return True 214 | return False -------------------------------------------------------------------------------- /Service/BAFileDecoder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF-8 -*- 3 | 4 | import sys, os, re 5 | sys.path.append('../') 6 | from Utils.BAFileUtil import BAFileUtil 7 | from Utils.BAEncryptUtil import BAEncryptUtil 8 | from Service.BAExceptionHelper import BAExceptionHelper 9 | 10 | class BAFileDecoder(object): 11 | 12 | def __init__(self): 13 | super(BAFileDecoder, self).__init__() 14 | self.__exceptionHelper = BAExceptionHelper() 15 | 16 | @property 17 | def excChars(self): 18 | return self.__exceptionHelper.excChars 19 | 20 | @excChars.setter 21 | def excChars(self, value): 22 | self.__exceptionHelper.excChars = value 23 | 24 | @property 25 | def excFolderNames(self): 26 | return self.__exceptionHelper.excFolderNames 27 | 28 | @excFolderNames.setter 29 | def excFolderNames(self, value): 30 | self.__exceptionHelper.excFolderNames = value 31 | 32 | @property 33 | def excFolderPrefixes(self): 34 | return self.__exceptionHelper.excFolderPrefixes 35 | 36 | @excFolderPrefixes.setter 37 | def excFolderPrefixes(self, value): 38 | self.__exceptionHelper.excFolderPrefixes = value 39 | 40 | @property 41 | def excFolderSuffixes(self): 42 | return self.__exceptionHelper.excFolderSuffixes 43 | 44 | @excFolderSuffixes.setter 45 | def excFolderSuffixes(self, value): 46 | self.__exceptionHelper.excFolderSuffixes = value 47 | 48 | @property 49 | def excFileNames(self): 50 | return self.__exceptionHelper.excFileNames 51 | 52 | @excFileNames.setter 53 | def excFileNames(self, value): 54 | self.__exceptionHelper.excFileNames = value 55 | 56 | @property 57 | def excFilePrefixes(self): 58 | return self.__exceptionHelper.excFilePrefixes 59 | 60 | @excFilePrefixes.setter 61 | def excFilePrefixes(self, value): 62 | self.__exceptionHelper.excFilePrefixes = value 63 | 64 | @property 65 | def excFileSuffixes(self): 66 | return self.__exceptionHelper.excFileSuffixes 67 | 68 | @excFileSuffixes.setter 69 | def excFileSuffixes(self, value): 70 | self.__exceptionHelper.excFileSuffixes = value 71 | 72 | def decode(self, fileName, filePath): 73 | return None, None -------------------------------------------------------------------------------- /Service/BAFileEncoder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF-8 -*- 3 | 4 | import sys, os, re, hashlib 5 | sys.path.append('../') 6 | from Utils.BAFileUtil import BAFileUtil 7 | from Utils.BAEncryptUtil import BAEncryptUtil 8 | from Service.BAExceptionHelper import BAExceptionHelper 9 | from BAClangUtils.RawTokenUtil import RawTokenUtil 10 | from BAAlgorithmUtils.HorspoolUtil import HorspoolUtil 11 | from Utils.BAErrorUtil import BAErrorUtil, BAErrorGrade 12 | 13 | class BAFileEncoder(object): 14 | 15 | def __init__(self): 16 | super(BAFileEncoder, self).__init__() 17 | self.encryptFunc = self.defaultEncryptFunc 18 | self.__exceptionHelper = BAExceptionHelper() 19 | self.__rawTokenUtil = RawTokenUtil() 20 | self.__horspoolUtil = HorspoolUtil() 21 | self.__horspoolUtil.setMatcher('\\n') 22 | 23 | @property 24 | def excChars(self): 25 | return self.__exceptionHelper.excChars 26 | 27 | @excChars.setter 28 | def excChars(self, value): 29 | self.__exceptionHelper.excChars = value 30 | 31 | def __parseStringCodes(self, filePath): 32 | output, error = self.__rawTokenUtil.parse(filePath) 33 | stringCodes = [] 34 | stringSuffixFinded = False 35 | atCodeInfo = None 36 | for infoItem in error: 37 | if infoItem['class'] == 'at': 38 | stringSuffixFinded = True 39 | atCodeInfo = infoItem 40 | else: 41 | if stringSuffixFinded == True and infoItem['class'] == 'string_literal': 42 | contentLen = len(infoItem['content']) 43 | tmpContent = infoItem['content'][1: contentLen - 1] 44 | unCleanContentLen = len(infoItem['unCleanContent']) 45 | unCleanUtf8ContentLen = len(infoItem['unCleanContent'].encode('UTF-8', 'ignore')) 46 | tmpUnCleanContent = infoItem['unCleanContent'][1: unCleanContentLen - 1] 47 | tmpResult = { 48 | 'line': atCodeInfo['line'], 49 | 'column': atCodeInfo['column'], 50 | 'codeLen': unCleanUtf8ContentLen + 1, 51 | 'stringContent': tmpContent, 52 | 'stringUnCleanContent': tmpUnCleanContent 53 | } 54 | stringCodes.append(tmpResult) 55 | atCodeInfo = None 56 | stringSuffixFinded = False 57 | return stringCodes 58 | 59 | def defaultEncryptFunc(self, content, unCleanContent, filePath, line, column): 60 | if content == None or len(content) == 0: 61 | return None, None 62 | key = 'BAHCKey' + hashlib.md5(('NEW_NAME_FOR_' + content + '_OF_' + filePath + '_AT_' + str(line) + ':' + str(column)).encode(encoding='UTF-8')).hexdigest() 63 | newContent = '@"' + content + '"' 64 | return key, newContent 65 | 66 | def encode(self, fileName, filePath): 67 | stringCodes = self.__parseStringCodes(filePath) 68 | if stringCodes == None or len(stringCodes) == 0: 69 | return None, None, BAErrorUtil.buildErrorModel(BAErrorGrade.normal, 'Skip file: '+filePath) 70 | 71 | linesSize = BAFileUtil.getLinesSize(filePath) 72 | if linesSize == None or len(linesSize) == 0: 73 | return None, None, BAErrorUtil.buildErrorModel(BAErrorGrade.normal, 'Skip file: '+filePath) 74 | 75 | if self.encryptFunc == None: 76 | return None, None, BAErrorUtil.buildErrorModel(BAErrorGrade.normal, 'Skip file: '+filePath) 77 | 78 | oldFileHandler = open(filePath, 'r') 79 | oldFileContent = oldFileHandler.read().encode('UTF-8', 'ignore') 80 | oldFileHandler.close() 81 | 82 | encodeLog = [] 83 | newFileContent = ''.encode('UTF-8') 84 | shouldRewrite = False 85 | lastOffset = 0 86 | for i in range(0, len(stringCodes), 1): 87 | stringCodeItem = stringCodes[i] 88 | lineTmp = stringCodeItem['line'] 89 | columnTmp = stringCodeItem['column'] 90 | offset = BAFileUtil.convertToOffset(linesSize, lineTmp - 1, columnTmp) 91 | newFileContent = newFileContent + oldFileContent[lastOffset: offset] 92 | oldContent = stringCodeItem['stringContent'] 93 | oldUnCleanContent = stringCodeItem['stringUnCleanContent'] 94 | if self.__exceptionHelper.shouldSkipContent(oldUnCleanContent) == True: 95 | lastOffset = offset + stringCodeItem['codeLen'] 96 | newFileContent = newFileContent + oldFileContent[offset: lastOffset] 97 | continue 98 | shouldRewrite = True 99 | newKey, newContent = self.encryptFunc(oldContent, oldUnCleanContent, filePath, lineTmp, columnTmp) 100 | if newKey != None and newContent != None: 101 | newFileContent = newFileContent + newKey.encode('UTF-8') 102 | lastOffset = offset + stringCodeItem['codeLen'] 103 | encodeLog.append({ 104 | 'path': filePath, 105 | 'line': lineTmp, 106 | 'column': columnTmp, 107 | 'oldContent': oldContent, 108 | 'oldUnCleanContent': oldUnCleanContent, 109 | 'key': newKey, 110 | 'newContent': newContent 111 | }) 112 | else: 113 | newFileContent = newFileContent + oldUnCleanContent 114 | newFileContent = newFileContent + oldFileContent[lastOffset: len(oldFileContent)] 115 | return encodeLog, newFileContent.decode('UTF-8', 'ignore'), None -------------------------------------------------------------------------------- /Service/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | 'BAExceptionHelper', 3 | 'BAFileEncoder', 4 | 'BAFileDecoder'] -------------------------------------------------------------------------------- /Utils/BAEncryptUtil.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF-8 -*- 3 | 4 | import sys, os, base64 5 | from Crypto.Cipher import AES 6 | 7 | class BAEncryptUtil(object): 8 | def __init__(self): 9 | super(BAEncryptUtil, self).__init__() 10 | 11 | @classmethod 12 | def __bytePad(cls, text, byteAlignLen): 13 | count = len(text) 14 | mod_num = count % byteAlignLen 15 | if mod_num == 0: 16 | return text 17 | add_num = ((count // byteAlignLen) + 1) * byteAlignLen - count 18 | return bytes(text.decode('utf-8') + chr(add_num) * add_num, 'utf-8') 19 | 20 | @classmethod 21 | def __byteUnpad(cls, text, byteAlignLen): 22 | textTmp = text.decode('utf-8') 23 | lastChar = textTmp[-1] 24 | lastLen = ord(lastChar) 25 | lastChunk = textTmp[-lastLen:] 26 | if lastChunk == chr(lastLen)*lastLen: 27 | return bytes(textTmp[:-lastLen], 'utf-8') 28 | return bytes(textTmp, 'utf-8') 29 | 30 | @classmethod 31 | def AESEncrypt(cls, source, key, iv): 32 | if source == None: 33 | return "⚠️ ERROR: Content null!" 34 | if key == None or iv == None: 35 | return "⚠️ ERROR: Key and iv can't be null!" 36 | if len(key) % 16 != 0 or len(iv) % 16 != 0: 37 | return '⚠️ ERROR: Length of key and iv must be a multiple of 16!' 38 | cryptor = AES.new(bytes(key, 'utf-8'), AES.MODE_CBC, bytes(iv, 'utf-8')) 39 | ciphertext = cryptor.encrypt(cls.__bytePad(bytes(source, 'utf-8'), 16)) 40 | result = base64.b64encode(ciphertext).decode('utf-8') 41 | return result 42 | 43 | @classmethod 44 | def AESDecrypt(cls, source, key, iv): 45 | if source == None: 46 | return "⚠️ ERROR: Content null!" 47 | if key == None or iv == None: 48 | return "⚠️ ERROR: Key and iv can't be null!" 49 | if len(key) % 16 != 0 or len(iv) % 16 != 0: 50 | return '⚠️ ERROR: Length of key and iv must be a multiple of 16!' 51 | encrData = base64.b64decode(bytes(source, 'utf-8')) 52 | cipher = AES.new(bytes(key, 'utf-8'), AES.MODE_CBC, bytes(iv, 'utf-8')) 53 | decrData = cipher.decrypt(encrData) 54 | decrData = cls.__byteUnpad(decrData, 16) 55 | result = decrData.decode('utf-8') 56 | return result -------------------------------------------------------------------------------- /Utils/BAErrorUtil.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF-8 -*- 3 | 4 | import sys, os 5 | from enum import Enum 6 | 7 | class BAErrorGrade(Enum): 8 | normal = 0 9 | success = 1 10 | warning = 2 11 | error = 3 12 | 13 | class BAErrorUtil(object): 14 | def __init__(self): 15 | super(BAErrorUtil, self).__init__() 16 | 17 | @classmethod 18 | def buildErrorModel(cls, grade, msg): 19 | return { 20 | 'grade': grade, 21 | 'msg': msg 22 | } 23 | 24 | @classmethod 25 | def printError(cls, grade, msg): 26 | if grade == BAErrorGrade.error: 27 | print('\033[1;31m' + str(msg) + '\033[0m') 28 | elif grade == BAErrorGrade.warning: 29 | print('\033[1;33m' + str(msg) + '\033[0m') 30 | elif grade == BAErrorGrade.success: 31 | print('\033[1;32m' + str(msg) + '\033[0m') 32 | else: 33 | print(str(msg)) 34 | 35 | @classmethod 36 | def printErrorModel(cls, model): 37 | if model == None or 'grade' not in model or 'msg' not in model: 38 | return 39 | cls.printError(model['grade'], model['msg']) -------------------------------------------------------------------------------- /Utils/BAFileUtil.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF-8 -*- 3 | 4 | import sys, os 5 | 6 | class BAFileUtil(object): 7 | def __init__(self): 8 | super(BAFileUtil, self).__init__() 9 | 10 | @classmethod 11 | def isPathExisted(cls, path, isDir): 12 | if path == None or isinstance(path, str) == False or len(path) == 0: 13 | return False 14 | if os.path.exists(path) == False: 15 | return False 16 | if os.path.isdir(path) != isDir: 17 | return False 18 | return True 19 | 20 | @classmethod 21 | def getFileNameByPath(cls, path): 22 | if cls.isPathExisted(path, False) == False: 23 | return None 24 | (filePath, fileName) = os.path.split(path) 25 | return fileName 26 | 27 | @classmethod 28 | def getDirPathByFilePath(cls, path): 29 | if cls.isPathExisted(path, False) == False: 30 | return None 31 | (filePath, fileName) = os.path.split(path) 32 | return filePath 33 | 34 | @classmethod 35 | def getDirNameByDirPath(cls, path): 36 | if cls.isPathExisted(path, True) == False: 37 | return None 38 | tmpPath = path 39 | lastChar = tmpPath[len(tmpPath) - 1 : len(tmpPath)] 40 | if lastChar == '/': 41 | tmpPath = tmpPath[0 : len(tmpPath)-1] 42 | location = tmpPath.rfind('/') 43 | result = None 44 | if location == -1: 45 | result = tmpPath 46 | else: 47 | result = tmpPath[location + 1 : len(tmpPath)] 48 | return result 49 | 50 | @classmethod 51 | def getFileSuffix(cls, fileName): 52 | if fileName == None or len(fileName) == 0: 53 | return None 54 | (realName, suffix) = os.path.splitext(fileName) 55 | return suffix 56 | 57 | @classmethod 58 | def getFileSuffixByPath(cls, path): 59 | if cls.isPathExisted(path, False) == False: 60 | return None 61 | (filePath, fileName) = os.path.split(path) 62 | (realName, suffix) = os.path.splitext(fileName) 63 | return suffix 64 | 65 | @classmethod 66 | def __ergodicToFind(cls, targetName, isDir, rootPath, resultArray): 67 | for itemName in os.listdir(rootPath): 68 | itemPath = os.path.join(rootPath, itemName) 69 | itemIsDir = os.path.isdir(itemPath) 70 | if targetName == itemName and itemIsDir == isDir: 71 | resultArray.append(itemPath) 72 | elif itemIsDir == True: 73 | cls.__ergodicToFind(targetName, isDir, itemPath, resultArray) 74 | 75 | @classmethod 76 | def findTargetPaths(cls, targetName, isDir, rootPath): 77 | if targetName == None or isinstance(targetName, str) == False or len(targetName) == 0: 78 | return None 79 | if cls.isPathExisted(rootPath, True) == False: 80 | return None 81 | resultArray = [] 82 | if isDir == True: 83 | rootDirName = cls.getDirNameByDirPath(rootPath) 84 | if rootDirName == targetName: 85 | resultArray.append(rootPath) 86 | cls.__ergodicToFind(targetName, isDir, rootPath, resultArray) 87 | return resultArray 88 | 89 | @classmethod 90 | def getLinesSize(cls, filePath): 91 | if cls.isPathExisted(filePath, False) == False: 92 | return None 93 | fileHandler = open(filePath, 'r') 94 | lines = fileHandler.readlines() 95 | fileHandler.close() 96 | result = [] 97 | for line in lines: 98 | result.append(len(line.encode('UTF-8', 'ignore'))) 99 | return result 100 | 101 | @classmethod 102 | def convertToOffset(cls, linesSize, line, column): 103 | if linesSize == None: 104 | return -1 105 | if len(linesSize) == 0: 106 | return 0 107 | result = 0 108 | for i in range(0, len(linesSize), 1): 109 | if i == line: 110 | result = result + column 111 | break 112 | else: 113 | result = result + linesSize[i] 114 | return result -------------------------------------------------------------------------------- /Utils/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | 'BAEncryptUtil', 3 | 'BAFileUtil'] -------------------------------------------------------------------------------- /oc-class/GTM/GTMBase64.h: -------------------------------------------------------------------------------- 1 | // 2 | // GTMBase64.h 3 | // 4 | // Copyright 2006-2008 Google Inc. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 7 | // use this file except in compliance with the License. You may obtain a copy 8 | // of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | // License for the specific language governing permissions and limitations under 16 | // the License. 17 | // 18 | 19 | #import 20 | #import "GTMDefines.h" 21 | 22 | // GTMBase64 23 | // 24 | /// Helper for handling Base64 and WebSafeBase64 encodings 25 | // 26 | /// The webSafe methods use different character set and also the results aren't 27 | /// always padded to a multiple of 4 characters. This is done so the resulting 28 | /// data can be used in urls and url query arguments without needing any 29 | /// encoding. You must use the webSafe* methods together, the data does not 30 | /// interop with the RFC methods. 31 | // 32 | @interface GTMBase64 : NSObject 33 | 34 | // 35 | // Standard Base64 (RFC) handling 36 | // 37 | 38 | // encodeData: 39 | // 40 | /// Base64 encodes contents of the NSData object. 41 | // 42 | /// Returns: 43 | /// A new autoreleased NSData with the encoded payload. nil for any error. 44 | // 45 | +(NSData *)encodeData:(NSData *)data; 46 | 47 | // decodeData: 48 | // 49 | /// Base64 decodes contents of the NSData object. 50 | // 51 | /// Returns: 52 | /// A new autoreleased NSData with the decoded payload. nil for any error. 53 | // 54 | +(NSData *)decodeData:(NSData *)data; 55 | 56 | // encodeBytes:length: 57 | // 58 | /// Base64 encodes the data pointed at by |bytes|. 59 | // 60 | /// Returns: 61 | /// A new autoreleased NSData with the encoded payload. nil for any error. 62 | // 63 | +(NSData *)encodeBytes:(const void *)bytes length:(NSUInteger)length; 64 | 65 | // decodeBytes:length: 66 | // 67 | /// Base64 decodes the data pointed at by |bytes|. 68 | // 69 | /// Returns: 70 | /// A new autoreleased NSData with the encoded payload. nil for any error. 71 | // 72 | +(NSData *)decodeBytes:(const void *)bytes length:(NSUInteger)length; 73 | 74 | // stringByEncodingData: 75 | // 76 | /// Base64 encodes contents of the NSData object. 77 | // 78 | /// Returns: 79 | /// A new autoreleased NSString with the encoded payload. nil for any error. 80 | // 81 | +(NSString *)stringByEncodingData:(NSData *)data; 82 | 83 | // stringByEncodingBytes:length: 84 | // 85 | /// Base64 encodes the data pointed at by |bytes|. 86 | // 87 | /// Returns: 88 | /// A new autoreleased NSString with the encoded payload. nil for any error. 89 | // 90 | +(NSString *)stringByEncodingBytes:(const void *)bytes length:(NSUInteger)length; 91 | 92 | // decodeString: 93 | // 94 | /// Base64 decodes contents of the NSString. 95 | // 96 | /// Returns: 97 | /// A new autoreleased NSData with the decoded payload. nil for any error. 98 | // 99 | +(NSData *)decodeString:(NSString *)string; 100 | 101 | // 102 | // Modified Base64 encoding so the results can go onto urls. 103 | // 104 | // The changes are in the characters generated and also allows the result to 105 | // not be padded to a multiple of 4. 106 | // Must use the matching call to encode/decode, won't interop with the 107 | // RFC versions. 108 | // 109 | 110 | // webSafeEncodeData:padded: 111 | // 112 | /// WebSafe Base64 encodes contents of the NSData object. If |padded| is YES 113 | /// then padding characters are added so the result length is a multiple of 4. 114 | // 115 | /// Returns: 116 | /// A new autoreleased NSData with the encoded payload. nil for any error. 117 | // 118 | +(NSData *)webSafeEncodeData:(NSData *)data 119 | padded:(BOOL)padded; 120 | 121 | // webSafeDecodeData: 122 | // 123 | /// WebSafe Base64 decodes contents of the NSData object. 124 | // 125 | /// Returns: 126 | /// A new autoreleased NSData with the decoded payload. nil for any error. 127 | // 128 | +(NSData *)webSafeDecodeData:(NSData *)data; 129 | 130 | // webSafeEncodeBytes:length:padded: 131 | // 132 | /// WebSafe Base64 encodes the data pointed at by |bytes|. If |padded| is YES 133 | /// then padding characters are added so the result length is a multiple of 4. 134 | // 135 | /// Returns: 136 | /// A new autoreleased NSData with the encoded payload. nil for any error. 137 | // 138 | +(NSData *)webSafeEncodeBytes:(const void *)bytes 139 | length:(NSUInteger)length 140 | padded:(BOOL)padded; 141 | 142 | // webSafeDecodeBytes:length: 143 | // 144 | /// WebSafe Base64 decodes the data pointed at by |bytes|. 145 | // 146 | /// Returns: 147 | /// A new autoreleased NSData with the encoded payload. nil for any error. 148 | // 149 | +(NSData *)webSafeDecodeBytes:(const void *)bytes length:(NSUInteger)length; 150 | 151 | // stringByWebSafeEncodingData:padded: 152 | // 153 | /// WebSafe Base64 encodes contents of the NSData object. If |padded| is YES 154 | /// then padding characters are added so the result length is a multiple of 4. 155 | // 156 | /// Returns: 157 | /// A new autoreleased NSString with the encoded payload. nil for any error. 158 | // 159 | +(NSString *)stringByWebSafeEncodingData:(NSData *)data 160 | padded:(BOOL)padded; 161 | 162 | // stringByWebSafeEncodingBytes:length:padded: 163 | // 164 | /// WebSafe Base64 encodes the data pointed at by |bytes|. If |padded| is YES 165 | /// then padding characters are added so the result length is a multiple of 4. 166 | // 167 | /// Returns: 168 | /// A new autoreleased NSString with the encoded payload. nil for any error. 169 | // 170 | +(NSString *)stringByWebSafeEncodingBytes:(const void *)bytes 171 | length:(NSUInteger)length 172 | padded:(BOOL)padded; 173 | 174 | // webSafeDecodeString: 175 | // 176 | /// WebSafe Base64 decodes contents of the NSString. 177 | // 178 | /// Returns: 179 | /// A new autoreleased NSData with the decoded payload. nil for any error. 180 | // 181 | +(NSData *)webSafeDecodeString:(NSString *)string; 182 | 183 | @end -------------------------------------------------------------------------------- /oc-class/GTM/GTMBase64.m: -------------------------------------------------------------------------------- 1 | // 2 | // GTMBase64.m 3 | // 4 | // Copyright 2006-2008 Google Inc. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 7 | // use this file except in compliance with the License. You may obtain a copy 8 | // of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | // License for the specific language governing permissions and limitations under 16 | // the License. 17 | // 18 | 19 | #import "GTMBase64.h" 20 | #import "GTMDefines.h" 21 | 22 | static const char *kBase64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 23 | static const char *kWebSafeBase64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; 24 | static const char kBase64PaddingChar = '='; 25 | static const char kBase64InvalidChar = 99; 26 | 27 | static const char kBase64DecodeChars[] = { 28 | // This array was generated by the following code: 29 | // #include 30 | // #include 31 | // #include 32 | // main() 33 | // { 34 | // static const char Base64[] = 35 | // "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 36 | // char *pos; 37 | // int idx, i, j; 38 | // printf(" "); 39 | // for (i = 0; i < 255; i += 8) { 40 | // for (j = i; j < i + 8; j++) { 41 | // pos = strchr(Base64, j); 42 | // if ((pos == NULL) || (j == 0)) 43 | // idx = 99; 44 | // else 45 | // idx = pos - Base64; 46 | // if (idx == 99) 47 | // printf(" %2d, ", idx); 48 | // else 49 | // printf(" %2d/*%c*/,", idx, j); 50 | // } 51 | // printf("\n "); 52 | // } 53 | // } 54 | 99, 99, 99, 99, 99, 99, 99, 99, 55 | 99, 99, 99, 99, 99, 99, 99, 99, 56 | 99, 99, 99, 99, 99, 99, 99, 99, 57 | 99, 99, 99, 99, 99, 99, 99, 99, 58 | 99, 99, 99, 99, 99, 99, 99, 99, 59 | 99, 99, 99, 62/*+*/, 99, 99, 99, 63/*/ */, 60 | 52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/, 61 | 60/*8*/, 61/*9*/, 99, 99, 99, 99, 99, 99, 62 | 99, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/, 63 | 7/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/, 64 | 15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/, 65 | 23/*X*/, 24/*Y*/, 25/*Z*/, 99, 99, 99, 99, 99, 66 | 99, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/, 67 | 33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/, 68 | 41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/, 69 | 49/*x*/, 50/*y*/, 51/*z*/, 99, 99, 99, 99, 99, 70 | 99, 99, 99, 99, 99, 99, 99, 99, 71 | 99, 99, 99, 99, 99, 99, 99, 99, 72 | 99, 99, 99, 99, 99, 99, 99, 99, 73 | 99, 99, 99, 99, 99, 99, 99, 99, 74 | 99, 99, 99, 99, 99, 99, 99, 99, 75 | 99, 99, 99, 99, 99, 99, 99, 99, 76 | 99, 99, 99, 99, 99, 99, 99, 99, 77 | 99, 99, 99, 99, 99, 99, 99, 99, 78 | 99, 99, 99, 99, 99, 99, 99, 99, 79 | 99, 99, 99, 99, 99, 99, 99, 99, 80 | 99, 99, 99, 99, 99, 99, 99, 99, 81 | 99, 99, 99, 99, 99, 99, 99, 99, 82 | 99, 99, 99, 99, 99, 99, 99, 99, 83 | 99, 99, 99, 99, 99, 99, 99, 99, 84 | 99, 99, 99, 99, 99, 99, 99, 99, 85 | 99, 99, 99, 99, 99, 99, 99, 99 86 | }; 87 | 88 | static const char kWebSafeBase64DecodeChars[] = { 89 | // This array was generated by the following code: 90 | // #include 91 | // #include 92 | // #include 93 | // main() 94 | // { 95 | // static const char Base64[] = 96 | // "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; 97 | // char *pos; 98 | // int idx, i, j; 99 | // printf(" "); 100 | // for (i = 0; i < 255; i += 8) { 101 | // for (j = i; j < i + 8; j++) { 102 | // pos = strchr(Base64, j); 103 | // if ((pos == NULL) || (j == 0)) 104 | // idx = 99; 105 | // else 106 | // idx = pos - Base64; 107 | // if (idx == 99) 108 | // printf(" %2d, ", idx); 109 | // else 110 | // printf(" %2d/*%c*/,", idx, j); 111 | // } 112 | // printf("\n "); 113 | // } 114 | // } 115 | 99, 99, 99, 99, 99, 99, 99, 99, 116 | 99, 99, 99, 99, 99, 99, 99, 99, 117 | 99, 99, 99, 99, 99, 99, 99, 99, 118 | 99, 99, 99, 99, 99, 99, 99, 99, 119 | 99, 99, 99, 99, 99, 99, 99, 99, 120 | 99, 99, 99, 99, 99, 62/*-*/, 99, 99, 121 | 52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/, 122 | 60/*8*/, 61/*9*/, 99, 99, 99, 99, 99, 99, 123 | 99, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/, 124 | 7/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/, 125 | 15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/, 126 | 23/*X*/, 24/*Y*/, 25/*Z*/, 99, 99, 99, 99, 63/*_*/, 127 | 99, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/, 128 | 33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/, 129 | 41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/, 130 | 49/*x*/, 50/*y*/, 51/*z*/, 99, 99, 99, 99, 99, 131 | 99, 99, 99, 99, 99, 99, 99, 99, 132 | 99, 99, 99, 99, 99, 99, 99, 99, 133 | 99, 99, 99, 99, 99, 99, 99, 99, 134 | 99, 99, 99, 99, 99, 99, 99, 99, 135 | 99, 99, 99, 99, 99, 99, 99, 99, 136 | 99, 99, 99, 99, 99, 99, 99, 99, 137 | 99, 99, 99, 99, 99, 99, 99, 99, 138 | 99, 99, 99, 99, 99, 99, 99, 99, 139 | 99, 99, 99, 99, 99, 99, 99, 99, 140 | 99, 99, 99, 99, 99, 99, 99, 99, 141 | 99, 99, 99, 99, 99, 99, 99, 99, 142 | 99, 99, 99, 99, 99, 99, 99, 99, 143 | 99, 99, 99, 99, 99, 99, 99, 99, 144 | 99, 99, 99, 99, 99, 99, 99, 99, 145 | 99, 99, 99, 99, 99, 99, 99, 99, 146 | 99, 99, 99, 99, 99, 99, 99, 99 147 | }; 148 | 149 | 150 | // Tests a character to see if it's a whitespace character. 151 | // 152 | // Returns: 153 | // YES if the character is a whitespace character. 154 | // NO if the character is not a whitespace character. 155 | // 156 | GTM_INLINE BOOL IsSpace(unsigned char c) { 157 | // we use our own mapping here because we don't want anything w/ locale 158 | // support. 159 | static BOOL kSpaces[256] = { 160 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // 0-9 161 | 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 10-19 162 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20-29 163 | 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 30-39 164 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40-49 165 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 50-59 166 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60-69 167 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 70-79 168 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80-89 169 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90-99 170 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 100-109 171 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 110-119 172 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 120-129 173 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 130-139 174 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 140-149 175 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 150-159 176 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160-169 177 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 170-179 178 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 180-189 179 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 190-199 180 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 200-209 181 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 210-219 182 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 220-229 183 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 230-239 184 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 240-249 185 | 0, 0, 0, 0, 0, 1, // 250-255 186 | }; 187 | return kSpaces[c]; 188 | } 189 | 190 | // Calculate how long the data will be once it's base64 encoded. 191 | // 192 | // Returns: 193 | // The guessed encoded length for a source length 194 | // 195 | GTM_INLINE NSUInteger CalcEncodedLength(NSUInteger srcLen, BOOL padded) { 196 | NSUInteger intermediate_result = 8 * srcLen + 5; 197 | NSUInteger len = intermediate_result / 6; 198 | if (padded) { 199 | len = ((len + 3) / 4) * 4; 200 | } 201 | return len; 202 | } 203 | 204 | // Tries to calculate how long the data will be once it's base64 decoded. 205 | // Unlike the above, this is always an upperbound, since the source data 206 | // could have spaces and might end with the padding characters on them. 207 | // 208 | // Returns: 209 | // The guessed decoded length for a source length 210 | // 211 | GTM_INLINE NSUInteger GuessDecodedLength(NSUInteger srcLen) { 212 | return (srcLen + 3) / 4 * 3; 213 | } 214 | 215 | 216 | @interface GTMBase64 (PrivateMethods) 217 | 218 | +(NSData *)baseEncode:(const void *)bytes 219 | length:(NSUInteger)length 220 | charset:(const char *)charset 221 | padded:(BOOL)padded; 222 | 223 | +(NSData *)baseDecode:(const void *)bytes 224 | length:(NSUInteger)length 225 | charset:(const char*)charset 226 | requirePadding:(BOOL)requirePadding; 227 | 228 | +(NSUInteger)baseEncode:(const char *)srcBytes 229 | srcLen:(NSUInteger)srcLen 230 | destBytes:(char *)destBytes 231 | destLen:(NSUInteger)destLen 232 | charset:(const char *)charset 233 | padded:(BOOL)padded; 234 | 235 | +(NSUInteger)baseDecode:(const char *)srcBytes 236 | srcLen:(NSUInteger)srcLen 237 | destBytes:(char *)destBytes 238 | destLen:(NSUInteger)destLen 239 | charset:(const char *)charset 240 | requirePadding:(BOOL)requirePadding; 241 | 242 | @end 243 | 244 | 245 | @implementation GTMBase64 246 | 247 | // 248 | // Standard Base64 (RFC) handling 249 | // 250 | 251 | +(NSData *)encodeData:(NSData *)data { 252 | return [self baseEncode:[data bytes] 253 | length:[data length] 254 | charset:kBase64EncodeChars 255 | padded:YES]; 256 | } 257 | 258 | +(NSData *)decodeData:(NSData *)data { 259 | return [self baseDecode:[data bytes] 260 | length:[data length] 261 | charset:kBase64DecodeChars 262 | requirePadding:YES]; 263 | } 264 | 265 | +(NSData *)encodeBytes:(const void *)bytes length:(NSUInteger)length { 266 | return [self baseEncode:bytes 267 | length:length 268 | charset:kBase64EncodeChars 269 | padded:YES]; 270 | } 271 | 272 | +(NSData *)decodeBytes:(const void *)bytes length:(NSUInteger)length { 273 | return [self baseDecode:bytes 274 | length:length 275 | charset:kBase64DecodeChars 276 | requirePadding:YES]; 277 | } 278 | 279 | +(NSString *)stringByEncodingData:(NSData *)data { 280 | NSString *result = nil; 281 | NSData *converted = [self baseEncode:[data bytes] 282 | length:[data length] 283 | charset:kBase64EncodeChars 284 | padded:YES]; 285 | if (converted) { 286 | result = [[NSString alloc] initWithData:converted 287 | encoding:NSASCIIStringEncoding] ; 288 | } 289 | return result; 290 | } 291 | 292 | +(NSString *)stringByEncodingBytes:(const void *)bytes length:(NSUInteger)length { 293 | NSString *result = nil; 294 | NSData *converted = [self baseEncode:bytes 295 | length:length 296 | charset:kBase64EncodeChars 297 | padded:YES]; 298 | if (converted) { 299 | result = [[NSString alloc] initWithData:converted 300 | encoding:NSASCIIStringEncoding] ; 301 | } 302 | return result; 303 | } 304 | 305 | +(NSData *)decodeString:(NSString *)string { 306 | NSData *result = nil; 307 | NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding]; 308 | if (data) { 309 | result = [self baseDecode:[data bytes] 310 | length:[data length] 311 | charset:kBase64DecodeChars 312 | requirePadding:YES]; 313 | } 314 | return result; 315 | } 316 | 317 | // 318 | // Modified Base64 encoding so the results can go onto urls. 319 | // 320 | // The changes are in the characters generated and also the result isn't 321 | // padded to a multiple of 4. 322 | // Must use the matching call to encode/decode, won't interop with the 323 | // RFC versions. 324 | // 325 | 326 | +(NSData *)webSafeEncodeData:(NSData *)data 327 | padded:(BOOL)padded { 328 | return [self baseEncode:[data bytes] 329 | length:[data length] 330 | charset:kWebSafeBase64EncodeChars 331 | padded:padded]; 332 | } 333 | 334 | +(NSData *)webSafeDecodeData:(NSData *)data { 335 | return [self baseDecode:[data bytes] 336 | length:[data length] 337 | charset:kWebSafeBase64DecodeChars 338 | requirePadding:NO]; 339 | } 340 | 341 | +(NSData *)webSafeEncodeBytes:(const void *)bytes 342 | length:(NSUInteger)length 343 | padded:(BOOL)padded { 344 | return [self baseEncode:bytes 345 | length:length 346 | charset:kWebSafeBase64EncodeChars 347 | padded:padded]; 348 | } 349 | 350 | +(NSData *)webSafeDecodeBytes:(const void *)bytes length:(NSUInteger)length { 351 | return [self baseDecode:bytes 352 | length:length 353 | charset:kWebSafeBase64DecodeChars 354 | requirePadding:NO]; 355 | } 356 | 357 | +(NSString *)stringByWebSafeEncodingData:(NSData *)data 358 | padded:(BOOL)padded { 359 | NSString *result = nil; 360 | NSData *converted = [self baseEncode:[data bytes] 361 | length:[data length] 362 | charset:kWebSafeBase64EncodeChars 363 | padded:padded]; 364 | if (converted) { 365 | result = [[NSString alloc] initWithData:converted 366 | encoding:NSASCIIStringEncoding]; 367 | } 368 | return result; 369 | } 370 | 371 | +(NSString *)stringByWebSafeEncodingBytes:(const void *)bytes 372 | length:(NSUInteger)length 373 | padded:(BOOL)padded { 374 | NSString *result = nil; 375 | NSData *converted = [self baseEncode:bytes 376 | length:length 377 | charset:kWebSafeBase64EncodeChars 378 | padded:padded]; 379 | if (converted) { 380 | result = [[NSString alloc] initWithData:converted 381 | encoding:NSASCIIStringEncoding] ; 382 | } 383 | return result; 384 | } 385 | 386 | +(NSData *)webSafeDecodeString:(NSString *)string { 387 | NSData *result = nil; 388 | NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding]; 389 | if (data) { 390 | result = [self baseDecode:[data bytes] 391 | length:[data length] 392 | charset:kWebSafeBase64DecodeChars 393 | requirePadding:NO]; 394 | } 395 | return result; 396 | } 397 | 398 | @end 399 | 400 | @implementation GTMBase64 (PrivateMethods) 401 | 402 | // 403 | // baseEncode:length:charset:padded: 404 | // 405 | // Does the common lifting of creating the dest NSData. it creates & sizes the 406 | // data for the results. |charset| is the characters to use for the encoding 407 | // of the data. |padding| controls if the encoded data should be padded to a 408 | // multiple of 4. 409 | // 410 | // Returns: 411 | // an autorelease NSData with the encoded data, nil if any error. 412 | // 413 | +(NSData *)baseEncode:(const void *)bytes 414 | length:(NSUInteger)length 415 | charset:(const char *)charset 416 | padded:(BOOL)padded { 417 | // how big could it be? 418 | NSUInteger maxLength = CalcEncodedLength(length, padded); 419 | // make space 420 | NSMutableData *result = [NSMutableData data]; 421 | [result setLength:maxLength]; 422 | // do it 423 | NSUInteger finalLength = [self baseEncode:bytes 424 | srcLen:length 425 | destBytes:[result mutableBytes] 426 | destLen:[result length] 427 | charset:charset 428 | padded:padded]; 429 | if (finalLength) { 430 | _GTMDevAssert(finalLength == maxLength, @"how did we calc the length wrong?"); 431 | } else { 432 | // shouldn't happen, this means we ran out of space 433 | result = nil; 434 | } 435 | return result; 436 | } 437 | 438 | // 439 | // baseDecode:length:charset:requirePadding: 440 | // 441 | // Does the common lifting of creating the dest NSData. it creates & sizes the 442 | // data for the results. |charset| is the characters to use for the decoding 443 | // of the data. 444 | // 445 | // Returns: 446 | // an autorelease NSData with the decoded data, nil if any error. 447 | // 448 | // 449 | +(NSData *)baseDecode:(const void *)bytes 450 | length:(NSUInteger)length 451 | charset:(const char *)charset 452 | requirePadding:(BOOL)requirePadding { 453 | // could try to calculate what it will end up as 454 | NSUInteger maxLength = GuessDecodedLength(length); 455 | // make space 456 | NSMutableData *result = [NSMutableData data]; 457 | [result setLength:maxLength]; 458 | // do it 459 | NSUInteger finalLength = [self baseDecode:bytes 460 | srcLen:length 461 | destBytes:[result mutableBytes] 462 | destLen:[result length] 463 | charset:charset 464 | requirePadding:requirePadding]; 465 | if (finalLength) { 466 | if (finalLength != maxLength) { 467 | // resize down to how big it was 468 | [result setLength:finalLength]; 469 | } 470 | } else { 471 | // either an error in the args, or we ran out of space 472 | result = nil; 473 | } 474 | return result; 475 | } 476 | 477 | // 478 | // baseEncode:srcLen:destBytes:destLen:charset:padded: 479 | // 480 | // Encodes the buffer into the larger. returns the length of the encoded 481 | // data, or zero for an error. 482 | // |charset| is the characters to use for the encoding 483 | // |padded| tells if the result should be padded to a multiple of 4. 484 | // 485 | // Returns: 486 | // the length of the encoded data. zero if any error. 487 | // 488 | +(NSUInteger)baseEncode:(const char *)srcBytes 489 | srcLen:(NSUInteger)srcLen 490 | destBytes:(char *)destBytes 491 | destLen:(NSUInteger)destLen 492 | charset:(const char *)charset 493 | padded:(BOOL)padded { 494 | if (!srcLen || !destLen || !srcBytes || !destBytes) { 495 | return 0; 496 | } 497 | 498 | char *curDest = destBytes; 499 | const unsigned char *curSrc = (const unsigned char *)(srcBytes); 500 | 501 | // Three bytes of data encodes to four characters of cyphertext. 502 | // So we can pump through three-byte chunks atomically. 503 | while (srcLen > 2) { 504 | // space? 505 | _GTMDevAssert(destLen >= 4, @"our calc for encoded length was wrong"); 506 | curDest[0] = charset[curSrc[0] >> 2]; 507 | curDest[1] = charset[((curSrc[0] & 0x03) << 4) + (curSrc[1] >> 4)]; 508 | curDest[2] = charset[((curSrc[1] & 0x0f) << 2) + (curSrc[2] >> 6)]; 509 | curDest[3] = charset[curSrc[2] & 0x3f]; 510 | 511 | curDest += 4; 512 | curSrc += 3; 513 | srcLen -= 3; 514 | destLen -= 4; 515 | } 516 | 517 | // now deal with the tail (<=2 bytes) 518 | switch (srcLen) { 519 | case 0: 520 | // Nothing left; nothing more to do. 521 | break; 522 | case 1: 523 | // One byte left: this encodes to two characters, and (optionally) 524 | // two pad characters to round out the four-character cypherblock. 525 | _GTMDevAssert(destLen >= 2, @"our calc for encoded length was wrong"); 526 | curDest[0] = charset[curSrc[0] >> 2]; 527 | curDest[1] = charset[(curSrc[0] & 0x03) << 4]; 528 | curDest += 2; 529 | destLen -= 2; 530 | if (padded) { 531 | _GTMDevAssert(destLen >= 2, @"our calc for encoded length was wrong"); 532 | curDest[0] = kBase64PaddingChar; 533 | curDest[1] = kBase64PaddingChar; 534 | curDest += 2; 535 | } 536 | break; 537 | case 2: 538 | // Two bytes left: this encodes to three characters, and (optionally) 539 | // one pad character to round out the four-character cypherblock. 540 | _GTMDevAssert(destLen >= 3, @"our calc for encoded length was wrong"); 541 | curDest[0] = charset[curSrc[0] >> 2]; 542 | curDest[1] = charset[((curSrc[0] & 0x03) << 4) + (curSrc[1] >> 4)]; 543 | curDest[2] = charset[(curSrc[1] & 0x0f) << 2]; 544 | curDest += 3; 545 | destLen -= 3; 546 | if (padded) { 547 | _GTMDevAssert(destLen >= 1, @"our calc for encoded length was wrong"); 548 | curDest[0] = kBase64PaddingChar; 549 | curDest += 1; 550 | } 551 | break; 552 | } 553 | // return the length 554 | return (curDest - destBytes); 555 | } 556 | 557 | // 558 | // baseDecode:srcLen:destBytes:destLen:charset:requirePadding: 559 | // 560 | // Decodes the buffer into the larger. returns the length of the decoded 561 | // data, or zero for an error. 562 | // |charset| is the character decoding buffer to use 563 | // 564 | // Returns: 565 | // the length of the encoded data. zero if any error. 566 | // 567 | +(NSUInteger)baseDecode:(const char *)srcBytes 568 | srcLen:(NSUInteger)srcLen 569 | destBytes:(char *)destBytes 570 | destLen:(NSUInteger)destLen 571 | charset:(const char *)charset 572 | requirePadding:(BOOL)requirePadding { 573 | if (!srcLen || !destLen || !srcBytes || !destBytes) { 574 | return 0; 575 | } 576 | 577 | int decode; 578 | NSUInteger destIndex = 0; 579 | int state = 0; 580 | char ch = 0; 581 | while (srcLen-- && (ch = *srcBytes++) != 0) { 582 | if (IsSpace(ch)) // Skip whitespace 583 | continue; 584 | 585 | if (ch == kBase64PaddingChar) 586 | break; 587 | 588 | decode = charset[(unsigned int)ch]; 589 | if (decode == kBase64InvalidChar) 590 | return 0; 591 | 592 | // Four cyphertext characters decode to three bytes. 593 | // Therefore we can be in one of four states. 594 | switch (state) { 595 | case 0: 596 | // We're at the beginning of a four-character cyphertext block. 597 | // This sets the high six bits of the first byte of the 598 | // plaintext block. 599 | _GTMDevAssert(destIndex < destLen, @"our calc for decoded length was wrong"); 600 | destBytes[destIndex] = decode << 2; 601 | state = 1; 602 | break; 603 | case 1: 604 | // We're one character into a four-character cyphertext block. 605 | // This sets the low two bits of the first plaintext byte, 606 | // and the high four bits of the second plaintext byte. 607 | _GTMDevAssert((destIndex+1) < destLen, @"our calc for decoded length was wrong"); 608 | destBytes[destIndex] |= decode >> 4; 609 | destBytes[destIndex+1] = (decode & 0x0f) << 4; 610 | destIndex++; 611 | state = 2; 612 | break; 613 | case 2: 614 | // We're two characters into a four-character cyphertext block. 615 | // This sets the low four bits of the second plaintext 616 | // byte, and the high two bits of the third plaintext byte. 617 | // However, if this is the end of data, and those two 618 | // bits are zero, it could be that those two bits are 619 | // leftovers from the encoding of data that had a length 620 | // of two mod three. 621 | _GTMDevAssert((destIndex+1) < destLen, @"our calc for decoded length was wrong"); 622 | destBytes[destIndex] |= decode >> 2; 623 | destBytes[destIndex+1] = (decode & 0x03) << 6; 624 | destIndex++; 625 | state = 3; 626 | break; 627 | case 3: 628 | // We're at the last character of a four-character cyphertext block. 629 | // This sets the low six bits of the third plaintext byte. 630 | _GTMDevAssert(destIndex < destLen, @"our calc for decoded length was wrong"); 631 | destBytes[destIndex] |= decode; 632 | destIndex++; 633 | state = 0; 634 | break; 635 | } 636 | } 637 | 638 | // We are done decoding Base-64 chars. Let's see if we ended 639 | // on a byte boundary, and/or with erroneous trailing characters. 640 | if (ch == kBase64PaddingChar) { // We got a pad char 641 | if ((state == 0) || (state == 1)) { 642 | return 0; // Invalid '=' in first or second position 643 | } 644 | if (srcLen == 0) { 645 | if (state == 2) { // We run out of input but we still need another '=' 646 | return 0; 647 | } 648 | // Otherwise, we are in state 3 and only need this '=' 649 | } else { 650 | if (state == 2) { // need another '=' 651 | while ((ch = *srcBytes++) && (srcLen-- > 0)) { 652 | if (!IsSpace(ch)) 653 | break; 654 | } 655 | if (ch != kBase64PaddingChar) { 656 | return 0; 657 | } 658 | } 659 | // state = 1 or 2, check if all remain padding is space 660 | while ((ch = *srcBytes++) && (srcLen-- > 0)) { 661 | if (!IsSpace(ch)) { 662 | return 0; 663 | } 664 | } 665 | } 666 | } else { 667 | // We ended by seeing the end of the string. 668 | 669 | if (requirePadding) { 670 | // If we require padding, then anything but state 0 is an error. 671 | if (state != 0) { 672 | return 0; 673 | } 674 | } else { 675 | // Make sure we have no partial bytes lying around. Note that we do not 676 | // require trailing '=', so states 2 and 3 are okay too. 677 | if (state == 1) { 678 | return 0; 679 | } 680 | } 681 | } 682 | 683 | // If then next piece of output was valid and got written to it means we got a 684 | // very carefully crafted input that appeared valid but contains some trailing 685 | // bits past the real length, so just toss the thing. 686 | if ((destIndex < destLen) && 687 | (destBytes[destIndex] != 0)) { 688 | return 0; 689 | } 690 | 691 | return destIndex; 692 | } 693 | 694 | @end -------------------------------------------------------------------------------- /oc-class/GTM/GTMDefines.h: -------------------------------------------------------------------------------- 1 | // 2 | // GTMDefines.h 3 | // 4 | // Copyright 2008 Google Inc. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 7 | // use this file except in compliance with the License. You may obtain a copy 8 | // of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | // License for the specific language governing permissions and limitations under 16 | // the License. 17 | // 18 | 19 | // ============================================================================ 20 | 21 | #include 22 | #include 23 | 24 | #if TARGET_OS_IPHONE 25 | #include 26 | #endif // TARGET_OS_IPHONE 27 | 28 | // Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs 29 | #ifndef MAC_OS_X_VERSION_10_5 30 | #define MAC_OS_X_VERSION_10_5 1050 31 | #endif 32 | #ifndef MAC_OS_X_VERSION_10_6 33 | #define MAC_OS_X_VERSION_10_6 1060 34 | #endif 35 | #ifndef MAC_OS_X_VERSION_10_7 36 | #define MAC_OS_X_VERSION_10_7 1070 37 | #endif 38 | 39 | // Not all __IPHONE_X macros defined in past SDKs 40 | #ifndef __IPHONE_3_0 41 | #define __IPHONE_3_0 30000 42 | #endif 43 | #ifndef __IPHONE_3_1 44 | #define __IPHONE_3_1 30100 45 | #endif 46 | #ifndef __IPHONE_3_2 47 | #define __IPHONE_3_2 30200 48 | #endif 49 | #ifndef __IPHONE_4_0 50 | #define __IPHONE_4_0 40000 51 | #endif 52 | #ifndef __IPHONE_4_3 53 | #define __IPHONE_4_3 40300 54 | #endif 55 | #ifndef __IPHONE_5_0 56 | #define __IPHONE_5_0 50000 57 | #endif 58 | 59 | // ---------------------------------------------------------------------------- 60 | // CPP symbols that can be overridden in a prefix to control how the toolbox 61 | // is compiled. 62 | // ---------------------------------------------------------------------------- 63 | 64 | 65 | // By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and 66 | // GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens 67 | // when a validation fails. If you implement your own validators, you may want 68 | // to control their internals using the same macros for consistency. 69 | #ifndef GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 70 | #define GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 0 71 | #endif 72 | 73 | // Give ourselves a consistent way to do inlines. Apple's macros even use 74 | // a few different actual definitions, so we're based off of the foundation 75 | // one. 76 | #if !defined(GTM_INLINE) 77 | #if (defined (__GNUC__) && (__GNUC__ == 4)) || defined (__clang__) 78 | #define GTM_INLINE static __inline__ __attribute__((always_inline)) 79 | #else 80 | #define GTM_INLINE static __inline__ 81 | #endif 82 | #endif 83 | 84 | // Give ourselves a consistent way of doing externs that links up nicely 85 | // when mixing objc and objc++ 86 | #if !defined (GTM_EXTERN) 87 | #if defined __cplusplus 88 | #define GTM_EXTERN extern "C" 89 | #define GTM_EXTERN_C_BEGIN extern "C" { 90 | #define GTM_EXTERN_C_END } 91 | #else 92 | #define GTM_EXTERN extern 93 | #define GTM_EXTERN_C_BEGIN 94 | #define GTM_EXTERN_C_END 95 | #endif 96 | #endif 97 | 98 | // Give ourselves a consistent way of exporting things if we have visibility 99 | // set to hidden. 100 | #if !defined (GTM_EXPORT) 101 | #define GTM_EXPORT __attribute__((visibility("default"))) 102 | #endif 103 | 104 | // Give ourselves a consistent way of declaring something as unused. This 105 | // doesn't use __unused because that is only supported in gcc 4.2 and greater. 106 | #if !defined (GTM_UNUSED) 107 | #define GTM_UNUSED(x) ((void)(x)) 108 | #endif 109 | 110 | // _GTMDevLog & _GTMDevAssert 111 | // 112 | // _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for 113 | // developer level errors. This implementation simply macros to NSLog/NSAssert. 114 | // It is not intended to be a general logging/reporting system. 115 | // 116 | // Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert 117 | // for a little more background on the usage of these macros. 118 | // 119 | // _GTMDevLog log some error/problem in debug builds 120 | // _GTMDevAssert assert if conditon isn't met w/in a method/function 121 | // in all builds. 122 | // 123 | // To replace this system, just provide different macro definitions in your 124 | // prefix header. Remember, any implementation you provide *must* be thread 125 | // safe since this could be called by anything in what ever situtation it has 126 | // been placed in. 127 | // 128 | 129 | // We only define the simple macros if nothing else has defined this. 130 | #ifndef _GTMDevLog 131 | 132 | #ifdef DEBUG 133 | #define _GTMDevLog(...) NSLog(__VA_ARGS__) 134 | #else 135 | #define _GTMDevLog(...) do { } while (0) 136 | #endif 137 | 138 | #endif // _GTMDevLog 139 | 140 | #ifndef _GTMDevAssert 141 | // we directly invoke the NSAssert handler so we can pass on the varargs 142 | // (NSAssert doesn't have a macro we can use that takes varargs) 143 | #if !defined(NS_BLOCK_ASSERTIONS) 144 | #define _GTMDevAssert(condition, ...) \ 145 | do { \ 146 | if (!(condition)) { \ 147 | [[NSAssertionHandler currentHandler] \ 148 | handleFailureInFunction:[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \ 149 | file:[NSString stringWithUTF8String:__FILE__] \ 150 | lineNumber:__LINE__ \ 151 | description:__VA_ARGS__]; \ 152 | } \ 153 | } while(0) 154 | #else // !defined(NS_BLOCK_ASSERTIONS) 155 | #define _GTMDevAssert(condition, ...) do { } while (0) 156 | #endif // !defined(NS_BLOCK_ASSERTIONS) 157 | 158 | #endif // _GTMDevAssert 159 | 160 | // _GTMCompileAssert 161 | // _GTMCompileAssert is an assert that is meant to fire at compile time if you 162 | // want to check things at compile instead of runtime. For example if you 163 | // want to check that a wchar is 4 bytes instead of 2 you would use 164 | // _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X) 165 | // Note that the second "arg" is not in quotes, and must be a valid processor 166 | // symbol in it's own right (no spaces, punctuation etc). 167 | 168 | // Wrapping this in an #ifndef allows external groups to define their own 169 | // compile time assert scheme. 170 | #ifndef _GTMCompileAssert 171 | // We got this technique from here: 172 | // http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html 173 | 174 | #define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg 175 | #define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg) 176 | #define _GTMCompileAssert(test, msg) \ 177 | typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ] 178 | #endif // _GTMCompileAssert 179 | 180 | // ---------------------------------------------------------------------------- 181 | // CPP symbols defined based on the project settings so the GTM code has 182 | // simple things to test against w/o scattering the knowledge of project 183 | // setting through all the code. 184 | // ---------------------------------------------------------------------------- 185 | 186 | // Provide a single constant CPP symbol that all of GTM uses for ifdefing 187 | // iPhone code. 188 | #if TARGET_OS_IPHONE // iPhone SDK 189 | // For iPhone specific stuff 190 | #define GTM_IPHONE_SDK 1 191 | #if TARGET_IPHONE_SIMULATOR 192 | #define GTM_IPHONE_SIMULATOR 1 193 | #else 194 | #define GTM_IPHONE_DEVICE 1 195 | #endif // TARGET_IPHONE_SIMULATOR 196 | // By default, GTM has provided it's own unittesting support, define this 197 | // to use the support provided by Xcode, especially for the Xcode4 support 198 | // for unittesting. 199 | #ifndef GTM_IPHONE_USE_SENTEST 200 | #define GTM_IPHONE_USE_SENTEST 0 201 | #endif 202 | #else 203 | // For MacOS specific stuff 204 | #define GTM_MACOS_SDK 1 205 | #endif 206 | 207 | // Some of our own availability macros 208 | #if GTM_MACOS_SDK 209 | #define GTM_AVAILABLE_ONLY_ON_IPHONE UNAVAILABLE_ATTRIBUTE 210 | #define GTM_AVAILABLE_ONLY_ON_MACOS 211 | #else 212 | #define GTM_AVAILABLE_ONLY_ON_IPHONE 213 | #define GTM_AVAILABLE_ONLY_ON_MACOS UNAVAILABLE_ATTRIBUTE 214 | #endif 215 | 216 | // Provide a symbol to include/exclude extra code for GC support. (This mainly 217 | // just controls the inclusion of finalize methods). 218 | #ifndef GTM_SUPPORT_GC 219 | #if GTM_IPHONE_SDK 220 | // iPhone never needs GC 221 | #define GTM_SUPPORT_GC 0 222 | #else 223 | // We can't find a symbol to tell if GC is supported/required, so best we 224 | // do on Mac targets is include it if we're on 10.5 or later. 225 | #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 226 | #define GTM_SUPPORT_GC 0 227 | #else 228 | #define GTM_SUPPORT_GC 1 229 | #endif 230 | #endif 231 | #endif 232 | 233 | // To simplify support for 64bit (and Leopard in general), we provide the type 234 | // defines for non Leopard SDKs 235 | #if !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) 236 | // NSInteger/NSUInteger and Max/Mins 237 | #ifndef NSINTEGER_DEFINED 238 | #if __LP64__ || NS_BUILD_32_LIKE_64 239 | typedef long NSInteger; 240 | typedef unsigned long NSUInteger; 241 | #else 242 | typedef int NSInteger; 243 | typedef unsigned int NSUInteger; 244 | #endif 245 | #define NSIntegerMax LONG_MAX 246 | #define NSIntegerMin LONG_MIN 247 | #define NSUIntegerMax ULONG_MAX 248 | #define NSINTEGER_DEFINED 1 249 | #endif // NSINTEGER_DEFINED 250 | // CGFloat 251 | #ifndef CGFLOAT_DEFINED 252 | #if defined(__LP64__) && __LP64__ 253 | // This really is an untested path (64bit on Tiger?) 254 | typedef double CGFloat; 255 | #define CGFLOAT_MIN DBL_MIN 256 | #define CGFLOAT_MAX DBL_MAX 257 | #define CGFLOAT_IS_DOUBLE 1 258 | #else /* !defined(__LP64__) || !__LP64__ */ 259 | typedef float CGFloat; 260 | #define CGFLOAT_MIN FLT_MIN 261 | #define CGFLOAT_MAX FLT_MAX 262 | #define CGFLOAT_IS_DOUBLE 0 263 | #endif /* !defined(__LP64__) || !__LP64__ */ 264 | #define CGFLOAT_DEFINED 1 265 | #endif // CGFLOAT_DEFINED 266 | #endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 267 | 268 | // Some support for advanced clang static analysis functionality 269 | // See http://clang-analyzer.llvm.org/annotations.html 270 | #ifndef __has_feature // Optional. 271 | #define __has_feature(x) 0 // Compatibility with non-clang compilers. 272 | #endif 273 | 274 | #ifndef NS_RETURNS_RETAINED 275 | #if __has_feature(attribute_ns_returns_retained) 276 | #define NS_RETURNS_RETAINED __attribute__((ns_returns_retained)) 277 | #else 278 | #define NS_RETURNS_RETAINED 279 | #endif 280 | #endif 281 | 282 | #ifndef NS_RETURNS_NOT_RETAINED 283 | #if __has_feature(attribute_ns_returns_not_retained) 284 | #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) 285 | #else 286 | #define NS_RETURNS_NOT_RETAINED 287 | #endif 288 | #endif 289 | 290 | #ifndef CF_RETURNS_RETAINED 291 | #if __has_feature(attribute_cf_returns_retained) 292 | #define CF_RETURNS_RETAINED __attribute__((cf_returns_retained)) 293 | #else 294 | #define CF_RETURNS_RETAINED 295 | #endif 296 | #endif 297 | 298 | #ifndef CF_RETURNS_NOT_RETAINED 299 | #if __has_feature(attribute_cf_returns_not_retained) 300 | #define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained)) 301 | #else 302 | #define CF_RETURNS_NOT_RETAINED 303 | #endif 304 | #endif 305 | 306 | #ifndef NS_CONSUMED 307 | #if __has_feature(attribute_ns_consumed) 308 | #define NS_CONSUMED __attribute__((ns_consumed)) 309 | #else 310 | #define NS_CONSUMED 311 | #endif 312 | #endif 313 | 314 | #ifndef CF_CONSUMED 315 | #if __has_feature(attribute_cf_consumed) 316 | #define CF_CONSUMED __attribute__((cf_consumed)) 317 | #else 318 | #define CF_CONSUMED 319 | #endif 320 | #endif 321 | 322 | #ifndef NS_CONSUMES_SELF 323 | #if __has_feature(attribute_ns_consumes_self) 324 | #define NS_CONSUMES_SELF __attribute__((ns_consumes_self)) 325 | #else 326 | #define NS_CONSUMES_SELF 327 | #endif 328 | #endif 329 | 330 | // Defined on 10.6 and above. 331 | #ifndef NS_FORMAT_ARGUMENT 332 | #define NS_FORMAT_ARGUMENT(A) 333 | #endif 334 | 335 | // Defined on 10.6 and above. 336 | #ifndef NS_FORMAT_FUNCTION 337 | #define NS_FORMAT_FUNCTION(F,A) 338 | #endif 339 | 340 | // Defined on 10.6 and above. 341 | #ifndef CF_FORMAT_ARGUMENT 342 | #define CF_FORMAT_ARGUMENT(A) 343 | #endif 344 | 345 | // Defined on 10.6 and above. 346 | #ifndef CF_FORMAT_FUNCTION 347 | #define CF_FORMAT_FUNCTION(F,A) 348 | #endif 349 | 350 | #ifndef GTM_NONNULL 351 | #define GTM_NONNULL(x) __attribute__((nonnull(x))) 352 | #endif 353 | 354 | #ifdef __OBJC__ 355 | 356 | // Declared here so that it can easily be used for logging tracking if 357 | // necessary. See GTMUnitTestDevLog.h for details. 358 | @class NSString; 359 | GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...); 360 | 361 | // Macro to allow you to create NSStrings out of other macros. 362 | // #define FOO foo 363 | // NSString *fooString = GTM_NSSTRINGIFY(FOO); 364 | #if !defined (GTM_NSSTRINGIFY) 365 | #define GTM_NSSTRINGIFY_INNER(x) @#x 366 | #define GTM_NSSTRINGIFY(x) GTM_NSSTRINGIFY_INNER(x) 367 | #endif 368 | 369 | // Macro to allow fast enumeration when building for 10.5 or later, and 370 | // reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration 371 | // does keys, so pick the right thing, nothing is done on the FastEnumeration 372 | // side to be sure you're getting what you wanted. 373 | #ifndef GTM_FOREACH_OBJECT 374 | #if TARGET_OS_IPHONE || !(MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) 375 | #define GTM_FOREACH_ENUMEREE(element, enumeration) \ 376 | for (element in enumeration) 377 | #define GTM_FOREACH_OBJECT(element, collection) \ 378 | for (element in collection) 379 | #define GTM_FOREACH_KEY(element, collection) \ 380 | for (element in collection) 381 | #else 382 | #define GTM_FOREACH_ENUMEREE(element, enumeration) \ 383 | for (NSEnumerator *_ ## element ## _enum = enumeration; \ 384 | (element = [_ ## element ## _enum nextObject]) != nil; ) 385 | #define GTM_FOREACH_OBJECT(element, collection) \ 386 | GTM_FOREACH_ENUMEREE(element, [collection objectEnumerator]) 387 | #define GTM_FOREACH_KEY(element, collection) \ 388 | GTM_FOREACH_ENUMEREE(element, [collection keyEnumerator]) 389 | #endif 390 | #endif 391 | 392 | // ============================================================================ 393 | 394 | // To simplify support for both Leopard and Snow Leopard we declare 395 | // the Snow Leopard protocols that we need here. 396 | #if !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) 397 | #define GTM_10_6_PROTOCOLS_DEFINED 1 398 | @protocol NSConnectionDelegate 399 | @end 400 | @protocol NSAnimationDelegate 401 | @end 402 | @protocol NSImageDelegate 403 | @end 404 | @protocol NSTabViewDelegate 405 | @end 406 | #endif // !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) 407 | 408 | // GTM_SEL_STRING is for specifying selector (usually property) names to KVC 409 | // or KVO methods. 410 | // In debug it will generate warnings for undeclared selectors if 411 | // -Wunknown-selector is turned on. 412 | // In release it will have no runtime overhead. 413 | #ifndef GTM_SEL_STRING 414 | #ifdef DEBUG 415 | #define GTM_SEL_STRING(selName) NSStringFromSelector(@selector(selName)) 416 | #else 417 | #define GTM_SEL_STRING(selName) @#selName 418 | #endif // DEBUG 419 | #endif // GTM_SEL_STRING 420 | 421 | #endif // __OBJC__ -------------------------------------------------------------------------------- /oc-class/NSString+BAHCCategory.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+BAHCCategory.h 3 | // BAHardCodeEncoder 4 | // 5 | // Created by BenArvin on 2018/11/2. 6 | // Copyright ©BenArvin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (BAHCCategory) 12 | 13 | - (NSString *)BAHC_Encrypt; 14 | - (NSString *)BAHC_Decrypt; 15 | 16 | - (NSString *)BAHC_AESEncrypt:(NSString *)key IV:(NSString *)IV; 17 | - (NSString *)BAHC_AESDecrypt:(NSString *)key IV:(NSString *)IV; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /oc-class/NSString+BAHCCategory.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+BAHCCategory.m 3 | // BAHardCodeEncoder 4 | // 5 | // Created by BenArvin on 2018/11/2. 6 | // Copyright ©BenArvin. All rights reserved. 7 | // 8 | 9 | #import "NSString+BAHCCategory.h" 10 | #import 11 | #import 12 | #import "GTMBase64.h" 13 | 14 | #define BAHC_AES_KEY @"9Jvae2bFOYL$JoTt" 15 | #define BAHC_AES_IV @"yg@t2lLZXmP8&J7r" 16 | 17 | @implementation NSString (BAHCCategory) 18 | 19 | #pragma mark - public methods 20 | - (NSString *)BAHC_Encrypt 21 | { 22 | return [self BAHC_AESEncrypt:BAHC_AES_KEY IV:BAHC_AES_IV]; 23 | } 24 | 25 | - (NSString *)BAHC_Decrypt 26 | { 27 | return [self BAHC_AESDecrypt:BAHC_AES_KEY IV:BAHC_AES_IV]; 28 | } 29 | 30 | - (NSString *)BAHC_AESEncrypt:(NSString *)key IV:(NSString *)IV 31 | { 32 | NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding]; 33 | NSData *AESData = [self AES128operation:kCCEncrypt 34 | data:data 35 | key:key 36 | iv:IV]; 37 | NSString *baseStr_GTM = [self encodeBase64Data:AESData]; 38 | return baseStr_GTM; 39 | } 40 | 41 | - (NSString *)BAHC_AESDecrypt:(NSString *)key IV:(NSString *)IV 42 | { 43 | NSData *baseData = [[NSData alloc]initWithBase64EncodedString:self options:0]; 44 | 45 | NSData *AESData = [self AES128operation:kCCDecrypt 46 | data:baseData 47 | key:key 48 | iv:IV]; 49 | 50 | NSString *decStr = [[NSString alloc] initWithData:AESData encoding:NSUTF8StringEncoding]; 51 | return decStr; 52 | } 53 | 54 | #pragma mark - private methods 55 | /** 56 | * AES加解密算法 57 | * 58 | * @param operation kCCEncrypt(加密)kCCDecrypt(解密) 59 | * @param data 待操作Data数据 60 | * @param key key 61 | * @param iv 向量 62 | * 63 | * @return 64 | */ 65 | - (NSData *)AES128operation:(CCOperation)operation data:(NSData *)data key:(NSString *)key iv:(NSString *)iv 66 | { 67 | char keyPtr[kCCKeySizeAES128 + 1]; 68 | bzero(keyPtr, sizeof(keyPtr)); 69 | [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; 70 | 71 | // IV 72 | char ivPtr[kCCBlockSizeAES128 + 1]; 73 | bzero(ivPtr, sizeof(ivPtr)); 74 | [iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding]; 75 | 76 | size_t bufferSize = [data length] + kCCBlockSizeAES128; 77 | void *buffer = malloc(bufferSize); 78 | size_t numBytesEncrypted = 0; 79 | 80 | CCCryptorStatus cryptorStatus = CCCrypt(operation, kCCAlgorithmAES128, kCCOptionPKCS7Padding, 81 | keyPtr, kCCKeySizeAES128, 82 | ivPtr, 83 | [data bytes], [data length], 84 | buffer, bufferSize, 85 | &numBytesEncrypted); 86 | 87 | if(cryptorStatus == kCCSuccess) { 88 | return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted]; 89 | } 90 | 91 | free(buffer); 92 | return nil; 93 | } 94 | 95 | /**< GTMBase64编码 */ 96 | - (NSString*)encodeBase64Data:(NSData *)data 97 | { 98 | data = [GTMBase64 encodeData:data]; 99 | NSString *base64String = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 100 | return base64String; 101 | } 102 | 103 | /**< GTMBase64解码 */ 104 | - (NSData*)decodeBase64Data:(NSData *)data 105 | { 106 | data = [GTMBase64 decodeData:data]; 107 | return data; 108 | } 109 | 110 | @end 111 | --------------------------------------------------------------------------------