├── GrapheneTests ├── includes │ └── lib │ │ ├── libssl-ios.a │ │ ├── libssl-osx.a │ │ ├── libcrypto-ios.a │ │ └── libcrypto-osx.a ├── Info.plist ├── GXPublicKeyTests.m ├── GXConnectionManagerTests.m ├── GXPrivateKeyTests.m ├── GXUtilTest.m └── GXTransactionTests.m ├── .gitignore ├── Graphene.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── davidlan.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── Graphene ├── category │ ├── NSArray+Expand.h │ ├── NSMutableData+ProtoBuff.h │ ├── NSDictionary+Expand.h │ ├── NSArray+Expand.m │ ├── NSMutableData+ProtoBuff.m │ ├── NSDictionary+Expand.m │ ├── NSData+Base64.h │ ├── NSString+Base64.h │ ├── NSString+Base64.m │ └── NSData+Base64.m ├── lib │ ├── chain │ │ ├── delegates │ │ │ └── GXSerializeDelegate.h │ │ ├── classes │ │ │ ├── GXAssetAmount.h │ │ │ ├── GXMemoData.h │ │ │ ├── GXAssetAmount.m │ │ │ └── GXMemoData.m │ │ ├── operations │ │ │ ├── GXBaseOperation.h │ │ │ ├── GXBaseOperation.m │ │ │ ├── GXTransferOperation.h │ │ │ ├── GXCallContractOperation.h │ │ │ ├── GXTransferOperation.m │ │ │ └── GXCallContractOperation.m │ │ ├── utils │ │ │ ├── GXUtil.h │ │ │ └── GXUtil.m │ │ ├── GXTransactionBuilder.h │ │ └── GXTransactionBuilder.m │ └── ecc │ │ ├── GXAES.h │ │ ├── GXPublicKey.h │ │ ├── GXPrivateKey.h │ │ ├── vendor │ │ ├── NS+BTCBase58.m │ │ ├── NSData+BTCData.h │ │ ├── NS+BTCBase58.h │ │ ├── BTCCurvePoint.h │ │ ├── BTCBase58.h │ │ ├── NSData+BTCData.m │ │ ├── BTCBigNumber.h │ │ ├── BTCData.h │ │ ├── BTCCurvePoint.m │ │ ├── BTCBase58.m │ │ ├── BTCBigNumber.m │ │ └── BTCData.m │ │ ├── GXPublicKey.m │ │ ├── GXAES.m │ │ └── GXPrivateKey.m ├── ws │ ├── GXConnectionManager.h │ ├── GXChainConfig.h │ ├── GXGrapheneApi.h │ ├── GXChainWebSocket.h │ ├── GXApiInstances.h │ ├── GXGrapheneApi.m │ ├── GXApiInstances.m │ ├── GXConnectionManager.m │ └── GXChainWebSocket.m └── Info.plist ├── Podfile ├── Graphene.podspec └── README.md /GrapheneTests/includes/lib/libssl-ios.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gxchain/graphene-ios/HEAD/GrapheneTests/includes/lib/libssl-ios.a -------------------------------------------------------------------------------- /GrapheneTests/includes/lib/libssl-osx.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gxchain/graphene-ios/HEAD/GrapheneTests/includes/lib/libssl-osx.a -------------------------------------------------------------------------------- /GrapheneTests/includes/lib/libcrypto-ios.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gxchain/graphene-ios/HEAD/GrapheneTests/includes/lib/libcrypto-ios.a -------------------------------------------------------------------------------- /GrapheneTests/includes/lib/libcrypto-osx.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gxchain/graphene-ios/HEAD/GrapheneTests/includes/lib/libcrypto-osx.a -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.xcworkspace 3 | Podfile.lock 4 | Pods/ 5 | *.swp 6 | *~.nib 7 | 8 | build/ 9 | 10 | *.pbxuser 11 | *.perspective 12 | *.perspectivev3 13 | *.mode1v3 14 | *.mode2v3 15 | xcuserdata 16 | .idea 17 | -------------------------------------------------------------------------------- /Graphene.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Graphene/category/NSArray+Expand.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Expand.h 3 | // gongfudai 4 | // 5 | // Created by David Lan on 15/7/29. 6 | // Copyright (c) 2015年 dashu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSArray(Expand) 12 | - (NSString*)json; 13 | @end 14 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | platform :ios, '8.0' 3 | 4 | def available_pods 5 | pod 'SocketRocket' 6 | pod 'OpenSSL-Universal' 7 | end 8 | 9 | target 'Graphene' do 10 | available_pods 11 | end 12 | 13 | target 'GrapheneTests' do 14 | available_pods 15 | end 16 | -------------------------------------------------------------------------------- /Graphene/lib/chain/delegates/GXSerializeDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SerializeDelegate.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/3/15. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol GXSerializeDelegate 12 | -(NSDictionary*)dictionaryValue; 13 | @end 14 | -------------------------------------------------------------------------------- /Graphene/category/NSMutableData+ProtoBuff.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableData+ProtoBuff.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/3/21. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSMutableData (ProtoBuff) 12 | - (void)writeVarInt32:(int32_t)value; 13 | @end 14 | -------------------------------------------------------------------------------- /Graphene/category/NSDictionary+Expand.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+Expand.h 3 | // gongfudai 4 | // 5 | // Created by David Lan on 15/8/14. 6 | // Copyright (c) 2015年 dashu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSDictionary(Expand) 12 | -(NSString*)json; 13 | + (NSDictionary *)fromJSON:(NSString *)json; 14 | @end 15 | -------------------------------------------------------------------------------- /Graphene/ws/GXConnectionManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // ConnectionManager.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/2/27. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GXConnectionManager : NSObject 12 | +(instancetype)sharedInstance; 13 | -(void)connectWithFallback:(NSArray*)urls callback:(void(^)(BOOL connected,NSString* url))callback; 14 | @end 15 | -------------------------------------------------------------------------------- /Graphene/ws/GXChainConfig.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChainConfig.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/2/28. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #ifndef GXChainConfig_h 10 | #define GXChainConfig_h 11 | 12 | #define GX_ADDRESS_PREFIX @"GXC" 13 | #define GX_DEFAULT_ASSET_ID @"1.3.1" 14 | #define GX_EXPIRE_IN_SECOND 15 15 | #define GX_DEFAULT_CHAIN_ID @"bc59e6e7f500fa56504ce7101f7df8eb74151398f62167567adcf18a026928d1" 16 | 17 | #endif /* GXChainConfig_h */ 18 | -------------------------------------------------------------------------------- /Graphene/lib/chain/classes/GXAssetAmount.h: -------------------------------------------------------------------------------- 1 | // 2 | // AssetAmount.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/3/15. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GXSerializeDelegate.h" 11 | 12 | @interface GXAssetAmount : NSObject 13 | @property (nonatomic,strong) NSString* asset_id; 14 | @property (nonatomic,assign) int64_t amount; 15 | -(instancetype)initWithAsset:(NSString*)asset_id amount:(int64_t)amount; 16 | @end 17 | -------------------------------------------------------------------------------- /Graphene/lib/chain/operations/GXBaseOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // BaseOperation.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/3/8. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GXSerializeDelegate.h" 11 | #import "GXAssetAmount.h" 12 | 13 | @interface GXBaseOperation : NSObject 14 | @property (nonatomic,strong) GXAssetAmount* fee; 15 | @property(nonatomic,readonly) int32_t operation_id; 16 | @property(nonatomic,readonly) NSArray* operation; 17 | @end 18 | -------------------------------------------------------------------------------- /Graphene/lib/chain/utils/GXUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // GXUtil.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/12/12. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GXUtil : NSObject 12 | +(uint64_t) string_to_name:(NSString*)str; 13 | +(NSString*) name_to_string :(uint64_t)name; 14 | +(NSString*) serialize_action_data:(NSString*)action params:(NSDictionary*)params abi:(NSDictionary*)abi; 15 | +(NSString*) serialize_transaction:(NSDictionary*)transaction; 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Graphene/ws/GXGrapheneApi.h: -------------------------------------------------------------------------------- 1 | // 2 | // GrapheneApi.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/2/27. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GXChainWebSocket.h" 11 | 12 | @interface GXGrapheneApi : NSObject 13 | -(instancetype)initWithName:(NSString*)api_name websocket:(GXChainWebSocket*)ws_rpc; 14 | -(void)initialize:(void(^)(NSError* err,id response))callback; 15 | -(void)exec:(NSString*)method params:(NSArray*)params callback:(void(^)(NSError* err,id resp))callback; 16 | @end 17 | -------------------------------------------------------------------------------- /Graphene/lib/chain/classes/GXMemoData.h: -------------------------------------------------------------------------------- 1 | // 2 | // MemoData.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/3/15. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GXSerializeDelegate.h" 11 | 12 | @interface GXMemoData : NSObject 13 | @property(nonatomic,strong) NSString* from; 14 | @property(nonatomic,strong) NSString* to; 15 | @property(nonatomic,assign) uint64_t nonce; 16 | @property(nonatomic,strong) NSData* message; 17 | +(instancetype)memoWithPrivate:(NSString*)privateKey public:(NSString*)publicKey message:(NSString*)message; 18 | @end 19 | -------------------------------------------------------------------------------- /Graphene/lib/ecc/GXAES.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHAES.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/3/21. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GXPrivateKey.h" 11 | #import "GXPublicKey.h" 12 | 13 | @interface GXAES : NSObject 14 | +(instancetype)fromSeed:(NSData*)seed; 15 | +(NSData*)encrypt_with_checksum:(GXPrivateKey*)privKey publicKey:(GXPublicKey*)publicKey nonce:(NSString*)nonce message:(NSString*)message; 16 | +(NSData*)decrypt_with_checksum:(GXPrivateKey*)privKey publicKey:(GXPublicKey*)publicKey nonce:(NSString*)nonce message:(NSString*)message; 17 | @end 18 | -------------------------------------------------------------------------------- /Graphene/category/NSArray+Expand.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Expand.m 3 | // gongfudai 4 | // 5 | // Created by David Lan on 15/7/29. 6 | // Copyright (c) 2015年 dashu. All rights reserved. 7 | // 8 | 9 | #import "NSArray+Expand.h" 10 | 11 | @implementation NSArray(Expand) 12 | - (NSString*)json 13 | { 14 | // NSMutableString* json = nil; 15 | NSString* json = nil; 16 | 17 | NSError* error = nil; 18 | NSData *data = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted error:&error]; 19 | json = [[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 20 | return (error ? nil : json); 21 | } 22 | @end 23 | -------------------------------------------------------------------------------- /Graphene/ws/GXChainWebSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChainWebSocket.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/2/25. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GXChainWebSocket : NSObject 12 | -(instancetype)initWithAddress:(NSString*)address; 13 | -(void)connect:(void(^)(BOOL connected,NSString* status))callback timeout:(NSTimeInterval)timeout; 14 | -(void)call:(NSArray*)params callback:(void (^)(NSError * error, id response))callback; 15 | -(void)close; 16 | -(void)login:(NSString*)user password:(NSString*)password callback:(void (^)(NSError * error, id response))callback; 17 | @end 18 | -------------------------------------------------------------------------------- /Graphene/lib/chain/classes/GXAssetAmount.m: -------------------------------------------------------------------------------- 1 | // 2 | // AssetAmount.m 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/3/15. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import "GXAssetAmount.h" 10 | #import "NSMutableData+ProtoBuff.h" 11 | 12 | @implementation GXAssetAmount 13 | 14 | -(instancetype)initWithAsset:(NSString*)asset_id amount:(int64_t)amount{ 15 | self=[self init]; 16 | self.asset_id=asset_id; 17 | self.amount=amount; 18 | return self; 19 | } 20 | 21 | -(NSDictionary *)dictionaryValue{ 22 | return @{ 23 | @"asset_id":_asset_id, 24 | @"amount":@(_amount) 25 | }; 26 | } 27 | @end 28 | -------------------------------------------------------------------------------- /Graphene/lib/chain/operations/GXBaseOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // BaseOperation.m 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/3/8. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import "GXBaseOperation.h" 10 | #import "GXChainConfig.h" 11 | 12 | @implementation GXBaseOperation 13 | -(instancetype)init{ 14 | self=[super init]; 15 | self.fee=[[GXAssetAmount alloc] initWithAsset:GX_DEFAULT_ASSET_ID amount:0]; 16 | return self; 17 | } 18 | 19 | -(int32_t)operation_id{ 20 | return -1; 21 | } 22 | 23 | -(NSArray*)operation{ 24 | return @[@(self.operation_id),self.dictionaryValue]; 25 | } 26 | 27 | -(NSData*)serialize{ 28 | return nil; 29 | } 30 | 31 | -(NSDictionary*)dictionaryValue{ 32 | return nil; 33 | } 34 | @end 35 | -------------------------------------------------------------------------------- /GrapheneTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Graphene/lib/ecc/GXPublicKey.h: -------------------------------------------------------------------------------- 1 | // 2 | // PublicKey.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/2/27. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "BTCBase58.h" 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | #import "BTCCurvePoint.h" 19 | 20 | @interface GXPublicKey : NSObject 21 | @property (nonatomic,assign) EC_KEY* _key; 22 | @property (nonatomic,strong) NSData* publicKeyData; 23 | +(instancetype)fromString:(NSString*)pubKeyString; 24 | +(instancetype)fromData:(NSData*)pubKeyData; 25 | -(NSData*) publicKeyWithCompression:(BOOL)compression; 26 | -(NSString*)toString; 27 | -(BTCCurvePoint*) curvePoint; 28 | @end 29 | -------------------------------------------------------------------------------- /Graphene.xcodeproj/xcuserdata/davidlan.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Graphene.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | GrapheneTests.xcscheme 13 | 14 | orderHint 15 | 4 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 1D33F7EA202B5694005806F2 21 | 22 | primary 23 | 24 | 25 | 1D92373320442AD30092A484 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Graphene/lib/ecc/GXPrivateKey.h: -------------------------------------------------------------------------------- 1 | // 2 | // PrivateKey.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/2/27. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "BTCBase58.h" 11 | #import "BTCData.h" 12 | #import "GXPublicKey.h" 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | #import 19 | #import 20 | #import 21 | 22 | @interface GXPrivateKey : NSObject 23 | @property (nonatomic,assign) EC_KEY* _key; 24 | @property (nonatomic,strong) NSData* privateKeyData; 25 | 26 | +(instancetype)fromWif:(NSString*)wifKey; 27 | -(NSString*)toWif; 28 | -(GXPublicKey*)getPublic; 29 | -(NSData*)sharedSecret:(GXPublicKey*)publicKey; 30 | -(NSData*)sign:(NSData*)data; 31 | @end 32 | -------------------------------------------------------------------------------- /Graphene/lib/chain/operations/GXTransferOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // TransferOperation.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/3/15. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import "GXBaseOperation.h" 10 | #import "GXAssetAmount.h" 11 | #import "GXMemoData.h" 12 | 13 | /* 14 | export const transfer = new Serializer ("transfer",{ 15 | fee: asset, 16 | from: protocol_id_type ("account"), 17 | to: protocol_id_type ("account"), 18 | amount: asset, 19 | memo: optional (memo_data), 20 | extensions: set (future_extensions) 21 | }); 22 | */ 23 | @interface GXTransferOperation : GXBaseOperation 24 | @property (nonatomic,strong) NSString* from; 25 | @property (nonatomic,strong) NSString* to; 26 | @property (nonatomic,strong) GXAssetAmount* amount; 27 | @property (nonatomic,strong) GXMemoData* memo; 28 | @property (nonatomic,strong) NSArray* extensions; 29 | @end 30 | -------------------------------------------------------------------------------- /Graphene/ws/GXApiInstances.h: -------------------------------------------------------------------------------- 1 | // 2 | // GXApiInstances.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/2/27. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GXChainWebSocket.h" 11 | #import "GXGrapheneApi.h" 12 | 13 | @interface GXApiInstances : NSObject 14 | 15 | @property (nonatomic,strong) NSString* chain_id; 16 | @property (nonatomic,strong) GXGrapheneApi* _db; 17 | @property (nonatomic,strong) GXGrapheneApi* _net; 18 | @property (nonatomic,strong) GXGrapheneApi* _hist; 19 | @property (nonatomic,strong) GXGrapheneApi* _crypt; 20 | @property (nonatomic,strong) GXChainWebSocket* ws_rpc; 21 | @property (atomic,assign) NSInteger initCount; 22 | 23 | +(instancetype)sharedInstance; 24 | -(void)connect:(NSString*)url timeout:(NSTimeInterval)timeout statusCallback:(void(^)(BOOL connected,NSString* status))callback; 25 | -(void)close; 26 | @end 27 | -------------------------------------------------------------------------------- /Graphene/lib/chain/operations/GXCallContractOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // CallContractOperation.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/12/10. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import "GXBaseOperation.h" 10 | 11 | /* 12 | export const call_contract = new Serializer("call_contract", { 13 | fee: asset, 14 | account: protocol_id_type ("account"), 15 | contract_id: protocol_id_type("account"), 16 | amount:optional (asset), 17 | method_name: name_type, 18 | data: bytes(), 19 | extensions: set (future_extensions) 20 | }); 21 | */ 22 | @interface GXCallContractOperation : GXBaseOperation 23 | @property (nonatomic,strong) NSString* account; 24 | @property (nonatomic,strong) NSString* contract_id; 25 | @property (nonatomic,strong) GXAssetAmount* amount; 26 | @property (nonatomic,strong) NSString* method_name; 27 | @property (nonatomic,strong) NSData* data; 28 | @property (nonatomic,strong) NSArray* extensions; 29 | @end 30 | -------------------------------------------------------------------------------- /Graphene/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2018年 GXChain. All rights reserved. 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Graphene/category/NSMutableData+ProtoBuff.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableData+ProtoBuff.m 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/3/21. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import "NSMutableData+ProtoBuff.h" 10 | 11 | int64_t convertUInt32ToInt32(uint32_t v) { 12 | union { int32_t i; uint32_t u; } u; 13 | u.u = v; 14 | return u.i; 15 | } 16 | 17 | int32_t logicalRightShift32(int32_t value, int32_t spaces) { 18 | return convertUInt32ToInt32((convertUInt32ToInt32(value) >> spaces)); 19 | } 20 | 21 | @implementation NSMutableData (ProtoBuff) 22 | 23 | - (void)writeVarInt32:(int32_t)value { 24 | while (YES) { 25 | if ((value & ~0x7F) == 0) { 26 | [self writeRawByte:value]; 27 | return; 28 | } else { 29 | [self writeRawByte:((value & 0x7F) | 0x80)]; 30 | value = logicalRightShift32(value, 7); 31 | } 32 | } 33 | } 34 | 35 | - (void)writeRawByte:(uint8_t)value { 36 | [self appendBytes:&value length:sizeof(value)]; 37 | } 38 | @end 39 | -------------------------------------------------------------------------------- /Graphene.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'Graphene' 3 | s.version = '1.2.0' 4 | s.summary = 'Graphene is an implementation of Graphene Blockchain protocol in Objective-C.' 5 | s.description = <<-DESC 6 | Graphene is a toolkit to work with Graphene blockchain. 7 | DESC 8 | s.homepage = 'https://github.com/gxchain/Graphene' 9 | s.license = 'BSD' 10 | s.authors = { 'lanhaoxiang' => 'lanhaoxiang@qq.com'} 11 | s.source = { :git => 'https://github.com/gxchain/Graphene.git', :tag => s.version.to_s } 12 | s.ios.deployment_target = '7.0' 13 | s.osx.deployment_target = '10.9' 14 | s.source_files = 'Graphene/**/*.{h,m}' 15 | s.resource = 'Graphene/Graphene.bundle' 16 | s.public_header_files = 'Graphene/**/*.h' 17 | s.exclude_files = ['GrapheneTests/**/*.{h,m}'] 18 | s.requires_arc = true 19 | s.framework = 'Foundation' 20 | s.ios.framework = 'UIKit' 21 | s.osx.framework = 'AppKit' 22 | s.dependency 'OpenSSL-Universal' 23 | s.dependency 'SocketRocket' 24 | end -------------------------------------------------------------------------------- /Graphene/lib/ecc/vendor/NS+BTCBase58.m: -------------------------------------------------------------------------------- 1 | // Oleg Andreev 2 | 3 | #import "NS+BTCBase58.h" 4 | 5 | // TODO. 6 | 7 | @implementation NSString (BTCBase58) 8 | 9 | - (NSMutableData*) dataFromBase58 { return BTCDataFromBase58(self); } 10 | - (NSMutableData*) dataFromBase58Check { return BTCDataFromBase58Check(self); } 11 | @end 12 | 13 | 14 | @implementation NSMutableData (BTCBase58) 15 | 16 | + (NSMutableData*) dataFromBase58CString:(const char*)cstring { 17 | return BTCDataFromBase58CString(cstring); 18 | } 19 | 20 | + (NSMutableData*) dataFromBase58CheckCString:(const char*)cstring { 21 | return BTCDataFromBase58CheckCString(cstring); 22 | } 23 | 24 | @end 25 | 26 | 27 | @implementation NSData (BTCBase58) 28 | 29 | - (char*) base58CString { 30 | return BTCBase58CStringWithData(self); 31 | } 32 | 33 | - (char*) base58CheckCString { 34 | return BTCBase58CheckCStringWithData(self); 35 | } 36 | 37 | - (NSString*) base58String { 38 | return BTCBase58StringWithData(self); 39 | } 40 | 41 | - (NSString*) base58CheckString { 42 | return BTCBase58CheckStringWithData(self); 43 | } 44 | 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Graphene/lib/chain/GXTransactionBuilder.h: -------------------------------------------------------------------------------- 1 | // 2 | // GXTransactionBuilder.h 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/2/27. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GXBaseOperation.h" 11 | #import "GXConnectionManager.h" 12 | #import "GXApiInstances.h" 13 | #import "GXSerializeDelegate.h" 14 | #import "GXPrivateKey.h" 15 | 16 | @interface GXTransactionBuilder : NSObject 17 | @property(nonatomic,assign) uint16_t ref_block_num; 18 | @property(nonatomic,assign) uint32_t ref_block_prefix; 19 | @property(nonatomic,assign) uint32_t expiration; 20 | @property (nonatomic,strong) NSArray* operations; 21 | @property(nonatomic,strong) NSArray* extensions; 22 | @property (nonatomic,strong) NSArray* signatures; 23 | 24 | -(instancetype)initWithOperations:(NSArray*)operations; 25 | -(void)processTransaction:(void(^)(NSError *err,NSDictionary* tx))callback broadcast:(BOOL)broadcast; 26 | -(void)add_signer:(GXPrivateKey*)private_key; 27 | -(NSDictionary*)signedTransaction; 28 | -(NSData*) serialize; 29 | @end 30 | -------------------------------------------------------------------------------- /GrapheneTests/GXPublicKeyTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PublicKeyTests.m 3 | // GrapheneTests 4 | // 5 | // Created by David Lan on 2018/3/1. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GXPublicKey.h" 11 | 12 | @interface GXPublicKeyTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation GXPublicKeyTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testFromString { 29 | NSString* pubkeyString = @"GXC7XzFVivuBtuc2rz3Efkb41JCN4KH7iENAx9rch9QkowEmc4UvV"; 30 | GXPublicKey* pubKey=[GXPublicKey fromString:pubkeyString]; 31 | EC_POINT* pubkeyP = EC_KEY_get0_public_key(pubKey._key); 32 | GXPublicKey* pubKey2= [GXPublicKey fromData:[pubKey publicKeyWithCompression:NO]]; 33 | NSLog(@"%@",[pubKey2 toString]); 34 | NSAssert([[pubKey toString] isEqualToString:pubkeyString], @"Invalid pubkey"); 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Graphene/category/NSDictionary+Expand.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+Expand.m 3 | // gongfudai 4 | // 5 | // Created by David Lan on 15/8/14. 6 | // Copyright (c) 2015年 dashu. All rights reserved. 7 | // 8 | 9 | #import "NSDictionary+Expand.h" 10 | 11 | @implementation NSDictionary(Expand) 12 | -(NSString *)json{ 13 | NSError *error; 14 | NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self 15 | options:NSJSONWritingPrettyPrinted error:&error]; 16 | 17 | if (! jsonData) { 18 | NSLog(@"json转换失败: error: %@", error.localizedDescription); 19 | return @"{}"; 20 | } else { 21 | NSString *result = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; 22 | return result; 23 | } 24 | } 25 | 26 | + (NSDictionary *)fromJSON:(NSString *)json 27 | { 28 | if (json == nil) { 29 | return nil; 30 | } 31 | NSData *jsonData = [json dataUsingEncoding:NSUTF8StringEncoding]; 32 | NSError *err; 33 | NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err]; 34 | if(err) 35 | { 36 | NSLog(@"json parse fail:%@",err); 37 | return nil; 38 | } 39 | return dic; 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Graphene/lib/ecc/vendor/NSData+BTCData.h: -------------------------------------------------------------------------------- 1 | // CoreBitcoin by Oleg Andreev , WTFPL. 2 | 3 | #import 4 | #import "BTCData.h" 5 | 6 | // This category is for user's convenience only. 7 | // For documentation look into BTCData.h. 8 | // If you link CoreBitcoin library without categories enabled, nothing will break. 9 | // This is also used in unit tests in CoreBitcoin. 10 | @interface NSData (BTCData) 11 | 12 | // Core hash functions 13 | - (NSData*) SHA1; 14 | - (NSData*) SHA256; 15 | - (NSData*) BTCHash256; // SHA256(SHA256(self)) aka Hash or Hash256 in BitcoinQT 16 | 17 | #if BTCDataRequiresOpenSSL 18 | - (NSData*) RIPEMD160; 19 | - (NSData*) BTCHash160; // RIPEMD160(SHA256(self)) aka Hash160 in BitcoinQT 20 | #endif 21 | 22 | // Formats data as a lowercase hex string 23 | - (NSString*) hex; 24 | - (NSString*) uppercaseHex; 25 | 26 | - (NSString*) hexString DEPRECATED_ATTRIBUTE; 27 | - (NSString*) hexUppercaseString DEPRECATED_ATTRIBUTE; 28 | 29 | 30 | // Encrypts/decrypts data using the key. 31 | // IV should either be nil or at least 128 bits long 32 | + (NSMutableData*) encryptData:(NSData*)data key:(NSData*)key iv:(NSData*)initializationVector; 33 | + (NSMutableData*) decryptData:(NSData*)data key:(NSData*)key iv:(NSData*)initializationVector; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Graphene/category/NSData+Base64.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+Base64.h 3 | // 4 | // Version 1.0.2 5 | // 6 | // Created by Nick Lockwood on 12/01/2012. 7 | // Copyright (C) 2012 Charcoal Design 8 | // 9 | // Distributed under the permissive zlib License 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/nicklockwood/Base64 13 | // 14 | // This software is provided 'as-is', without any express or implied 15 | // warranty. In no event will the authors be held liable for any damages 16 | // arising from the use of this software. 17 | // 18 | // Permission is granted to anyone to use this software for any purpose, 19 | // including commercial applications, and to alter it and redistribute it 20 | // freely, subject to the following restrictions: 21 | // 22 | // 1. The origin of this software must not be misrepresented; you must not 23 | // claim that you wrote the original software. If you use this software 24 | // in a product, an acknowledgment in the product documentation would be 25 | // appreciated but is not required. 26 | // 27 | // 2. Altered source versions must be plainly marked as such, and must not be 28 | // misrepresented as being the original software. 29 | // 30 | // 3. This notice may not be removed or altered from any source distribution. 31 | // 32 | 33 | #import 34 | 35 | @interface NSData (Base64) 36 | 37 | + (NSData *)dataWithBase64EncodedString:(NSString *)string; 38 | - (NSString *)base64EncodedStringWithWrapWidth:(NSUInteger)wrapWidth; 39 | - (NSString *)base64EncodedString; 40 | 41 | @end -------------------------------------------------------------------------------- /Graphene/lib/chain/operations/GXTransferOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // TransferOperation.m 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/3/15. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import "GXTransferOperation.h" 10 | #import "BTCData.h" 11 | #import "NSMutableData+ProtoBuff.h" 12 | @interface GXTransferOperation() 13 | @end 14 | 15 | @implementation GXTransferOperation 16 | 17 | -(int32_t)operation_id{ 18 | return 0; 19 | } 20 | 21 | -(NSDictionary *)dictionaryValue{ 22 | NSMutableDictionary* result=@{ 23 | @"fee":[self.fee dictionaryValue], 24 | @"from":_from, 25 | @"to":_to, 26 | @"amount":[_amount dictionaryValue] 27 | }.mutableCopy; 28 | if(_memo){ 29 | [result setObject:[_memo dictionaryValue] forKey:@"memo"]; 30 | } 31 | NSMutableArray* exts=[NSMutableArray array]; 32 | if(_extensions){ 33 | [_extensions enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 34 | if([obj respondsToSelector:NSSelectorFromString(@"dictionaryValue")]){ 35 | [exts addObject:[obj performSelector:NSSelectorFromString(@"dictionaryValue") withObject:nil]]; 36 | } 37 | else{ 38 | NSLog(@"Unknow extension object,%@", obj); 39 | } 40 | }]; 41 | } 42 | [result setObject:exts forKey:@"extensions"]; 43 | return result; 44 | } 45 | @end 46 | -------------------------------------------------------------------------------- /GrapheneTests/GXConnectionManagerTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // GrapheneTests.m 3 | // GrapheneTests 4 | // 5 | // Created by David Lan on 2018/3/1. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GXConnectionManager.h" 11 | #import "GXApiInstances.h" 12 | 13 | @interface GXConnectionManagerTests : XCTestCase 14 | 15 | @end 16 | 17 | @implementation GXConnectionManagerTests 18 | 19 | - (void)setUp { 20 | [super setUp]; 21 | // Put setup code here. This method is called before the invocation of each test method in the class. 22 | } 23 | 24 | - (void)tearDown { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testConnect { 30 | XCTestExpectation * expectation = [self expectationWithDescription:@"Connection Exception"]; 31 | [[GXConnectionManager sharedInstance] connectWithFallback:@[@"wss://node1.gxb.io",@"wss://node5.gxb.io",@"wss://node8.gxb.io",@"wss://node11.gxb.io",@"wss://node18.gxb.io"] callback:^(BOOL connected, NSString *url) { 32 | if(connected){ 33 | NSLog(@"Conncted to:%@",url); 34 | } 35 | [[GXApiInstances sharedInstance]._db exec:@"get_objects" params:@[@[@"2.1.0"]] callback:^(NSError *err, id resp) { 36 | NSLog(@"%@",resp); 37 | [expectation fulfill]; 38 | }]; 39 | 40 | }]; 41 | [self waitForExpectationsWithTimeout:60 handler:^(NSError *error) { 42 | NSLog(@"%@",error.localizedDescription); 43 | }]; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Graphene/lib/ecc/vendor/NS+BTCBase58.h: -------------------------------------------------------------------------------- 1 | // CoreBitcoin by Oleg Andreev , WTFPL. 2 | 3 | #import 4 | #import "BTCBase58.h" 5 | 6 | // These categories are optional and provided for convenience only. 7 | // For documentation look into BTCBase58.h. 8 | // They are also used in CoreBitcoin unit tests. 9 | @interface NSString (BTCBase58) 10 | 11 | // Returns data for Base58 string without checksum 12 | // Data is mutable so you can clear sensitive information as soon as possible. 13 | - (NSMutableData*) dataFromBase58; 14 | 15 | // Returns data for Base58 string with checksum 16 | - (NSMutableData*) dataFromBase58Check; 17 | 18 | @end 19 | 20 | @interface NSMutableData (BTCBase58) 21 | 22 | // Returns data for Base58 string without checksum 23 | // Data is mutable so you can clear sensitive information as soon as possible. 24 | + (NSMutableData*) dataFromBase58CString:(const char*)cstring; 25 | 26 | // Returns data for Base58 string with checksum 27 | + (NSMutableData*) dataFromBase58CheckCString:(const char*)cstring; 28 | 29 | @end 30 | 31 | @interface NSData (BTCBase58) 32 | 33 | // String in Base58 without checksum, you need to free it yourself. 34 | // It's mutable so you can clear it securely yourself. 35 | - (char*) base58CString; 36 | 37 | // String in Base58 with checksum, you need to free it yourself. 38 | // It's mutable so you can clear it securely yourself. 39 | - (char*) base58CheckCString; 40 | 41 | // String in Base58 without checksum 42 | - (NSString*) base58String; 43 | 44 | // String in Base58 with checksum 45 | - (NSString*) base58CheckString; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /Graphene/category/NSString+Base64.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Base64.h 3 | // 4 | // Version 1.0.2 5 | // 6 | // Created by Nick Lockwood on 12/01/2012. 7 | // Copyright (C) 2012 Charcoal Design 8 | // 9 | // Distributed under the permissive zlib License 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/nicklockwood/Base64 13 | // 14 | // This software is provided 'as-is', without any express or implied 15 | // warranty. In no event will the authors be held liable for any damages 16 | // arising from the use of this software. 17 | // 18 | // Permission is granted to anyone to use this software for any purpose, 19 | // including commercial applications, and to alter it and redistribute it 20 | // freely, subject to the following restrictions: 21 | // 22 | // 1. The origin of this software must not be misrepresented; you must not 23 | // claim that you wrote the original software. If you use this software 24 | // in a product, an acknowledgment in the product documentation would be 25 | // appreciated but is not required. 26 | // 27 | // 2. Altered source versions must be plainly marked as such, and must not be 28 | // misrepresented as being the original software. 29 | // 30 | // 3. This notice may not be removed or altered from any source distribution. 31 | // 32 | 33 | #import 34 | 35 | @interface NSString (Base64) 36 | 37 | + (NSString *)stringWithBase64EncodedString:(NSString *)string; 38 | - (NSString *)base64EncodedStringWithWrapWidth:(NSUInteger)wrapWidth; 39 | - (NSString *)base64EncodedString; 40 | - (NSString *)base64DecodedString; 41 | - (NSData *)base64DecodedData; 42 | 43 | @end -------------------------------------------------------------------------------- /Graphene/lib/chain/operations/GXCallContractOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // CallContractOperation.m 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/12/10. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import "GXCallContractOperation.h" 10 | #import "BTCData.h" 11 | #import "NSMutableData+ProtoBuff.h" 12 | #import "GXUtil.h" 13 | 14 | @implementation GXCallContractOperation 15 | 16 | -(int32_t)operation_id{ 17 | return 75; 18 | } 19 | 20 | -(NSDictionary *)dictionaryValue{ 21 | NSMutableDictionary* result=@{ 22 | @"fee":[self.fee dictionaryValue], 23 | @"account":_account, 24 | @"contract_id":_contract_id, 25 | @"method_name":_method_name, 26 | @"data": BTCHexFromData(_data), 27 | }.mutableCopy; 28 | if(_amount!=nil){ 29 | [result setObject:[_amount dictionaryValue] forKey:@"amount"]; 30 | } 31 | NSMutableArray* exts=[NSMutableArray array]; 32 | if(_extensions){ 33 | [_extensions enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 34 | if([obj respondsToSelector:NSSelectorFromString(@"dictionaryValue")]){ 35 | [exts addObject:[obj performSelector:NSSelectorFromString(@"dictionaryValue") withObject:nil]]; 36 | } 37 | else{ 38 | NSLog(@"Unknow extension object,%@", obj); 39 | } 40 | }]; 41 | } 42 | [result setObject:exts forKey:@"extensions"]; 43 | return result; 44 | } 45 | @end 46 | -------------------------------------------------------------------------------- /GrapheneTests/GXPrivateKeyTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PrivateKeyTests.m 3 | // GrapheneTests 4 | // 5 | // Created by David Lan on 2018/3/1. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GXPrivateKey.h" 11 | 12 | @interface GXPrivateKeyTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation GXPrivateKeyTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testFromWif { 29 | NSString* wifKey=@"5JNFf2y7JN75HMytcTJVANPVXAzv5iQbxdVDrtJNWfcSsyWUrXU"; 30 | GXPrivateKey* privateKey=[GXPrivateKey fromWif:wifKey]; 31 | NSAssert(privateKey!=nil, @"Invalid private key"); 32 | NSAssert([wifKey isEqualToString:[privateKey toWif]], @"Bad private key"); 33 | } 34 | 35 | - (void)testSharedSecret{ 36 | GXPrivateKey* privateKey=[GXPrivateKey fromWif:@"5JNFf2y7JN75HMytcTJVANPVXAzv5iQbxdVDrtJNWfcSsyWUrXU"]; 37 | NSLog(@"public:%@",[[privateKey getPublic] toString]); 38 | NSLog(@"shared:%@",BTCHexFromData([privateKey sharedSecret:[privateKey getPublic]])); 39 | } 40 | 41 | -(void)testSignature{ 42 | NSString* message = @"😜"; 43 | GXPrivateKey* privateKey=[GXPrivateKey fromWif:@"5JNFf2y7JN75HMytcTJVANPVXAzv5iQbxdVDrtJNWfcSsyWUrXU"]; 44 | NSData* signature=[privateKey sign:[message dataUsingEncoding:NSUTF8StringEncoding]]; 45 | NSLog(@"signature:%@",BTCHexFromData(signature)); 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /Graphene/lib/chain/classes/GXMemoData.m: -------------------------------------------------------------------------------- 1 | // 2 | // MemoData.m 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/3/15. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import "GXMemoData.h" 10 | #import "GXChainConfig.h" 11 | #import "GXPublicKey.h" 12 | #import "GXPrivateKey.h" 13 | #import "BTCData.h" 14 | #import "GXAES.h" 15 | #import "NSMutableData+ProtoBuff.h" 16 | 17 | uint64_t unique_nonce_uint64(){ 18 | FILE *fp = fopen("/dev/random", "r"); 19 | if (!fp) { 20 | perror("randgetter"); 21 | exit(-1); 22 | } 23 | 24 | uint64_t value = 0; 25 | int i; 26 | for (i=0; i, WTFPL. 2 | 3 | #import 4 | #include 5 | 6 | // Represents a point on the elliptic curve secp256k1. 7 | // Combined with BTCBigNumber arithmetic, you can do usual EC operations to manipulate private and public keys. 8 | // Private key is a big integer (represented by raw NSData or BTCBigNumber). 9 | // Public key is a point on the curve represented by BTCCurvePoint or BTCKey. 10 | // BTCCurvePoint is mutable. There is no immutable counterpart. 11 | //@class BTCKey; 12 | @class BTCBigNumber; 13 | @interface BTCCurvePoint : NSObject 14 | 15 | // Serialized form of a curve point as a compressed public key (32-byte X coordinate with 1-byte prefix) 16 | @property(nonatomic, readonly) NSData* data; 17 | 18 | // Underlying data structure in OpenSSL. 19 | @property(nonatomic, readonly) const EC_POINT* EC_POINT; 20 | 21 | // Returns YES if the point is at infinity. 22 | @property(nonatomic, readonly) BOOL isInfinity; 23 | 24 | // Coordinates of the point 25 | @property(nonatomic, readonly) BTCBigNumber* x; 26 | @property(nonatomic, readonly) BTCBigNumber* y; 27 | 28 | // Returns the generator point. Same as [BTCCurvePoint alloc] init]. 29 | + (instancetype) generator; 30 | 31 | // Returns order of the secp256k1 curve (FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141). 32 | + (BTCBigNumber*) curveOrder; 33 | 34 | // Initializes point with its binary representation (corresponds to -data). 35 | - (id) initWithData:(NSData*)data; 36 | 37 | // Initializes point with OpenSSL EC_POINT. 38 | - (id) initWithEC_POINT:(const EC_POINT*)ecpoint; 39 | 40 | // These modify the receiver and return self (or nil in case of error). To create another point use -copy: [[point copy] multiply:number] 41 | - (instancetype) multiply:(BTCBigNumber*)number; 42 | - (instancetype) add:(BTCCurvePoint*)point; 43 | 44 | // Efficiently adds n*G to the receiver. Equivalent to [point add:[[G copy] multiply:number]] 45 | - (instancetype) addGeneratorMultipliedBy:(BTCBigNumber*)number; 46 | 47 | // Re-declared `-copy` to provide exact return type. 48 | - (BTCCurvePoint*) copy; 49 | 50 | // Clears internal point data. 51 | - (void) clear; 52 | 53 | 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /Graphene/lib/ecc/GXPublicKey.m: -------------------------------------------------------------------------------- 1 | // 2 | // PublicKey.m 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/2/27. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import "GXPublicKey.h" 10 | #import "GXChainConfig.h" 11 | 12 | 13 | @interface GXPublicKey() 14 | 15 | @end 16 | 17 | @implementation GXPublicKey 18 | +(instancetype)fromString:(NSString*)pubKeyString{ 19 | NSString* prefix = [pubKeyString substringToIndex:[GX_ADDRESS_PREFIX length]]; 20 | NSAssert([prefix isEqualToString:GX_ADDRESS_PREFIX], @"Expecting key to begin with $%@, instead got %@",GX_ADDRESS_PREFIX,prefix); 21 | NSMutableData* pubKeyData = BTCDataFromBase58CheckRIPEMD160([pubKeyString substringFromIndex:[prefix length]]); 22 | return [GXPublicKey fromData:pubKeyData]; 23 | } 24 | 25 | +(instancetype)fromData:(NSData*)pubKeyData{ 26 | GXPublicKey* instance = [[GXPublicKey alloc]init]; 27 | NSAssert(pubKeyData!=nil, @"Invalid public key, checksum did not match"); 28 | const unsigned char* bytes = pubKeyData.bytes; 29 | EC_KEY* _key =EC_KEY_new_by_curve_name(NID_secp256k1); 30 | o2i_ECPublicKey(&_key, &bytes, pubKeyData.length); 31 | instance._key=_key; 32 | instance.publicKeyData = pubKeyData; 33 | 34 | return instance; 35 | 36 | } 37 | 38 | -(NSString*)toString{ 39 | return [NSString stringWithFormat:@"%@%@",GX_ADDRESS_PREFIX,BTCBase58CheckStringWithDataRIPEMD160([self publicKeyWithCompression:YES])]; 40 | } 41 | 42 | -(NSData*) publicKeyWithCompression:(BOOL)compression{ 43 | if(!self._key){ 44 | return nil; 45 | } 46 | EC_KEY_set_conv_form(self._key, compression ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); 47 | int length = i2o_ECPublicKey(self._key, NULL); 48 | if (!length) return nil; 49 | NSAssert(length <= 65, @"Pubkey length must be up to 65 bytes."); 50 | NSMutableData* data = [[NSMutableData alloc] initWithLength:length]; 51 | unsigned char* bytes = [data mutableBytes]; 52 | if (i2o_ECPublicKey(self._key, &bytes) != length) return nil; 53 | return data; 54 | } 55 | 56 | - (BTCCurvePoint*) curvePoint { 57 | const EC_POINT* ecpoint = EC_KEY_get0_public_key(self._key); 58 | BTCCurvePoint* cp = [[BTCCurvePoint alloc] initWithEC_POINT:ecpoint]; 59 | return cp; 60 | } 61 | @end 62 | 63 | -------------------------------------------------------------------------------- /Graphene/lib/ecc/vendor/BTCBase58.h: -------------------------------------------------------------------------------- 1 | // CoreBitcoin by Oleg Andreev , WTFPL. 2 | 3 | #import 4 | 5 | // Base58 is used for compact human-friendly representation of Bitcoin addresses and private keys. 6 | // Typically Base58-encoded text also contains a checksum. 7 | // Addresses look like 19FGfswVqxNubJbh1NW8A4t51T9x9RDVWQ 8 | // Private keys look like 5KQntKuhYWSRXNqp2yhdXzjekYAR7US3MT1715Mbv5CyUKV6hVe 9 | // 10 | // Here is what Satoshi said about Base58: 11 | // Why base-58 instead of standard base-64 encoding? 12 | // - Don't want 0OIl characters that look the same in some fonts and 13 | // could be used to create visually identical looking account numbers. 14 | // - A string with non-alphanumeric characters is not as easily accepted as an account number. 15 | // - E-mail usually won't line-break if there's no punctuation to break at. 16 | // - Double-clicking selects the whole number as one word if it's all alphanumeric. 17 | 18 | 19 | // See NS+BTCBase58.h for easy to use categories. 20 | 21 | // Returns data for a Base58 string without checksum 22 | // Data is mutable so you can clear sensitive information as soon as possible. 23 | NSMutableData* BTCDataFromBase58(NSString* string); 24 | NSMutableData* BTCDataFromBase58CString(const char* cstring); 25 | 26 | // Returns data for a Base58 string with checksum 27 | NSMutableData* BTCDataFromBase58Check(NSString* string); 28 | NSMutableData* BTCDataFromBase58CheckCString(const char* cstring); 29 | NSMutableData* BTCDataFromBase58CheckRIPEMD160(NSString* string); 30 | NSMutableData* BTCDataFromBase58CheckCStringRIPEMD160(const char* cstring); 31 | 32 | // String in Base58 without checksum, you need to free it yourself. 33 | // It's mutable so you can clear it securely yourself. 34 | char* BTCBase58CStringWithData(NSData* data); 35 | 36 | // Same as above, but returns an immutable autoreleased string. Suitable for non-sensitive data. 37 | NSString* BTCBase58StringWithData(NSData* data); 38 | 39 | // String in Base58 with checksum, you need to free it yourself. 40 | // It's mutable so you can clear it securely yourself. 41 | char* BTCBase58CheckCStringWithData(NSData* data); 42 | char* BTCBase58CheckCStringWithDataRIPEMD160(NSData* data); 43 | 44 | // Same as above, but returns an immutable autoreleased string. Suitable for non-sensitive data. 45 | NSString* BTCBase58CheckStringWithData(NSData* data); 46 | NSString* BTCBase58CheckStringWithDataRIPEMD160(NSData* data); 47 | 48 | -------------------------------------------------------------------------------- /Graphene/category/NSString+Base64.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Base64.m 3 | // 4 | // Version 1.0.2 5 | // 6 | // Created by Nick Lockwood on 12/01/2012. 7 | // Copyright (C) 2012 Charcoal Design 8 | // 9 | // Distributed under the permissive zlib License 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/nicklockwood/Base64 13 | // 14 | // This software is provided 'as-is', without any express or implied 15 | // warranty. In no event will the authors be held liable for any damages 16 | // arising from the use of this software. 17 | // 18 | // Permission is granted to anyone to use this software for any purpose, 19 | // including commercial applications, and to alter it and redistribute it 20 | // freely, subject to the following restrictions: 21 | // 22 | // 1. The origin of this software must not be misrepresented; you must not 23 | // claim that you wrote the original software. If you use this software 24 | // in a product, an acknowledgment in the product documentation would be 25 | // appreciated but is not required. 26 | // 27 | // 2. Altered source versions must be plainly marked as such, and must not be 28 | // misrepresented as being the original software. 29 | // 30 | // 3. This notice may not be removed or altered from any source distribution. 31 | // 32 | 33 | #import "NSString+Base64.h" 34 | #import "NSData+Base64.h" 35 | 36 | @implementation NSString (Base64) 37 | 38 | + (NSString *)stringWithBase64EncodedString:(NSString *)string 39 | { 40 | NSData *data = [NSData dataWithBase64EncodedString:string]; 41 | if (data) 42 | { 43 | NSString *result = [[self alloc] initWithData:data encoding:NSUTF8StringEncoding]; 44 | 45 | #if !__has_feature(objc_arc) 46 | [result autorelease]; 47 | #endif 48 | 49 | return result; 50 | } 51 | return nil; 52 | } 53 | 54 | - (NSString *)base64EncodedStringWithWrapWidth:(NSUInteger)wrapWidth 55 | { 56 | NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES]; 57 | return [data base64EncodedStringWithWrapWidth:wrapWidth]; 58 | } 59 | 60 | - (NSString *)base64EncodedString 61 | { 62 | NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES]; 63 | return [data base64EncodedString]; 64 | } 65 | 66 | - (NSString *)base64DecodedString 67 | { 68 | return [NSString stringWithBase64EncodedString:self]; 69 | } 70 | 71 | - (NSData *)base64DecodedData 72 | { 73 | return [NSData dataWithBase64EncodedString:self]; 74 | } 75 | 76 | @end -------------------------------------------------------------------------------- /Graphene/lib/ecc/GXAES.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHAES.m 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/3/21. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import "GXAES.h" 10 | #import "NSData+BTCData.h" 11 | #import "BTCData.h" 12 | @interface GXAES() 13 | @property (nonatomic,strong) NSData* iv; 14 | @property (nonatomic,strong) NSData* key; 15 | @end 16 | 17 | @implementation GXAES 18 | +(instancetype)fromSeed:(NSData*)seed{ 19 | GXAES* aes = [[GXAES alloc] init]; 20 | NSData* hashedSeedData =BTCSHA512(seed); 21 | NSString* hashedSeed = BTCHexFromData(hashedSeedData); 22 | aes.key=BTCDataFromHex([hashedSeed substringWithRange:NSMakeRange(0, 64)]); 23 | aes.iv=BTCDataFromHex([hashedSeed substringWithRange:NSMakeRange(64, 32)]); 24 | return aes; 25 | } 26 | 27 | +(NSData*)encrypt_with_checksum:(GXPrivateKey*)privKey publicKey:(GXPublicKey*)publicKey nonce:(NSString*)nonce message:(NSString*)message{ 28 | NSData* sharedSecret = [privKey sharedSecret:publicKey]; 29 | NSMutableData* secretWithNonce = [NSMutableData data]; 30 | [secretWithNonce appendData:[nonce dataUsingEncoding:NSUTF8StringEncoding]]; 31 | [secretWithNonce appendData:[BTCHexFromData(sharedSecret) dataUsingEncoding:NSUTF8StringEncoding]]; 32 | GXAES * aes =[GXAES fromSeed:secretWithNonce]; 33 | NSData* checksum = [[[message dataUsingEncoding:NSUTF8StringEncoding] SHA256] subdataWithRange:NSMakeRange(0, 4)]; 34 | NSMutableData* payload = checksum.mutableCopy; 35 | [payload appendData:[message dataUsingEncoding:NSUTF8StringEncoding]]; 36 | return [aes encrypt:payload]; 37 | } 38 | 39 | +(NSData*)decrypt_with_checksum:(GXPrivateKey*)privKey publicKey:(GXPublicKey*)publicKey nonce:(NSString*)nonce message:(NSString*)message{ 40 | NSData* sharedSecret = [privKey sharedSecret:publicKey]; 41 | 42 | NSMutableData* secretWithNonce = [NSMutableData data]; 43 | [secretWithNonce appendData:[nonce dataUsingEncoding:NSUTF8StringEncoding]]; 44 | [secretWithNonce appendData:sharedSecret]; 45 | GXAES * aes =[GXAES fromSeed:secretWithNonce]; 46 | NSData* payload = [aes decrypt:BTCDataFromHex(message)]; 47 | NSData* checksum = [payload subdataWithRange:NSMakeRange(0, 4)]; 48 | NSData* plaintext = [payload subdataWithRange:NSMakeRange(4, payload.length-1)]; 49 | 50 | NSData* new_checksum = [[[message dataUsingEncoding:NSUTF8StringEncoding] SHA256] subdataWithRange:NSMakeRange(0, 4)]; 51 | 52 | if(![checksum isEqualToData:new_checksum]){ 53 | [NSException exceptionWithName:@"decrypt_with_checksum" reason:@"fail to decrypt message with checksum" userInfo:nil]; 54 | } 55 | return plaintext; 56 | } 57 | 58 | -(NSData*)encrypt:(NSData*)payload{ 59 | return [NSMutableData encryptData:payload key:self.key iv:self.iv]; 60 | } 61 | 62 | -(NSData*)decrypt:(NSData*)payload{ 63 | return [NSMutableData decryptData:payload key:self.key iv:self.iv]; 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /GrapheneTests/GXUtilTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // GXUtilTest.m 3 | // GrapheneTests 4 | // 5 | // Created by David Lan on 2018/12/19. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GXUtil.h" 11 | #import "NSDictionary+Expand.h" 12 | 13 | @interface GXUtilTest : XCTestCase 14 | 15 | @end 16 | 17 | @implementation GXUtilTest 18 | 19 | - (void)setUp { 20 | [super setUp]; 21 | // Put setup code here. This method is called before the invocation of each test method in the class. 22 | } 23 | 24 | - (void)tearDown { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | -(void) testStringToName{ 30 | uint64_t name =[GXUtil string_to_name:@"hi"]; 31 | NSString* str = [GXUtil name_to_string:name]; 32 | NSLog(@"%llu,%@",name,str); 33 | } 34 | 35 | -(void) testActionSerializer{ 36 | NSDictionary* params = [NSDictionary fromJSON:@"{\"to_account\":\"lzydophin94\",\"amount\":{\"asset_id\":1,\"amount\":1000000}}"]; 37 | NSDictionary* abi = [NSDictionary fromJSON:@"{\"version\":\"gxc::abi/1.0\",\"types\":[],\"structs\":[{\"name\":\"account\",\"base\":\"\",\"fields\":[{\"name\":\"owner\",\"type\":\"uint64\"},{\"name\":\"balances\",\"type\":\"contract_asset[]\"}]},{\"name\":\"deposit\",\"base\":\"\",\"fields\":[]},{\"name\":\"withdraw\",\"base\":\"\",\"fields\":[{\"name\":\"to_account\",\"type\":\"string\"},{\"name\":\"amount\",\"type\":\"contract_asset\"}]}],\"actions\":[{\"name\":\"deposit\",\"type\":\"deposit\",\"payable\":true},{\"name\":\"withdraw\",\"type\":\"withdraw\",\"payable\":false}],\"tables\":[{\"name\":\"account\",\"index_type\":\"i64\",\"key_names\":[\"owner\"],\"key_types\":[\"uint64\"],\"type\":\"account\"}],\"error_messages\":[],\"abi_extensions\":[]}"]; 38 | NSString* result = [GXUtil serialize_action_data:@"withdraw" params:params abi:abi]; 39 | NSLog(@"%@",result); // 0b6c7a79646f7068696e393440420f00000000000100000000000000 40 | } 41 | 42 | -(void) testTransactionSerializer{ 43 | NSDictionary* transaction = [NSDictionary fromJSON:@"{\"expiration\":\"2018-03-20T20:46:38\",\"extensions\":[],\"operations\":[[0,{\"amount\":{\"asset_id\":\"1.3.1\",\"amount\":1000000},\"fee\":{\"asset_id\":\"1.3.1\",\"amount\":2500},\"to\":\"1.2.254\",\"memo\":{\"nonce\":2680142845,\"to\":\"GXC7xSR83xcXECGCtyxboNbuhQwnyjVksgtMLX422nDhSM9d2TPRF\",\"message\":\"70fed4bf910021bd4e01c221dcc93570\",\"from\":\"GXC8H1wXTAUWcTtogBmA5EW8TUWLA6T1kAXwMKYtnNuqAe1VCXFD9\"},\"extensions\":[],\"from\":\"1.2.850\"}]],\"ref_block_prefix\":2568833528,\"ref_block_num\":53519}"]; 44 | NSString* result = [GXUtil serialize_transaction:transaction]; 45 | NSLog(@"%@",result); 46 | //0fd1f8491d99ae02b15a0100c40900000000000001d206fe0140420f0000000000010103be3d4b21c275a5dd5a8e61959feecce56589e30e8aeaf4c15798aca17872eaea03940e48cb1fe1c5975ee9ea5876ccbbe132d69396eebc68201d0198588e44b887fdbbbf9f000000001070fed4bf910021bd4e01c221dcc935700000 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # graphene-ios 2 | 3 | Implementation of Graphene protocol in Objective-C 4 | 5 | ## Use cases 6 | 7 | ### Transfer 8 | 9 | ```Objective-c 10 | [[GXConnectionManager sharedInstance] connectWithFallback:@[@"wss://testnet.gxchain.org"] callback:^(BOOL connected, NSString *url) { 11 | GXTransferOperation* transferOp=[[GXTransferOperation alloc] init]; 12 | transferOp.from=@"1.2.850"; 13 | transferOp.to=@"1.2.254"; 14 | transferOp.amount=[[GXAssetAmount alloc] initWithAsset:@"1.3.1" amount:1000000]; 15 | transferOp.fee=[[GXAssetAmount alloc] initWithAsset:@"1.3.1" amount:0]; 16 | transferOp.memo=[GXMemoData memoWithPrivate:@"5JNFf2y7JN75HMytcTJVANPVXAzv5iQbxdVDrtJNWfcSsyWUrXU" public:@"GXC7xSR83xcXECGCtyxboNbuhQwnyjVksgtMLX422nDhSM9d2TPRF" message:@"屌不屌,来自GXS Native的转账"]; 17 | GXTransactionBuilder* tx = [[GXTransactionBuilder alloc] initWithOperations:@[transferOp]]; 18 | [tx add_signer:[GXPrivateKey fromWif:@"5JNFf2y7JN75HMytcTJVANPVXAzv5iQbxdVDrtJNWfcSsyWUrXU"]]; 19 | [tx processTransaction:^(NSError* err,NSDictionary *transaction) { 20 | NSLog(@"%@",transaction.json); 21 | [expectation fulfill]; 22 | } broadcast:YES]; 23 | }]; 24 | ``` 25 | 26 | ### CallContract 27 | 28 | ``` Objective-C 29 | [[GXConnectionManager sharedInstance] connectWithFallback:@[@"wss://testnet.gxchain.org"] callback:^(BOOL connected, NSString *url) { 30 | GXCallContractOperation* callOp = [[GXCallContractOperation alloc] init]; 31 | callOp.fee=[[GXAssetAmount alloc] initWithAsset:@"1.3.1" amount:0]; 32 | callOp.account = @"1.2.850"; 33 | callOp.contract_id=@"1.2.282"; 34 | callOp.method_name=@"hi"; 35 | callOp.data=[NSData data]; 36 | GXTransactionBuilder* tx = [[GXTransactionBuilder alloc] initWithOperations:@[callOp]]; 37 | [tx add_signer:[GXPrivateKey fromWif:@"5JNFf2y7JN75HMytcTJVANPVXAzv5iQbxdVDrtJNWfcSsyWUrXU"]]; 38 | [tx processTransaction:^(NSError* err,NSDictionary *transaction) { 39 | NSLog(@"%@",transaction.json); 40 | [expectation fulfill]; 41 | } broadcast:YES]; 42 | }]; 43 | ``` 44 | 45 | ### Contract Parameters Serializer 46 | 47 | ``` Objective-C 48 | NSDictionary* params = [NSDictionary fromJSON:@"{\"to_account\":\"lzydophin94\",\"amount\":{\"asset_id\":1,\"amount\":1000000}}"]; 49 | 50 | NSDictionary* abi = [NSDictionary fromJSON:@"{\"version\":\"gxc::abi/1.0\",\"types\":[],\"structs\":[{\"name\":\"account\",\"base\":\"\",\"fields\":[{\"name\":\"owner\",\"type\":\"uint64\"},{\"name\":\"balances\",\"type\":\"contract_asset[]\"}]},{\"name\":\"deposit\",\"base\":\"\",\"fields\":[]},{\"name\":\"withdraw\",\"base\":\"\",\"fields\":[{\"name\":\"to_account\",\"type\":\"string\"},{\"name\":\"amount\",\"type\":\"contract_asset\"}]}],\"actions\":[{\"name\":\"deposit\",\"type\":\"deposit\",\"payable\":true},{\"name\":\"withdraw\",\"type\":\"withdraw\",\"payable\":false}],\"tables\":[{\"name\":\"account\",\"index_type\":\"i64\",\"key_names\":[\"owner\"],\"key_types\":[\"uint64\"],\"type\":\"account\"}],\"error_messages\":[],\"abi_extensions\":[]}"]; 51 | 52 | NSString* result = [GXUtil serialize_action_data:@"withdraw" params:params abi:abi]; 53 | ``` 54 | 55 | 56 | -------------------------------------------------------------------------------- /Graphene/lib/chain/utils/GXUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // GXUtil.m 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/12/12. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GXUtil.h" 11 | #import "NSDictionary+Expand.h" 12 | 13 | static uint64_t char_to_symbol( char c ) { 14 | if( c >= 'a' && c <= 'z' ) 15 | return (c - 'a') + 6; 16 | if( c >= '1' && c <= '5' ) 17 | return (c - '1') + 1; 18 | return 0; 19 | } 20 | 21 | // Each char of the string is encoded into 5-bit chunk and left-shifted 22 | // to its 5-bit slot starting with the highest slot for the first char. 23 | // The 13th char, if str is long enough, is encoded into 4-bit chunk 24 | // and placed in the lowest 4 bits. 64 = 12 * 5 + 4 25 | static uint64_t string_to_name( const char* str ) 26 | { 27 | uint64_t name = 0; 28 | int i = 0; 29 | for ( ; str[i] && i < 12; ++i) { 30 | // NOTE: char_to_symbol() returns char type, and without this explicit 31 | // expansion to uint64 type, the compilation fails at the point of usage 32 | // of string_to_name(), where the usage requires constant (compile time) expression. 33 | name |= (char_to_symbol(str[i]) & 0x1f) << (64 - 5 * (i + 1)); 34 | } 35 | 36 | // The for-loop encoded up to 60 high bits into uint64 'name' variable, 37 | // if (strlen(str) > 12) then encode str[12] into the low (remaining) 38 | // 4 bits of 'name' 39 | if (i == 12) 40 | name |= char_to_symbol(str[12]) & 0x0F; 41 | return name; 42 | } 43 | 44 | @implementation GXUtil 45 | 46 | +(uint64_t) string_to_name:(NSString*)str{ 47 | return string_to_name([str UTF8String]); 48 | } 49 | 50 | +(NSString*) name_to_string :(uint64_t) name{ 51 | static const char* charmap = ".12345abcdefghijklmnopqrstuvwxyz"; 52 | 53 | char str[14]= ".............\0"; 54 | 55 | uint64_t tmp = name; 56 | for( uint32_t i = 0; i <= 12; ++i ) { 57 | char c = charmap[tmp & (i == 0 ? 0x0f : 0x1f)]; 58 | str[12-i] = c; 59 | tmp >>= (i == 0 ? 4 : 5); 60 | } 61 | 62 | for( int32_t i = 12; i >= 0; --i ) { 63 | char c = str[i]; 64 | if(c=='.'){ 65 | str[i] = ' '; 66 | } 67 | else{ 68 | break; 69 | } 70 | } 71 | 72 | return [[NSString stringWithUTF8String:str] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 73 | } 74 | 75 | +(JSContext*)jsContext{ 76 | static dispatch_once_t onceToken; 77 | static JSContext* instance; 78 | dispatch_once(&onceToken, ^{ 79 | NSBundle* bundle = [NSBundle bundleForClass:[GXUtil class]]; 80 | NSString * path = [bundle pathForResource:@"Graphene.bundle/tx_serializer.min" ofType:@"js"]; 81 | NSData * jsData = [[NSData alloc]initWithContentsOfFile:path]; 82 | NSString * jsCode = [[NSString alloc]initWithData:jsData encoding:NSUTF8StringEncoding]; 83 | instance=[[JSContext alloc] init]; 84 | [instance evaluateScript:jsCode]; 85 | }); 86 | 87 | return instance; 88 | } 89 | 90 | +(NSString*) serialize_action_data:(NSString*)action params:(NSDictionary*)params abi:(NSDictionary*)abi{ 91 | NSString* jsCode = [NSString stringWithFormat:@"serializer.serializeCallData('%@',%@,%@).toString('hex')",action, [params json],[abi json]]; 92 | NSString* result = [[[GXUtil jsContext] evaluateScript:jsCode] toString]; 93 | return result; 94 | } 95 | 96 | +(NSString*) serialize_transaction:(NSDictionary*)transaction{ 97 | NSString* jsCode = [NSString stringWithFormat:@"serializer.serializeTransaction(%@).toString('hex')", [transaction json]]; 98 | NSString* result = [[[GXUtil jsContext] evaluateScript:jsCode] toString]; 99 | return result; 100 | } 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /Graphene/ws/GXApiInstances.m: -------------------------------------------------------------------------------- 1 | // 2 | // GXApiInstances.m 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/2/27. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import "GXApiInstances.h" 10 | 11 | @implementation GXApiInstances 12 | 13 | +(instancetype)sharedInstance{ 14 | static dispatch_once_t onceToken; 15 | static GXApiInstances* instance; 16 | dispatch_once(&onceToken, ^{ 17 | instance=[[GXApiInstances alloc]init]; 18 | }); 19 | return instance; 20 | } 21 | 22 | -(void)connect:(NSString*)url timeout:(NSTimeInterval)timeout statusCallback:(void(^)(BOOL connected,NSString* status))callback{ 23 | if(self.ws_rpc){ 24 | [self.ws_rpc close]; 25 | } 26 | self.ws_rpc=[[GXChainWebSocket alloc] initWithAddress:url]; 27 | [self.ws_rpc connect:^(BOOL connected, NSString *status) { 28 | callback(connected,status); 29 | if(connected&&[status isEqualToString:@"open"]){ 30 | [self.ws_rpc login:@"" password:@"" callback:^(NSError *error, id response) { 31 | if(!error){ 32 | NSLog(@"Conncted to API node:%@",url); 33 | self.initCount=0; 34 | self._db=[[GXGrapheneApi alloc] initWithName:@"database" websocket:self.ws_rpc]; 35 | self._net=[[GXGrapheneApi alloc] initWithName:@"network_broadcast" websocket:self.ws_rpc]; 36 | self._hist=[[GXGrapheneApi alloc] initWithName:@"history" websocket:self.ws_rpc]; 37 | self._crypt=[[GXGrapheneApi alloc] initWithName:@"crypto" websocket:self.ws_rpc]; 38 | [self._db initialize:^(NSError *err, id response) { 39 | if(!err){ 40 | self.initCount+=1; 41 | if(self.initCount==4){ 42 | callback(YES,@"ready"); 43 | } 44 | [self._db exec:@"get_chain_id" params:@[] callback:^(NSError *err, id resp) { 45 | if(!err){ 46 | self.chain_id=resp; 47 | } 48 | }]; 49 | }else{ 50 | callback(NO,@"initFail"); 51 | } 52 | }]; 53 | [self._net initialize:^(NSError *err, id response) { 54 | if(!err){ 55 | self.initCount+=1; 56 | if(self.initCount==4){ 57 | callback(YES,@"ready"); 58 | } 59 | }else{ 60 | callback(NO,@"initFail"); 61 | } 62 | }]; 63 | [self._hist initialize:^(NSError *err, id response) { 64 | if(!err){ 65 | self.initCount+=1; 66 | if(self.initCount==4){ 67 | callback(YES,@"ready"); 68 | } 69 | }else{ 70 | callback(NO,@"initFail"); 71 | } 72 | }]; 73 | [self._crypt initialize:^(NSError *err, id response) { 74 | if(!err){ 75 | self.initCount+=1; 76 | if(self.initCount==4){ 77 | callback(YES,@"ready"); 78 | } 79 | }else{ 80 | callback(NO,@"initFail"); 81 | } 82 | }]; 83 | } else{ 84 | NSLog(@"Login to %@ failed:%@",url,error.localizedDescription); 85 | } 86 | }]; 87 | } 88 | } timeout:timeout]; 89 | } 90 | 91 | -(void)close{ 92 | if(self.ws_rpc){ 93 | [self.ws_rpc close]; 94 | self.ws_rpc=nil; 95 | } 96 | } 97 | @end 98 | -------------------------------------------------------------------------------- /Graphene/ws/GXConnectionManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // ConnectionManager.m 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/2/27. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import "GXConnectionManager.h" 10 | #import "GXApiInstances.h" 11 | #import "GXChainWebSocket.h" 12 | 13 | @interface GXConnectionManager() 14 | @property (nonatomic,strong) GXApiInstances* Api; 15 | @end 16 | 17 | @implementation GXConnectionManager 18 | 19 | +(instancetype)sharedInstance{ 20 | static dispatch_once_t onceToken; 21 | static GXConnectionManager* instance; 22 | dispatch_once(&onceToken, ^{ 23 | instance=[[GXConnectionManager alloc] init]; 24 | instance.Api=[GXApiInstances sharedInstance]; 25 | }); 26 | return instance; 27 | } 28 | 29 | -(void)connect:(NSString*)url callback:(void(^)(BOOL connected))callback{ 30 | [self.Api close]; 31 | [self.Api connect:url timeout:4 statusCallback:^(BOOL connected, NSString *status) { 32 | if(!connected && ![status isEqualToString:@"closed"]){ //error, timeout, initFail 33 | [self.Api close]; 34 | } 35 | else if(!connected&&[status isEqualToString:@"closed"]){ // closed 36 | NSLog(@"Connection closed: %@",url); 37 | callback(NO); 38 | } 39 | else if(connected&&[status isEqualToString:@"ready"]){ // connected and initialized 40 | callback(YES); 41 | } 42 | // no more case 43 | }]; 44 | } 45 | 46 | -(void)checkConnect:(NSString*)url callback:(void(^)(NSArray* info))callback{ 47 | GXChainWebSocket* conn = [[GXChainWebSocket alloc]initWithAddress:url]; 48 | NSDate* start = [NSDate date]; 49 | [conn connect:^(BOOL connected, NSString *status) { 50 | [conn close]; 51 | if(connected){ 52 | callback(@[url,@([[NSDate date] timeIntervalSinceDate:start])]); 53 | } 54 | else{ 55 | callback(@[url,@(-1)]); 56 | } 57 | } timeout:4]; 58 | } 59 | 60 | -(void)checkConnections:(NSArray*)urls callback:(void(^)(NSArray* urlInfo))callback{ 61 | NSMutableArray* results=[NSMutableArray array]; 62 | [urls enumerateObjectsUsingBlock:^(NSString* url, NSUInteger idx, BOOL * _Nonnull stop) { 63 | [self checkConnect:url callback:^(NSArray *info) { 64 | [results addObject:info]; 65 | if([results count]==[urls count]){ 66 | callback(results); 67 | } 68 | }]; 69 | }]; 70 | } 71 | 72 | -(void)tryConnect:(NSArray*)urls index:(NSInteger)index callback:(void(^)(BOOL connected,NSString* url))callback{ 73 | if(index<[urls count]){ 74 | NSString* url = [[urls objectAtIndex:index] objectAtIndex:0]; 75 | [self connect:url callback:^(BOOL connected) { 76 | if(connected){ 77 | callback(YES,url); 78 | } 79 | else{ 80 | [self tryConnect:urls index:index+1 callback:callback]; 81 | } 82 | }]; 83 | } 84 | else{ 85 | callback(NO,nil); 86 | } 87 | } 88 | 89 | -(void)connectWithFallback:(NSArray*)urls callback:(void(^)(BOOL connected,NSString* url))callback{ 90 | [self checkConnections:urls callback:^(NSArray *urlInfo) { 91 | NSMutableArray* arr = [NSMutableArray array]; 92 | [urlInfo enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 93 | if([obj[1] integerValue]>-1){ 94 | [arr addObject:obj]; 95 | } 96 | }]; 97 | [arr sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { 98 | return [obj1[1] doubleValue]>[obj2[1] doubleValue]; 99 | }]; 100 | NSLog(@"%@",arr); 101 | [self tryConnect:arr index:0 callback:^(BOOL connected,NSString* url) { 102 | if(!connected){ 103 | NSLog(@"Tried %lu connections, none of which worked,%@",(unsigned long)[arr count],arr); 104 | } 105 | callback(connected,url); 106 | }]; 107 | }]; 108 | } 109 | 110 | 111 | @end 112 | 113 | -------------------------------------------------------------------------------- /GrapheneTests/GXTransactionTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TransactionTests.m 3 | // GrapheneTests 4 | // 5 | // Created by David Lan on 2018/3/16. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GXConnectionManager.h" 11 | #import "GXTransactionBuilder.h" 12 | #import "GXTransferOperation.h" 13 | #import "GXAssetAmount.h" 14 | #import "GXPrivateKey.h" 15 | #import "NSDictionary+Expand.h" 16 | #import "GXCallContractOperation.h" 17 | #import "GXUtil.h" 18 | 19 | @interface GXTransactionTests : XCTestCase 20 | 21 | @end 22 | 23 | @implementation GXTransactionTests 24 | 25 | - (void)setUp { 26 | [super setUp]; 27 | // Put setup code here. This method is called before the invocation of each test method in the class. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | // Register a free testnet account here: https://testnet.wallet.gxchain.org 35 | - (void)testTransfer { 36 | XCTestExpectation * expectation = [self expectationWithDescription:@"Connection Exception"]; 37 | [[GXConnectionManager sharedInstance] connectWithFallback:@[@"wss://testnet.gxchain.org"] callback:^(BOOL connected, NSString *url) { 38 | GXTransferOperation* transferOp=[[GXTransferOperation alloc] init]; 39 | transferOp.from=@"1.2.850"; 40 | transferOp.to=@"1.2.254"; 41 | transferOp.amount=[[GXAssetAmount alloc] initWithAsset:@"1.3.1" amount:1000000]; 42 | transferOp.fee=[[GXAssetAmount alloc] initWithAsset:@"1.3.1" amount:0]; 43 | transferOp.memo=[GXMemoData memoWithPrivate:@"5JNFf2y7JN75HMytcTJVANPVXAzv5iQbxdVDrtJNWfcSsyWUrXU" public:@"GXC7xSR83xcXECGCtyxboNbuhQwnyjVksgtMLX422nDhSM9d2TPRF" message:@"屌不屌,来自GXS Native的转账"]; 44 | GXTransactionBuilder* tx = [[GXTransactionBuilder alloc] initWithOperations:@[transferOp]]; 45 | [tx add_signer:[GXPrivateKey fromWif:@"5JNFf2y7JN75HMytcTJVANPVXAzv5iQbxdVDrtJNWfcSsyWUrXU"]]; 46 | [tx processTransaction:^(NSError* err,NSDictionary *transaction) { 47 | NSLog(@"%@",transaction.json); 48 | [expectation fulfill]; 49 | } broadcast:YES]; 50 | }]; 51 | [self waitForExpectationsWithTimeout:60 handler:^(NSError *error) { 52 | NSLog(@"%@",error.localizedDescription); 53 | }]; 54 | } 55 | 56 | - (void)testTransferSign{ 57 | GXTransferOperation* transferOp=[[GXTransferOperation alloc] init]; 58 | transferOp.fee=[[GXAssetAmount alloc] initWithAsset:@"1.3.1" amount:2500]; 59 | transferOp.from=@"1.2.850"; 60 | transferOp.to=@"1.2.254"; 61 | transferOp.amount=[[GXAssetAmount alloc] initWithAsset:@"1.3.1" amount:1000000]; 62 | transferOp.memo=[GXMemoData memoWithPrivate:@"5JNFf2y7JN75HMytcTJVANPVXAzv5iQbxdVDrtJNWfcSsyWUrXU" public:@"GXC7xSR83xcXECGCtyxboNbuhQwnyjVksgtMLX422nDhSM9d2TPRF" message:@"123"]; 63 | GXTransactionBuilder* tx = [[GXTransactionBuilder alloc] initWithOperations:@[transferOp]]; 64 | tx.ref_block_num=53519; 65 | tx.ref_block_prefix=2568833528; 66 | tx.expiration=1521578798; 67 | tx.extensions=@[]; 68 | [tx add_signer:[GXPrivateKey fromWif:@"5JNFf2y7JN75HMytcTJVANPVXAzv5iQbxdVDrtJNWfcSsyWUrXU"]]; 69 | 70 | NSLog(@"%@,%@,%@",BTCHexFromData([tx serialize]),[[tx dictionaryValue] json],[tx signedTransaction]); 71 | } 72 | 73 | -(void) testCallContract{ 74 | XCTestExpectation * expectation = [self expectationWithDescription:@"Connection Exception"]; 75 | [[GXConnectionManager sharedInstance] connectWithFallback:@[@"wss://testnet.gxchain.org"] callback:^(BOOL connected, NSString *url) { 76 | GXCallContractOperation* callOp = [[GXCallContractOperation alloc] init]; 77 | callOp.fee=[[GXAssetAmount alloc] initWithAsset:@"1.3.1" amount:0]; 78 | callOp.account = @"1.2.850"; 79 | callOp.contract_id=@"1.2.282"; 80 | callOp.method_name=@"hi"; 81 | callOp.data=[NSData data]; 82 | GXTransactionBuilder* tx = [[GXTransactionBuilder alloc] initWithOperations:@[callOp]]; 83 | [tx add_signer:[GXPrivateKey fromWif:@"5JNFf2y7JN75HMytcTJVANPVXAzv5iQbxdVDrtJNWfcSsyWUrXU"]]; 84 | [tx processTransaction:^(NSError* err,NSDictionary *transaction) { 85 | NSLog(@"%@",transaction.json); 86 | [expectation fulfill]; 87 | } broadcast:YES]; 88 | }]; 89 | [self waitForExpectationsWithTimeout:60 handler:^(NSError *error) { 90 | NSLog(@"%@",error.localizedDescription); 91 | }]; 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /Graphene/lib/ecc/vendor/NSData+BTCData.m: -------------------------------------------------------------------------------- 1 | // Oleg Andreev 2 | 3 | #import "NSData+BTCData.h" 4 | #import 5 | 6 | @implementation NSData (BTC) 7 | 8 | 9 | 10 | #pragma mark - Hash Functions 11 | 12 | 13 | - (NSData*) SHA1 { return BTCSHA1(self); } 14 | - (NSData*) SHA256 { return BTCSHA256(self); } 15 | - (NSData*) BTCHash256 { return BTCHash256(self); } 16 | 17 | #if BTCDataRequiresOpenSSL 18 | - (NSData*) RIPEMD160 { return BTCRIPEMD160(self); } 19 | - (NSData*) BTCHash160 { return BTCHash160(self); } 20 | #endif 21 | 22 | 23 | 24 | 25 | #pragma mark - Formatting 26 | 27 | 28 | - (NSString*) hex { 29 | return BTCHexFromData(self); 30 | } 31 | 32 | - (NSString*) uppercaseHex { 33 | return BTCUppercaseHexFromData(self); 34 | } 35 | 36 | - (NSString*) hexString { 37 | return BTCHexFromData(self); 38 | } 39 | 40 | - (NSString*) hexUppercaseString { 41 | return BTCUppercaseHexFromData(self); 42 | } 43 | 44 | #pragma mark - Encryption / Decryption 45 | 46 | + (NSMutableData*) encryptData:(NSData*)data key:(NSData*)key iv:(NSData*)initializationVector { 47 | return [self cryptData:data key:key iv:initializationVector operation:kCCEncrypt]; 48 | } 49 | 50 | + (NSMutableData*) decryptData:(NSData*)data key:(NSData*)key iv:(NSData*)initializationVector { 51 | return [self cryptData:data key:key iv:initializationVector operation:kCCDecrypt]; 52 | } 53 | 54 | 55 | + (NSMutableData*) cryptData:(NSData*)data key:(NSData*)key iv:(NSData*)iv operation:(CCOperation)operation { 56 | if (!data || !key) return nil; 57 | 58 | int blockSize = kCCBlockSizeAES128; 59 | int encryptedDataCapacity = (int)(data.length / blockSize + 1) * blockSize; 60 | NSMutableData* encryptedData = [[NSMutableData alloc] initWithLength:encryptedDataCapacity]; 61 | 62 | // Treat empty IV as nil 63 | if (iv.length == 0) { 64 | iv = nil; 65 | } 66 | 67 | // If IV is supplied, validate it. 68 | if (iv) { 69 | if (iv.length == blockSize) { 70 | // perfect. 71 | } else if (iv.length > blockSize) { 72 | // IV is bigger than the block size. CCCrypt will take only the first 16 bytes. 73 | } else { 74 | // IV is smaller than needed. This should not happen. It's better to crash than to leak something. 75 | @throw [NSException exceptionWithName:@"NSData+BTC IV is invalid" 76 | reason:[NSString stringWithFormat:@"Invalid size of IV: %d", (int)iv.length] 77 | userInfo:nil]; 78 | } 79 | } 80 | 81 | size_t dataOutMoved = 0; 82 | CCCryptorStatus cryptstatus = CCCrypt( 83 | operation, // CCOperation op, /* kCCEncrypt, kCCDecrypt */ 84 | kCCAlgorithmAES, // CCAlgorithm alg, /* kCCAlgorithmAES128, etc. */ 85 | kCCOptionPKCS7Padding, // CCOptions options, /* kCCOptionPKCS7Padding, etc. */ 86 | key.bytes, // const void *key, 87 | key.length, // size_t keyLength, 88 | iv ? iv.bytes : NULL, // const void *iv, /* optional initialization vector */ 89 | data.bytes, // const void *dataIn, /* optional per op and alg */ 90 | data.length, // size_t dataInLength, 91 | encryptedData.mutableBytes, // void *dataOut, /* data RETURNED here */ 92 | encryptedData.length, // size_t dataOutAvailable, 93 | &dataOutMoved // size_t *dataOutMoved 94 | ); 95 | 96 | if (cryptstatus == kCCSuccess) { 97 | // Resize the result key to the correct size. 98 | encryptedData.length = dataOutMoved; 99 | return encryptedData; 100 | } else { 101 | //kCCSuccess = 0, 102 | //kCCParamError = -4300, 103 | //kCCBufferTooSmall = -4301, 104 | //kCCMemoryFailure = -4302, 105 | //kCCAlignmentError = -4303, 106 | //kCCDecodeError = -4304, 107 | //kCCUnimplemented = -4305, 108 | //kCCOverflow = -4306 109 | @throw [NSException exceptionWithName:@"NSData+BTC CCCrypt failed" 110 | reason:[NSString stringWithFormat:@"error: %d", cryptstatus] userInfo:nil]; 111 | return nil; 112 | } 113 | } 114 | 115 | 116 | @end 117 | -------------------------------------------------------------------------------- /Graphene/lib/ecc/vendor/BTCBigNumber.h: -------------------------------------------------------------------------------- 1 | // CoreBitcoin by Oleg Andreev , WTFPL. 2 | 3 | #import 4 | #import 5 | 6 | // Bitcoin-flavoured big number wrapping OpenSSL BIGNUM. 7 | // It is doing byte ordering like bitcoind does to stay compatible. 8 | // BTCBigNumber is immutable. BTCMutableBigNumber is its mutable counterpart. 9 | // -copy always returns immutable instance, like in other Cocoa containers. 10 | @class BTCBigNumber; 11 | @class BTCMutableBigNumber; 12 | 13 | @interface BTCBigNumber : NSObject 14 | 15 | @property(nonatomic, readonly) uint32_t compact; // compact representation used for the difficulty target 16 | @property(nonatomic, readonly) uint32_t uint32value; 17 | @property(nonatomic, readonly) int32_t int32value; 18 | @property(nonatomic, readonly) uint64_t uint64value; 19 | @property(nonatomic, readonly) int64_t int64value; 20 | @property(nonatomic, readonly) NSString* hexString; 21 | @property(nonatomic, readonly) NSString* decimalString; 22 | @property(nonatomic, readonly) NSData* signedLittleEndian; 23 | @property(nonatomic, readonly) NSData* unsignedBigEndian; 24 | 25 | // Deprecated. Use `-signedLittleEndian` instead. 26 | @property(nonatomic, readonly) NSData* littleEndianData DEPRECATED_ATTRIBUTE; 27 | 28 | // Deprecated. Use `-unsignedBigEndian` instead. 29 | @property(nonatomic, readonly) NSData* unsignedData DEPRECATED_ATTRIBUTE; 30 | 31 | // Pointer to an internal BIGNUM value. You should not modify it. 32 | // To modify, use [[bn mutableCopy] mutableBIGNUM] methods. 33 | @property(nonatomic, readonly) const BIGNUM* BIGNUM; 34 | 35 | @property(nonatomic, readonly) BOOL isZero; 36 | @property(nonatomic, readonly) BOOL isOne; 37 | 38 | 39 | // BTCBigNumber returns always the same object for these constants. 40 | // BTCMutableBigNumber returns a new object every time. 41 | + (instancetype) zero; // 0 42 | + (instancetype) one; // 1 43 | + (instancetype) negativeOne; // -1 44 | 45 | - (id) init; 46 | - (id) initWithCompact:(uint32_t)compact; 47 | - (id) initWithUInt32:(uint32_t)value; 48 | - (id) initWithInt32:(int32_t)value; 49 | - (id) initWithUInt64:(uint64_t)value; 50 | - (id) initWithInt64:(int64_t)value; 51 | - (id) initWithSignedLittleEndian:(NSData*)data; 52 | - (id) initWithUnsignedBigEndian:(NSData*)data; 53 | - (id) initWithLittleEndianData:(NSData*)data DEPRECATED_ATTRIBUTE; 54 | - (id) initWithUnsignedData:(NSData*)data DEPRECATED_ATTRIBUTE; 55 | 56 | 57 | // Initialized with OpenSSL representation of bignum. 58 | - (id) initWithBIGNUM:(const BIGNUM*)bignum; 59 | 60 | // Inits with setString:base: 61 | - (id) initWithString:(NSString*)string base:(NSUInteger)base; 62 | 63 | // Same as initWithString:base:16 64 | - (id) initWithHexString:(NSString*)hexString DEPRECATED_ATTRIBUTE; 65 | 66 | // Same as initWithString:base:10 67 | - (id) initWithDecimalString:(NSString*)decimalString; 68 | 69 | - (NSString*) stringInBase:(NSUInteger)base; 70 | 71 | // Re-declared copy and mutableCopy to provide exact return type. 72 | - (BTCBigNumber*) copy; 73 | - (BTCMutableBigNumber*) mutableCopy; 74 | 75 | // TODO: maybe add support for hash, figure out what the heck is that. 76 | //void set_hash(hash_digest load_hash); 77 | //hash_digest hash() const; 78 | 79 | // Returns MIN(self, other) 80 | - (BTCBigNumber*) min:(BTCBigNumber*)other; 81 | 82 | // Returns MAX(self, other) 83 | - (BTCBigNumber*) max:(BTCBigNumber*)other; 84 | 85 | 86 | - (BOOL) less:(BTCBigNumber*)other; 87 | - (BOOL) lessOrEqual:(BTCBigNumber*)other; 88 | - (BOOL) greater:(BTCBigNumber*)other; 89 | - (BOOL) greaterOrEqual:(BTCBigNumber*)other; 90 | 91 | 92 | // Divides receiver by another bignum. 93 | // Returns an array of two new BTCBigNumber instances: @[ quotient, remainder ] 94 | - (NSArray*) divmod:(BTCBigNumber*)other; 95 | 96 | // Destroys sensitive data and sets the value to 0. 97 | // It is also called on dealloc. 98 | // This method is available for both mutable and immutable numbers by design. 99 | - (void) clear; 100 | 101 | @end 102 | 103 | 104 | @interface BTCMutableBigNumber : BTCBigNumber 105 | 106 | @property(nonatomic, readwrite) uint32_t compact; // compact representation used for the difficulty target 107 | @property(nonatomic, readwrite) uint32_t uint32value; 108 | @property(nonatomic, readwrite) int32_t int32value; 109 | @property(nonatomic, readwrite) uint64_t uint64value; 110 | @property(nonatomic, readwrite) int64_t int64value; 111 | @property(nonatomic, readwrite) NSString* hexString; 112 | @property(nonatomic, readwrite) NSString* decimalString; 113 | @property(nonatomic, readwrite) NSData* signedLittleEndian; 114 | @property(nonatomic, readwrite) NSData* unsignedBigEndian; 115 | @property(nonatomic, readwrite) NSData* littleEndianData DEPRECATED_ATTRIBUTE; 116 | @property(nonatomic, readwrite) NSData* unsignedData DEPRECATED_ATTRIBUTE; 117 | 118 | @property(nonatomic, readonly) BIGNUM* mutableBIGNUM; 119 | 120 | // BTCBigNumber returns always the same object for these constants. 121 | // BTCMutableBigNumber returns a new object every time. 122 | + (instancetype) zero; // 0 123 | + (instancetype) one; // 1 124 | + (instancetype) negativeOne; // -1 125 | 126 | // Supports bases from 2 to 36. For base 2 allows optional 0b prefix, base 16 allows optional 0x prefix. Spaces are ignored. 127 | - (void) setString:(NSString*)string base:(NSUInteger)base; 128 | 129 | // Operators modify the receiver and return self. 130 | // To create a new instance z = x + y use copy method: z = [[x copy] add:y] 131 | - (instancetype) add:(BTCBigNumber*)other; // += 132 | - (instancetype) add:(BTCBigNumber*)other mod:(BTCBigNumber*)mod; 133 | - (instancetype) subtract:(BTCBigNumber*)other; // -= 134 | - (instancetype) subtract:(BTCBigNumber*)other mod:(BTCBigNumber*)mod; 135 | - (instancetype) multiply:(BTCBigNumber*)other; // *= 136 | - (instancetype) multiply:(BTCBigNumber*)other mod:(BTCBigNumber*)mod; 137 | - (instancetype) divide:(BTCBigNumber*)other; // /= 138 | - (instancetype) mod:(BTCBigNumber*)other; // %= 139 | - (instancetype) lshift:(unsigned int)shift; // <<= 140 | - (instancetype) rshift:(unsigned int)shift; // >>= 141 | - (instancetype) inverseMod:(BTCBigNumber*)mod; // (a^-1) mod n 142 | - (instancetype) exp:(BTCBigNumber*)power; 143 | - (instancetype) exp:(BTCBigNumber*)power mod:(BTCBigNumber *)mod; 144 | 145 | @end 146 | -------------------------------------------------------------------------------- /Graphene/lib/ecc/vendor/BTCData.h: -------------------------------------------------------------------------------- 1 | // CoreBitcoin by Oleg Andreev , WTFPL. 2 | 3 | #import 4 | 5 | // Change to 0 to disable code that requires OpenSSL (if you need some of these routines in your own project and you don't need OpenSSL) 6 | #define BTCDataRequiresOpenSSL 1 7 | 8 | // Securely overwrites memory buffer with a specified character. 9 | void *BTCSecureMemset(void *v, unsigned char c, size_t n); 10 | 11 | // Securely overwrites string with zeros. 12 | void BTCSecureClearCString(char *s); 13 | 14 | // Returns data with securely random bytes of the specified length. Uses /dev/random. 15 | NSMutableData* BTCRandomDataWithLength(NSUInteger length); 16 | 17 | // Returns random string with securely random bytes of the specified length. Uses /dev/random. 18 | // Caller should use free() to release the memory occupied by the buffer. 19 | void *BTCCreateRandomBytesOfLength(size_t length); 20 | 21 | // Returns data produced by flipping the coin as proposed by Dan Kaminsky: 22 | // https://gist.github.com/PaulCapestany/6148566 23 | NSData* BTCCoinFlipDataWithLength(NSUInteger length); 24 | 25 | // Creates data with zero-terminated string in UTF-8 encoding. 26 | NSData* BTCDataWithUTF8CString(const char* utf8cstring); 27 | NSData* BTCDataWithUTF8String(const char* utf8string) DEPRECATED_ATTRIBUTE; // will repurpose for NSString later. 28 | 29 | // Init with hex string (lower- or uppercase, with optional 0x prefix) 30 | NSData* BTCDataFromHex(NSString* hex); 31 | NSData* BTCDataWithHexString(NSString* hexString) DEPRECATED_ATTRIBUTE; 32 | 33 | // Init with zero-terminated hex string (lower- or uppercase, with optional 0x prefix) 34 | NSData* BTCDataWithHexCString(const char* hexString); 35 | 36 | // Converts data to a hex string 37 | NSString* BTCHexFromData(NSData* data); 38 | NSString* BTCUppercaseHexFromData(NSData* data); // more efficient than calling -uppercaseString on a lower-case result. 39 | 40 | // Deprecated. Use BTCHexFromData and BTCUppercaseHexFromData instead. 41 | NSString* BTCHexStringFromData(NSData* data) DEPRECATED_ATTRIBUTE; 42 | NSString* BTCUppercaseHexStringFromData(NSData* data) DEPRECATED_ATTRIBUTE; 43 | 44 | // Returns a copy of data with reversed byte order. 45 | // This is useful in Bitcoin: things get reversed here and there all the time. 46 | NSData* BTCReversedData(NSData* data); 47 | 48 | // Returns a reversed mutable copy so you wouldn't need to make another mutable copy from -reversedData 49 | NSMutableData* BTCReversedMutableData(NSData* data); 50 | 51 | // Reverses byte order in the internal buffer of mutable data object. 52 | void BTCDataReverse(NSMutableData* data); 53 | 54 | // If NSData is NSMutableData clears contents of the data to prevent leaks through swapping or buffer-overflow attacks. Returns YES. 55 | // If NSData is actually an immutable data, does nothing and returns NO. 56 | BOOL BTCDataClear(NSData* data); 57 | 58 | // Returns a subdata with a given range. 59 | // If range is invalid, returns nil. 60 | NSMutableData* BTCDataRange(NSData* data, NSRange range); 61 | 62 | // Core hash functions that we need. 63 | // If the argument is nil, returns nil. 64 | NSMutableData* BTCSHA1(NSData* data); 65 | NSMutableData* BTCSHA256(NSData* data); 66 | NSMutableData* BTCSHA512(NSData* data); 67 | NSMutableData* BTCSHA256Concat(NSData* data1, NSData* data2); // SHA256(data1 || data2) 68 | NSMutableData* BTCHash256(NSData* data); // == SHA256(SHA256(data)) (aka Hash() in BitcoinQT) 69 | NSMutableData* BTCHash256Concat(NSData* data1, NSData* data2); // SHA256(SHA256(data1 || data2)) 70 | 71 | // Standard HMAC-SHA256 and HMAC-SHA512 functions. 72 | NSMutableData* BTCHMACSHA256(NSData* key, NSData* data); 73 | NSMutableData* BTCHMACSHA512(NSData* key, NSData* data); 74 | 75 | #if BTCDataRequiresOpenSSL 76 | // RIPEMD160 today is provided only by OpenSSL. SHA1 and SHA2 are provided by CommonCrypto framework. 77 | NSMutableData* BTCRIPEMD160(NSData* data); 78 | NSMutableData* BTCHash160(NSData* data); // == RIPEMD160(SHA256(data)) (aka Hash160 in BitcoinQT) 79 | #endif 80 | 81 | // 160-bit zero string 82 | NSMutableData* BTCZero160(); 83 | 84 | // 256-bit zero string 85 | NSMutableData* BTCZero256(); 86 | 87 | // Pointer to a static array of zeros (256 bits long). 88 | const unsigned char* BTCZeroString256(); 89 | 90 | 91 | // Hashes input with salt using specified number of rounds and the minimum amount of memory (rounded up to a whole number of 256-bit blocks). 92 | // Actual number of hash function computations is a number of rounds multiplied by a number of 256-bit blocks. 93 | // So rounds=1 for 256 Mb of memory would mean 8M hash function calculations (8M blocks by 32 bytes to form 256 Mb total). 94 | // Uses SHA256 as an internal hash function. 95 | // Password and salt are hashed before being placed in the first block. 96 | // The whole memory region is hashed after all rounds to generate the result. 97 | // Based on proposal by Sergio Demian Lerner http://bitslog.files.wordpress.com/2013/12/memohash-v0-3.pdf 98 | // Returns a mutable data, so you can cleanup the memory when needed. 99 | NSMutableData* BTCMemoryHardKDF256(NSData* password, NSData* salt, unsigned int rounds, unsigned int numberOfBytes); 100 | 101 | 102 | // Hashes input with salt using specified number of rounds and the minimum amount of memory (rounded up to a whole number of 128-bit blocks) 103 | NSMutableData* BTCMemoryHardAESKDF(NSData* password, NSData* salt, unsigned int rounds, unsigned int numberOfBytes); 104 | 105 | // Probabilistic memory-hard KDF with 256-bit output and only one difficulty parameter - amount of memory. 106 | // Actual amount of memory is rounded to a whole number of 256-bit blocks. 107 | // Uses SHA512 as internal hash function. 108 | // Computational time is proportional to amount of memory. 109 | // Brutefore with half the memory raises amount of hash computations quadratically. 110 | NSMutableData* BTCLocustKDF128(NSData* password, NSData* salt, unsigned int numberOfBytes); 111 | NSMutableData* BTCLocustKDF160(NSData* password, NSData* salt, unsigned int numberOfBytes); 112 | NSMutableData* BTCLocustKDF256(NSData* password, NSData* salt, unsigned int numberOfBytes); 113 | NSMutableData* BTCLocustKDF512(NSData* password, NSData* salt, unsigned int numberOfBytes); 114 | 115 | // Makes arbitrary-length output. 116 | NSMutableData* BTCLocustKDF(NSData* password, NSData* salt, unsigned int numberOfBytes, unsigned int outputLength); 117 | -------------------------------------------------------------------------------- /Graphene/ws/GXChainWebSocket.m: -------------------------------------------------------------------------------- 1 | // 2 | // GrapheneWS.m 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/2/25. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import "GXChainWebSocket.h" 10 | #import 11 | @interface GXChainWebSocket() 12 | { 13 | uint32_t cbId; 14 | } 15 | @property (nonatomic,strong) SRWebSocket* socket; 16 | @property (nonatomic,assign) BOOL connected; 17 | @property (nonatomic,readwrite,copy) void(^connctCallback)(BOOL connected,NSString* status); 18 | @property (atomic,strong) NSMutableDictionary* callbackMaps; 19 | @property (atomic,strong) NSMutableDictionary* broadcastCallbackMaps; 20 | @property (nonatomic,strong) NSTimer* timeoutTimer; 21 | @property (nonatomic,assign) BOOL connectTimeout; 22 | @end 23 | 24 | @implementation GXChainWebSocket 25 | -(instancetype)initWithAddress:(NSString*)address{ 26 | self=[self init]; 27 | cbId=0; 28 | self.callbackMaps=[NSMutableDictionary dictionary]; 29 | self.broadcastCallbackMaps=[NSMutableDictionary dictionary]; 30 | self.socket=[[SRWebSocket alloc] initWithURL:[NSURL URLWithString:address]]; 31 | self.socket.delegate=self; 32 | [self.socket open]; 33 | return self; 34 | } 35 | 36 | #pragma mark - custom methods 37 | -(void)timeout{ 38 | [self.timeoutTimer invalidate]; 39 | self.timeoutTimer=nil; 40 | if(!self.connectTimeout){ 41 | self.connectTimeout=YES; 42 | self.connctCallback(NO, @"timeout"); 43 | } 44 | } 45 | 46 | -(void)connect:(void(^)(BOOL connected,NSString* status))callback timeout:(NSTimeInterval)timeout{ 47 | self.connctCallback=callback; 48 | self.connectTimeout=NO; 49 | self.timeoutTimer=[NSTimer scheduledTimerWithTimeInterval:timeout target:self selector:@selector(timeout) userInfo:nil repeats:NO]; 50 | if(self.connected && self.connctCallback){ 51 | self.connctCallback(YES,@"open"); 52 | } 53 | else{ 54 | [self.socket open]; 55 | } 56 | } 57 | 58 | -(void)call:(NSArray*)params callback:(void (^)(NSError * error, id response))callback{ 59 | if(!self.connected){ 60 | callback([NSError errorWithDomain:@"GrapheneWS" code:-1 userInfo:nil],nil); 61 | } 62 | else{ 63 | NSString* _cbId = [NSString stringWithFormat:@"%u",++cbId]; 64 | [self.callbackMaps setObject:callback forKey:_cbId]; 65 | NSString* method = params[1]; 66 | if([method isEqualToString:@"broadcast_transaction_with_callback"]){ 67 | id bradcast_callback = params[2][0]; 68 | [self.broadcastCallbackMaps setObject:bradcast_callback forKey:_cbId]; 69 | NSMutableArray* mutableParams= params.mutableCopy; 70 | mutableParams[2]=@[_cbId,params[2][1]]; 71 | params=mutableParams; 72 | } 73 | 74 | NSLog(@"sending: ===========\n %@ \n===========", params); 75 | 76 | NSDictionary* request=@{@"method":@"call",@"params":params,@"id":_cbId}; 77 | NSError *error; 78 | NSData *jsonData = [NSJSONSerialization dataWithJSONObject:request 79 | options:0 80 | error:&error]; 81 | if (!jsonData) { 82 | NSLog(@"Got an error: %@", error); 83 | callback(error,nil); 84 | } 85 | else{ 86 | [self.socket send:jsonData]; 87 | } 88 | } 89 | } 90 | 91 | -(void)login:(NSString*)user password:(NSString*)password callback:(void (^)(NSError * error, id response))callback{ 92 | if(self.connected){ 93 | [self call:@[@1,@"login",@[user,password]] callback:^(NSError *error, id response) { 94 | callback(error,response); 95 | }]; 96 | } else{ 97 | callback([NSError errorWithDomain:@"GrapheneWS" code:-1 userInfo:nil],nil); 98 | } 99 | } 100 | 101 | -(void)close{ 102 | [self.socket close]; 103 | self.connctCallback=nil; 104 | } 105 | 106 | #pragma mark - websocket delegate methods 107 | - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message{ 108 | NSLog(@"\n>>%@\n",message); 109 | NSError* error; 110 | NSDictionary *string2dic = [NSJSONSerialization JSONObjectWithData: [message dataUsingEncoding:NSUTF8StringEncoding] 111 | options: NSJSONReadingMutableContainers 112 | error: &error]; 113 | if([string2dic objectForKey:@"id"]){ 114 | NSString* _cbId = [[string2dic objectForKey:@"id"] stringValue]; 115 | void(^cb)(NSError*,id result) = [self.callbackMaps objectForKey:_cbId]; 116 | if(cb){ 117 | cb(nil,string2dic[@"result"]); 118 | [self.callbackMaps removeObjectForKey:_cbId]; 119 | } 120 | } 121 | else if([[string2dic objectForKey:@"method"] isEqualToString:@"notice"]){ 122 | NSString* _cbId = [string2dic[@"params"][0] stringValue]; 123 | void(^cb)(NSError*,id result) = [self.broadcastCallbackMaps objectForKey:_cbId]; 124 | if(cb){ 125 | cb(nil,string2dic[@"params"][1]); 126 | [self.broadcastCallbackMaps removeObjectForKey:_cbId]; 127 | } 128 | } 129 | } 130 | 131 | - (void)webSocketDidOpen:(SRWebSocket *)webSocket{ 132 | self.connected=YES; 133 | [self.timeoutTimer invalidate]; 134 | if(self.connctCallback && !self.connectTimeout){ 135 | self.connctCallback(YES,@"open"); 136 | } 137 | } 138 | 139 | - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error{ 140 | self.connected=NO; 141 | [self.timeoutTimer invalidate]; 142 | [webSocket close]; 143 | NSLog(@"socket connect failed:%@",error.localizedDescription); 144 | if(self.connctCallback && !self.connectTimeout){ 145 | self.connctCallback(NO,@"error"); 146 | } 147 | } 148 | 149 | - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean{ 150 | self.connected=NO; 151 | [self.timeoutTimer invalidate]; 152 | NSLog(@"socket connect lost:%@,%ld,%d",reason,(long)code,wasClean); 153 | if(self.connctCallback && !self.connectTimeout){ 154 | self.connctCallback(NO,@"closed"); 155 | } 156 | } 157 | 158 | - (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload{ 159 | [self.timeoutTimer invalidate]; 160 | NSLog(@"pong:%@",[[NSString alloc] initWithData:pongPayload encoding:NSUTF8StringEncoding]); 161 | } 162 | @end 163 | 164 | -------------------------------------------------------------------------------- /Graphene/category/NSData+Base64.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+Base64.m 3 | // 4 | // Version 1.0.2 5 | // 6 | // Created by Nick Lockwood on 12/01/2012. 7 | // Copyright (C) 2012 Charcoal Design 8 | // 9 | // Distributed under the permissive zlib License 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/nicklockwood/Base64 13 | // 14 | // This software is provided 'as-is', without any express or implied 15 | // warranty. In no event will the authors be held liable for any damages 16 | // arising from the use of this software. 17 | // 18 | // Permission is granted to anyone to use this software for any purpose, 19 | // including commercial applications, and to alter it and redistribute it 20 | // freely, subject to the following restrictions: 21 | // 22 | // 1. The origin of this software must not be misrepresented; you must not 23 | // claim that you wrote the original software. If you use this software 24 | // in a product, an acknowledgment in the product documentation would be 25 | // appreciated but is not required. 26 | // 27 | // 2. Altered source versions must be plainly marked as such, and must not be 28 | // misrepresented as being the original software. 29 | // 30 | // 3. This notice may not be removed or altered from any source distribution. 31 | // 32 | 33 | #import "NSData+Base64.h" 34 | 35 | @implementation NSData (Base64) 36 | 37 | - (NSData*)formatWithData:(NSData*)data{ 38 | if (data && [data length]>76) { 39 | 40 | }return data; 41 | } 42 | 43 | + (NSData *)dataWithBase64EncodedString:(NSString *)string 44 | { 45 | const char lookup[] = 46 | { 47 | 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 48 | 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 49 | 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 62, 99, 99, 99, 63, 50 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 99, 99, 99, 99, 99, 99, 51 | 99, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 52 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 99, 99, 99, 99, 99, 53 | 99, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 54 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 99, 99, 99, 99, 99 55 | }; 56 | 57 | NSData *inputData = [string dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; 58 | long long inputLength = [inputData length]; 59 | const unsigned char *inputBytes = [inputData bytes]; 60 | 61 | long long maxOutputLength = (inputLength / 4 + 1) * 3; 62 | NSMutableData *outputData = [NSMutableData dataWithLength:maxOutputLength]; 63 | unsigned char *outputBytes = (unsigned char *)[outputData mutableBytes]; 64 | 65 | int accumulator = 0; 66 | long long outputLength = 0; 67 | unsigned char accumulated[] = {0, 0, 0, 0}; 68 | for (long long i = 0; i < inputLength; i++) 69 | { 70 | unsigned char decoded = lookup[inputBytes[i] & 0x7F]; 71 | if (decoded != 99) 72 | { 73 | accumulated[accumulator] = decoded; 74 | if (accumulator == 3) 75 | { 76 | outputBytes[outputLength++] = (accumulated[0] << 2) | (accumulated[1] >> 4); 77 | outputBytes[outputLength++] = (accumulated[1] << 4) | (accumulated[2] >> 2); 78 | outputBytes[outputLength++] = (accumulated[2] << 6) | accumulated[3]; 79 | } 80 | accumulator = (accumulator + 1) % 4; 81 | } 82 | } 83 | 84 | //handle left-over data 85 | if (accumulator > 0) outputBytes[outputLength] = (accumulated[0] << 2) | (accumulated[1] >> 4); 86 | if (accumulator > 1) outputBytes[++outputLength] = (accumulated[1] << 4) | (accumulated[2] >> 2); 87 | if (accumulator > 2) outputLength++; 88 | 89 | //truncate data to match actual output length 90 | outputData.length = outputLength; 91 | return outputLength? outputData: nil; 92 | } 93 | 94 | - (NSString *)base64EncodedStringWithWrapWidth:(NSUInteger)wrapWidth 95 | { 96 | //ensure wrapWidth is a multiple of 4 97 | wrapWidth = (wrapWidth / 4) * 4; 98 | 99 | const char lookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 100 | 101 | long long inputLength = [self length]; 102 | const unsigned char *inputBytes = [self bytes]; 103 | 104 | long long maxOutputLength = (inputLength / 3 + 1) * 4; 105 | maxOutputLength += wrapWidth? (maxOutputLength / wrapWidth) * 2: 0; 106 | unsigned char *outputBytes = (unsigned char *)malloc(maxOutputLength); 107 | 108 | long long i; 109 | long long outputLength = 0; 110 | for (i = 0; i < inputLength - 2; i += 3) 111 | { 112 | outputBytes[outputLength++] = lookup[(inputBytes[i] & 0xFC) >> 2]; 113 | outputBytes[outputLength++] = lookup[((inputBytes[i] & 0x03) << 4) | ((inputBytes[i + 1] & 0xF0) >> 4)]; 114 | outputBytes[outputLength++] = lookup[((inputBytes[i + 1] & 0x0F) << 2) | ((inputBytes[i + 2] & 0xC0) >> 6)]; 115 | outputBytes[outputLength++] = lookup[inputBytes[i + 2] & 0x3F]; 116 | 117 | //add line break 118 | if (wrapWidth && (outputLength + 2) % (wrapWidth + 2) == 0) 119 | { 120 | outputBytes[outputLength++] = '\r'; 121 | outputBytes[outputLength++] = '\n'; 122 | } 123 | } 124 | 125 | //handle left-over data 126 | if (i == inputLength - 2) 127 | { 128 | // = terminator 129 | outputBytes[outputLength++] = lookup[(inputBytes[i] & 0xFC) >> 2]; 130 | outputBytes[outputLength++] = lookup[((inputBytes[i] & 0x03) << 4) | ((inputBytes[i + 1] & 0xF0) >> 4)]; 131 | outputBytes[outputLength++] = lookup[(inputBytes[i + 1] & 0x0F) << 2]; 132 | outputBytes[outputLength++] = '='; 133 | } 134 | else if (i == inputLength - 1) 135 | { 136 | // == terminator 137 | outputBytes[outputLength++] = lookup[(inputBytes[i] & 0xFC) >> 2]; 138 | outputBytes[outputLength++] = lookup[(inputBytes[i] & 0x03) << 4]; 139 | outputBytes[outputLength++] = '='; 140 | outputBytes[outputLength++] = '='; 141 | } 142 | 143 | //truncate data to match actual output length 144 | outputBytes = realloc(outputBytes, outputLength); 145 | NSString *result = [[NSString alloc] initWithBytesNoCopy:outputBytes length:outputLength encoding:NSASCIIStringEncoding freeWhenDone:YES]; 146 | 147 | #if !__has_feature(objc_arc) 148 | [result autorelease]; 149 | #endif 150 | 151 | return (outputLength >= 4)? result: nil; 152 | } 153 | 154 | - (NSString *)base64EncodedString 155 | { 156 | return [self base64EncodedStringWithWrapWidth:0]; 157 | } 158 | 159 | @end 160 | -------------------------------------------------------------------------------- /Graphene/lib/ecc/vendor/BTCCurvePoint.m: -------------------------------------------------------------------------------- 1 | // Oleg Andreev 2 | 3 | #import "BTCCurvePoint.h" 4 | //#import "BTCKey.h" 5 | #import "BTCData.h" 6 | #import "BTCBigNumber.h" 7 | #include 8 | #include 9 | #include 10 | 11 | @implementation BTCCurvePoint { 12 | EC_GROUP* _group; 13 | EC_POINT* _point; 14 | BN_CTX* _bnctx; 15 | } 16 | 17 | - (void) dealloc { 18 | if (_point) EC_POINT_clear_free(_point); 19 | _point = NULL; 20 | 21 | if (_group) EC_GROUP_free(_group); 22 | _group = NULL; 23 | 24 | if (_bnctx) BN_CTX_free(_bnctx); 25 | _bnctx = NULL; 26 | } 27 | 28 | + (instancetype) generator { 29 | return [[self alloc] init]; 30 | } 31 | 32 | + (BTCBigNumber*) curveOrder { 33 | static BTCBigNumber* order; 34 | static dispatch_once_t onceToken; 35 | dispatch_once(&onceToken, ^{ 36 | order = [[BTCBigNumber alloc] initWithUnsignedBigEndian:BTCDataWithHexCString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141")]; 37 | }); 38 | return order; 39 | } 40 | 41 | - (id) initEmpty { 42 | if (self = [super init]) { 43 | _group = NULL; 44 | _point = NULL; 45 | _bnctx = NULL; 46 | 47 | _group = EC_GROUP_new_by_curve_name(NID_secp256k1); 48 | if (!_group) { 49 | NSLog(@"BTCCurvePoint: EC_GROUP_new_by_curve_name(NID_secp256k1) failed"); 50 | goto finish; 51 | } 52 | 53 | _point = EC_POINT_new(_group); 54 | if (!_point) { 55 | NSLog(@"BTCCurvePoint: EC_POINT_new(_group) failed"); 56 | goto finish; 57 | } 58 | 59 | _bnctx = BN_CTX_new(); 60 | if (!_bnctx) { 61 | NSLog(@"BTCCurvePoint: BN_CTX_new() failed"); 62 | goto finish; 63 | } 64 | 65 | return self; 66 | 67 | finish: 68 | if (_group) EC_GROUP_free(_group); 69 | if (_point) EC_POINT_clear_free(_point); 70 | 71 | return nil; 72 | } 73 | return self; 74 | } 75 | 76 | - (id) init { 77 | if (self = [self initEmpty]) { 78 | if (!EC_POINT_copy(_point, EC_GROUP_get0_generator(_group))) { 79 | return nil; 80 | } 81 | } 82 | return self; 83 | } 84 | 85 | // Initializes point with its binary representation (corresponds to -data). 86 | - (id) initWithData:(NSData*)data { 87 | if (self = [self initEmpty]) { 88 | 89 | BIGNUM* bn = BN_bin2bn(data.bytes, (int)data.length, NULL); 90 | if (!bn) { 91 | return nil; 92 | } 93 | 94 | if (!EC_POINT_bn2point(_group, bn, _point, _bnctx)) { 95 | if (bn) BN_clear_free(bn); 96 | return nil; 97 | } 98 | 99 | // Point is imported, only need to cleanup an intermediate BIGNUM structure. 100 | if (bn) BN_clear_free(bn); 101 | } 102 | return self; 103 | } 104 | 105 | // Initializes point with OpenSSL 106 | - (id) initWithEC_POINT:(const EC_POINT*)ecpoint { 107 | if (self = [self initEmpty]) { 108 | if (!EC_POINT_copy(_point, ecpoint)) { 109 | return nil; 110 | } 111 | } 112 | return self; 113 | } 114 | 115 | - (NSData*) data { 116 | NSMutableData* data = [NSMutableData dataWithLength:33]; 117 | 118 | BIGNUM* bn = BN_new(); 119 | 120 | if (!bn) { 121 | return nil; 122 | } 123 | 124 | if (!EC_POINT_point2bn(_group, _point, POINT_CONVERSION_COMPRESSED, bn, _bnctx)) { 125 | if (bn) BN_clear_free(bn); 126 | return nil; 127 | } 128 | 129 | NSAssert(BN_num_bytes(bn) == 33, @"compressed point must be 33 bytes long"); 130 | 131 | BN_bn2bin(bn, data.mutableBytes); 132 | 133 | if (bn) BN_clear_free(bn); 134 | 135 | return data; 136 | } 137 | 138 | - (const EC_POINT*) EC_POINT { 139 | return _point; 140 | } 141 | 142 | // These modify the receiver. To create another point use -copy: [[point copy] multiply:number] 143 | - (instancetype) multiply:(BTCBigNumber*)number { 144 | if (!number) return nil; 145 | 146 | if (!EC_POINT_mul(_group, _point, NULL, _point, number.BIGNUM, _bnctx)) { 147 | return nil; 148 | } 149 | return self; 150 | } 151 | 152 | - (instancetype) add:(BTCCurvePoint*)otherPoint { 153 | if (!otherPoint) return nil; 154 | 155 | if (!EC_POINT_add(_group, _point, _point, otherPoint.EC_POINT, _bnctx)) { 156 | return nil; 157 | } 158 | return self; 159 | } 160 | 161 | // Efficiently adds n*G to the receiver. Equivalent to [point add:[[G copy] multiply:number]] 162 | - (instancetype) addGeneratorMultipliedBy:(BTCBigNumber*)number { 163 | if (!number) return nil; 164 | 165 | if (!EC_POINT_mul(_group, _point, number.BIGNUM, _point, BN_value_one(), _bnctx)) { 166 | return nil; 167 | } 168 | 169 | return self; 170 | } 171 | 172 | - (BOOL) isInfinity { 173 | return 1 == EC_POINT_is_at_infinity(_group, _point); 174 | } 175 | 176 | - (BTCBigNumber*) x { 177 | BN_CTX_start(_bnctx); 178 | BIGNUM* bn = BN_CTX_get(_bnctx); 179 | if (!EC_POINT_get_affine_coordinates_GFp(_group, _point, bn /* x */, NULL /* y */, _bnctx)) { 180 | BN_CTX_end(_bnctx); 181 | return nil; 182 | } 183 | BTCBigNumber* result = [[BTCBigNumber alloc] initWithBIGNUM:bn]; 184 | BN_CTX_end(_bnctx); 185 | return result; 186 | } 187 | 188 | - (BTCBigNumber*) y { 189 | BN_CTX_start(_bnctx); 190 | BIGNUM* bn = BN_CTX_get(_bnctx); 191 | if (!EC_POINT_get_affine_coordinates_GFp(_group, _point, NULL /* x */, bn /* y */, _bnctx)) { 192 | BN_CTX_end(_bnctx); 193 | return nil; 194 | } 195 | BTCBigNumber* result = [[BTCBigNumber alloc] initWithBIGNUM:bn]; 196 | BN_CTX_end(_bnctx); 197 | return result; 198 | } 199 | 200 | // Clears internal point data. 201 | - (void) clear { 202 | if (_point) EC_POINT_clear_free(_point); 203 | _point = NULL; 204 | } 205 | 206 | 207 | 208 | #pragma mark - NSObject & NSCopying 209 | 210 | 211 | // Re-declared copy to provide exact return type. 212 | - (BTCCurvePoint*) copy { 213 | return [self copyWithZone:nil]; 214 | } 215 | 216 | - (id) copyWithZone:(NSZone *)zone { 217 | return [[BTCCurvePoint alloc] initWithEC_POINT:_point]; 218 | } 219 | 220 | - (BOOL) isEqual:(BTCCurvePoint*)otherPoint { 221 | if (![otherPoint isKindOfClass:[self class]]) return NO; 222 | return 0 == EC_POINT_cmp(_group, _point, otherPoint.EC_POINT, _bnctx); 223 | } 224 | 225 | - (NSUInteger) hash { 226 | return self.data.hash; 227 | } 228 | 229 | - (NSString*) description { 230 | return [NSString stringWithFormat:@"", self, BTCHexFromData(self.data)]; 231 | } 232 | 233 | 234 | @end 235 | -------------------------------------------------------------------------------- /Graphene/lib/chain/GXTransactionBuilder.m: -------------------------------------------------------------------------------- 1 | // 2 | // GXTransactionBuilder.m 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/2/27. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import "GXTransactionBuilder.h" 10 | #import "NSArray+Expand.h" 11 | #import "NSDictionary+Expand.h" 12 | #import "GXChainConfig.h" 13 | #import "NSMutableData+ProtoBuff.h" 14 | #import "GXUtil.h" 15 | 16 | @interface GXTransactionBuilder() 17 | @property(nonatomic,strong) NSMutableArray* signer_private_keys; 18 | @end 19 | 20 | @implementation GXTransactionBuilder 21 | -(instancetype)initWithOperations:(NSArray*)operations{ 22 | self = [self init]; 23 | self.operations=operations; 24 | self.signer_private_keys=[NSMutableArray array]; 25 | return self; 26 | } 27 | 28 | -(void)add_signer:(GXPrivateKey*)private_key{ 29 | [self.signer_private_keys addObject:private_key]; 30 | } 31 | 32 | -(void)sign{ 33 | NSString* chain_id = [[GXApiInstances sharedInstance] chain_id]; 34 | if(chain_id == nil){ 35 | chain_id = GX_DEFAULT_CHAIN_ID; 36 | } 37 | NSMutableData* data=[NSMutableData data]; 38 | [data appendData:BTCDataFromHex(chain_id)]; 39 | [data appendData:[self serialize]]; 40 | NSLog(@"%@",BTCHexFromData([self serialize])); 41 | NSLog(@"%@",BTCHexFromData(data)); 42 | NSMutableArray* signatures = [NSMutableArray array]; 43 | [_signer_private_keys enumerateObjectsUsingBlock:^(GXPrivateKey * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 44 | NSData* signature=[obj sign:data]; 45 | [signatures addObject:signature]; 46 | }]; 47 | self.signatures=signatures; 48 | } 49 | 50 | -(void)setRequiredFees:(void(^)(void))callback{ 51 | NSMutableArray* operations = [NSMutableArray array]; 52 | NSString* asset_id = [(GXBaseOperation*)[self.operations objectAtIndex:0] fee].asset_id; 53 | 54 | [self.operations enumerateObjectsUsingBlock:^(GXBaseOperation * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 55 | [operations addObject:[obj operation]]; 56 | }]; 57 | [[GXApiInstances sharedInstance]._db exec:@"get_required_fees" params:@[operations,asset_id] callback:^(NSError *err, id resp) { 58 | __block int index=0; 59 | [self.operations enumerateObjectsUsingBlock:^(GXBaseOperation * _Nonnull op, NSUInteger idx, BOOL * _Nonnull stop) { 60 | [self setFee:resp[index++] forOperation:op]; 61 | SEL sel_proposed_ops = NSSelectorFromString(@"proposed_ops"); 62 | if([op respondsToSelector:sel_proposed_ops]){ 63 | NSArray* proposed_ops = [op performSelector:sel_proposed_ops]; 64 | [proposed_ops enumerateObjectsUsingBlock:^(GXBaseOperation * _Nonnull pop, NSUInteger idx, BOOL * _Nonnull stop) { 65 | [self setFee:resp[index++] forOperation:pop]; 66 | }]; 67 | } 68 | }]; 69 | if(callback){ 70 | callback(); 71 | } 72 | }]; 73 | } 74 | 75 | -(void)setFee:(NSDictionary*)fee forOperation:(GXBaseOperation*)op{ 76 | op.fee.amount=[[fee objectForKey:@"amount"] longValue]; 77 | op.fee.asset_id=[fee objectForKey:@"asset_id"]; 78 | } 79 | 80 | -(void)setBlockHeader:(void(^)(void))callback{ 81 | __block GXTransactionBuilder* _self = self; 82 | [[GXApiInstances sharedInstance]._db exec:@"get_objects" params:@[@[@"2.1.0"]] callback:^(NSError *err, id resp) { 83 | NSDate* time = [_self dateFromUTCString:[[resp objectAtIndex:0] objectForKey:@"time"]]; 84 | _self.expiration=[time timeIntervalSince1970]+GX_EXPIRE_IN_SECOND; 85 | _self.ref_block_num = [resp[0][@"head_block_number"] longValue] & 0xFFFF; 86 | NSData* head_block_id = BTCDataFromHex([[resp objectAtIndex:0] objectForKey:@"head_block_id"]); 87 | uint32_t ref_block_prefix; 88 | const size_t length = sizeof(ref_block_prefix); 89 | Byte byte[length] = {}; 90 | [head_block_id getBytes:byte range:NSMakeRange(4, 4+length)]; 91 | ref_block_prefix = (uint32_t) (((byte[0] & 0xFF)) 92 | | ((byte[1] & 0xFF)<<8) 93 | | ((byte[2] & 0xFF)<<16) 94 | | (byte[3] & 0xFF)<<24); 95 | _self.ref_block_prefix = ref_block_prefix; 96 | if(callback){ 97 | callback(); 98 | } 99 | }]; 100 | } 101 | -(NSData *)dataWithHexString:(NSString *)hexString { 102 | const char *chars = [hexString UTF8String]; 103 | int i = 0; 104 | NSUInteger len = hexString.length; 105 | 106 | NSMutableData *data = [NSMutableData dataWithCapacity:len / 2]; 107 | char byteChars[3] = {'\0','\0','\0'}; 108 | unsigned long wholeByte; 109 | 110 | while (i < len) { 111 | byteChars[0] = chars[i++]; 112 | byteChars[1] = chars[i++]; 113 | wholeByte = strtoul(byteChars, NULL, 16); 114 | [data appendBytes:&wholeByte length:1]; 115 | } 116 | return data; 117 | } 118 | 119 | -(NSDate*)dateFromUTCString:(NSString*)dateStr{ 120 | NSDateFormatter* df = [[NSDateFormatter alloc] init]; 121 | NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"]; 122 | [df setTimeZone:timeZone]; 123 | [df setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss"]; 124 | return [df dateFromString:dateStr]; 125 | } 126 | 127 | -(NSString*)utcStringFromDate:(NSDate*)date{ 128 | NSDateFormatter* df = [[NSDateFormatter alloc] init]; 129 | NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"]; 130 | [df setTimeZone:timeZone]; 131 | [df setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss"]; 132 | return [df stringFromDate:date]; 133 | } 134 | 135 | -(void)broadcast:(void(^)(NSError *err,NSDictionary* tx))callback{ 136 | [[GXApiInstances sharedInstance]._net exec:@"broadcast_transaction_with_callback" params:@[^(NSError *err, id resp) { 137 | if(err){ 138 | NSLog(@"%@",err.description); 139 | } 140 | callback(err,resp); 141 | },[self signedTransaction]] callback:^(NSError *err, id resp) { 142 | NSLog(@"boradcasted:%@",resp); 143 | }]; 144 | } 145 | 146 | -(void)processTransaction:(void(^)(NSError *err,NSDictionary* tx))callback broadcast:(BOOL)broadcast{ 147 | if(_signer_private_keys==nil||_signer_private_keys.count==0){ 148 | [[NSException exceptionWithName:@"Process transaction" reason:@"no signer key" userInfo:nil] raise]; 149 | } 150 | __weak GXTransactionBuilder* weakSelf = self; 151 | [self setRequiredFees:^(){ 152 | __strong GXTransactionBuilder* strongSelf = weakSelf; 153 | __weak GXTransactionBuilder* weakSelf = strongSelf; 154 | [strongSelf setBlockHeader:^(){ 155 | __strong GXTransactionBuilder* strongSelf = weakSelf; 156 | if (broadcast) { 157 | [strongSelf broadcast:callback]; 158 | } 159 | else{ 160 | callback(nil,[strongSelf signedTransaction]); 161 | } 162 | }]; 163 | }]; 164 | } 165 | 166 | 167 | -(NSDictionary*)signedTransaction{ 168 | [self sign]; 169 | NSMutableDictionary* tx=[self dictionaryValue].mutableCopy; 170 | NSMutableArray* signatures = [NSMutableArray array]; 171 | [self.signatures enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 172 | [signatures addObject:BTCHexFromData(obj)]; 173 | }]; 174 | [tx setObject:signatures forKey:@"signatures"]; 175 | return tx; 176 | } 177 | 178 | #pragma mark - serialize delegate methods 179 | -(NSData*)serialize{ 180 | NSDictionary* dict = [self dictionaryValue]; 181 | NSString* result= [GXUtil serialize_transaction:dict]; 182 | return BTCDataFromHex(result); 183 | } 184 | 185 | -(NSArray*)sortedOperations{ 186 | return [_operations sortedArrayUsingComparator:^NSComparisonResult(GXBaseOperation* _Nonnull op1, GXBaseOperation* _Nonnull op2) { 187 | return op1.operation_id-op2.operation_id; 188 | }]; 189 | } 190 | 191 | -(NSDictionary *)dictionaryValue{ 192 | NSDate* _exp = [NSDate dateWithTimeIntervalSince1970:_expiration]; 193 | NSMutableDictionary* result=@{ 194 | @"ref_block_num":@(_ref_block_num), 195 | @"ref_block_prefix":@(_ref_block_prefix), 196 | @"expiration":[self utcStringFromDate:_exp] 197 | }.mutableCopy; 198 | NSMutableArray* ops=[NSMutableArray array]; 199 | if(_operations){ 200 | _operations=[self sortedOperations]; 201 | [_operations enumerateObjectsUsingBlock:^(GXBaseOperation * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 202 | [ops addObject:[obj operation]]; 203 | }]; 204 | } 205 | [result setObject:ops forKey:@"operations"]; 206 | if(!_extensions){ 207 | _extensions=@[]; 208 | } 209 | NSMutableArray* exts = [NSMutableArray array]; 210 | [_extensions enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 211 | if([obj respondsToSelector:NSSelectorFromString(@"dictionaryValue")]){ 212 | [exts addObject:[obj performSelector:NSSelectorFromString(@"dictionaryValue")]]; 213 | } 214 | else{ 215 | NSLog(@"Unknow extension object,%@", obj); 216 | } 217 | }]; 218 | [result setObject:exts forKey:@"extensions"]; 219 | return result; 220 | } 221 | @end 222 | -------------------------------------------------------------------------------- /Graphene/lib/ecc/vendor/BTCBase58.m: -------------------------------------------------------------------------------- 1 | // Oleg Andreev 2 | 3 | #import "BTCBase58.h" 4 | #import "BTCData.h" 5 | #import 6 | 7 | static const char* BTCBase58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; 8 | 9 | NSMutableData* BTCDataFromBase58(NSString* string) { 10 | return BTCDataFromBase58CString([string cStringUsingEncoding:NSASCIIStringEncoding]); 11 | } 12 | 13 | NSMutableData* BTCDataFromBase58Check(NSString* string) { 14 | return BTCDataFromBase58CheckCString([string cStringUsingEncoding:NSASCIIStringEncoding]); 15 | } 16 | 17 | NSMutableData* BTCDataFromBase58CheckRIPEMD160(NSString* string) { 18 | return BTCDataFromBase58CheckCStringRIPEMD160([string cStringUsingEncoding:NSASCIIStringEncoding]); 19 | } 20 | 21 | NSMutableData* BTCDataFromBase58CString(const char* cstring) { 22 | if (cstring == NULL) return nil; 23 | 24 | // empty string -> empty data. 25 | if (cstring[0] == '\0') return [NSMutableData data]; 26 | 27 | NSMutableData* result = nil; 28 | 29 | BN_CTX* pctx = BN_CTX_new(); 30 | __block BIGNUM bn58; BN_init(&bn58); BN_set_word(&bn58, 58); 31 | __block BIGNUM bn; BN_init(&bn); BN_zero(&bn); 32 | __block BIGNUM bnChar; BN_init(&bnChar); 33 | 34 | void(^finish)() = ^{ 35 | if (pctx) BN_CTX_free(pctx); 36 | BN_clear_free(&bn58); 37 | BN_clear_free(&bn); 38 | BN_clear_free(&bnChar); 39 | }; 40 | 41 | while (isspace(*cstring)) cstring++; 42 | 43 | 44 | // Convert big endian string to bignum 45 | for (const char* p = cstring; *p; p++) { 46 | const char* p1 = strchr(BTCBase58Alphabet, *p); 47 | if (p1 == NULL) { 48 | while (isspace(*p)) 49 | p++; 50 | if (*p != '\0') { 51 | finish(); 52 | return nil; 53 | } 54 | break; 55 | } 56 | 57 | BN_set_word(&bnChar, (BN_ULONG)(p1 - BTCBase58Alphabet)); 58 | 59 | if (!BN_mul(&bn, &bn, &bn58, pctx)) { 60 | finish(); 61 | return nil; 62 | } 63 | 64 | if (!BN_add(&bn, &bn, &bnChar)) { 65 | finish(); 66 | return nil; 67 | } 68 | } 69 | 70 | // Get bignum as little endian data 71 | 72 | NSMutableData* bndata = nil; 73 | { 74 | size_t bnsize = BN_bn2mpi(&bn, NULL); 75 | if (bnsize <= 4) { 76 | bndata = [NSMutableData data]; 77 | } else { 78 | bndata = [NSMutableData dataWithLength:bnsize]; 79 | BN_bn2mpi(&bn, bndata.mutableBytes); 80 | [bndata replaceBytesInRange:NSMakeRange(0, 4) withBytes:NULL length:0]; 81 | BTCDataReverse(bndata); 82 | } 83 | } 84 | size_t bnsize = bndata.length; 85 | 86 | // Trim off sign byte if present 87 | if (bnsize >= 2 88 | && ((unsigned char*)bndata.bytes)[bnsize - 1] == 0 89 | && ((unsigned char*)bndata.bytes)[bnsize - 2] >= 0x80) { 90 | bnsize -= 1; 91 | [bndata setLength:bnsize]; 92 | } 93 | 94 | // Restore leading zeros 95 | int nLeadingZeros = 0; 96 | for (const char* p = cstring; *p == BTCBase58Alphabet[0]; p++) 97 | nLeadingZeros++; 98 | 99 | result = [NSMutableData dataWithLength:nLeadingZeros + bnsize]; 100 | 101 | // Copy the bignum to the beginning of array. We'll reverse it then and zeros will become leading zeros. 102 | [result replaceBytesInRange:NSMakeRange(0, bnsize) withBytes:bndata.bytes length:bnsize]; 103 | 104 | // Convert little endian data to big endian 105 | BTCDataReverse(result); 106 | 107 | finish(); 108 | 109 | return result; 110 | } 111 | 112 | NSMutableData* BTCDataFromBase58CheckCString(const char* cstring) { 113 | if (cstring == NULL) return nil; 114 | 115 | NSMutableData* result = BTCDataFromBase58CString(cstring); 116 | size_t length = result.length; 117 | if (length < 4) { 118 | return nil; 119 | } 120 | NSData* hash = BTCHash256([result subdataWithRange:NSMakeRange(0, length - 4)]); 121 | 122 | // Last 4 bytes should be equal first 4 bytes of the hash. 123 | if (memcmp(hash.bytes, result.bytes + length - 4, 4) != 0) { 124 | return nil; 125 | } 126 | [result setLength:length - 4]; 127 | return result; 128 | } 129 | 130 | NSMutableData* BTCDataFromBase58CheckCStringRIPEMD160(const char* cstring) { 131 | if (cstring == NULL) return nil; 132 | 133 | NSMutableData* result = BTCDataFromBase58CString(cstring); 134 | size_t length = result.length; 135 | if (length < 4) { 136 | return nil; 137 | } 138 | // GRAPHENE uses RIPEMD160 instead of SHA256 in public key 139 | NSData* hash = BTCRIPEMD160([result subdataWithRange:NSMakeRange(0, length - 4)]); 140 | 141 | // Last 4 bytes should be equal first 4 bytes of the hash. 142 | if (memcmp(hash.bytes, result.bytes + length - 4, 4) != 0) { 143 | return nil; 144 | } 145 | [result setLength:length - 4]; 146 | return result; 147 | } 148 | 149 | 150 | char* BTCBase58CStringWithData(NSData* data) { 151 | if (!data) return NULL; 152 | 153 | BN_CTX* pctx = BN_CTX_new(); 154 | __block BIGNUM bn58; BN_init(&bn58); BN_set_word(&bn58, 58); 155 | __block BIGNUM bn0; BN_init(&bn0); BN_zero(&bn0); 156 | __block BIGNUM bn; BN_init(&bn); BN_zero(&bn); 157 | __block BIGNUM dv; BN_init(&dv); BN_zero(&dv); 158 | __block BIGNUM rem; BN_init(&rem); BN_zero(&rem); 159 | 160 | void(^finish)() = ^{ 161 | if (pctx) BN_CTX_free(pctx); 162 | BN_clear_free(&bn58); 163 | BN_clear_free(&bn0); 164 | BN_clear_free(&bn); 165 | BN_clear_free(&dv); 166 | BN_clear_free(&rem); 167 | }; 168 | 169 | // Convert big endian data to little endian. 170 | // Extra zero at the end make sure bignum will interpret as a positive number. 171 | NSMutableData* tmp = BTCReversedMutableData(data); 172 | tmp.length += 1; 173 | 174 | // Convert little endian data to bignum 175 | { 176 | NSUInteger size = tmp.length; 177 | NSMutableData* mdata = [tmp mutableCopy]; 178 | 179 | // Reverse to convert to OpenSSL bignum endianess 180 | BTCDataReverse(mdata); 181 | 182 | // BIGNUM's byte stream format expects 4 bytes of 183 | // big endian size data info at the front 184 | [mdata replaceBytesInRange:NSMakeRange(0, 0) withBytes:"\0\0\0\0" length:4]; 185 | unsigned char* bytes = mdata.mutableBytes; 186 | bytes[0] = (size >> 24) & 0xff; 187 | bytes[1] = (size >> 16) & 0xff; 188 | bytes[2] = (size >> 8) & 0xff; 189 | bytes[3] = (size >> 0) & 0xff; 190 | 191 | BN_mpi2bn(bytes, (int)mdata.length, &bn); 192 | } 193 | 194 | // Expected size increase from base58 conversion is approximately 137% 195 | // use 138% to be safe 196 | NSMutableData* stringData = [NSMutableData dataWithCapacity:data.length*138/100 + 1]; 197 | 198 | while (BN_cmp(&bn, &bn0) > 0) { 199 | if (!BN_div(&dv, &rem, &bn, &bn58, pctx)) { 200 | finish(); 201 | return nil; 202 | } 203 | BN_copy(&bn, &dv); 204 | unsigned long c = BN_get_word(&rem); 205 | [stringData appendBytes:BTCBase58Alphabet + c length:1]; 206 | } 207 | finish(); 208 | 209 | // Leading zeroes encoded as base58 ones ("1") 210 | const unsigned char* pbegin = data.bytes; 211 | const unsigned char* pend = data.bytes + data.length; 212 | for (const unsigned char* p = pbegin; p < pend && *p == 0; p++) { 213 | [stringData appendBytes:BTCBase58Alphabet + 0 length:1]; 214 | } 215 | 216 | // Convert little endian std::string to big endian 217 | BTCDataReverse(stringData); 218 | 219 | [stringData appendBytes:"" length:1]; 220 | 221 | char* r = malloc(stringData.length); 222 | memcpy(r, stringData.bytes, stringData.length); 223 | BTCDataClear(stringData); 224 | return r; 225 | } 226 | 227 | char* BTCBase58CheckCStringWithDataRIPEMD160(NSData* immutabledata) { 228 | if (!immutabledata) return NULL; 229 | // add 4-byte hash check to the end 230 | NSMutableData* data = [immutabledata mutableCopy]; 231 | NSData* checksum = BTCRIPEMD160(data); 232 | [data appendBytes:checksum.bytes length:4]; 233 | char* result = BTCBase58CStringWithData(data); 234 | BTCDataClear(data); 235 | return result; 236 | } 237 | 238 | // String in Base58 with checksum 239 | char* BTCBase58CheckCStringWithData(NSData* immutabledata) { 240 | if (!immutabledata) return NULL; 241 | // add 4-byte hash check to the end 242 | NSMutableData* data = [immutabledata mutableCopy]; 243 | NSData* checksum = BTCHash256(data); 244 | [data appendBytes:checksum.bytes length:4]; 245 | char* result = BTCBase58CStringWithData(data); 246 | BTCDataClear(data); 247 | return result; 248 | } 249 | 250 | NSString* BTCBase58StringWithData(NSData* data) { 251 | if (!data) return nil; 252 | char* s = BTCBase58CStringWithData(data); 253 | id r = [NSString stringWithCString:s encoding:NSASCIIStringEncoding]; 254 | BTCSecureClearCString(s); 255 | free(s); 256 | return r; 257 | } 258 | 259 | 260 | NSString* BTCBase58CheckStringWithData(NSData* data) { 261 | if (!data) return nil; 262 | char* s = BTCBase58CheckCStringWithData(data); 263 | id r = [NSString stringWithCString:s encoding:NSASCIIStringEncoding]; 264 | BTCSecureClearCString(s); 265 | free(s); 266 | return r; 267 | } 268 | 269 | NSString* BTCBase58CheckStringWithDataRIPEMD160(NSData* data) { 270 | if (!data) return nil; 271 | char* s = BTCBase58CheckCStringWithDataRIPEMD160(data); 272 | id r = [NSString stringWithCString:s encoding:NSASCIIStringEncoding]; 273 | BTCSecureClearCString(s); 274 | free(s); 275 | return r; 276 | } 277 | 278 | 279 | 280 | 281 | 282 | 283 | -------------------------------------------------------------------------------- /Graphene/lib/ecc/GXPrivateKey.m: -------------------------------------------------------------------------------- 1 | // 2 | // PrivateKey.m 3 | // Graphene 4 | // 5 | // Created by David Lan on 2018/2/27. 6 | // Copyright © 2018年 GXChain. All rights reserved. 7 | // 8 | 9 | #import "GXPrivateKey.h" 10 | #import "BTCBigNumber.h" 11 | 12 | static int BTCRegenerateKey(EC_KEY *eckey, BIGNUM *priv_key); 13 | static void * ecies_key_derivation(const void *input, size_t ilen, void *output, size_t *olen); 14 | static int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check); 15 | 16 | @implementation GXPrivateKey 17 | +(instancetype)fromWif:(NSString*)wifKey{ 18 | GXPrivateKey* instance = [[GXPrivateKey alloc]init]; 19 | NSMutableData* private_wif = BTCDataFromBase58(wifKey); 20 | uint8_t version; 21 | [private_wif getBytes:&version length:sizeof(uint8_t)]; 22 | NSAssert(version==0x80, @"Expected version %d, instead got %d",0x80,version); 23 | NSMutableData* privKeyData = BTCDataFromBase58Check(wifKey); 24 | NSAssert(privKeyData!=nil, @"Invalid private key, checksum does not match"); 25 | instance.privateKeyData=[privKeyData subdataWithRange:NSMakeRange(1, [privKeyData length]-1)]; 26 | EC_KEY* _key =EC_KEY_new_by_curve_name(NID_secp256k1); 27 | BIGNUM *bignum = BN_bin2bn(instance.privateKeyData.bytes, (int)instance.privateKeyData.length, BN_new()); 28 | BTCRegenerateKey(_key, bignum); 29 | instance._key=_key; 30 | BN_clear_free(bignum); 31 | return instance; 32 | } 33 | 34 | -(NSString*)toWif{ 35 | const uint8_t version = 0x80; 36 | NSMutableData* data=[NSMutableData dataWithBytes:&version length:sizeof(uint8_t)]; 37 | [data appendData:self.privateKeyData]; 38 | return BTCBase58CheckStringWithData(data); 39 | } 40 | 41 | - (BTCCurvePoint*) curvePoint { 42 | const EC_POINT* ecpoint = EC_KEY_get0_public_key(self._key); 43 | BTCCurvePoint* cp = [[BTCCurvePoint alloc] initWithEC_POINT:ecpoint]; 44 | return cp; 45 | } 46 | 47 | -(GXPublicKey*)getPublic{ 48 | NSData* pubkeyData=[self publicKeyWithCompression:YES]; 49 | return [GXPublicKey fromData:pubkeyData]; 50 | } 51 | 52 | -(NSData*)sharedSecret:(GXPublicKey*)publicKey{ 53 | BTCBigNumber* pk = [[BTCBigNumber alloc] initWithUnsignedBigEndian:self.privateKeyData]; 54 | publicKey= [GXPublicKey fromData:[publicKey publicKeyWithCompression:NO]]; 55 | BTCCurvePoint* sharedPoint = [publicKey.curvePoint multiply:pk]; 56 | NSMutableData* hash = BTCSHA512(sharedPoint.x.unsignedBigEndian); 57 | [pk clear]; 58 | [sharedPoint clear]; 59 | return hash; 60 | } 61 | 62 | //Signature Malleability: https://github.com/bitshares/bitshares1-core/issues/1129 63 | -(BOOL) isSignatureCanonical:(NSData*) signData{ 64 | unsigned char *sigBytes = (unsigned char *)[signData bytes]; 65 | return !(sigBytes[1] & 0x80) 66 | && !(sigBytes[1] == 0 && !(sigBytes[2] & 0x80)) 67 | && !(sigBytes[33] & 0x80) 68 | && !(sigBytes[33] == 0 && !(sigBytes[34] & 0x80)); 69 | } 70 | 71 | -(NSData*)sign:(NSData*)data{ 72 | NSData* sig=nil; 73 | NSInteger i = 0; 74 | do { 75 | i++; 76 | sig = [self compactSignatureForHash:BTCSHA256(data)]; 77 | NSLog(@"%@",BTCHexFromData(sig)); 78 | if(i>3){ 79 | NSLog(@"Signature tried %ld times", i); 80 | } 81 | } while (![self isSignatureCanonical:sig]); 82 | return sig; 83 | } 84 | 85 | - (NSData*) compactSignatureForHash:(NSData*)hash { 86 | NSData* my_pub_key = [self publicKeyWithCompression:YES]; // just for good measure 87 | 88 | while( true ) 89 | { 90 | NSMutableData* sigdata = [NSMutableData dataWithLength:65]; 91 | unsigned char* sigbytes = sigdata.mutableBytes; 92 | const unsigned char* hashbytes = hash.bytes; 93 | int hashlength = (int)hash.length; 94 | unsigned char *p64 = (sigbytes + 1); // first byte is reserved for header. 95 | 96 | ECDSA_SIG *sig = ECDSA_do_sign(hashbytes, hashlength, self._key); 97 | 98 | if (sig==NULL) { 99 | return nil; 100 | } 101 | memset(p64, 0, 64); 102 | 103 | int nBitsR = BN_num_bits(sig->r); 104 | int nBitsS = BN_num_bits(sig->s); 105 | if (nBitsR <= 256 && nBitsS <= 256) 106 | { 107 | int nRecId = -1; 108 | EC_KEY* key = EC_KEY_new_by_curve_name( NID_secp256k1 ); 109 | NSAssert( key,@"Null key should not happen"); 110 | EC_KEY_set_conv_form( key, POINT_CONVERSION_COMPRESSED ); 111 | for (int i=0; i<4; i++) 112 | { 113 | if (ECDSA_SIG_recover_key_GFp(key, sig, hashbytes, hashlength, i, 1) == 1) { 114 | GXPublicKey* _pubkey=[GXPublicKey new]; 115 | _pubkey._key=key; 116 | NSData* key_data=[_pubkey publicKeyWithCompression:YES]; 117 | if ( [key_data isEqual:my_pub_key] ) 118 | { 119 | nRecId = i; 120 | break; 121 | } 122 | } 123 | } 124 | EC_KEY_free( key ); 125 | 126 | if (nRecId == -1) 127 | { 128 | [[NSException exceptionWithName:@"unable to construct recoverable key" reason:@"" userInfo:nil] raise]; 129 | } 130 | unsigned char* result = NULL; 131 | i2d_ECDSA_SIG( sig, &result ); 132 | int lenR = result[3]; 133 | int lenS = result[5+lenR]; 134 | if( lenR != 32 ) { free(result); continue; } 135 | if( lenS != 32 ) { free(result); continue; } 136 | memcpy( &sigbytes[1], &result[4], lenR ); 137 | memcpy( &sigbytes[33], &result[6+lenR], lenS ); 138 | free(result); 139 | sigbytes[0] = nRecId+27+4;//(fCompressedPubKey ? 4 : 0); 140 | } 141 | return sigdata; 142 | } // while true 143 | } 144 | 145 | - (NSMutableData*) publicKeyWithCompression:(BOOL)compression { 146 | if (!self._key) return nil; 147 | EC_KEY_set_conv_form(self._key, compression ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); 148 | int length = i2o_ECPublicKey(self._key, NULL); 149 | if (!length) return nil; 150 | NSAssert(length <= 65, @"Pubkey length must be up to 65 bytes."); 151 | NSMutableData* data = [[NSMutableData alloc] initWithLength:length]; 152 | unsigned char* bytes = [data mutableBytes]; 153 | if (i2o_ECPublicKey(self._key, &bytes) != length) return nil; 154 | return data; 155 | } 156 | 157 | @end 158 | 159 | static void * ecies_key_derivation(const void *input, size_t ilen, void *output, size_t *olen) 160 | { 161 | if (*olen < SHA512_DIGEST_LENGTH) { 162 | return NULL; 163 | } 164 | *olen = SHA512_DIGEST_LENGTH; 165 | return (void*)SHA512((const unsigned char*)input, ilen, (unsigned char*)output); 166 | } 167 | 168 | static int BTCRegenerateKey(EC_KEY *eckey, BIGNUM *priv_key) { 169 | BN_CTX *ctx = NULL; 170 | EC_POINT *pub_key = NULL; 171 | 172 | if (!eckey) return 0; 173 | 174 | const EC_GROUP *group = EC_KEY_get0_group(eckey); 175 | 176 | BOOL success = NO; 177 | if ((ctx = BN_CTX_new())) { 178 | if ((pub_key = EC_POINT_new(group))) { 179 | if (EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx)) { 180 | EC_KEY_set_private_key(eckey, priv_key); 181 | EC_KEY_set_public_key(eckey, pub_key); 182 | success = YES; 183 | } 184 | } 185 | } 186 | 187 | if (pub_key) EC_POINT_free(pub_key); 188 | if (ctx) BN_CTX_free(ctx); 189 | 190 | return success; 191 | } 192 | 193 | // Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields 194 | // recid selects which key is recovered 195 | // if check is non-zero, additional checks are performed 196 | static int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check) { 197 | if (!eckey) return 0; 198 | 199 | int ret = 0; 200 | BN_CTX *ctx = NULL; 201 | 202 | BIGNUM *x = NULL; 203 | BIGNUM *e = NULL; 204 | BIGNUM *order = NULL; 205 | BIGNUM *sor = NULL; 206 | BIGNUM *eor = NULL; 207 | BIGNUM *field = NULL; 208 | EC_POINT *R = NULL; 209 | EC_POINT *O = NULL; 210 | EC_POINT *Q = NULL; 211 | BIGNUM *rr = NULL; 212 | BIGNUM *zero = NULL; 213 | int n = 0; 214 | int i = recid / 2; 215 | 216 | const EC_GROUP *group = EC_KEY_get0_group(eckey); 217 | if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; } 218 | BN_CTX_start(ctx); 219 | order = BN_CTX_get(ctx); 220 | if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; } 221 | x = BN_CTX_get(ctx); 222 | if (!BN_copy(x, order)) { ret=-1; goto err; } 223 | if (!BN_mul_word(x, i)) { ret=-1; goto err; } 224 | if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; } 225 | field = BN_CTX_get(ctx); 226 | if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; } 227 | if (BN_cmp(x, field) >= 0) { ret=0; goto err; } 228 | if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } 229 | if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; } 230 | if (check) { 231 | if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } 232 | if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; } 233 | if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; } 234 | } 235 | if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } 236 | n = EC_GROUP_get_degree(group); 237 | e = BN_CTX_get(ctx); 238 | if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; } 239 | if (8*msglen > n) BN_rshift(e, e, 8-(n & 7)); 240 | zero = BN_CTX_get(ctx); 241 | if (!BN_zero(zero)) { ret=-1; goto err; } 242 | if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; } 243 | rr = BN_CTX_get(ctx); 244 | if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; } 245 | sor = BN_CTX_get(ctx); 246 | if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; } 247 | eor = BN_CTX_get(ctx); 248 | if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; } 249 | if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; } 250 | if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; } 251 | 252 | ret = 1; 253 | 254 | err: 255 | if (ctx) { 256 | BN_CTX_end(ctx); 257 | BN_CTX_free(ctx); 258 | } 259 | if (R != NULL) EC_POINT_free(R); 260 | if (O != NULL) EC_POINT_free(O); 261 | if (Q != NULL) EC_POINT_free(Q); 262 | return ret; 263 | } 264 | 265 | -------------------------------------------------------------------------------- /Graphene/lib/ecc/vendor/BTCBigNumber.m: -------------------------------------------------------------------------------- 1 | // Oleg Andreev 2 | 3 | #import "BTCBigNumber.h" 4 | #import "BTCData.h" 5 | 6 | #define BTCBigNumberCompare(a, b) (BN_cmp(&(a->_bignum), &(b->_bignum))) 7 | 8 | @implementation BTCBigNumber { 9 | @package 10 | BIGNUM _bignum; 11 | 12 | // Used as a guard in case a private setter is called on immutable instance after initialization. 13 | BOOL _immutable; 14 | } 15 | 16 | @dynamic compact; 17 | @dynamic uint32value; 18 | @dynamic int32value; 19 | @dynamic uint64value; 20 | @dynamic int64value; 21 | @dynamic hexString; 22 | @dynamic decimalString; 23 | @dynamic signedLittleEndian; 24 | @dynamic unsignedBigEndian; 25 | @dynamic littleEndianData; // deprecated 26 | @dynamic unsignedData; // deprecated 27 | 28 | + (instancetype) zero { 29 | static BTCBigNumber* bn = nil; 30 | static dispatch_once_t onceToken; 31 | dispatch_once(&onceToken, ^{ 32 | bn = [[self alloc] init]; 33 | BN_zero(&(bn->_bignum)); 34 | }); 35 | return bn; 36 | } 37 | 38 | + (instancetype) one { 39 | static BTCBigNumber* bn = nil; 40 | static dispatch_once_t onceToken; 41 | dispatch_once(&onceToken, ^{ 42 | bn = [[self alloc] init]; 43 | BN_one(&(bn->_bignum)); 44 | }); 45 | return bn; 46 | } 47 | 48 | + (instancetype) negativeOne { 49 | static BTCBigNumber* bn = nil; 50 | static dispatch_once_t onceToken; 51 | dispatch_once(&onceToken, ^{ 52 | bn = [[self alloc] initWithInt32:-1]; 53 | }); 54 | return bn; 55 | } 56 | 57 | - (id) init { 58 | if (self = [super init]) { 59 | BN_init(&_bignum); 60 | } 61 | return self; 62 | } 63 | 64 | - (void) dealloc { 65 | BN_clear_free(&_bignum); 66 | } 67 | 68 | - (void) clear { 69 | BN_clear(&_bignum); 70 | } 71 | 72 | - (void) throwIfImmutable { 73 | if (_immutable) { 74 | @throw [NSException exceptionWithName:@"Immutable BTCBigNumber is modified" reason:@"" userInfo:nil]; 75 | } 76 | } 77 | 78 | // Since we use private setters in the init* methods, 79 | - (id) initWithCompact:(uint32_t)value { 80 | if (self = [self init]) self.compact = value; 81 | _immutable = YES; 82 | return self; 83 | } 84 | - (id) initWithUInt32:(uint32_t)value { 85 | if (self = [self init]) self.uint32value = value; 86 | _immutable = YES; 87 | return self; 88 | } 89 | - (id) initWithInt32:(int32_t)value { 90 | if (self = [self init]) self.int32value = value; 91 | _immutable = YES; 92 | return self; 93 | } 94 | - (id) initWithUInt64:(uint64_t)value { 95 | if (self = [self init]) self.uint64value = value; 96 | _immutable = YES; 97 | return self; 98 | } 99 | - (id) initWithInt64:(int64_t)value { 100 | if (self = [self init]) self.int64value = value; 101 | _immutable = YES; 102 | return self; 103 | } 104 | - (id) initWithSignedLittleEndian:(NSData *)data { 105 | if (!data) return nil; 106 | if (self = [self init]) self.signedLittleEndian = data; 107 | _immutable = YES; 108 | return self; 109 | } 110 | - (id) initWithUnsignedBigEndian:(NSData *)data { 111 | if (!data) return nil; 112 | if (self = [self init]) self.unsignedBigEndian = data; 113 | _immutable = YES; 114 | return self; 115 | } 116 | - (id) initWithLittleEndianData:(NSData*)data { // deprecated 117 | if (!data) return nil; 118 | if (self = [self init]) self.signedLittleEndian = data; 119 | _immutable = YES; 120 | return self; 121 | } 122 | - (id) initWithUnsignedData:(NSData *)data { // deprecated 123 | if (!data) return nil; 124 | if (self = [self init]) self.unsignedBigEndian = data; 125 | _immutable = YES; 126 | return self; 127 | } 128 | - (id) initWithString:(NSString*)string base:(NSUInteger)base { 129 | if (!string) return nil; 130 | if (self = [self init]) [self setString:string base:base]; 131 | _immutable = YES; 132 | return self; 133 | } 134 | 135 | - (id) initWithHexString:(NSString*)hexString { 136 | return [self initWithString:hexString base:16]; 137 | } 138 | 139 | - (id) initWithDecimalString:(NSString*)decimalString { 140 | return [self initWithString:decimalString base:10]; 141 | } 142 | 143 | - (id) initWithBIGNUM:(const BIGNUM*)otherBIGNUM { 144 | if (self = [self init]) { 145 | BN_copy(&_bignum, otherBIGNUM); 146 | } 147 | return self; 148 | } 149 | 150 | - (const BIGNUM*) BIGNUM { 151 | return &_bignum; 152 | } 153 | 154 | - (BOOL) isZero { 155 | return BN_is_zero(&_bignum); 156 | } 157 | 158 | - (BOOL) isOne { 159 | return BN_is_one(&_bignum); 160 | } 161 | 162 | 163 | #pragma mark - NSObject 164 | 165 | 166 | 167 | - (BTCBigNumber*) copy { 168 | return [self copyWithZone:nil]; 169 | } 170 | 171 | - (BTCMutableBigNumber*) mutableCopy { 172 | return [self mutableCopyWithZone:nil]; 173 | } 174 | 175 | - (BTCBigNumber*) copyWithZone:(NSZone *)zone { 176 | BTCBigNumber* to = [[BTCBigNumber alloc] init]; 177 | if (BN_copy(&(to->_bignum), &_bignum)) { 178 | return to; 179 | } 180 | return nil; 181 | } 182 | 183 | - (BTCMutableBigNumber*) mutableCopyWithZone:(NSZone *)zone { 184 | BTCMutableBigNumber* to = [[BTCMutableBigNumber alloc] init]; 185 | if (BN_copy(&(to->_bignum), &_bignum)) { 186 | return to; 187 | } 188 | return nil; 189 | } 190 | 191 | 192 | - (BOOL) isEqual:(BTCBigNumber*)other { 193 | if (![other isKindOfClass:[BTCBigNumber class]]) return NO; 194 | return BTCBigNumberCompare(self, other) == NSOrderedSame; 195 | } 196 | 197 | - (NSComparisonResult)compare:(BTCBigNumber *)other { 198 | return BTCBigNumberCompare(self, other); 199 | } 200 | 201 | - (NSUInteger)hash { 202 | return (NSUInteger)BN_get_word(&_bignum); 203 | } 204 | 205 | - (NSString*) description { 206 | return [NSString stringWithFormat:@"<%@:0x%p 0x%@ (%@)>", [self class], self, [self stringInBase:16], [self stringInBase:10]]; 207 | } 208 | 209 | 210 | 211 | 212 | #pragma mark - Comparison 213 | 214 | 215 | - (BTCBigNumber*) min:(BTCBigNumber*)other { 216 | return (BTCBigNumberCompare(self, other) <= 0) ? self : other; 217 | } 218 | 219 | - (BTCBigNumber*) max:(BTCBigNumber*)other { 220 | return (BTCBigNumberCompare(self, other) >= 0) ? self : other; 221 | } 222 | 223 | - (BOOL) less:(BTCBigNumber *)other { return BTCBigNumberCompare(self, other) < 0; } 224 | - (BOOL) lessOrEqual:(BTCBigNumber *)other { return BTCBigNumberCompare(self, other) <= 0; } 225 | - (BOOL) greater:(BTCBigNumber *)other { return BTCBigNumberCompare(self, other) > 0; } 226 | - (BOOL) greaterOrEqual:(BTCBigNumber *)other { return BTCBigNumberCompare(self, other) >= 0; } 227 | 228 | 229 | 230 | 231 | #pragma mark - Conversion 232 | 233 | 234 | 235 | 236 | - (NSString*) hexString { 237 | return [self stringInBase:16]; 238 | } 239 | 240 | - (void) setHexString:(NSString *)hexString { 241 | [self throwIfImmutable]; 242 | [self setString:hexString base:16]; 243 | } 244 | 245 | - (NSString*) decimalString { 246 | return [self stringInBase:10]; 247 | } 248 | 249 | - (void) setDecimalString:(NSString *)decimalString { 250 | [self throwIfImmutable]; 251 | [self setString:decimalString base:10]; 252 | } 253 | 254 | - (void) setString:(NSString*)string base:(NSUInteger)base { 255 | [self throwIfImmutable]; 256 | if (base > 36 || base < 2) return; 257 | 258 | BN_set_word(&_bignum, 0); 259 | 260 | if (string.length == 0) return; 261 | 262 | const unsigned char *psz = (const unsigned char*)[string cStringUsingEncoding:NSASCIIStringEncoding]; 263 | 264 | while (isspace(*psz)) psz++; 265 | 266 | bool isNegative = false; 267 | if (*psz == '-') { 268 | isNegative = true; 269 | psz++; 270 | } 271 | 272 | // Strip 0x from a 16-base string and 0b from a binary string. 273 | // String is null-terminated, so it's safe to check for [1] if [0] is not null 274 | if (base == 16 && psz[0] == '0' && tolower(psz[1]) == 'x') psz += 2; 275 | if (base == 2 && psz[0] == '0' && tolower(psz[1]) == 'b') psz += 2; 276 | 277 | while (isspace(*psz)) psz++; 278 | 279 | static const signed char digits[256] = { 280 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 281 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 282 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 283 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 284 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 285 | // @ A B C D E F G H I J K L M N O 286 | 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 287 | // P Q R S T U V W X Y Z [ \ ] ^ _ 288 | 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0, 289 | // ` a b c d e f g h i j k l m n o 290 | 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 291 | // p q r s t u v w x y z { | } ~ del 292 | 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0, 293 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 294 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 295 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 296 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 297 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 298 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 299 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 300 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 301 | }; 302 | 303 | BN_CTX* pctx = NULL; 304 | BIGNUM bnBase; BN_init(&bnBase); BN_set_word(&bnBase, (BN_ULONG)base); 305 | 306 | while (1) { 307 | unsigned char c = (unsigned char)*psz++; 308 | if (c == 0) break; // break when null-terminator is hit 309 | 310 | if (c != 0x20) { // skip space 311 | 312 | int n = digits[c]; 313 | if (n == 0 && c != 0x30) break; // discard characters outside the 36-char range 314 | if (n >= base) break; // discard character outside the base range. 315 | 316 | if (base == 16) { 317 | BN_lshift(&_bignum, &_bignum, 4); 318 | } else if (base == 8) { 319 | BN_lshift(&_bignum, &_bignum, 3); 320 | } else if (base == 4) { 321 | BN_lshift(&_bignum, &_bignum, 2); 322 | } else if (base == 2) { 323 | BN_lshift(&_bignum, &_bignum, 1); 324 | } else if (base == 32) { 325 | BN_lshift(&_bignum, &_bignum, 5); 326 | } else { 327 | if (!pctx) pctx = BN_CTX_new(); 328 | BN_mul(&_bignum, &_bignum, &bnBase, pctx); 329 | } 330 | 331 | BN_add_word(&_bignum, n); 332 | } 333 | } 334 | 335 | if (isNegative) { 336 | BN_set_negative(&_bignum, 1); 337 | } 338 | 339 | BN_free(&bnBase); 340 | if (pctx) BN_CTX_free(pctx); 341 | } 342 | 343 | - (NSString*) stringInBase:(NSUInteger)base { 344 | if (base > 36 || base < 2) return nil; 345 | 346 | NSMutableData* resultData = nil; 347 | 348 | BN_CTX* pctx = BN_CTX_new(); 349 | BIGNUM bnBase; BN_init(&bnBase); BN_set_word(&bnBase, (BN_ULONG)base); 350 | BIGNUM bn0; BN_init(&bn0); BN_zero(&bn0); 351 | BIGNUM bn; BN_init(&bn); BN_copy(&bn, &_bignum); 352 | 353 | BN_set_negative(&bn, false); 354 | 355 | BIGNUM dv; BN_init(&dv); 356 | BIGNUM rem; BN_init(&rem); 357 | 358 | if (BN_cmp(&bn, &bn0) == 0) { 359 | resultData = [NSMutableData dataWithBytes:"0" length:1]; 360 | } else { 361 | while (BN_cmp(&bn, &bn0) > 0) { 362 | if (!BN_div(&dv, &rem, &bn, &bnBase, pctx)) { 363 | NSLog(@"BTCBigNumber: stringInBase failed to BN_div"); 364 | break; 365 | } 366 | BN_copy(&bn, &dv); 367 | BN_ULONG c = BN_get_word(&rem); 368 | 369 | if (!resultData) resultData = [NSMutableData data]; 370 | 371 | // 36 characters: 0123456789 123456789 123456789 12345 372 | unsigned char ch = "0123456789abcdefghijklmnopqrstuvwxyz"[c]; 373 | 374 | [resultData replaceBytesInRange:NSMakeRange(0, 0) withBytes:&ch length:1]; 375 | } 376 | if (resultData && BN_is_negative(&_bignum)) { 377 | unsigned char ch = '-'; 378 | [resultData replaceBytesInRange:NSMakeRange(0, 0) withBytes:&ch length:1]; 379 | } 380 | } 381 | 382 | BN_clear_free(&dv); 383 | BN_clear_free(&rem); 384 | BN_clear_free(&bn); 385 | BN_free(&bn0); 386 | BN_free(&bnBase); 387 | BN_CTX_free(pctx); 388 | return resultData ? [[NSString alloc] initWithData:resultData encoding:NSASCIIStringEncoding] : nil; 389 | } 390 | 391 | 392 | // The "compact" format is a representation of a whole 393 | // number N using an unsigned 32bit number similar to a 394 | // floating point format. 395 | // The most significant 8 bits are the unsigned exponent of base 256. 396 | // This exponent can be thought of as "number of bytes of N". 397 | // The lower 23 bits are the mantissa. 398 | // Bit number 24 (0x800000) represents the sign of N. 399 | // N = (-1^sign) * mantissa * 256^(exponent-3) 400 | // 401 | // Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn(). 402 | // MPI uses the most significant bit of the first byte as sign. 403 | // Thus 0x1234560000 is compact (0x05123456) 404 | // and 0xc0de000000 is compact (0x0600c0de) 405 | // (0x05c0de00) would be -0x40de000000 406 | // 407 | // Bitcoin only uses this "compact" format for encoding difficulty 408 | // targets, which are unsigned 256bit quantities. Thus, all the 409 | // complexities of the sign bit and using base 256 are probably an 410 | // implementation accident. 411 | // 412 | // This implementation directly uses shifts instead of going 413 | // through an intermediate MPI representation. 414 | - (uint32_t) compact { 415 | uint32_t size = BN_num_bytes(&_bignum); 416 | uint32_t result = 0; 417 | if (size <= 3) { 418 | result = (uint32_t)(BN_get_word(&_bignum) << 8*(3-size)); 419 | } else { 420 | BIGNUM bn; 421 | BN_init(&bn); 422 | BN_rshift(&bn, &_bignum, 8*(size-3)); 423 | result = (uint32_t)BN_get_word(&bn); 424 | } 425 | // The 0x00800000 bit denotes the sign. 426 | // Thus, if it is already set, divide the mantissa by 256 and increase the exponent. 427 | if (result & 0x00800000) { 428 | result >>= 8; 429 | size++; 430 | } 431 | result |= size << 24; 432 | result |= (BN_is_negative(&_bignum) ? 0x00800000 : 0); 433 | return result; 434 | } 435 | 436 | - (void) setCompact:(uint32_t)value { 437 | [self throwIfImmutable]; 438 | unsigned int size = value >> 24; 439 | bool isNegative = (value & 0x00800000) != 0; 440 | unsigned int word = value & 0x007fffff; 441 | if (size <= 3) { 442 | word >>= 8*(3-size); 443 | BN_set_word(&_bignum, word); 444 | } else { 445 | BN_set_word(&_bignum, word); 446 | BN_lshift(&_bignum, &_bignum, 8*(size-3)); 447 | } 448 | BN_set_negative(&_bignum, isNegative); 449 | } 450 | 451 | - (uint32_t) uint32value { 452 | return (uint32_t)BN_get_word(&_bignum); 453 | } 454 | 455 | - (void) setUint32value:(uint32_t)value { 456 | [self throwIfImmutable]; 457 | BN_set_word(&_bignum, value); 458 | } 459 | 460 | - (int32_t) int32value { 461 | uint32_t value = [self uint32value]; 462 | if (!BN_is_negative(&_bignum)) { 463 | if (value > INT32_MAX) 464 | return INT32_MAX; 465 | else 466 | return value; 467 | } else { 468 | if (value > INT32_MAX) 469 | return INT32_MIN; 470 | else 471 | return -value; 472 | } 473 | } 474 | 475 | - (void) setInt32value:(int32_t)value { 476 | [self throwIfImmutable]; 477 | if (value >= 0) { 478 | self.uint32value = value; 479 | } else { 480 | self.int64value = value; 481 | } 482 | } 483 | 484 | - (uint64_t) uint64value { 485 | return (uint64_t)BN_get_word(&_bignum); 486 | } 487 | 488 | - (int64_t) int64value { 489 | return (int64_t)BN_get_word(&_bignum); 490 | } 491 | 492 | - (void) setUint64value:(uint64_t)value { 493 | [self throwIfImmutable]; 494 | [self setUint64valuePrivate:value negative:NO]; 495 | } 496 | 497 | - (void) setInt64value:(int64_t)value { 498 | [self throwIfImmutable]; 499 | bool isNegative = NO; 500 | uint64_t uintValue; 501 | if (value < 0) { 502 | // Since the minimum signed integer cannot be represented as 503 | // positive so long as its type is signed, and it's not well-defined 504 | // what happens if you make it unsigned before negating it, we 505 | // instead increment the negative integer by 1, convert it, then 506 | // increment the (now positive) unsigned integer by 1 to compensate. 507 | uintValue = -(value + 1); 508 | ++uintValue; 509 | isNegative = YES; 510 | } else { 511 | uintValue = value; 512 | } 513 | 514 | [self setUint64valuePrivate:uintValue negative:isNegative]; 515 | } 516 | 517 | - (void) setUint64valuePrivate:(uint64_t)value negative:(BOOL)isNegative { 518 | // Numbers are represented in OpenSSL using the MPI format. 4 byte length. 519 | unsigned char rawMPI[sizeof(value) + 6]; 520 | unsigned char* currentByte = &rawMPI[4]; 521 | BOOL leadingZeros = YES; 522 | for (int i = 0; i < 8; ++i) { 523 | uint8_t c = (value >> 56) & 0xff; 524 | value <<= 8; 525 | if (leadingZeros) { 526 | if (c == 0) continue; // Skip beginning zeros 527 | 528 | if (c & 0x80) { 529 | *currentByte = (isNegative ? 0x80 : 0); 530 | ++currentByte; 531 | } else if (isNegative) { 532 | c |= 0x80; 533 | } 534 | leadingZeros = false; 535 | } 536 | *currentByte = c; 537 | ++currentByte; 538 | } 539 | unsigned long size = currentByte - (rawMPI + 4); 540 | rawMPI[0] = (size >> 24) & 0xff; 541 | rawMPI[1] = (size >> 16) & 0xff; 542 | rawMPI[2] = (size >> 8) & 0xff; 543 | rawMPI[3] = (size) & 0xff; 544 | BN_mpi2bn(rawMPI, (int)(currentByte - rawMPI), &_bignum); 545 | } 546 | 547 | - (NSData*) signedLittleEndian { 548 | size_t size = BN_bn2mpi(&_bignum, NULL); 549 | if (size <= 4) { 550 | return [NSData data]; 551 | } 552 | NSMutableData* data = [NSMutableData dataWithLength:size]; 553 | BN_bn2mpi(&_bignum, data.mutableBytes); 554 | [data replaceBytesInRange:NSMakeRange(0, 4) withBytes:NULL length:0]; 555 | BTCDataReverse(data); 556 | return data; 557 | } 558 | 559 | - (void) setSignedLittleEndian:(NSData *)data { 560 | [self throwIfImmutable]; 561 | NSUInteger size = data.length; 562 | NSMutableData* mdata = [data mutableCopy]; 563 | // Reverse to convert to OpenSSL bignum endianess 564 | BTCDataReverse(mdata); 565 | // BIGNUM's byte stream format expects 4 bytes of 566 | // big endian size data info at the front 567 | [mdata replaceBytesInRange:NSMakeRange(0, 0) withBytes:"\0\0\0\0" length:4]; 568 | unsigned char* bytes = mdata.mutableBytes; 569 | bytes[0] = (size >> 24) & 0xff; 570 | bytes[1] = (size >> 16) & 0xff; 571 | bytes[2] = (size >> 8) & 0xff; 572 | bytes[3] = (size >> 0) & 0xff; 573 | 574 | BN_mpi2bn(bytes, (int)mdata.length, &_bignum); 575 | } 576 | 577 | // deprecated 578 | - (NSData*) littleEndianData { 579 | return self.signedLittleEndian; 580 | } 581 | // deprecated 582 | - (void) setLittleEndianData:(NSData *)data { 583 | [self setSignedLittleEndian:data]; 584 | } 585 | 586 | - (NSData*) unsignedBigEndian { 587 | int num_bytes = BN_num_bytes(&_bignum); 588 | NSMutableData* data = [[NSMutableData alloc] initWithLength:32]; // zeroed data 589 | int copied_bytes = BN_bn2bin(&_bignum, &data.mutableBytes[32 - num_bytes]); // fill the tail of the data so it's zero-padded to the left 590 | if (copied_bytes != num_bytes) return nil; 591 | return data; 592 | } 593 | 594 | - (void) setUnsignedBigEndian:(NSData *)data { 595 | [self throwIfImmutable]; 596 | if (!data) return; 597 | if (!BN_bin2bn(data.bytes, (int)data.length, &_bignum)) { 598 | return; 599 | } 600 | } 601 | 602 | // deprecated 603 | - (NSData*) unsignedData { 604 | return self.unsignedBigEndian; 605 | } 606 | 607 | // deprecated 608 | - (void) setUnsignedData:(NSData *)data { 609 | self.unsignedBigEndian = data; 610 | } 611 | 612 | 613 | // Divides receiver by another bignum. 614 | // Returns an array of two new BTCBigNumber instances: @[ quotient, remainder ] 615 | - (NSArray*) divmod:(BTCBigNumber*)other 616 | { 617 | BN_CTX* pctx = BN_CTX_new(); 618 | BTCBigNumber* r = [BTCBigNumber new]; 619 | BTCBigNumber* m = [BTCBigNumber new]; 620 | BN_div(&(r->_bignum), &(m->_bignum), &(self->_bignum), &(other->_bignum), pctx); 621 | BN_CTX_free(pctx); 622 | return @[r, m]; 623 | } 624 | 625 | 626 | #pragma mark - Util 627 | 628 | 629 | 630 | - (void) withContext:(void(^)(BN_CTX* pctx))block 631 | { 632 | BN_CTX* pctx = BN_CTX_new(); 633 | block(pctx); 634 | BN_CTX_free(pctx); 635 | } 636 | 637 | 638 | 639 | @end 640 | 641 | 642 | 643 | 644 | @implementation BTCMutableBigNumber 645 | 646 | @dynamic compact; 647 | @dynamic uint32value; 648 | @dynamic int32value; 649 | @dynamic uint64value; 650 | @dynamic int64value; 651 | @dynamic hexString; 652 | @dynamic decimalString; 653 | @dynamic signedLittleEndian; 654 | @dynamic unsignedBigEndian; 655 | @dynamic littleEndianData; 656 | @dynamic unsignedData; 657 | 658 | - (BIGNUM*) mutableBIGNUM { 659 | return &(self->_bignum); 660 | } 661 | 662 | + (instancetype) zero { 663 | return [[BTCBigNumber zero] mutableCopy]; 664 | } 665 | 666 | + (instancetype) one { 667 | return [[BTCBigNumber one] mutableCopy]; 668 | } 669 | 670 | + (instancetype) negativeOne { 671 | return [[BTCBigNumber negativeOne] mutableCopy]; 672 | } 673 | 674 | // We are mutable, disable checks. 675 | - (void) throwIfImmutable { 676 | } 677 | 678 | - (void) setString:(NSString*)string base:(NSUInteger)base { 679 | [super setString:string base:base]; 680 | } 681 | 682 | 683 | #pragma mark - Operations 684 | 685 | 686 | - (instancetype) add:(BTCBigNumber*)other { // += 687 | BN_add(&(self->_bignum), &(self->_bignum), &(other->_bignum)); 688 | return self; 689 | } 690 | 691 | - (instancetype) add:(BTCBigNumber*)other mod:(BTCBigNumber*)mod { 692 | BN_CTX* pctx = BN_CTX_new(); 693 | BN_mod_add(&(self->_bignum), &(self->_bignum), &(other->_bignum), &(mod->_bignum), pctx); 694 | BN_CTX_free(pctx); 695 | return self; 696 | } 697 | 698 | - (instancetype) subtract:(BTCBigNumber *)other { // -= 699 | BN_sub(&(self->_bignum), &(self->_bignum), &(other->_bignum)); 700 | return self; 701 | } 702 | 703 | - (instancetype) subtract:(BTCBigNumber*)other mod:(BTCBigNumber*)mod { 704 | BN_CTX* pctx = BN_CTX_new(); 705 | BN_mod_sub(&(self->_bignum), &(self->_bignum), &(other->_bignum), &(mod->_bignum), pctx); 706 | BN_CTX_free(pctx); 707 | return self; 708 | } 709 | 710 | - (instancetype) multiply:(BTCBigNumber*)other { // *= 711 | BN_CTX* pctx = BN_CTX_new(); 712 | BN_mul(&(self->_bignum), &(self->_bignum), &(other->_bignum), pctx); 713 | BN_CTX_free(pctx); 714 | return self; 715 | } 716 | 717 | - (instancetype) multiply:(BTCBigNumber*)other mod:(BTCBigNumber *)mod { 718 | BN_CTX* pctx = BN_CTX_new(); 719 | BN_mod_mul(&(self->_bignum), &(self->_bignum), &(other->_bignum), &(mod->_bignum), pctx); 720 | BN_CTX_free(pctx); 721 | return self; 722 | } 723 | 724 | - (instancetype) divide:(BTCBigNumber*)other { // /= 725 | BN_CTX* pctx = BN_CTX_new(); 726 | BN_div(&(self->_bignum), NULL, &(self->_bignum), &(other->_bignum), pctx); 727 | BN_CTX_free(pctx); 728 | return self; 729 | } 730 | 731 | - (instancetype) mod:(BTCBigNumber*)other { // %= 732 | BN_CTX* pctx = BN_CTX_new(); 733 | BN_div(NULL, &(self->_bignum), &(self->_bignum), &(other->_bignum), pctx); 734 | BN_CTX_free(pctx); 735 | return self; 736 | } 737 | 738 | - (instancetype) lshift:(unsigned int)shift { // <<= 739 | BN_lshift(&(self->_bignum), &(self->_bignum), shift); 740 | return self; 741 | } 742 | 743 | - (instancetype) rshift:(unsigned int)shift { // >>= 744 | // Note: BN_rshift segfaults on 64-bit if 2^shift is greater than the number 745 | // if built on ubuntu 9.04 or 9.10, probably depends on version of OpenSSL 746 | BTCMutableBigNumber* a = [BTCMutableBigNumber one]; 747 | [a lshift:shift]; 748 | if (BN_cmp(&(a->_bignum), &(self->_bignum)) > 0) { 749 | BN_zero(&(self->_bignum)); 750 | return self; 751 | } 752 | 753 | BN_rshift(&(self->_bignum), &(self->_bignum), shift); 754 | return self; 755 | } 756 | 757 | - (instancetype) inverseMod:(BTCBigNumber*)mod { // (a^-1) mod n 758 | BN_CTX* pctx = BN_CTX_new(); 759 | BN_mod_inverse(&(self->_bignum), &(self->_bignum), &(mod->_bignum), pctx); 760 | BN_CTX_free(pctx); 761 | return self; 762 | } 763 | 764 | - (instancetype) exp:(BTCBigNumber*)power { // pow(self, p) 765 | BN_CTX* pctx = BN_CTX_new(); 766 | BN_exp(&(self->_bignum), &(self->_bignum), &(power->_bignum), pctx); 767 | BN_CTX_free(pctx); 768 | return self; 769 | } 770 | 771 | - (instancetype) exp:(BTCBigNumber*)power mod:(BTCBigNumber *)mod { // pow(self,p) % m 772 | BN_CTX* pctx = BN_CTX_new(); 773 | BN_mod_exp(&(self->_bignum), &(self->_bignum), &(power->_bignum), &(mod->_bignum), pctx); 774 | BN_CTX_free(pctx); 775 | return self; 776 | } 777 | 778 | 779 | 780 | @end 781 | 782 | 783 | -------------------------------------------------------------------------------- /Graphene/lib/ecc/vendor/BTCData.m: -------------------------------------------------------------------------------- 1 | // Oleg Andreev 2 | 3 | #import "BTCData.h" 4 | #import 5 | #if BTCDataRequiresOpenSSL 6 | #include 7 | #include 8 | #endif 9 | 10 | static const unsigned char _BTCZeroString256[32] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 11 | 12 | // This is designed to be not optimized out by compiler like memset 13 | void *BTCSecureMemset(void *v, unsigned char c, size_t n) { 14 | if (!v) return v; 15 | volatile unsigned char *p = v; 16 | while (n--) 17 | *p++ = c; 18 | 19 | return v; 20 | } 21 | 22 | void BTCSecureClearCString(char *s) { 23 | if (!s) return; 24 | BTCSecureMemset(s, 0, strlen(s)); 25 | } 26 | 27 | void *BTCCreateRandomBytesOfLength(size_t length) { 28 | FILE *fp = fopen("/dev/random", "r"); 29 | if (!fp) 30 | { 31 | NSLog(@"NSData+BTC: cannot fopen /dev/random"); 32 | exit(-1); 33 | return NULL; 34 | } 35 | char* bytes = (char*)malloc(length); 36 | for (int i = 0; i < length; i++) 37 | { 38 | char c = fgetc(fp); 39 | bytes[i] = c; 40 | } 41 | 42 | fclose(fp); 43 | return bytes; 44 | } 45 | 46 | // Returns data with securely random bytes of the specified length. Uses /dev/random. 47 | NSMutableData* BTCRandomDataWithLength(NSUInteger length) { 48 | void *bytes = BTCCreateRandomBytesOfLength(length); 49 | if (!bytes) return nil; 50 | return [[NSMutableData alloc] initWithBytesNoCopy:bytes length:length]; 51 | } 52 | 53 | // Returns data produced by flipping the coin as proposed by Dan Kaminsky: 54 | // https://gist.github.com/PaulCapestany/6148566 55 | 56 | static inline int BTCCoinFlip() { 57 | __block int n = 0; 58 | //int c = 0; 59 | dispatch_time_t then = dispatch_time(DISPATCH_TIME_NOW, 999000ull); 60 | 61 | // We need to increase variance of number of flips, so we force system to schedule some threads 62 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ 63 | while (dispatch_time(DISPATCH_TIME_NOW, 0) <= then) { 64 | n = !n; 65 | } 66 | }); 67 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 68 | while (dispatch_time(DISPATCH_TIME_NOW, 0) <= then) { 69 | n = !n; 70 | } 71 | }); 72 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 73 | while (dispatch_time(DISPATCH_TIME_NOW, 0) <= then) { 74 | n = !n; 75 | } 76 | }); 77 | 78 | while (dispatch_time(DISPATCH_TIME_NOW, 0) <= then) { 79 | //c++; 80 | n = !n; // flipping the coin 81 | } 82 | //NSLog(@"Flips: %d", c); 83 | return n; 84 | } 85 | 86 | // Simple Von Neumann debiasing - throwing away two flips that return the same value. 87 | static inline int BTCFairCoinFlip() { 88 | while(1) { 89 | int a = BTCCoinFlip(); 90 | if (a != BTCCoinFlip()) { 91 | return a; 92 | } 93 | } 94 | } 95 | 96 | NSData* BTCCoinFlipDataWithLength(NSUInteger length) { 97 | NSMutableData* data = [NSMutableData dataWithLength:length]; 98 | unsigned char* bytes = data.mutableBytes; 99 | for (int i = 0; i < length; i++) { 100 | unsigned char byte = 0; 101 | int bits = 8; 102 | while(bits--) { 103 | byte <<= 1; 104 | byte |= BTCFairCoinFlip(); 105 | } 106 | bytes[i] = byte; 107 | } 108 | return data; 109 | } 110 | 111 | NSData* BTCDataWithUTF8String(const char* utf8string) { // deprecated 112 | return BTCDataWithUTF8CString(utf8string); 113 | } 114 | 115 | // Creates data with zero-terminated string in UTF-8 encoding. 116 | NSData* BTCDataWithUTF8CString(const char* utf8string) { 117 | return [[NSData alloc] initWithBytes:utf8string length:strlen(utf8string)]; 118 | } 119 | 120 | NSData* BTCDataWithHexString(NSString* hexString) { // deprecated 121 | return BTCDataFromHex(hexString); 122 | } 123 | 124 | // Init with hex string (lower- or uppercase, with optional 0x prefix) 125 | NSData* BTCDataFromHex(NSString* hexString) { 126 | return BTCDataWithHexCString([hexString cStringUsingEncoding:NSASCIIStringEncoding]); 127 | } 128 | 129 | // Init with zero-terminated hex string (lower- or uppercase, with optional 0x prefix) 130 | NSData* BTCDataWithHexCString(const char* hexCString) { 131 | if (hexCString == NULL) return nil; 132 | 133 | const unsigned char *psz = (const unsigned char*)hexCString; 134 | 135 | while (isspace(*psz)) psz++; 136 | 137 | // Skip optional 0x prefix 138 | if (psz[0] == '0' && tolower(psz[1]) == 'x') psz += 2; 139 | 140 | while (isspace(*psz)) psz++; 141 | 142 | size_t len = strlen((const char*)psz); 143 | 144 | // If the string is not full number of bytes (each byte 2 hex characters), return nil. 145 | if (len % 2 != 0) return nil; 146 | 147 | unsigned char* buf = (unsigned char*)malloc(len/2); 148 | 149 | static const signed char digits[256] = { 150 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 151 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 152 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 153 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, 154 | -1,0xa,0xb,0xc,0xd,0xe,0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, 155 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 156 | -1,0xa,0xb,0xc,0xd,0xe,0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, 157 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 158 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 159 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 160 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 161 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 162 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 164 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 165 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 166 | }; 167 | 168 | unsigned char* bufpointer = buf; 169 | 170 | while (1) { 171 | unsigned char c1 = (unsigned char)*psz++; 172 | signed char n1 = digits[c1]; 173 | if (n1 == (signed char)-1) break; // break when null-terminator is hit 174 | 175 | unsigned char c2 = (unsigned char)*psz++; 176 | signed char n2 = digits[c2]; 177 | if (n2 == (signed char)-1) break; // break when null-terminator is hit 178 | 179 | *bufpointer = (unsigned char)((n1 << 4) | n2); 180 | bufpointer++; 181 | } 182 | 183 | return [[NSData alloc] initWithBytesNoCopy:buf length:len/2]; 184 | } 185 | 186 | 187 | NSString* BTCHexFromDataWithFormat(NSData* data, const char* format) { 188 | if (!data) return nil; 189 | 190 | NSUInteger length = data.length; 191 | if (length == 0) return @""; 192 | 193 | NSMutableData* resultdata = [NSMutableData dataWithLength:length * 2]; 194 | char *dest = resultdata.mutableBytes; 195 | unsigned const char *src = data.bytes; 196 | for (int i = 0; i < length; ++i) { 197 | sprintf(dest + i*2, format, (unsigned int)(src[i])); 198 | } 199 | return [[NSString alloc] initWithData:resultdata encoding:NSASCIIStringEncoding]; 200 | } 201 | 202 | NSString* BTCHexStringFromData(NSData* data) { // deprecated 203 | return BTCHexFromDataWithFormat(data, "%02x"); 204 | } 205 | 206 | NSString* BTCUppercaseHexStringFromData(NSData* data) { // deprecated 207 | return BTCHexFromDataWithFormat(data, "%02X"); 208 | } 209 | 210 | NSString* BTCHexFromData(NSData* data) { 211 | return BTCHexFromDataWithFormat(data, "%02x"); 212 | } 213 | 214 | NSString* BTCUppercaseHexFromData(NSData* data) { 215 | return BTCHexFromDataWithFormat(data, "%02X"); 216 | } 217 | 218 | 219 | NSData* BTCReversedData(NSData* data) { 220 | return BTCReversedMutableData(data); 221 | } 222 | 223 | NSMutableData* BTCReversedMutableData(NSData* data) { 224 | if (!data) return nil; 225 | NSMutableData* md = [NSMutableData dataWithData:data]; 226 | BTCDataReverse(md); 227 | return md; 228 | } 229 | 230 | void BTCReverseBytesLength(void* bytes, NSUInteger length) { 231 | // K&R 232 | if (length <= 1) return; 233 | unsigned char* buf = bytes; 234 | unsigned char byte; 235 | NSUInteger i, j; 236 | for (i = 0, j = length - 1; i < j; i++, j--) { 237 | byte = buf[i]; 238 | buf[i] = buf[j]; 239 | buf[j] = byte; 240 | } 241 | } 242 | 243 | // Reverses byte order in the internal buffer of mutable data object. 244 | void BTCDataReverse(NSMutableData* self) { 245 | BTCReverseBytesLength(self.mutableBytes, self.length); 246 | } 247 | 248 | // Clears contents of the data to prevent leaks through swapping or buffer-overflow attacks. 249 | BOOL BTCDataClear(NSData* data) { 250 | if ([data isKindOfClass:[NSMutableData class]]) { 251 | [(NSMutableData*)data resetBytesInRange:NSMakeRange(0, data.length)]; 252 | return YES; 253 | } 254 | return NO; 255 | } 256 | 257 | NSMutableData* BTCDataRange(NSData* data, NSRange range) { 258 | NSCAssert(range.location != NSNotFound, @"range location should be correct"); 259 | NSCAssert(range.location + range.length <= data.length, @"range should be within bounds of data"); 260 | 261 | if (range.location == NSNotFound) return nil; 262 | if (range.length == 0) return [NSMutableData data]; 263 | if (range.location + range.length > data.length) return nil; 264 | 265 | return [NSMutableData dataWithBytes:((unsigned char*)data.bytes) + range.location length:range.length]; 266 | } 267 | 268 | NSMutableData* BTCSHA1(NSData* data) { 269 | if (!data) return nil; 270 | unsigned char digest[CC_SHA1_DIGEST_LENGTH]; 271 | 272 | __block CC_SHA1_CTX ctx; 273 | CC_SHA1_Init(&ctx); 274 | [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) { 275 | CC_SHA1_Update(&ctx, bytes, (CC_LONG)byteRange.length); 276 | }]; 277 | CC_SHA1_Final(digest, &ctx); 278 | 279 | NSMutableData* result = [NSMutableData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH]; 280 | BTCSecureMemset(digest, 0, CC_SHA1_DIGEST_LENGTH); 281 | return result; 282 | } 283 | 284 | NSMutableData* BTCSHA256(NSData* data) { 285 | if (!data) return nil; 286 | unsigned char digest[CC_SHA256_DIGEST_LENGTH]; 287 | 288 | __block CC_SHA256_CTX ctx; 289 | CC_SHA256_Init(&ctx); 290 | [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) { 291 | CC_SHA256_Update(&ctx, bytes, (CC_LONG)byteRange.length); 292 | }]; 293 | CC_SHA256_Final(digest, &ctx); 294 | 295 | NSMutableData* result = [NSMutableData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH]; 296 | BTCSecureMemset(digest, 0, CC_SHA256_DIGEST_LENGTH); 297 | return result; 298 | } 299 | 300 | NSMutableData* BTCSHA512(NSData* data) { 301 | if (!data) return nil; 302 | unsigned char digest[CC_SHA512_DIGEST_LENGTH]; 303 | 304 | __block CC_SHA512_CTX ctx; 305 | CC_SHA512_Init(&ctx); 306 | [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) { 307 | CC_SHA512_Update(&ctx, bytes, (CC_LONG)byteRange.length); 308 | }]; 309 | CC_SHA512_Final(digest, &ctx); 310 | 311 | NSMutableData* result = [NSMutableData dataWithBytes:digest length:CC_SHA512_DIGEST_LENGTH]; 312 | BTCSecureMemset(digest, 0, CC_SHA512_DIGEST_LENGTH); 313 | return result; 314 | } 315 | 316 | NSMutableData* BTCSHA256Concat(NSData* data1, NSData* data2) { 317 | if (!data1 || !data2) return nil; 318 | unsigned char digest[CC_SHA256_DIGEST_LENGTH]; 319 | 320 | __block CC_SHA256_CTX ctx; 321 | CC_SHA256_Init(&ctx); 322 | [data1 enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) { 323 | CC_SHA256_Update(&ctx, bytes, (CC_LONG)byteRange.length); 324 | }]; 325 | [data2 enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) { 326 | CC_SHA256_Update(&ctx, bytes, (CC_LONG)byteRange.length); 327 | }]; 328 | CC_SHA256_Final(digest, &ctx); 329 | 330 | NSMutableData* result = [NSMutableData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH]; 331 | BTCSecureMemset(digest, 0, CC_SHA256_DIGEST_LENGTH); 332 | return result; 333 | } 334 | 335 | NSMutableData* BTCHash256(NSData* data) { 336 | if (!data) return nil; 337 | unsigned char digest1[CC_SHA256_DIGEST_LENGTH]; 338 | unsigned char digest2[CC_SHA256_DIGEST_LENGTH]; 339 | __block CC_SHA256_CTX ctx; 340 | CC_SHA256_Init(&ctx); 341 | [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) { 342 | CC_SHA256_Update(&ctx, bytes, (CC_LONG)byteRange.length); 343 | }]; 344 | CC_SHA256_Final(digest1, &ctx); 345 | CC_SHA256(digest1, CC_SHA256_DIGEST_LENGTH, digest2); 346 | NSMutableData* result = [NSMutableData dataWithBytes:digest2 length:CC_SHA256_DIGEST_LENGTH]; 347 | BTCSecureMemset(digest1, 0, CC_SHA256_DIGEST_LENGTH); 348 | BTCSecureMemset(digest2, 0, CC_SHA256_DIGEST_LENGTH); 349 | return result; 350 | } 351 | 352 | NSMutableData* BTCHash256Concat(NSData* data1, NSData* data2) { 353 | if (!data1 || !data2) return nil; 354 | 355 | unsigned char digest1[CC_SHA256_DIGEST_LENGTH]; 356 | unsigned char digest2[CC_SHA256_DIGEST_LENGTH]; 357 | 358 | __block CC_SHA256_CTX ctx; 359 | CC_SHA256_Init(&ctx); 360 | [data1 enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) { 361 | CC_SHA256_Update(&ctx, bytes, (CC_LONG)byteRange.length); 362 | }]; 363 | [data2 enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) { 364 | CC_SHA256_Update(&ctx, bytes, (CC_LONG)byteRange.length); 365 | }]; 366 | CC_SHA256_Final(digest1, &ctx); 367 | CC_SHA256(digest1, CC_SHA256_DIGEST_LENGTH, digest2); 368 | 369 | NSMutableData* result = [NSMutableData dataWithBytes:digest2 length:CC_SHA256_DIGEST_LENGTH]; 370 | BTCSecureMemset(digest1, 0, CC_SHA256_DIGEST_LENGTH); 371 | BTCSecureMemset(digest2, 0, CC_SHA256_DIGEST_LENGTH); 372 | return result; 373 | } 374 | 375 | NSMutableData* BTCZero160() { 376 | return [NSMutableData dataWithBytes:_BTCZeroString256 length:20]; 377 | } 378 | 379 | NSMutableData* BTCZero256() { 380 | return [NSMutableData dataWithBytes:_BTCZeroString256 length:32]; 381 | } 382 | 383 | const unsigned char* BTCZeroString256() { 384 | return _BTCZeroString256; 385 | } 386 | 387 | NSMutableData* BTCHMACSHA256(NSData* key, NSData* data) { 388 | if (!key) return nil; 389 | if (!data) return nil; 390 | unsigned char digest[CC_SHA256_DIGEST_LENGTH]; 391 | CCHmac(kCCHmacAlgSHA256, key.bytes, key.length, data.bytes, data.length, digest); 392 | NSMutableData* result = [NSMutableData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH]; 393 | BTCSecureMemset(digest, 0, CC_SHA256_DIGEST_LENGTH); 394 | return result; 395 | } 396 | 397 | NSMutableData* BTCHMACSHA512(NSData* key, NSData* data) { 398 | if (!key) return nil; 399 | if (!data) return nil; 400 | unsigned char digest[CC_SHA512_DIGEST_LENGTH]; 401 | CCHmac(kCCHmacAlgSHA512, key.bytes, key.length, data.bytes, data.length, digest); 402 | NSMutableData* result = [NSMutableData dataWithBytes:digest length:CC_SHA512_DIGEST_LENGTH]; 403 | BTCSecureMemset(digest, 0, CC_SHA512_DIGEST_LENGTH); 404 | return result; 405 | } 406 | 407 | #if BTCDataRequiresOpenSSL 408 | 409 | NSMutableData* BTCRIPEMD160(NSData* data) { 410 | if (!data) return nil; 411 | unsigned char digest[RIPEMD160_DIGEST_LENGTH]; 412 | __block RIPEMD160_CTX ctx; 413 | RIPEMD160_Init(&ctx); 414 | [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) { 415 | RIPEMD160_Update(&ctx, bytes, (size_t)byteRange.length); 416 | }]; 417 | RIPEMD160_Final(digest, &ctx); 418 | 419 | NSMutableData* result = [NSMutableData dataWithBytes:digest length:RIPEMD160_DIGEST_LENGTH]; 420 | BTCSecureMemset(digest, 0, RIPEMD160_DIGEST_LENGTH); 421 | return result; 422 | } 423 | 424 | NSMutableData* BTCHash160(NSData* data) { 425 | if (!data) return nil; 426 | unsigned char digest1[CC_SHA256_DIGEST_LENGTH]; 427 | unsigned char digest2[RIPEMD160_DIGEST_LENGTH]; 428 | __block CC_SHA256_CTX ctx; 429 | CC_SHA256_Init(&ctx); 430 | [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) { 431 | CC_SHA256_Update(&ctx, bytes, (CC_LONG)byteRange.length); 432 | }]; 433 | CC_SHA256_Final(digest1, &ctx); 434 | RIPEMD160(digest1, CC_SHA256_DIGEST_LENGTH, digest2); 435 | 436 | NSMutableData* result = [NSMutableData dataWithBytes:digest2 length:RIPEMD160_DIGEST_LENGTH]; 437 | BTCSecureMemset(digest1, 0, CC_SHA256_DIGEST_LENGTH); 438 | BTCSecureMemset(digest2, 0, RIPEMD160_DIGEST_LENGTH); 439 | return result; 440 | } 441 | 442 | #endif 443 | 444 | 445 | 446 | 447 | // Hashes input with salt using specified number of rounds and the minimum amount of memory (rounded up to a whole number of 256-bit blocks). 448 | // Actual number of hash function computations is a number of rounds multiplied by a number of 256-bit blocks. 449 | // So rounds=1 for 256 Mb of memory would mean 8M hash function calculations (8M blocks by 32 byte to form 256 Mb total). 450 | // Uses SHA256 as an internal hash function. 451 | // Password and salt are hashed before being placed in the first block. 452 | // The whole memory region is hashed after all rounds to generate the result. 453 | // Based on proposal by Sergio Demian Lerner http://bitslog.files.wordpress.com/2013/12/memohash-v0-3.pdf 454 | // Returns a mutable data, so you can cleanup the memory when needed. 455 | NSMutableData* BTCMemoryHardKDF256(NSData* password, NSData* salt, unsigned int rounds, unsigned int numberOfBytes) { 456 | const unsigned int blockSize = CC_SHA256_DIGEST_LENGTH; 457 | 458 | // Will be used for intermediate hash computation 459 | unsigned char block[blockSize]; 460 | 461 | // Context for computing hashes. 462 | CC_SHA256_CTX ctx; 463 | 464 | // Round up the required memory to integral number of blocks 465 | unsigned int numberOfBlocks = numberOfBytes / blockSize; 466 | if (numberOfBytes % blockSize) numberOfBlocks++; 467 | numberOfBytes = numberOfBlocks * blockSize; 468 | 469 | // Make sure we have at least 1 round 470 | rounds = rounds ? rounds : 1; 471 | 472 | // Allocate the required memory 473 | NSMutableData* space = [NSMutableData dataWithLength:numberOfBytes]; 474 | unsigned char* spaceBytes = space.mutableBytes; 475 | 476 | // Hash the password with the salt to produce the initial seed 477 | CC_SHA256_Init(&ctx); 478 | CC_SHA256_Update(&ctx, password.bytes, (CC_LONG)password.length); 479 | CC_SHA256_Update(&ctx, salt.bytes, (CC_LONG)salt.length); 480 | CC_SHA256_Final(block, &ctx); 481 | 482 | // Set the seed to the first block 483 | memcpy(spaceBytes, block, blockSize); 484 | 485 | // Produce a chain of hashes to fill the memory with initial data 486 | for (unsigned int i = 1; i < numberOfBlocks; i++) { 487 | // Put a hash of the previous block into the next block. 488 | CC_SHA256_Init(&ctx); 489 | CC_SHA256_Update(&ctx, spaceBytes + (i - 1) * blockSize, blockSize); 490 | CC_SHA256_Final(block, &ctx); 491 | memcpy(spaceBytes + i * blockSize, block, blockSize); 492 | } 493 | 494 | // Each round consists of hashing the entire space block by block. 495 | for (unsigned int r = 0; r < rounds; r++) { 496 | // For each block, update it with the hash of the previous block 497 | // mixed with the randomly shifted block around the current one. 498 | for (unsigned int b = 0; b < numberOfBlocks; b++) { 499 | unsigned int prevb = (numberOfBlocks + b - 1) % numberOfBlocks; 500 | 501 | // Interpret the previous block as an integer to provide some randomness to memory location. 502 | // This reduces potential for memory access optimization. 503 | // We are simplifying a task here by simply taking first 64 bits instead of full 256 bits. 504 | // In theory it may give some room for optimization, but it would be equivalent to a slightly more efficient prediction of the next block, 505 | // which does not remove the need to store all blocks in memory anyway. 506 | // Also, this optimization would be meaningless if the amount of memory is a power of two. E.g. 16, 32, 64 or 128 Mb. 507 | unsigned long long offset = (*((unsigned long long*)(spaceBytes + prevb * blockSize))) % (numberOfBlocks - 1); // (N-1) is taken to exclude prevb block. 508 | 509 | // Calculate actual index relative to the current block. 510 | offset = (b + offset) % numberOfBlocks; 511 | 512 | // Mix previous block with a random one. 513 | CC_SHA256_Init(&ctx); 514 | CC_SHA256_Update(&ctx, spaceBytes + prevb * blockSize, blockSize); // mix previous block 515 | CC_SHA256_Update(&ctx, spaceBytes + offset * blockSize, blockSize); // mix random block around the current one 516 | CC_SHA256_Final(block, &ctx); 517 | memcpy(spaceBytes + b * blockSize, block, blockSize); 518 | } 519 | } 520 | 521 | // Hash the whole space to arrive at a final derived key. 522 | CC_SHA256_Init(&ctx); 523 | for (unsigned int b = 0; b < numberOfBlocks; b++) { 524 | CC_SHA256_Update(&ctx, spaceBytes + b * blockSize, blockSize); 525 | } 526 | CC_SHA256_Final(block, &ctx); 527 | 528 | NSMutableData* derivedKey = [NSMutableData dataWithBytes:block length:blockSize]; 529 | 530 | // Clean all the buffers to leave no traces of sensitive data 531 | BTCSecureMemset(&ctx, 0, sizeof(ctx)); 532 | BTCSecureMemset(block, 0, blockSize); 533 | BTCSecureMemset(spaceBytes, 0, numberOfBytes); 534 | 535 | return derivedKey; 536 | } 537 | 538 | 539 | 540 | // Hashes input with salt using specified number of rounds and the minimum amount of memory (rounded up to a whole number of 128-bit blocks) 541 | NSMutableData* BTCMemoryHardAESKDF(NSData* password, NSData* salt, unsigned int rounds, unsigned int numberOfBytes) { 542 | // The idea is to use a highly optimized AES implementation in CBC mode to quickly transform a lot of memory. 543 | // For the first round, a SHA256(password+salt) is used as AES key and SHA256(key+salt) is used as Initialization Vector (IV). 544 | // After each round, last 256 bits of space are hashed with IV to produce new IV for the next round. Key remains the same. 545 | // After the final round, last 256 bits are hashed with the AES key to arrive at the resulting key. 546 | // This is based on proposal by Sergio Demian Lerner http://bitslog.files.wordpress.com/2013/12/memohash-v0-3.pdf 547 | // More specifically, on his SeqMemoHash where he shows that when number of rounds is equal to number of memory blocks, 548 | // hash function is strictly memory hard: any less memory than N blocks will make computation impossible. 549 | // If less than N number of rounds is used, execution time grows exponentially with number of rounds, thus quickly making memory/time tradeoff 550 | // increasingly towards choosing an optimal amount of memory. 551 | 552 | // 1 round can be optimized to using just one small block of memory for block cipher operation (n = 1). 553 | // 2 rounds can reduce memory to 2 blocks, but the 2nd round would need recomputation of the 1st round in parallel (n = 1 + (1 + 1) = 3). 554 | // 3 rounds can reduce memory to 3 blocks, but the 3rd round would need recomputation of the 2nd round in parallel (n = 3 + (1 + 3) = 7). 555 | // k-th round can reduce memory to k blocks, the k-th round would need recomputation of the (k-1)-th round in parallel (n(k) = n(k-1) + (1 + n(k-1)) = 1 + 2*n(k-1)) 556 | // Ultimately, k rounds with N blocks of memory would need at minimum k blocks of memory at expense of (2^k - 1) rounds. 557 | 558 | const unsigned int digestSize = CC_SHA256_DIGEST_LENGTH; 559 | const unsigned int blockSize = 128/8; 560 | 561 | // Round up the required memory to integral number of blocks 562 | { 563 | if (numberOfBytes < digestSize) numberOfBytes = digestSize; 564 | unsigned int numberOfBlocks = numberOfBytes / blockSize; 565 | if (numberOfBytes % blockSize) numberOfBlocks++; 566 | numberOfBytes = numberOfBlocks * blockSize; 567 | } 568 | 569 | // Make sure we have at least 3 rounds (1 round would be equivalent to using just 32 bytes of memory; 2 rounds would become 3 rounds if memory was reduced to 32 bytes) 570 | if (rounds < 3) rounds = 3; 571 | 572 | // Will be used for intermediate hash computation 573 | unsigned char key[digestSize]; 574 | unsigned char iv[digestSize]; 575 | 576 | // Context for computing hashes. 577 | CC_SHA256_CTX ctx; 578 | 579 | // Allocate the required memory 580 | NSMutableData* space = [NSMutableData dataWithLength:numberOfBytes + blockSize]; // extra block for the cipher. 581 | unsigned char* spaceBytes = space.mutableBytes; 582 | 583 | // key = SHA256(password + salt) 584 | CC_SHA256_Init(&ctx); 585 | CC_SHA256_Update(&ctx, password.bytes, (CC_LONG)password.length); 586 | CC_SHA256_Update(&ctx, salt.bytes, (CC_LONG)salt.length); 587 | CC_SHA256_Final(key, &ctx); 588 | 589 | // iv = SHA256(key + salt) 590 | CC_SHA256_Init(&ctx); 591 | CC_SHA256_Update(&ctx, key, (CC_LONG)digestSize); 592 | CC_SHA256_Update(&ctx, salt.bytes, (CC_LONG)salt.length); 593 | CC_SHA256_Final(iv, &ctx); 594 | 595 | // Set the space to 1010101010... 596 | memset(spaceBytes, (1 + 4 + 16 + 64), numberOfBytes); 597 | 598 | // Each round consists of encrypting the entire space using AES-CBC 599 | BOOL failed = NO; 600 | for (unsigned int r = 0; r < rounds; r++) { 601 | // Apple implementation - slightly faster than OpenSSL one. 602 | if (1) { 603 | size_t dataOutMoved = 0; 604 | CCCryptorStatus cryptstatus = CCCrypt( 605 | kCCEncrypt, // CCOperation op, /* kCCEncrypt, kCCDecrypt */ 606 | kCCAlgorithmAES, // CCAlgorithm alg, /* kCCAlgorithmAES128, etc. */ 607 | kCCOptionPKCS7Padding, // CCOptions options, /* kCCOptionPKCS7Padding, etc. */ 608 | key, // const void *key, 609 | digestSize, // size_t keyLength, 610 | iv, // const void *iv, /* optional initialization vector */ 611 | spaceBytes, // const void *dataIn, /* optional per op and alg */ 612 | numberOfBytes, // size_t dataInLength, 613 | spaceBytes, // void *dataOut, /* data RETURNED here */ 614 | numberOfBytes + blockSize, // size_t dataOutAvailable, 615 | &dataOutMoved // size_t *dataOutMoved 616 | ); 617 | 618 | if (cryptstatus != kCCSuccess || dataOutMoved != (numberOfBytes + blockSize)) { 619 | failed = YES; 620 | break; 621 | } 622 | } else { // OpenSSL implementation 623 | // EVP_CIPHER_CTX evpctx; 624 | // int outlen1, outlen2; 625 | // 626 | // EVP_EncryptInit(&evpctx, EVP_aes_256_cbc(), key, iv); 627 | // EVP_EncryptUpdate(&evpctx, spaceBytes, &outlen1, spaceBytes, (int)numberOfBytes); 628 | // EVP_EncryptFinal(&evpctx, spaceBytes + outlen1, &outlen2); 629 | // 630 | // if (outlen1 != numberOfBytes || outlen2 != blockSize) 631 | // { 632 | // failed = YES; 633 | // break; 634 | // } 635 | } 636 | 637 | // iv2 = SHA256(iv1 + tail) 638 | CC_SHA256_Init(&ctx); 639 | CC_SHA256_Update(&ctx, iv, digestSize); // mix the current IV. 640 | CC_SHA256_Update(&ctx, spaceBytes + numberOfBytes - digestSize, digestSize); // mix in last 256 bits. 641 | CC_SHA256_Final(iv, &ctx); 642 | } 643 | 644 | NSMutableData* derivedKey = nil; 645 | 646 | if (!failed) { 647 | // derivedKey = SHA256(key + tail) 648 | CC_SHA256_Init(&ctx); 649 | CC_SHA256_Update(&ctx, key, digestSize); // mix the current key. 650 | CC_SHA256_Update(&ctx, spaceBytes + numberOfBytes - digestSize, digestSize); // mix in last 256 bits. 651 | CC_SHA256_Final(key, &ctx); 652 | 653 | derivedKey = [NSMutableData dataWithBytes:key length:digestSize]; 654 | } 655 | 656 | // Clean all the buffers to leave no traces of sensitive data 657 | BTCSecureMemset(&ctx, 0, sizeof(ctx)); 658 | BTCSecureMemset(key, 0, digestSize); 659 | BTCSecureMemset(iv, 0, digestSize); 660 | BTCSecureMemset(spaceBytes, 0, numberOfBytes + blockSize); 661 | 662 | return derivedKey; 663 | 664 | } 665 | 666 | 667 | 668 | 669 | 670 | // Probabilistic memory-hard KDF with 256-bit output and only one difficulty parameter - amount of memory. 671 | // Actual amount of memory is rounded to a whole number of 512-bit blocks. 672 | // Uses SHA512 as internal hash function. 673 | // Computational time is proportional to amount of memory. 674 | // Brutefore with half the memory raises amount of hash computations at least quadratically. 675 | NSMutableData* BTCLocustKDF(NSData* password, NSData* salt, unsigned int numberOfBytes, unsigned int outputLength) { 676 | @autoreleasepool { 677 | 678 | if (outputLength == 0) return [NSMutableData data]; 679 | 680 | const unsigned maxJumps = 4; 681 | const unsigned int blockSize = CC_SHA512_DIGEST_LENGTH; 682 | 683 | // Round up the required memory to integral number of blocks. 684 | // Minimum size is 512 bits. 685 | numberOfBytes = (numberOfBytes / blockSize) * blockSize + ((numberOfBytes % blockSize) ? blockSize : 0); 686 | if (numberOfBytes < 2*blockSize) numberOfBytes = 2*blockSize; 687 | 688 | // Cap output to the total space length. 689 | outputLength = MIN(numberOfBytes, outputLength); 690 | 691 | // Context for computing hashes. 692 | CC_SHA512_CTX ctx; 693 | 694 | // Allocate the required memory 695 | NSMutableData* space = [NSMutableData dataWithLength:numberOfBytes]; 696 | unsigned char* spaceBytes = space.mutableBytes; 697 | 698 | // Initial two blocks: 699 | // 1. SHA512(password + salt) 700 | // 2. SHA512(SHA512(password + salt)) 701 | 702 | CC_SHA512_Init(&ctx); 703 | CC_SHA512_Update(&ctx, password.bytes, (CC_LONG)password.length); 704 | CC_SHA512_Update(&ctx, salt.bytes, (CC_LONG)salt.length); 705 | CC_SHA512_Final(spaceBytes, &ctx); 706 | 707 | CC_SHA512_Init(&ctx); 708 | CC_SHA512_Update(&ctx, spaceBytes, blockSize); 709 | CC_SHA512_Final(spaceBytes + blockSize, &ctx); 710 | 711 | // At each step we try to reinforce memory requirement while spending a constant amount of time. 712 | // Some applications wouldn't like to waste more than 100 ms on KDF, some are okay to spend 5 sec. 713 | // Yet, the more memory we can use in that period of time, the better. 714 | 715 | // We start with just 2 blocks of data. It's pointless to waste time filling the whole space. 716 | // It's also pointless to use any of the remaining space. The only source of entropy we have is in the very beginning. 717 | // We use pseudo-random locations in the initial state to produce the next block therefore forcing the attacker to keep the result around. 718 | 719 | // When we arrive at the end, we take the last 256 bits and return them as a result. 720 | 721 | uint64_t buf[8] = {0}; 722 | uint64_t a; 723 | uint64_t b; 724 | 725 | for (unsigned long i = 2*blockSize; i < numberOfBytes; i += blockSize) { 726 | // A = previous block (filled). 727 | // A is composed of 8 64-bit numbers: {A1, A2, A3, A4, A5, A6, A7, A8}. 728 | // Each number is treated as a byte pointer to a 64-bit word located between the beginning and 729 | // the previous block (i.e. modulo i - blockSize - wordSize). Offset is counted in bytes, not in number 730 | // of words which produces better diffusion. 731 | 732 | // Security analysis (work in progress): 733 | // Lets say attacker wants to reduce amount of memory by a factor of 2. 734 | // He will complete 50% of necessary computations with the available memory. 735 | // Then he would have to overwrite some previous results with new data. 736 | // One possible attack is to throw away every second block (or word). This way if the pointer arrives on a missing 737 | // word, it can be quickly recomputed from the previous data. However that data will also cause touching missing words 738 | // with overwhelming probability (we have 8 pseudo-random jumps K times). 739 | // 740 | // Amount of memory is M words. 741 | // Amount of space at step N is 8*N words. 742 | // Amount of pseudo-random jumps is 8*K. 743 | // Probability for one jump to arrive within stored words is R = M/(8*N). 744 | // Probability for 8*K jumps to arrive within stored words is R^(8*K). 745 | // Probability that one will need some thrown away block is (1 - R)^(8*K)) which for K = 2 and R < 0.5 is close to 99.99%. 746 | // We need to compute a probability of one specific word not being used over total N iterations. 747 | // For word number n is not used until n/8 steps performed. 748 | // At each step s from n/8 till N we have this probability that the word will not be used: (1 - 1/(8*s)) 749 | // Total probability that a specific word n won't be used throughout entire computation is ∏(1 - 1/(8*s)) over s = n/8 till N. 750 | // This probability converges to a not very small probability mostly defined by the first terms. 751 | // Lets for simplicity define an upper bound for this probability as 1 - 1/(8*s) and see how it goes for multiple words. 752 | // 753 | // The real model is when we throw away some words after X steps. 754 | // We need to compute real cost for throwing these words away and prove that it'll surpass any winnings or make computation impractically slower. 755 | // One approach would be like this: each miss requires some amount of temporary memory. 756 | // At some number of misses amount of temporary memory may reach the amount of memory being thrown away (on average). 757 | // If that so, it is not important how slower the computation becomes: memory requirement still holds. 758 | 759 | // B = block size of memory 760 | // pi = probability of miss of a block 761 | // miss_cost(n) = (B*miss_prob(n) + miss_prob(n)*miss_cost(n+1)) = miss_prob(n)*(Block + 8*miss_cost(n+1)) 762 | // 763 | 764 | uint64_t *src = (uint64_t*)(spaceBytes + i - blockSize); 765 | 766 | for (int w = 0; w < 8; w++) { buf[w] = *(src+w); } 767 | 768 | // We have several rounds of jumps to make sure it's costlier to throw away previously computed values. 769 | for (int jumps = 0; jumps < maxJumps; jumps++) { 770 | // At each round of jumps we split the recently computed 512-bit block in 8 words (64 bit each). 771 | // Each word acts as a random offset in the space before current block. 772 | // The word at which we arrive is interpreted as another offset for next round of jumps. 773 | for (int w = 0; w < 8; w++) { 774 | a = buf[w]; 775 | // Initial step modulo: (2*64 - 64 - 8 + 1) = 64-8 = 57. So the max offset is 56 and the whole last byte of the prev block can be consumed. This as 776 | b = *(uint64_t *)(spaceBytes + (a % (i - blockSize - 8 + 1))); 777 | 778 | // Make this jump unique so this word b does not always point to the same location. 779 | // So attacker cannot predict which blocks are less likely to be hit. 780 | // SHA512 guarantees lack of correlation between input (b) and hash value (a) 781 | // therefore XORing them should not introduce bias. 782 | buf[w] = b ^ a; 783 | } 784 | } 785 | 786 | CC_SHA512_Init(&ctx); 787 | 788 | // Hash all the resulting words after jumping and XORing. 789 | CC_SHA512_Update(&ctx, buf, 8*sizeof(uint64_t)); 790 | 791 | // Hash also the entire previous block. 792 | // This guarantees us security level of PBKDF2 with equivalent number of rounds. 793 | // Even if we have bias due to jumps at some point, this will give us a well-diffused hash value. 794 | CC_SHA512_Update(&ctx, spaceBytes + i - blockSize, blockSize); 795 | 796 | CC_SHA512_Final(spaceBytes + i, &ctx); 797 | } 798 | 799 | // The resulting key is simply the remaining bits of the data space. 800 | 801 | NSMutableData* result = [NSMutableData dataWithBytes:spaceBytes + numberOfBytes - outputLength length:outputLength]; 802 | 803 | // Clear sensitive data from memory. 804 | 805 | BTCSecureMemset(&ctx, 0, sizeof(ctx)); 806 | BTCSecureMemset(spaceBytes, 0, space.length); 807 | BTCSecureMemset(buf, 0, sizeof(buf)); 808 | a = 0; 809 | b = 0; 810 | 811 | return result; 812 | } 813 | } 814 | 815 | NSMutableData* BTCLocustKDF128(NSData* password, NSData* salt, unsigned int numberOfBytes) { 816 | return BTCLocustKDF(password, salt, numberOfBytes, 16); 817 | } 818 | 819 | NSMutableData* BTCLocustKDF160(NSData* password, NSData* salt, unsigned int numberOfBytes) { 820 | return BTCLocustKDF(password, salt, numberOfBytes, 20); 821 | } 822 | 823 | NSMutableData* BTCLocustKDF256(NSData* password, NSData* salt, unsigned int numberOfBytes) { 824 | return BTCLocustKDF(password, salt, numberOfBytes, 32); 825 | } 826 | 827 | NSMutableData* BTCLocustKDF512(NSData* password, NSData* salt, unsigned int numberOfBytes) { 828 | return BTCLocustKDF(password, salt, numberOfBytes, 64); 829 | } 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | --------------------------------------------------------------------------------