├── 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 |
--------------------------------------------------------------------------------