├── .DS_Store ├── Classes ├── .DS_Store ├── DataTool.h ├── DataTool.m ├── ECDHAlgorithmiOS.h ├── GMEllipticCurveCrypto │ ├── GMEllipticCurveCrypto+hash.h │ ├── GMEllipticCurveCrypto.h │ ├── GMEllipticCurveCrypto+hash.m │ └── GMEllipticCurveCrypto.m └── ECDHAlgorithmiOS.m ├── LICENSE ├── README.md └── ECDHAlgorithmiOS.podspec /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Json031/ECDHAlgorithmiOS/HEAD/.DS_Store -------------------------------------------------------------------------------- /Classes/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Json031/ECDHAlgorithmiOS/HEAD/Classes/.DS_Store -------------------------------------------------------------------------------- /Classes/DataTool.h: -------------------------------------------------------------------------------- 1 | // 2 | // DataTool.h 3 | // 4 | // Created by MorganChen on 2025/3/26. 5 | // 6 | 7 | 8 | @interface DataTool : NSObject 9 | 10 | /// convert hexStr to data 11 | /// - Parameter hexStr: hexStr 12 | + (NSData *)dataFromHexStr:(NSString *)hexStr; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2025 Morgan Chen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Classes/DataTool.m: -------------------------------------------------------------------------------- 1 | // 2 | // DataTool.m 3 | // 4 | // Created by MorganChen on 2025/3/26. 5 | // 6 | 7 | #import "DataTool.h" 8 | 9 | @implementation DataTool 10 | 11 | /// convert hexStr to data 12 | /// - Parameter hexStr: hexStr 13 | + (NSData *)dataFromHexStr:(NSString *)hexStr { 14 | if (!hexStr || [hexStr length] == 0) { 15 | return nil; 16 | } 17 | 18 | NSMutableData *data = [NSMutableData dataWithCapacity:[hexStr length] / 2]; 19 | NSUInteger index = 0; 20 | NSUInteger length = [hexStr length]; 21 | BOOL isOddLength = (length % 2) != 0; 22 | 23 | while (index < length) { 24 | NSRange range = NSMakeRange(index, 2); 25 | NSString *hexString = [hexStr substringWithRange:range]; 26 | if (range.length > [hexStr length] - index) { 27 | // Handle case where string length is odd 28 | hexString = [hexStr substringWithRange:NSMakeRange(index, 1)]; 29 | hexString = [NSString stringWithFormat:@"0%@", hexString]; // Pad with leading zero 30 | } 31 | 32 | unsigned int wholeNumber; 33 | NSScanner *scanner = [NSScanner scannerWithString:hexString]; 34 | BOOL success = [scanner scanHexInt:&wholeNumber]; 35 | if (!success) { 36 | // Handle error, e.g., return nil or log an error 37 | return nil; 38 | } 39 | 40 | uint8_t byte = (uint8_t)wholeNumber; 41 | [data appendBytes:&byte length:1]; 42 | 43 | index += 2; 44 | } 45 | 46 | return data; 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /Classes/ECDHAlgorithmiOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // ECDHAlgorithmiOS.h 3 | // 4 | // Created by MorganChen on 2025/3/26. 5 | // 6 | 7 | #import 8 | #import "GMEllipticCurveCrypto.h" 9 | #import "DataTool.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface ECDHAlgorithmiOS : NSObject 14 | 15 | @property (nonatomic, assign) GMEllipticCurve gmellipticCurve; 16 | 17 | // If false, the public key is 65 bytes, with the first byte being 04. If true, the public key is 32 bytes 18 | @property (nonatomic, assign) Boolean compressedPublicKey; 19 | 20 | + (instancetype)shareRequest; 21 | #pragma mark GMEllipticCurveCrypto 22 | /// generate a key pair (64 bytes for public key and 32 bytes for private key) 23 | /// - Parameters: 24 | /// - gmellipticCurve: GMEllipticCurveSecp256r1 25 | /// - compressedPublicKey: If false, the public key is 65 bytes, with the first byte being 04. If true, the public key is 32 bytes 26 | - (void)generateKeys:(GMEllipticCurve)gmellipticCurve compressedPublicKey:(Boolean)compressedPublicKey; 27 | 28 | /// Obtain the public key and send it to the other party, the other party can get the share key with this public key 29 | - (NSData *)getPublicKey; 30 | 31 | /// After sending one's own public key to the other party, the other party (server or device) returns the processing of the public key 32 | /// Wait for the other party to notify and send its public key, call configThirdPublicKey to generate a share key by combining the other party's public key with its own private key 33 | /// - Parameter otherPKStr: the other party public key 34 | - (void)generateSharedKeyWithOtherPK:(NSString *)otherPKStr; 35 | 36 | /// A shared key generated from the public key of another party (server or device) of 64 bytes and its own private key of 32 bytes 37 | - (NSData * _Nullable)getSharedKey; 38 | 39 | - (void)resetGMEllipticCurveCrypto; 40 | 41 | @end 42 | 43 | NS_ASSUME_NONNULL_END 44 | -------------------------------------------------------------------------------- /Classes/GMEllipticCurveCrypto/GMEllipticCurveCrypto+hash.h: -------------------------------------------------------------------------------- 1 | // 2 | // GMEllipticCurveCrypto+hash.h 3 | // 4 | // BSD 2-Clause License 5 | // 6 | // Copyright (c) 2014 Richard Moore. 7 | // 8 | // All rights reserved. 9 | // 10 | // Redistribution and use in source and binary forms, with or without modification, 11 | // are permitted provided that the following conditions are met: 12 | // 13 | // 1. Redistributions of source code must retain the above copyright notice, this 14 | // list of conditions and the following disclaimer. 15 | // 16 | // 2. Redistributions in binary form must reproduce the above copyright notice, 17 | // this list of conditions and the following disclaimer in the documentation 18 | // and/or other materials provided with the distribution. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 | // IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 | // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | // POSSIBILITY OF SUCH DAMAGE. 30 | // 31 | 32 | #import 33 | 34 | #import "GMEllipticCurveCrypto.h" 35 | 36 | @interface GMEllipticCurveCrypto (hash) 37 | 38 | - (NSData*)hashSHA256AndSignData: (NSData*)data; 39 | - (BOOL)hashSHA256AndVerifySignature: (NSData*)signature forData: (NSData*)data; 40 | 41 | - (NSData*)hashSHA384AndSignData: (NSData*)data; 42 | - (BOOL)hashSHA384AndVerifySignature: (NSData*)signature forData: (NSData*)data; 43 | 44 | - (NSData*)encodedSignatureForHash: (NSData*)hash; 45 | - (BOOL)verifyEncodedSignature: (NSData*)encodedSignature forHash: (NSData*)hash; 46 | 47 | - (NSData*)hashSHA256AndSignDataEncoded: (NSData*)data; 48 | - (BOOL)hashSHA256AndVerifyEncodedSignature: (NSData*)encodedSignature forData: (NSData*)data; 49 | 50 | - (NSData*)hashSHA384AndSignDataEncoded: (NSData*)data; 51 | - (BOOL)hashSHA384AndVerifyEncodedSignature: (NSData*)encodedSignature forData: (NSData*)data; 52 | 53 | @end 54 | 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Click here to go to the Swift version

2 | 3 | # ECDHAlgorithmiOS 4 | [![CocoaPods](https://img.shields.io/cocoapods/v/ECDHAlgorithmiOS.svg)](https://cocoapods.org/pods/ECDHAlgorithmiOS) 5 | [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/Json031/ECDHAlgorithmiOS/blob/main/LICENSE) 6 |
7 | 一种基于椭圆曲线密码学的OC密钥交换协议算法开源项目。 8 |
An open-source project for OC key exchange protocol algorithm based on elliptic curve cryptography. 9 | 10 | # Installation 安装: 11 | 12 | ## CocoaPods 13 | The [ECDHAlgorithmiOS SDK for iOS](https://github.com/Json031/ECDHAlgorithmiOS) is available through [CocoaPods](http://cocoapods.org). If CocoaPods is not installed, install it using the following command. Note that Ruby will also be installed, as it is a dependency of Cocoapods. 14 | ```bash 15 | brew install cocoapods 16 | pod setup 17 | ``` 18 | ```bash 19 | $iOSVersion = '11.0' 20 | 21 | platform :ios, $iOSVersion 22 | use_frameworks! 23 | 24 | target 'YourProjectName' do 25 | pod 'ECDHAlgorithmiOS' # Full version with all features 26 | end 27 | ``` 28 | 29 | ## 手动安装 30 | 将Classes文件夹拽入项目中,导入头文件:#import "ECDHAlgorithmiOS.h" 31 | 32 | ## ECDH算法 33 | 34 | ECDH非对称加密方式交换对称密钥流程: 35 |
1️⃣调用方法generateKeys生成密钥对(公钥64字节和私钥32字节) 36 |
2️⃣通过蓝牙或http方式,将步骤1️⃣生成的公钥发给对方 37 |
3️⃣等待对方蓝牙Notify等方式发送它的公钥过来,调用configThirdPublicKey将对方的公钥和自己的私钥生成share key,对方也通过步骤2️⃣接收到的公钥与其私钥生成share key,根据椭圆曲线点乘的交换性原理这两个share key是一样的 38 |
4️⃣将共享密钥发送到后端服务器,如果后端服务器验证两者相同,则表示身份验证成功; 39 |
可用于双方身份验证及绑定关联; 40 |
还可以作为后续通信过程的数据对称加密算法的密钥,基于椭圆曲线离散对数问题的困难性,使得攻击者难以从公开的通信信息中获取共享密钥,确保了通信数据安全性; 41 | 42 | The process of exchanging symmetric keys using ECDH asymmetric encryption method: 43 |
1 Call method generateKeys to generate a key pair (64 bytes for public key and 32 bytes for private key). 44 |
2 Call the sendPublicKey method to convert step 2 Send the public key to the other party. 45 |
3 Wait for the other party to notify and send its public key, call configThirdPublicKey to generate a share key from the other party's public key and its own private key, and the other party also uses step 3 The received public key and its private key generate a share key, and the two share keys are the same. 46 |
4 Send the share key to the backend server, and if the backend server verifies that both are the same, it indicates successful authentication. 47 |
Can be used for mutual authentication and binding association; 48 |
It can also serve as a key for data symmetric encryption algorithms in subsequent communication processes to ensure the security of communication data; 49 | 50 | # ECDH asymmetric encryption example: 51 | ✳️Param: set compressedPublicKey = false 52 | 53 | 1️⃣GMEllipticCurveCrypto1 generateKeyPair: 54 |
publicKey1: "d4b78cec17668f06ae96943d71049c7f75a620cb50b6facff9bdb09a174f7a808c22f0e51f1b2578e9fd7682be17fb8e07deb6517b68880273baee7fc4d6efdd" 55 |
privateKey1: "jCfYOOEE+t2BHvUHjp1O0RObXhND7JLV9BaHGR1XDZE=" 56 | 57 | 2️⃣GMEllipticCurveCrypto2 generateKeyPair: 58 |
publicKey2: "79cff9b55e086234c43f5c64a775eb20f39c7dc11bf3b2962677d6019c42af5cf57d6d5007fa7ccc94bddec7b1b8fdbf68e50642de88b7223e40007602290e50" 59 |
privateKey2: "ISfGAyQrHKX4ELRoZLls3TqBXVf7yqoahEgj7RMX0Us=" 60 | 61 | 3️⃣generateSharedKeyWithOtherPK 62 |
sharedKey1 = GMEllipticCurveCrypto1.sharedSecret(forPublicKey: publicKey2) 63 |
sharedKey2 = GMEllipticCurveCrypto2.sharedSecret(forPublicKey: publicKey1) 64 | 65 | ✅Result: sharedKey1 should equal to sharedKey2 66 |
sharedKey1: "2fd727d984828a28ab6a521f53dd2d06c67fbb80104aef8c1369a9e352094424" 67 |
sharedKey2: "2fd727d984828a28ab6a521f53dd2d06c67fbb80104aef8c1369a9e352094424" 68 | 69 | # Troubleshooting 70 | 71 |
72 | Missing sharedKey,Please obtain the public key from a third party first, and then pass it to the sharedScreetForPublicKey method of GMEllipticCurveCrypto to generate the share key 73 | 74 | Need to obtain the public key from a third party first, then go to generateSharedKeyWithOtherPK. 75 | 76 |
77 | 78 |
79 | 80 | # License 81 | This library is licensed under the [MIT License](https://github.com/Json031/ECDHAlgorithmiOS/blob/main/LICENSE). 82 | -------------------------------------------------------------------------------- /Classes/ECDHAlgorithmiOS.m: -------------------------------------------------------------------------------- 1 | // 2 | // ECDHAlgorithmiOS.m 3 | // 4 | // Created by MorganChen on 2025/3/26. 5 | // 6 | 7 | #import "ECDHAlgorithmiOS.h" 8 | 9 | //Default Settings 10 | #define ECDH_DefaultGMEllipticCurve GMEllipticCurveSecp256r1 11 | #define ECDH_DefaultCompressedPublicKey YES 12 | 13 | @interface ECDHAlgorithmiOS() 14 | 15 | /// A shared key generated from the public key of another party (server or device) of 64 bytes and its own private key of 32 bytes 16 | @property (nonatomic, strong) NSData * _Nullable sharedKey; 17 | 18 | 19 | //GME elliptic curve encryption object 20 | @property (nonatomic, strong) GMEllipticCurveCrypto * _Nullable gmellipticCurveCrypto; 21 | 22 | @end 23 | 24 | @implementation ECDHAlgorithmiOS 25 | 26 | + (instancetype)shareRequest { 27 | static ECDHAlgorithmiOS *shared; 28 | static dispatch_once_t onceToken; 29 | dispatch_once(&onceToken, ^{ 30 | shared = [[ECDHAlgorithmiOS alloc] init]; 31 | }); 32 | return shared; 33 | } 34 | 35 | - (void)resetGMEllipticCurveCrypto { 36 | self.gmellipticCurveCrypto = nil; 37 | } 38 | 39 | - (void)setGmellipticCurve:(GMEllipticCurve)gmellipticCurve { 40 | _gmellipticCurve = gmellipticCurve; 41 | //GMEllipticCurveSecp256r1 42 | self.gmellipticCurveCrypto = [GMEllipticCurveCrypto generateKeyPairForCurve:gmellipticCurve]; 43 | } 44 | 45 | //If false, the public key is 65 bytes, with the first byte being 04. If true, the public key is 32 bytes 46 | - (void)setCompressedPublicKey:(Boolean)compressedPublicKey { 47 | _compressedPublicKey = compressedPublicKey; 48 | self.gmellipticCurveCrypto.compressedPublicKey = compressedPublicKey; 49 | } 50 | 51 | #pragma mark GMEllipticCurveCrypto 52 | /// generate a key pair (64 bytes for public key and 32 bytes for private key) 53 | /// - Parameters: 54 | /// - gmellipticCurve: GMEllipticCurveSecp256r1 55 | /// - compressedPublicKey: If false, the public key is 65 bytes, with the first byte being 04. If true, the public key is 32 bytes 56 | - (void)generateKeys:(GMEllipticCurve)gmellipticCurve compressedPublicKey:(Boolean)compressedPublicKey { 57 | self.gmellipticCurve = gmellipticCurve; 58 | self.compressedPublicKey = compressedPublicKey; 59 | } 60 | 61 | /// Obtain the public key and send it to the other party, the other party can get the share key with this public key 62 | - (NSData *)getPublicKey { 63 | if (self.gmellipticCurveCrypto == nil) { 64 | [self generateKeys:ECDH_DefaultGMEllipticCurve compressedPublicKey:ECDH_DefaultCompressedPublicKey]; 65 | } 66 | return [self.gmellipticCurveCrypto.publicKey subdataWithRange:NSMakeRange(1, self.gmellipticCurveCrypto.publicKey.length - 1)]; 67 | } 68 | 69 | 70 | 71 | /// After sending one's own public key to the other party, the other party (server or device) returns the processing of the public key 72 | /// Wait for the other party to notify and send its public key, call configThirdPublicKey to generate a share key by combining the other party's public key with its own private key 73 | /// - Parameter otherPKStr: the other party public key 74 | - (void)generateSharedKeyWithOtherPK:(NSString *)otherPKStr { 75 | if (self.gmellipticCurveCrypto == nil) { 76 | [self generateKeys:ECDH_DefaultGMEllipticCurve compressedPublicKey:ECDH_DefaultCompressedPublicKey]; 77 | } 78 | // attach a 0x04 byte to the first byte of the received public key, call the compressPublicKey method to compress it into a 32-bit public key, and then pass it to the sharedSecretForPublicKey method to obtain the shared key 79 | Byte bytes[1]; 80 | bytes[0] = 0x04; 81 | NSMutableData *data = [NSMutableData dataWithBytes:bytes length:1]; 82 | NSData *opkData = [DataTool dataFromHexStr:otherPKStr]; 83 | [data appendData:opkData]; 84 | NSData *compressPublicKey = [self.gmellipticCurveCrypto compressPublicKey:data]; 85 | //Generate a share key by combining the other party's public key with their own private key 86 | self.sharedKey = [self.gmellipticCurveCrypto sharedSecretForPublicKey:compressPublicKey]; 87 | //send sharedKey to device or server for verification 88 | } 89 | 90 | /// A shared key generated from the public key of another party (server or device) of 64 bytes and its own private key of 32 bytes 91 | - (NSData * _Nullable)getSharedKey { 92 | if (self.gmellipticCurveCrypto == nil) { 93 | [self generateKeys:GMEllipticCurveSecp256r1 compressedPublicKey:YES]; 94 | } 95 | if (!self.sharedKey) { 96 | [NSException raise:@"Missing sharedKey" format:@"Please obtain the public key from a third party first, and then pass it to the sharedScreetForPublicKey method of GMEllipticCurveCrypto to generate the share key"]; 97 | } 98 | return self.sharedKey; 99 | } 100 | 101 | @end 102 | -------------------------------------------------------------------------------- /Classes/GMEllipticCurveCrypto/GMEllipticCurveCrypto.h: -------------------------------------------------------------------------------- 1 | // 2 | // GMEllipticCurveCrypto.h 3 | // 4 | // BSD 2-Clause License 5 | // 6 | // Copyright (c) 2014 Richard Moore. 7 | // 8 | // All rights reserved. 9 | // 10 | // Redistribution and use in source and binary forms, with or without modification, 11 | // are permitted provided that the following conditions are met: 12 | // 13 | // 1. Redistributions of source code must retain the above copyright notice, this 14 | // list of conditions and the following disclaimer. 15 | // 16 | // 2. Redistributions in binary form must reproduce the above copyright notice, 17 | // this list of conditions and the following disclaimer in the documentation 18 | // and/or other materials provided with the distribution. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 | // IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 | // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | // POSSIBILITY OF SUCH DAMAGE. 30 | // 31 | 32 | #import 33 | 34 | 35 | /** 36 | * This is an Objective-C wrapper for easy-ecc by Kenneth MacKay, slightly re-written 37 | * to support choosing a curve at runtime. 38 | * 39 | * To generate a new key-pair: 40 | * GMEllipticCurveCrypto *crypto = [GMEllipticCurveCrypto generateKeyPairForCurve:GMEllipticCurveSecp256r1]; 41 | * NSLog(@"PublicKey=%@, PrivateKey=%@", crypto.publicKeyBase64, crypto.privateKeyBase64); 42 | * 43 | * To sign a message hash: 44 | * GMEllipticCurveCrypto *crypto = [GMEllipticCurveCrypto initWithCurve:GMEllipticCurveSecp256r1]; 45 | * crypto.privateKeyBase64 = @"PimSbZgj6h+Q7kqRZMn9Bo6qAhoDhEsvXa+uiO4mJD8="; 46 | * NSData *signature = [crypto signatureForHash:hash]; 47 | * 48 | * To verify a signature: 49 | * GMEllipticCurveCrypto *crypto = [GMEllipticCurveCrypto initWithCurve:GMEllipticCurveSecp256r1]; 50 | * crypto.publicKeyBase64 = @"A2BKB2Io3hPPuQAGDDDUeGr/juQr9OxdlDFypcyKDm1M" 51 | * int valid = [crypto verifySignature:signature forHash:hash]; 52 | * 53 | */ 54 | 55 | 56 | typedef enum GMEllipticCurve { 57 | GMEllipticCurveNone = 0, 58 | GMEllipticCurveSecp128r1 = 128, 59 | GMEllipticCurveSecp192r1 = 192, 60 | GMEllipticCurveSecp256r1 = 256, 61 | GMEllipticCurveSecp384r1 = 384, 62 | } GMEllipticCurve; 63 | 64 | 65 | 66 | @interface GMEllipticCurveCrypto : NSObject 67 | 68 | 69 | /** 70 | * Create a new instance with new public key and private key pair. 71 | */ 72 | + (GMEllipticCurveCrypto*)generateKeyPairForCurve: (GMEllipticCurve)curve; 73 | 74 | 75 | /** 76 | * Given a private key or public key, determine which is the appropriate curve 77 | */ 78 | + (GMEllipticCurve)curveForKey: (NSData*)privateOrPublicKey; 79 | + (GMEllipticCurve)curveForKeyBase64: (NSString*)privateOrPublicKey; 80 | 81 | 82 | /** 83 | * Given a private key or public key, create an instance with the appropriate curve and key 84 | */ 85 | + (GMEllipticCurveCrypto*)cryptoForKey: (NSData*)privateOrPublicKey; 86 | + (GMEllipticCurveCrypto*)cryptoForKeyBase64: (NSString*)privateOrPublicKey; 87 | 88 | 89 | + (id)cryptoForCurve: (GMEllipticCurve)curve; 90 | - (id)initWithCurve: (GMEllipticCurve)curve; 91 | 92 | /** 93 | * The length of the curve in bits. 94 | */ 95 | @property (nonatomic, readonly) int bits; 96 | 97 | /** 98 | * The common name given to the curve (e.g. secp192r1). 99 | */ 100 | @property (nonatomic, readonly) NSString *name; 101 | 102 | /** 103 | * Determines whether the public key will be compressed or uncompressed. 104 | * 105 | * It is updated when a public key is assigned and can be changed anytime 106 | * to select what the publicKey property emits. 107 | * 108 | * A compressed point stores only the x co-ordinate of the point as well as 109 | * a leading byte to indicate the parity of the y co-ordinate, which can then 110 | * be computed from x. 111 | * 112 | * By default, keys are compressed. 113 | */ 114 | @property (nonatomic, assign) BOOL compressedPublicKey; 115 | 116 | /** 117 | * The public key for an elliptic curve. 118 | * 119 | * A compressed public key's length is ((curve_bits / 8) + 1) bytes. 120 | * An uncompressed public key's length is (2 * (curve_bits / 8) + 1) bytes. 121 | */ 122 | @property (nonatomic, strong) NSData *publicKey; 123 | 124 | /** 125 | * The public key encoded in base64 126 | */ 127 | @property (nonatomic, strong) NSString *publicKeyBase64; 128 | 129 | /** 130 | * The public key x coordinate encoded in base64 131 | */ 132 | @property (nonatomic, strong) NSString *publicKeyXBase64; 133 | 134 | /** 135 | * The public key y coordinate encoded in base64 136 | */ 137 | @property (nonatomic, strong) NSString *publicKeyYBase64; 138 | 139 | /** 140 | * The private key for an elliptic curve. 141 | * 142 | * This is also sometimes referred to as the secret exponent. 143 | * 144 | * A private key's length is (crypto_bits / 8) bytes. 145 | */ 146 | @property (nonatomic, strong) NSData *privateKey; 147 | 148 | /** 149 | * The private key encoded in base64 150 | */ 151 | @property (nonatomic, strong) NSString *privateKeyBase64; 152 | 153 | 154 | @property (nonatomic, readonly) int sharedSecretLength; 155 | - (NSData*)sharedSecretForPublicKey: (NSData*)otherPublicKey; 156 | - (NSData*)sharedSecretForPublicKeyBase64: (NSString*)otherPublicKeyBase64; 157 | 158 | @property (nonatomic, readonly) int hashLength; 159 | - (NSData*)signatureForHash: (NSData*)hash; 160 | 161 | @property (nonatomic, readonly) int signatureLength; 162 | - (BOOL)verifySignature: (NSData*)signature forHash: (NSData*)hash; 163 | //需要将公钥压缩成32位的公钥并附加一个0x04字节到第一个字节,然后再去传给sharedSecretForPublicKey方法来获取共享密钥share key 164 | - (NSData*)compressPublicKey: (NSData*)publicKey; 165 | @end 166 | -------------------------------------------------------------------------------- /ECDHAlgorithmiOS.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint ECDHAlgorithmiOS.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |spec| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | spec.name = "ECDHAlgorithmiOS" 19 | spec.version = "1.0.6" 20 | spec.summary = "A key exchange protocol algorithm based on elliptic curve cryptography for iOS." 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | spec.description = <<-DESC 28 | 29 | A key exchange protocol algorithm based on elliptic curve cryptography for iOS. 30 | 31 | The process of exchanging symmetric keys using ECDH asymmetric encryption method: 32 | 1 Connect the device, obtain CBService, obtain CBCharacteristic, subscribe to CBCharacteristic Notify: peripheral.setNotifyValue(true, for: characteristic) 33 | 2 Call method generateKeys to generate a key pair (64 bytes for public key and 32 bytes for private key) 34 | 3 Call the sendPublicKey method to convert step 2 Send the public key to the other party 35 | 4 Wait for the other party to notify and send its public key, call configThirdPublicKey to generate a share key from the other party's public key and its own private key, and the other party also uses step 3 The received public key and its private key generate a share key, and the two share keys are the same 36 | Send the share key to the backend server, and if the backend server verifies that both are the same, it indicates successful authentication 37 | 38 | DESC 39 | 40 | spec.homepage = "https://github.com/Json031/ECDHAlgorithmiOS" 41 | # spec.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 42 | 43 | 44 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 45 | # 46 | # Licensing your code is important. See https://choosealicense.com for more info. 47 | # CocoaPods will detect a license file if there is a named LICENSE* 48 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 49 | # 50 | 51 | # spec.license = "MIT (example)" 52 | spec.license = { :type => "MIT", :file => "LICENSE" } 53 | 54 | 55 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 56 | # 57 | # Specify the authors of the library, with email addresses. Email addresses 58 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 59 | # accepts just a name if you'd rather not provide an email address. 60 | # 61 | # Specify a social_media_url where others can refer to, for example a twitter 62 | # profile URL. 63 | # 64 | 65 | spec.author = { "MorganChen" => "cjfmail@foxmail.com" } 66 | # Or just: spec.author = "Rate" 67 | # spec.authors = { "Rate" => "cjfmail@foxmail.com" } 68 | # spec.social_media_url = "https://twitter.com/Rate" 69 | 70 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 71 | # 72 | # If this Pod runs only on iOS or OS X, then specify the platform and 73 | # the deployment target. You can optionally include the target after the platform. 74 | # 75 | 76 | # spec.platform = :ios 77 | spec.platform = :ios, "12.0" 78 | 79 | # When using multiple platforms 80 | # spec.ios.deployment_target = "5.0" 81 | # spec.osx.deployment_target = "10.7" 82 | # spec.watchos.deployment_target = "2.0" 83 | # spec.tvos.deployment_target = "9.0" 84 | 85 | 86 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 87 | # 88 | # Specify the location from where the source should be retrieved. 89 | # Supports git, hg, bzr, svn and HTTP. 90 | # 91 | 92 | spec.source = { :git => "https://github.com/Json031/ECDHAlgorithmiOS.git", :tag => "#{spec.version}" } 93 | 94 | 95 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 96 | # 97 | # CocoaPods is smart about how it includes source code. For source files 98 | # giving a folder will include any swift, h, m, mm, c & cpp files. 99 | # For header files it will include any header in the folder. 100 | # Not including the public_header_files will make all headers public. 101 | # 102 | 103 | spec.source_files = "Classes", "Classes/**/*.{h,m,swift}" 104 | spec.exclude_files = "Classes/Exclude" 105 | 106 | # spec.public_header_files = "Classes/**/*.h" 107 | 108 | 109 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 110 | # 111 | # A list of resources included with the Pod. These are copied into the 112 | # target bundle with a build phase script. Anything else will be cleaned. 113 | # You can preserve files from being cleaned, please don't preserve 114 | # non-essential files like tests, examples and documentation. 115 | # 116 | 117 | # spec.resource = "icon.png" 118 | # spec.resources = "Resources/*.png" 119 | 120 | # spec.preserve_paths = "FilesToSave", "MoreFilesToSave" 121 | 122 | 123 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 124 | # 125 | # Link your library with frameworks, or libraries. Libraries do not include 126 | # the lib prefix of their name. 127 | # 128 | 129 | # spec.framework = "SomeFramework" 130 | # spec.frameworks = "SomeFramework", "AnotherFramework" 131 | 132 | # spec.library = "iconv" 133 | # spec.libraries = "iconv", "xml2" 134 | 135 | 136 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 137 | # 138 | # If your library depends on compiler flags you can set them in the xcconfig hash 139 | # where they will only apply to your library. If you depend on other Podspecs 140 | # you can include multiple dependencies to ensure it works. 141 | 142 | # spec.requires_arc = true 143 | 144 | # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 145 | # spec.dependency "JSONKit", "~> 1.4" 146 | 147 | end 148 | -------------------------------------------------------------------------------- /Classes/GMEllipticCurveCrypto/GMEllipticCurveCrypto+hash.m: -------------------------------------------------------------------------------- 1 | // 2 | // GMEllipticCurveCrypto+hash.m 3 | // 4 | // BSD 2-Clause License 5 | // 6 | // Copyright (c) 2014 Richard Moore. 7 | // 8 | // All rights reserved. 9 | // 10 | // Redistribution and use in source and binary forms, with or without modification, 11 | // are permitted provided that the following conditions are met: 12 | // 13 | // 1. Redistributions of source code must retain the above copyright notice, this 14 | // list of conditions and the following disclaimer. 15 | // 16 | // 2. Redistributions in binary form must reproduce the above copyright notice, 17 | // this list of conditions and the following disclaimer in the documentation 18 | // and/or other materials provided with the distribution. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 | // IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 | // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | // POSSIBILITY OF SUCH DAMAGE. 30 | // 31 | 32 | 33 | #import "GMEllipticCurveCrypto+hash.h" 34 | 35 | #import 36 | 37 | NSData *derEncodeInteger(NSData *value) { 38 | int length = (int)[value length]; 39 | const unsigned char *data = [value bytes]; 40 | 41 | int outputIndex = 0; 42 | unsigned char output[[value length] + 3]; 43 | 44 | output[outputIndex++] = 0x02; 45 | 46 | // Find the first non-zero entry in value 47 | int start = 0; 48 | while (start < length && data[start] == 0){ start++; } 49 | 50 | // Add the length and zero padding to preserve sign 51 | if (start == length || data[start] >= 0x80) { 52 | output[outputIndex++] = length - start + 1; 53 | output[outputIndex++] = 0x00; 54 | } else { 55 | output[outputIndex++] = length - start; 56 | } 57 | 58 | [value getBytes:&output[outputIndex] range:NSMakeRange(start, length - start)]; 59 | outputIndex += length - start; 60 | 61 | return [NSData dataWithBytes:output length:outputIndex]; 62 | } 63 | 64 | NSData *derEncodeSignature(NSData *signature) { 65 | 66 | int length = (int)[signature length]; 67 | if (length % 2) { return nil; } 68 | 69 | NSData *rValue = derEncodeInteger([signature subdataWithRange:NSMakeRange(0, length / 2)]); 70 | NSData *sValue = derEncodeInteger([signature subdataWithRange:NSMakeRange(length / 2, length / 2)]); 71 | 72 | // Begin with the sequence tag and sequence length 73 | unsigned char header[2]; 74 | header[0] = 0x30; 75 | header[1] = [rValue length] + [sValue length]; 76 | 77 | // This requires a long definite octet stream (signatures aren't this long) 78 | if (header[1] >= 0x80) { return nil; } 79 | 80 | NSMutableData *encoded = [NSMutableData dataWithBytes:header length:2]; 81 | [encoded appendData:rValue]; 82 | [encoded appendData:sValue]; 83 | 84 | return [encoded copy]; 85 | } 86 | 87 | 88 | NSRange derDecodeSequence(const unsigned char *bytes, int length, int index) { 89 | NSRange result; 90 | result.location = NSNotFound; 91 | 92 | // Make sure we are long enough and have a sequence 93 | if (length - index > 2 && bytes[index] == 0x30) { 94 | 95 | // Make sure the input buffer is large enough 96 | int sequenceLength = bytes[index + 1]; 97 | if (index + 2 + sequenceLength <= length) { 98 | result.location = index + 2; 99 | result.length = sequenceLength; 100 | } 101 | } 102 | 103 | return result; 104 | } 105 | 106 | NSRange derDecodeInteger(const unsigned char *bytes, int length, int index) { 107 | NSRange result; 108 | result.location = NSNotFound; 109 | 110 | // Make sure we are long enough and have an integer 111 | if (length - index > 3 && bytes[index] == 0x02) { 112 | 113 | // Make sure the input buffer is large enough 114 | int integerLength = bytes[index + 1]; 115 | if (index + 2 + integerLength <= length) { 116 | 117 | // Strip any leading zero, used to preserve sign 118 | if (bytes[index + 2] == 0x00) { 119 | result.location = index + 3; 120 | result.length = integerLength - 1; 121 | 122 | } else { 123 | result.location = index + 2; 124 | result.length = integerLength; 125 | } 126 | } 127 | } 128 | 129 | return result; 130 | } 131 | 132 | NSData *derDecodeSignature(NSData *der, int keySize) { 133 | NSInteger length = [der length]; 134 | const unsigned char *data = [der bytes]; 135 | 136 | // Make sure we have a sequence 137 | NSRange sequence = derDecodeSequence(data, (int)length, 0); 138 | if (sequence.location == NSNotFound) { return nil; } 139 | 140 | // Extract the r value (first item) 141 | NSRange rValue = derDecodeInteger(data, (int)length, (int)sequence.location); 142 | if (rValue.location == NSNotFound || rValue.length > keySize) { return nil; } 143 | 144 | // Extract the s value (second item) 145 | int sStart = (int)rValue.location + (int)rValue.length; 146 | NSRange sValue = derDecodeInteger(data, (int)length, sStart); 147 | if (sValue.location == NSNotFound || sValue.length > keySize) { return nil; } 148 | 149 | // Create an empty array with 0's 150 | unsigned char output[2 * keySize]; 151 | bzero(output, 2 * keySize); 152 | 153 | // Copy the r and s value in, right aligned to zero adding 154 | [der getBytes:&output[keySize - rValue.length] range:NSMakeRange(rValue.location, rValue.length)]; 155 | [der getBytes:&output[2 * keySize - sValue.length] range:NSMakeRange(sValue.location, sValue.length)]; 156 | 157 | return [NSData dataWithBytes:output length:2 * keySize]; 158 | } 159 | 160 | 161 | @implementation GMEllipticCurveCrypto (hash) 162 | 163 | - (BOOL)hashSHA256AndVerifySignature:(NSData *)signature forData:(NSData *)data { 164 | int bytes = self.bits / 8; 165 | 166 | if (bytes > CC_SHA256_DIGEST_LENGTH) { 167 | NSLog(@"ERROR: SHA256 hash is too short for curve"); 168 | return NO; 169 | } 170 | 171 | unsigned char hash[CC_SHA256_DIGEST_LENGTH]; 172 | CC_SHA256([data bytes], (int)[data length], hash); 173 | return [self verifySignature:signature forHash:[NSData dataWithBytes:hash length:bytes]]; 174 | } 175 | 176 | 177 | - (NSData*)hashSHA256AndSignData:(NSData *)data { 178 | int bytes = self.bits / 8; 179 | 180 | if (bytes > CC_SHA256_DIGEST_LENGTH) { 181 | NSLog(@"ERROR: SHA256 hash is too short for curve"); 182 | return nil; 183 | } 184 | 185 | unsigned char hash[CC_SHA256_DIGEST_LENGTH]; 186 | CC_SHA256([data bytes], (int)[data length], hash); 187 | return [self signatureForHash:[NSData dataWithBytes:hash length:bytes]]; 188 | } 189 | 190 | 191 | - (BOOL)hashSHA384AndVerifySignature:(NSData *)signature forData:(NSData *)data { 192 | int bytes = self.bits / 8; 193 | 194 | unsigned char hash[CC_SHA384_DIGEST_LENGTH]; 195 | CC_SHA384([data bytes], (int)[data length], hash); 196 | return [self verifySignature:signature forHash:[NSData dataWithBytes:hash length:bytes]]; 197 | } 198 | 199 | 200 | - (NSData*)hashSHA384AndSignData:(NSData *)data { 201 | int bytes = self.bits / 8; 202 | 203 | unsigned char hash[CC_SHA384_DIGEST_LENGTH]; 204 | CC_SHA384([data bytes], (int)[data length], hash); 205 | return [self signatureForHash:[NSData dataWithBytes:hash length:bytes]]; 206 | } 207 | 208 | 209 | - (NSData*)encodedSignatureForHash: (NSData*)hash { 210 | NSData *signature = [self signatureForHash:hash]; 211 | return derEncodeSignature(signature); 212 | } 213 | 214 | - (NSData*)hashSHA256AndSignDataEncoded: (NSData*)data { 215 | NSData *signature = [self hashSHA256AndSignData:data]; 216 | return derEncodeSignature(signature); 217 | } 218 | 219 | - (NSData*)hashSHA384AndSignDataEncoded: (NSData*)data { 220 | NSData *signature = [self hashSHA384AndSignData:data]; 221 | return derEncodeSignature(signature); 222 | } 223 | 224 | 225 | - (BOOL)verifyEncodedSignature: (NSData*)encodedSignature forHash: (NSData*)hash { 226 | NSData *signature = derDecodeSignature(encodedSignature, self.bits / 8); 227 | return [self verifySignature:signature forHash:hash]; 228 | } 229 | 230 | - (BOOL)hashSHA256AndVerifyEncodedSignature: (NSData*)encodedSignature forData: (NSData*)data { 231 | NSData *signature = derDecodeSignature(encodedSignature, self.bits / 8); 232 | return [self hashSHA256AndVerifySignature:signature forData:data]; 233 | } 234 | 235 | - (BOOL)hashSHA384AndVerifyEncodedSignature: (NSData*)encodedSignature forData: (NSData*)data { 236 | NSData *signature = derDecodeSignature(encodedSignature, self.bits / 8); 237 | return [self hashSHA384AndVerifySignature:signature forData:data]; 238 | } 239 | 240 | 241 | @end 242 | -------------------------------------------------------------------------------- /Classes/GMEllipticCurveCrypto/GMEllipticCurveCrypto.m: -------------------------------------------------------------------------------- 1 | // 2 | // GMEllipticCurveCrypto.m 3 | // 4 | // BSD 2-Clause License 5 | // 6 | // Copyright (c) 2014 Richard Moore. 7 | // 8 | // All rights reserved. 9 | // 10 | // Redistribution and use in source and binary forms, with or without modification, 11 | // are permitted provided that the following conditions are met: 12 | // 13 | // 1. Redistributions of source code must retain the above copyright notice, this 14 | // list of conditions and the following disclaimer. 15 | // 16 | // 2. Redistributions in binary form must reproduce the above copyright notice, 17 | // this list of conditions and the following disclaimer in the documentation 18 | // and/or other materials provided with the distribution. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 | // IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 | // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | // POSSIBILITY OF SUCH DAMAGE. 30 | // 31 | 32 | 33 | #import "GMEllipticCurveCrypto.h" 34 | 35 | /** Easy ecc - Relevant parts of ecc.c 36 | * 37 | * The original easy-ecc code was left as untouched as possible: 38 | * - The #define values were added as parameters with the same name as the #define 39 | * - EccPoint was changed to be two uint64_t* with the suffixes, "x" and "y" 40 | * - Optimized vli_mmod_fast functions have been prefixed with _BITS_ and a new dispatch function was added 41 | * - Several casts to int were added to remove warnings (marked with /// Cast by RicMoo) 42 | * - Switched from reading /dev/random to use "Randomiztion services". 43 | * 44 | * The original code can be found at https://github.com/kmackay/easy-ecc 45 | * 46 | * It was released under the BSD 2-Clause License: 47 | 48 | * Copyright (c) 2013, Kenneth MacKay 49 | * All rights reserved. 50 | * 51 | * Redistribution and use in source and binary forms, with or without modification, 52 | * are permitted provided that the following conditions are met: 53 | * * Redistributions of source code must retain the above copyright notice, this 54 | * list of conditions and the following disclaimer. 55 | * * Redistributions in binary form must reproduce the above copyright notice, 56 | * this list of conditions and the following disclaimer in the documentation 57 | * and/or other materials provided with the distribution. 58 | * 59 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 60 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 61 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 62 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 63 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 64 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 65 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 66 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 67 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 68 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 69 | * 70 | */ 71 | 72 | #define MAX_TRIES 16 73 | 74 | typedef struct 75 | { 76 | uint64_t m_low; 77 | uint64_t m_high; 78 | } uint128_t; 79 | 80 | 81 | static int getRandomNumber(uint64_t *p_vli, int NUM_ECC_DIGITS) 82 | { 83 | int success = SecRandomCopyBytes(kSecRandomDefault, NUM_ECC_DIGITS * 8, (uint8_t*)p_vli); 84 | return (success == 0) ? 1: 0; 85 | 86 | // int l_fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); 87 | // if(l_fd == -1) 88 | // { 89 | // l_fd = open("/dev/random", O_RDONLY | O_CLOEXEC); 90 | // if(l_fd == -1) 91 | // { 92 | // return 0; 93 | // } 94 | // } 95 | // 96 | // char *l_ptr = (char *)p_vli; 97 | // size_t l_left = NUM_ECC_DIGITS * 8; 98 | // while(l_left > 0) 99 | // { 100 | // int l_read = read(l_fd, l_ptr, l_left); 101 | // if(l_read <= 0) 102 | // { // read failed 103 | // close(l_fd); 104 | // return 0; 105 | // } 106 | // l_left -= l_read; 107 | // l_ptr += l_read; 108 | // } 109 | // 110 | // close(l_fd); 111 | // return 1; 112 | } 113 | 114 | static void vli_clear(uint64_t *p_vli, int NUM_ECC_DIGITS) 115 | { 116 | uint i; 117 | for(i=0; i= 0 && p_vli[i] == 0; --i) 150 | { 151 | } 152 | 153 | return (i + 1); 154 | } 155 | 156 | /* Counts the number of bits required for p_vli. */ 157 | static uint vli_numBits(uint64_t *p_vli, int NUM_ECC_DIGITS) 158 | { 159 | uint i; 160 | uint64_t l_digit; 161 | 162 | uint l_numDigits = vli_numDigits(p_vli, NUM_ECC_DIGITS); 163 | if(l_numDigits == 0) 164 | { 165 | return 0; 166 | } 167 | 168 | l_digit = p_vli[l_numDigits - 1]; 169 | for(i=0; l_digit; ++i) 170 | { 171 | l_digit >>= 1; 172 | } 173 | 174 | return ((l_numDigits - 1) * 64 + i); 175 | } 176 | 177 | /* Sets p_dest = p_src. */ 178 | static void vli_set(uint64_t *p_dest, uint64_t *p_src, int NUM_ECC_DIGITS) 179 | { 180 | uint i; 181 | for(i=0; i= 0; --i) 192 | { 193 | if(p_left[i] > p_right[i]) 194 | { 195 | return 1; 196 | } 197 | else if(p_left[i] < p_right[i]) 198 | { 199 | return -1; 200 | } 201 | } 202 | return 0; 203 | } 204 | 205 | /* Computes p_result = p_in << c, returning carry. Can modify in place (if p_result == p_in). 0 < p_shift < 64. */ 206 | static uint64_t vli_lshift(uint64_t *p_result, uint64_t *p_in, uint p_shift, int NUM_ECC_DIGITS) 207 | { 208 | uint64_t l_carry = 0; 209 | uint i; 210 | for(i = 0; i < NUM_ECC_DIGITS; ++i) 211 | { 212 | uint64_t l_temp = p_in[i]; 213 | p_result[i] = (l_temp << p_shift) | l_carry; 214 | l_carry = l_temp >> (64 - p_shift); 215 | } 216 | 217 | return l_carry; 218 | } 219 | 220 | /* Computes p_vli = p_vli >> 1. */ 221 | static void vli_rshift1(uint64_t *p_vli, int NUM_ECC_DIGITS) 222 | { 223 | uint64_t *l_end = p_vli; 224 | uint64_t l_carry = 0; 225 | 226 | p_vli += NUM_ECC_DIGITS; 227 | while(p_vli-- > l_end) 228 | { 229 | uint64_t l_temp = *p_vli; 230 | *p_vli = (l_temp >> 1) | l_carry; 231 | l_carry = l_temp << 63; 232 | } 233 | } 234 | 235 | /* Computes p_result = p_left + p_right, returning carry. Can modify in place. */ 236 | static uint64_t vli_add(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, int NUM_ECC_DIGITS) 237 | { 238 | uint64_t l_carry = 0; 239 | uint i; 240 | for(i=0; i p_left[i]); 263 | } 264 | p_result[i] = l_diff; 265 | } 266 | return l_borrow; 267 | } 268 | 269 | static uint128_t mul_64_64(uint64_t p_left, uint64_t p_right) 270 | { 271 | uint128_t l_result; 272 | 273 | uint64_t a0 = p_left & 0xffffffffull; 274 | uint64_t a1 = p_left >> 32; 275 | uint64_t b0 = p_right & 0xffffffffull; 276 | uint64_t b1 = p_right >> 32; 277 | 278 | uint64_t m0 = a0 * b0; 279 | uint64_t m1 = a0 * b1; 280 | uint64_t m2 = a1 * b0; 281 | uint64_t m3 = a1 * b1; 282 | 283 | m2 += (m0 >> 32); 284 | m2 += m1; 285 | if(m2 < m1) 286 | { // overflow 287 | m3 += 0x100000000ull; 288 | } 289 | 290 | l_result.m_low = (m0 & 0xffffffffull) | (m2 << 32); 291 | l_result.m_high = m3 + (m2 >> 32); 292 | 293 | return l_result; 294 | } 295 | 296 | static uint128_t add_128_128(uint128_t a, uint128_t b) 297 | { 298 | uint128_t l_result; 299 | l_result.m_low = a.m_low + b.m_low; 300 | l_result.m_high = a.m_high + b.m_high + (l_result.m_low < a.m_low); 301 | return l_result; 302 | } 303 | 304 | static void vli_mult(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, int NUM_ECC_DIGITS) 305 | { 306 | uint128_t r01 = {0, 0}; 307 | uint64_t r2 = 0; 308 | 309 | uint i, k; 310 | 311 | /* Compute each digit of p_result in sequence, maintaining the carries. */ 312 | for(k=0; k < NUM_ECC_DIGITS*2 - 1; ++k) 313 | { 314 | uint l_min = (k < NUM_ECC_DIGITS ? 0 : (k + 1) - NUM_ECC_DIGITS); 315 | for(i=l_min; i<=k && i> 63; 345 | l_product.m_high = (l_product.m_high << 1) | (l_product.m_low >> 63); 346 | l_product.m_low <<= 1; 347 | } 348 | r01 = add_128_128(r01, l_product); 349 | r2 += (r01.m_high < l_product.m_high); 350 | } 351 | p_result[k] = r01.m_low; 352 | r01.m_low = r01.m_high; 353 | r01.m_high = r2; 354 | r2 = 0; 355 | } 356 | 357 | p_result[NUM_ECC_DIGITS*2 - 1] = r01.m_low; 358 | } 359 | 360 | 361 | /* Computes p_result = (p_left + p_right) % p_mod. 362 | Assumes that p_left < p_mod and p_right < p_mod, p_result != p_mod. */ 363 | static void vli_modAdd(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, uint64_t *p_mod, int NUM_ECC_DIGITS) 364 | { 365 | uint64_t l_carry = vli_add(p_result, p_left, p_right, NUM_ECC_DIGITS); 366 | if(l_carry || vli_cmp(p_result, p_mod, NUM_ECC_DIGITS) >= 0) 367 | { /* p_result > p_mod (p_result = p_mod + remainder), so subtract p_mod to get remainder. */ 368 | vli_sub(p_result, p_result, p_mod, NUM_ECC_DIGITS); 369 | } 370 | } 371 | 372 | /* Computes p_result = (p_left - p_right) % p_mod. 373 | Assumes that p_left < p_mod and p_right < p_mod, p_result != p_mod. */ 374 | static void vli_modSub(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, uint64_t *p_mod, int NUM_ECC_DIGITS) 375 | { 376 | uint64_t l_borrow = vli_sub(p_result, p_left, p_right, NUM_ECC_DIGITS); 377 | if(l_borrow) 378 | { /* In this case, p_result == -diff == (max int) - diff. 379 | Since -x % d == d - x, we can get the correct result from p_result + p_mod (with overflow). */ 380 | vli_add(p_result, p_result, p_mod, NUM_ECC_DIGITS); 381 | } 382 | } 383 | 384 | /* Computes p_result = p_product % curve_p. 385 | See algorithm 5 and 6 from http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf */ 386 | static void _128_vli_mmod_fast(uint64_t *p_result, uint64_t *p_product, int NUM_ECC_DIGITS, uint64_t *curve_p) 387 | { 388 | uint64_t l_tmp[NUM_ECC_DIGITS]; 389 | int l_carry; 390 | 391 | vli_set(p_result, p_product, NUM_ECC_DIGITS); 392 | 393 | l_tmp[0] = p_product[2]; 394 | l_tmp[1] = (p_product[3] & 0x1FFFFFFFFull) | (p_product[2] << 33); 395 | l_carry = (int)vli_add(p_result, p_result, l_tmp, NUM_ECC_DIGITS); /// Cast by RicMoo 396 | 397 | l_tmp[0] = (p_product[2] >> 31) | (p_product[3] << 33); 398 | l_tmp[1] = (p_product[3] >> 31) | ((p_product[2] & 0xFFFFFFFF80000000ull) << 2); 399 | l_carry += vli_add(p_result, p_result, l_tmp, NUM_ECC_DIGITS); 400 | 401 | l_tmp[0] = (p_product[2] >> 62) | (p_product[3] << 2); 402 | l_tmp[1] = (p_product[3] >> 62) | ((p_product[2] & 0xC000000000000000ull) >> 29) | (p_product[3] << 35); 403 | l_carry += vli_add(p_result, p_result, l_tmp, NUM_ECC_DIGITS); 404 | 405 | l_tmp[0] = (p_product[3] >> 29); 406 | l_tmp[1] = ((p_product[3] & 0xFFFFFFFFE0000000ull) << 4); 407 | l_carry += vli_add(p_result, p_result, l_tmp, NUM_ECC_DIGITS); 408 | 409 | l_tmp[0] = (p_product[3] >> 60); 410 | l_tmp[1] = (p_product[3] & 0xFFFFFFFE00000000ull); 411 | l_carry += vli_add(p_result, p_result, l_tmp, NUM_ECC_DIGITS); 412 | 413 | l_tmp[0] = 0; 414 | l_tmp[1] = ((p_product[3] & 0xF000000000000000ull) >> 27); 415 | l_carry += vli_add(p_result, p_result, l_tmp, NUM_ECC_DIGITS); 416 | 417 | while(l_carry || vli_cmp(curve_p, p_result, NUM_ECC_DIGITS) != 1) 418 | { 419 | l_carry -= vli_sub(p_result, p_result, curve_p, NUM_ECC_DIGITS); 420 | } 421 | } 422 | 423 | /* Computes p_result = p_product % curve_p. 424 | See algorithm 5 and 6 from http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf */ 425 | static void _192_vli_mmod_fast(uint64_t *p_result, uint64_t *p_product, int NUM_ECC_DIGITS, uint64_t *curve_p) 426 | { 427 | uint64_t l_tmp[NUM_ECC_DIGITS]; 428 | int l_carry; 429 | 430 | vli_set(p_result, p_product, NUM_ECC_DIGITS); 431 | 432 | vli_set(l_tmp, &p_product[3], NUM_ECC_DIGITS); 433 | l_carry = (int)vli_add(p_result, p_result, l_tmp, NUM_ECC_DIGITS); /// Cast by RicMoo 434 | 435 | l_tmp[0] = 0; 436 | l_tmp[1] = p_product[3]; 437 | l_tmp[2] = p_product[4]; 438 | l_carry += vli_add(p_result, p_result, l_tmp, NUM_ECC_DIGITS); 439 | 440 | l_tmp[0] = l_tmp[1] = p_product[5]; 441 | l_tmp[2] = 0; 442 | l_carry += vli_add(p_result, p_result, l_tmp, NUM_ECC_DIGITS); 443 | 444 | while(l_carry || vli_cmp(curve_p, p_result, NUM_ECC_DIGITS) != 1) 445 | { 446 | l_carry -= vli_sub(p_result, p_result, curve_p, NUM_ECC_DIGITS); 447 | } 448 | } 449 | 450 | /* Computes p_result = p_product % curve_p 451 | from http://www.nsa.gov/ia/_files/nist-routines.pdf */ 452 | static void _256_vli_mmod_fast(uint64_t *p_result, uint64_t *p_product, int NUM_ECC_DIGITS, uint64_t *curve_p) 453 | { 454 | uint64_t l_tmp[NUM_ECC_DIGITS]; 455 | int l_carry; 456 | 457 | /* t */ 458 | vli_set(p_result, p_product, NUM_ECC_DIGITS); 459 | 460 | /* s1 */ 461 | l_tmp[0] = 0; 462 | l_tmp[1] = p_product[5] & 0xffffffff00000000ull; 463 | l_tmp[2] = p_product[6]; 464 | l_tmp[3] = p_product[7]; 465 | l_carry = (int)vli_lshift(l_tmp, l_tmp, 1, NUM_ECC_DIGITS); /// Cast by RicMoo 466 | l_carry += vli_add(p_result, p_result, l_tmp, NUM_ECC_DIGITS); 467 | 468 | /* s2 */ 469 | l_tmp[1] = p_product[6] << 32; 470 | l_tmp[2] = (p_product[6] >> 32) | (p_product[7] << 32); 471 | l_tmp[3] = p_product[7] >> 32; 472 | l_carry += vli_lshift(l_tmp, l_tmp, 1, NUM_ECC_DIGITS); 473 | l_carry += vli_add(p_result, p_result, l_tmp, NUM_ECC_DIGITS); 474 | 475 | /* s3 */ 476 | l_tmp[0] = p_product[4]; 477 | l_tmp[1] = p_product[5] & 0xffffffff; 478 | l_tmp[2] = 0; 479 | l_tmp[3] = p_product[7]; 480 | l_carry += vli_add(p_result, p_result, l_tmp, NUM_ECC_DIGITS); 481 | 482 | /* s4 */ 483 | l_tmp[0] = (p_product[4] >> 32) | (p_product[5] << 32); 484 | l_tmp[1] = (p_product[5] >> 32) | (p_product[6] & 0xffffffff00000000ull); 485 | l_tmp[2] = p_product[7]; 486 | l_tmp[3] = (p_product[6] >> 32) | (p_product[4] << 32); 487 | l_carry += vli_add(p_result, p_result, l_tmp, NUM_ECC_DIGITS); 488 | 489 | /* d1 */ 490 | l_tmp[0] = (p_product[5] >> 32) | (p_product[6] << 32); 491 | l_tmp[1] = (p_product[6] >> 32); 492 | l_tmp[2] = 0; 493 | l_tmp[3] = (p_product[4] & 0xffffffff) | (p_product[5] << 32); 494 | l_carry -= vli_sub(p_result, p_result, l_tmp, NUM_ECC_DIGITS); 495 | 496 | /* d2 */ 497 | l_tmp[0] = p_product[6]; 498 | l_tmp[1] = p_product[7]; 499 | l_tmp[2] = 0; 500 | l_tmp[3] = (p_product[4] >> 32) | (p_product[5] & 0xffffffff00000000ull); 501 | l_carry -= vli_sub(p_result, p_result, l_tmp, NUM_ECC_DIGITS); 502 | 503 | /* d3 */ 504 | l_tmp[0] = (p_product[6] >> 32) | (p_product[7] << 32); 505 | l_tmp[1] = (p_product[7] >> 32) | (p_product[4] << 32); 506 | l_tmp[2] = (p_product[4] >> 32) | (p_product[5] << 32); 507 | l_tmp[3] = (p_product[6] << 32); 508 | l_carry -= vli_sub(p_result, p_result, l_tmp, NUM_ECC_DIGITS); 509 | 510 | /* d4 */ 511 | l_tmp[0] = p_product[7]; 512 | l_tmp[1] = p_product[4] & 0xffffffff00000000ull; 513 | l_tmp[2] = p_product[5]; 514 | l_tmp[3] = p_product[6] & 0xffffffff00000000ull; 515 | l_carry -= vli_sub(p_result, p_result, l_tmp, NUM_ECC_DIGITS); 516 | 517 | if(l_carry < 0) 518 | { 519 | do 520 | { 521 | l_carry += vli_add(p_result, p_result, curve_p, NUM_ECC_DIGITS); 522 | } while(l_carry < 0); 523 | } 524 | else 525 | { 526 | while(l_carry || vli_cmp(curve_p, p_result, NUM_ECC_DIGITS) != 1) 527 | { 528 | l_carry -= vli_sub(p_result, p_result, curve_p, NUM_ECC_DIGITS); 529 | } 530 | } 531 | } 532 | 533 | static void omega_mult(uint64_t *p_result, uint64_t *p_right, int NUM_ECC_DIGITS) 534 | { 535 | uint64_t l_tmp[NUM_ECC_DIGITS]; 536 | uint64_t l_carry, l_diff; 537 | 538 | /* Multiply by (2^128 + 2^96 - 2^32 + 1). */ 539 | vli_set(p_result, p_right, NUM_ECC_DIGITS); /* 1 */ 540 | l_carry = vli_lshift(l_tmp, p_right, 32, NUM_ECC_DIGITS); 541 | p_result[1 + NUM_ECC_DIGITS] = l_carry + vli_add(p_result + 1, p_result + 1, l_tmp, NUM_ECC_DIGITS); /* 2^96 + 1 */ 542 | p_result[2 + NUM_ECC_DIGITS] = vli_add(p_result + 2, p_result + 2, p_right, NUM_ECC_DIGITS); /* 2^128 + 2^96 + 1 */ 543 | l_carry += vli_sub(p_result, p_result, l_tmp, NUM_ECC_DIGITS); /* 2^128 + 2^96 - 2^32 + 1 */ 544 | l_diff = p_result[NUM_ECC_DIGITS] - l_carry; 545 | if(l_diff > p_result[NUM_ECC_DIGITS]) 546 | { /* Propagate borrow if necessary. */ 547 | uint i; 548 | for(i = 1 + NUM_ECC_DIGITS; ; ++i) 549 | { 550 | --p_result[i]; 551 | if(p_result[i] != (uint64_t)-1) 552 | { 553 | break; 554 | } 555 | } 556 | } 557 | p_result[NUM_ECC_DIGITS] = l_diff; 558 | } 559 | 560 | /* Computes p_result = p_product % curve_p 561 | see PDF "Comparing Elliptic Curve Cryptography and RSA on 8-bit CPUs" 562 | section "Curve-Specific Optimizations" */ 563 | static void _384_vli_mmod_fast(uint64_t *p_result, uint64_t *p_product, int NUM_ECC_DIGITS, uint64_t *curve_p) 564 | { 565 | uint64_t l_tmp[2*NUM_ECC_DIGITS]; 566 | 567 | while(!vli_isZero(p_product + NUM_ECC_DIGITS, NUM_ECC_DIGITS)) /* While c1 != 0 */ 568 | { 569 | uint64_t l_carry = 0; 570 | uint i; 571 | 572 | vli_clear(l_tmp, NUM_ECC_DIGITS); 573 | vli_clear(l_tmp + NUM_ECC_DIGITS, NUM_ECC_DIGITS); 574 | omega_mult(l_tmp, p_product + NUM_ECC_DIGITS, NUM_ECC_DIGITS); /* tmp = w * c1 */ 575 | vli_clear(p_product + NUM_ECC_DIGITS, NUM_ECC_DIGITS); /* p = c0 */ 576 | 577 | /* (c1, c0) = c0 + w * c1 */ 578 | for(i=0; i 0) 590 | { 591 | vli_sub(p_product, p_product, curve_p, NUM_ECC_DIGITS); 592 | } 593 | vli_set(p_result, p_product, NUM_ECC_DIGITS); 594 | } 595 | 596 | static void vli_mmod_fast(uint64_t *p_result, uint64_t *p_product, int NUM_ECC_DIGITS, uint64_t *curve_p) 597 | { 598 | switch (NUM_ECC_DIGITS) { 599 | case 2: 600 | _128_vli_mmod_fast(p_result, p_product, NUM_ECC_DIGITS, curve_p); 601 | break; 602 | case 3: 603 | _192_vli_mmod_fast(p_result, p_product, NUM_ECC_DIGITS, curve_p); 604 | break; 605 | case 4: 606 | _256_vli_mmod_fast(p_result, p_product, NUM_ECC_DIGITS, curve_p); 607 | break; 608 | case 6: 609 | _384_vli_mmod_fast(p_result, p_product, NUM_ECC_DIGITS, curve_p); 610 | break; 611 | 612 | default: 613 | NSLog(@"Curve undefined; no vli_mmod_fast defined"); 614 | break; 615 | } 616 | 617 | } 618 | 619 | /* Computes p_result = (p_left * p_right) % curve_p. */ 620 | static void vli_modMult_fast(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, int NUM_ECC_DIGITS, uint64_t *curve_p) 621 | { 622 | uint64_t l_product[2 * NUM_ECC_DIGITS]; 623 | vli_mult(l_product, p_left, p_right, NUM_ECC_DIGITS); 624 | vli_mmod_fast(p_result, l_product, NUM_ECC_DIGITS, curve_p); 625 | } 626 | 627 | /* Computes p_result = p_left^2 % curve_p. */ 628 | static void vli_modSquare_fast(uint64_t *p_result, uint64_t *p_left, int NUM_ECC_DIGITS, uint64_t *curve_p) 629 | { 630 | uint64_t l_product[2 * NUM_ECC_DIGITS]; 631 | vli_square(l_product, p_left, NUM_ECC_DIGITS); 632 | vli_mmod_fast(p_result, l_product, NUM_ECC_DIGITS, curve_p); 633 | } 634 | 635 | #define EVEN(vli) (!(vli[0] & 1)) 636 | /* Computes p_result = (1 / p_input) % p_mod. All VLIs are the same size. 637 | See "From Euclid's GCD to Montgomery Multiplication to the Great Divide" 638 | https://labs.oracle.com/techrep/2001/smli_tr-2001-95.pdf */ 639 | static void vli_modInv(uint64_t *p_result, uint64_t *p_input, uint64_t *p_mod, int NUM_ECC_DIGITS) 640 | { 641 | uint64_t a[NUM_ECC_DIGITS], b[NUM_ECC_DIGITS], u[NUM_ECC_DIGITS], v[NUM_ECC_DIGITS]; 642 | uint64_t l_carry; 643 | int l_cmpResult; 644 | 645 | if(vli_isZero(p_input, NUM_ECC_DIGITS)) 646 | { 647 | vli_clear(p_result, NUM_ECC_DIGITS); 648 | return; 649 | } 650 | 651 | vli_set(a, p_input, NUM_ECC_DIGITS); 652 | vli_set(b, p_mod, NUM_ECC_DIGITS); 653 | vli_clear(u, NUM_ECC_DIGITS); 654 | u[0] = 1; 655 | vli_clear(v, NUM_ECC_DIGITS); 656 | 657 | while((l_cmpResult = vli_cmp(a, b, NUM_ECC_DIGITS)) != 0) 658 | { 659 | l_carry = 0; 660 | if(EVEN(a)) 661 | { 662 | vli_rshift1(a, NUM_ECC_DIGITS); 663 | if(!EVEN(u)) 664 | { 665 | l_carry = vli_add(u, u, p_mod, NUM_ECC_DIGITS); 666 | } 667 | vli_rshift1(u, NUM_ECC_DIGITS); 668 | if(l_carry) 669 | { 670 | u[NUM_ECC_DIGITS-1] |= 0x8000000000000000ull; 671 | } 672 | } 673 | else if(EVEN(b)) 674 | { 675 | vli_rshift1(b, NUM_ECC_DIGITS); 676 | if(!EVEN(v)) 677 | { 678 | l_carry = vli_add(v, v, p_mod, NUM_ECC_DIGITS); 679 | } 680 | vli_rshift1(v, NUM_ECC_DIGITS); 681 | if(l_carry) 682 | { 683 | v[NUM_ECC_DIGITS-1] |= 0x8000000000000000ull; 684 | } 685 | } 686 | else if(l_cmpResult > 0) 687 | { 688 | vli_sub(a, a, b, NUM_ECC_DIGITS); 689 | vli_rshift1(a, NUM_ECC_DIGITS); 690 | if(vli_cmp(u, v, NUM_ECC_DIGITS) < 0) 691 | { 692 | vli_add(u, u, p_mod, NUM_ECC_DIGITS); 693 | } 694 | vli_sub(u, u, v, NUM_ECC_DIGITS); 695 | if(!EVEN(u)) 696 | { 697 | l_carry = vli_add(u, u, p_mod, NUM_ECC_DIGITS); 698 | } 699 | vli_rshift1(u, NUM_ECC_DIGITS); 700 | if(l_carry) 701 | { 702 | u[NUM_ECC_DIGITS-1] |= 0x8000000000000000ull; 703 | } 704 | } 705 | else 706 | { 707 | vli_sub(b, b, a, NUM_ECC_DIGITS); 708 | vli_rshift1(b, NUM_ECC_DIGITS); 709 | if(vli_cmp(v, u, NUM_ECC_DIGITS) < 0) 710 | { 711 | vli_add(v, v, p_mod, NUM_ECC_DIGITS); 712 | } 713 | vli_sub(v, v, u, NUM_ECC_DIGITS); 714 | if(!EVEN(v)) 715 | { 716 | l_carry = vli_add(v, v, p_mod, NUM_ECC_DIGITS); 717 | } 718 | vli_rshift1(v, NUM_ECC_DIGITS); 719 | if(l_carry) 720 | { 721 | v[NUM_ECC_DIGITS-1] |= 0x8000000000000000ull; 722 | } 723 | } 724 | } 725 | 726 | vli_set(p_result, u, NUM_ECC_DIGITS); 727 | } 728 | 729 | /* ------ Point operations ------ */ 730 | 731 | /* Returns 1 if p_point is the point at infinity, 0 otherwise. */ 732 | static int EccPoint_isZero(uint64_t *x, uint64_t *y, int NUM_ECC_DIGITS) 733 | { 734 | return (vli_isZero(x, NUM_ECC_DIGITS) && vli_isZero(y, NUM_ECC_DIGITS)); 735 | } 736 | 737 | /* Point multiplication algorithm using Montgomery's ladder with co-Z coordinates. 738 | From http://eprint.iacr.org/2011/338.pdf 739 | */ 740 | 741 | /* Double in place */ 742 | static void EccPoint_double_jacobian(uint64_t *X1, uint64_t *Y1, uint64_t *Z1, int NUM_ECC_DIGITS, uint64_t *curve_p) 743 | { 744 | /* t1 = X, t2 = Y, t3 = Z */ 745 | uint64_t t4[NUM_ECC_DIGITS]; 746 | uint64_t t5[NUM_ECC_DIGITS]; 747 | 748 | if(vli_isZero(Z1, NUM_ECC_DIGITS)) 749 | { 750 | return; 751 | } 752 | 753 | vli_modSquare_fast(t4, Y1, NUM_ECC_DIGITS, curve_p); /* t4 = y1^2 */ 754 | vli_modMult_fast(t5, X1, t4, NUM_ECC_DIGITS, curve_p); /* t5 = x1*y1^2 = A */ 755 | vli_modSquare_fast(t4, t4, NUM_ECC_DIGITS, curve_p); /* t4 = y1^4 */ 756 | vli_modMult_fast(Y1, Y1, Z1, NUM_ECC_DIGITS, curve_p); /* t2 = y1*z1 = z3 */ 757 | vli_modSquare_fast(Z1, Z1, NUM_ECC_DIGITS, curve_p); /* t3 = z1^2 */ 758 | 759 | vli_modAdd(X1, X1, Z1, curve_p, NUM_ECC_DIGITS); /* t1 = x1 + z1^2 */ 760 | vli_modAdd(Z1, Z1, Z1, curve_p, NUM_ECC_DIGITS); /* t3 = 2*z1^2 */ 761 | vli_modSub(Z1, X1, Z1, curve_p, NUM_ECC_DIGITS); /* t3 = x1 - z1^2 */ 762 | vli_modMult_fast(X1, X1, Z1, NUM_ECC_DIGITS, curve_p); /* t1 = x1^2 - z1^4 */ 763 | 764 | vli_modAdd(Z1, X1, X1, curve_p, NUM_ECC_DIGITS); /* t3 = 2*(x1^2 - z1^4) */ 765 | vli_modAdd(X1, X1, Z1, curve_p, NUM_ECC_DIGITS); /* t1 = 3*(x1^2 - z1^4) */ 766 | if(vli_testBit(X1, 0)) 767 | { 768 | uint64_t l_carry = vli_add(X1, X1, curve_p, NUM_ECC_DIGITS); 769 | vli_rshift1(X1, NUM_ECC_DIGITS); 770 | X1[NUM_ECC_DIGITS-1] |= l_carry << 63; 771 | } 772 | else 773 | { 774 | vli_rshift1(X1, NUM_ECC_DIGITS); 775 | } 776 | /* t1 = 3/2*(x1^2 - z1^4) = B */ 777 | 778 | vli_modSquare_fast(Z1, X1, NUM_ECC_DIGITS, curve_p); /* t3 = B^2 */ 779 | vli_modSub(Z1, Z1, t5, curve_p, NUM_ECC_DIGITS); /* t3 = B^2 - A */ 780 | vli_modSub(Z1, Z1, t5, curve_p, NUM_ECC_DIGITS); /* t3 = B^2 - 2A = x3 */ 781 | vli_modSub(t5, t5, Z1, curve_p, NUM_ECC_DIGITS); /* t5 = A - x3 */ 782 | vli_modMult_fast(X1, X1, t5, NUM_ECC_DIGITS, curve_p); /* t1 = B * (A - x3) */ 783 | vli_modSub(t4, X1, t4, curve_p, NUM_ECC_DIGITS); /* t4 = B * (A - x3) - y1^4 = y3 */ 784 | 785 | vli_set(X1, Z1, NUM_ECC_DIGITS); 786 | vli_set(Z1, Y1, NUM_ECC_DIGITS); 787 | vli_set(Y1, t4, NUM_ECC_DIGITS); 788 | } 789 | 790 | /* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */ 791 | static void apply_z(uint64_t *X1, uint64_t *Y1, uint64_t *Z, int NUM_ECC_DIGITS, uint64_t *curve_p) 792 | { 793 | uint64_t t1[NUM_ECC_DIGITS]; 794 | 795 | vli_modSquare_fast(t1, Z, NUM_ECC_DIGITS, curve_p); /* z^2 */ 796 | vli_modMult_fast(X1, X1, t1, NUM_ECC_DIGITS, curve_p); /* x1 * z^2 */ 797 | vli_modMult_fast(t1, t1, Z, NUM_ECC_DIGITS, curve_p); /* z^3 */ 798 | vli_modMult_fast(Y1, Y1, t1, NUM_ECC_DIGITS, curve_p); /* y1 * z^3 */ 799 | } 800 | 801 | /* P = (x1, y1) => 2P, (x2, y2) => P' */ 802 | static void XYcZ_initial_double(uint64_t *X1, uint64_t *Y1, uint64_t *X2, uint64_t *Y2, uint64_t *p_initialZ, int NUM_ECC_DIGITS, uint64_t *curve_p) 803 | { 804 | uint64_t z[NUM_ECC_DIGITS]; 805 | 806 | vli_set(X2, X1, NUM_ECC_DIGITS); 807 | vli_set(Y2, Y1, NUM_ECC_DIGITS); 808 | 809 | vli_clear(z, NUM_ECC_DIGITS); 810 | z[0] = 1; 811 | if(p_initialZ) 812 | { 813 | vli_set(z, p_initialZ, NUM_ECC_DIGITS); 814 | } 815 | 816 | apply_z(X1, Y1, z, NUM_ECC_DIGITS, curve_p); 817 | 818 | EccPoint_double_jacobian(X1, Y1, z, NUM_ECC_DIGITS, curve_p); 819 | 820 | apply_z(X2, Y2, z, NUM_ECC_DIGITS, curve_p); 821 | } 822 | 823 | /* Input P = (x1, y1, Z), Q = (x2, y2, Z) 824 | Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3) 825 | or P => P', Q => P + Q 826 | */ 827 | static void XYcZ_add(uint64_t *X1, uint64_t *Y1, uint64_t *X2, uint64_t *Y2, int NUM_ECC_DIGITS, uint64_t *curve_p) 828 | { 829 | /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ 830 | uint64_t t5[NUM_ECC_DIGITS]; 831 | 832 | vli_modSub(t5, X2, X1, curve_p, NUM_ECC_DIGITS); /* t5 = x2 - x1 */ 833 | vli_modSquare_fast(t5, t5, NUM_ECC_DIGITS, curve_p); /* t5 = (x2 - x1)^2 = A */ 834 | vli_modMult_fast(X1, X1, t5, NUM_ECC_DIGITS, curve_p); /* t1 = x1*A = B */ 835 | vli_modMult_fast(X2, X2, t5, NUM_ECC_DIGITS, curve_p); /* t3 = x2*A = C */ 836 | vli_modSub(Y2, Y2, Y1, curve_p, NUM_ECC_DIGITS); /* t4 = y2 - y1 */ 837 | vli_modSquare_fast(t5, Y2, NUM_ECC_DIGITS, curve_p); /* t5 = (y2 - y1)^2 = D */ 838 | 839 | vli_modSub(t5, t5, X1, curve_p, NUM_ECC_DIGITS); /* t5 = D - B */ 840 | vli_modSub(t5, t5, X2, curve_p, NUM_ECC_DIGITS); /* t5 = D - B - C = x3 */ 841 | vli_modSub(X2, X2, X1, curve_p, NUM_ECC_DIGITS); /* t3 = C - B */ 842 | vli_modMult_fast(Y1, Y1, X2, NUM_ECC_DIGITS, curve_p); /* t2 = y1*(C - B) */ 843 | vli_modSub(X2, X1, t5, curve_p, NUM_ECC_DIGITS); /* t3 = B - x3 */ 844 | vli_modMult_fast(Y2, Y2, X2, NUM_ECC_DIGITS, curve_p); /* t4 = (y2 - y1)*(B - x3) */ 845 | vli_modSub(Y2, Y2, Y1, curve_p, NUM_ECC_DIGITS); /* t4 = y3 */ 846 | 847 | vli_set(X2, t5, NUM_ECC_DIGITS); 848 | } 849 | 850 | /* Input P = (x1, y1, Z), Q = (x2, y2, Z) 851 | Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3) 852 | or P => P - Q, Q => P + Q 853 | */ 854 | static void XYcZ_addC(uint64_t *X1, uint64_t *Y1, uint64_t *X2, uint64_t *Y2, int NUM_ECC_DIGITS, uint64_t *curve_p) 855 | { 856 | /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ 857 | uint64_t t5[NUM_ECC_DIGITS]; 858 | uint64_t t6[NUM_ECC_DIGITS]; 859 | uint64_t t7[NUM_ECC_DIGITS]; 860 | 861 | vli_modSub(t5, X2, X1, curve_p, NUM_ECC_DIGITS); /* t5 = x2 - x1 */ 862 | vli_modSquare_fast(t5, t5, NUM_ECC_DIGITS, curve_p); /* t5 = (x2 - x1)^2 = A */ 863 | vli_modMult_fast(X1, X1, t5, NUM_ECC_DIGITS, curve_p); /* t1 = x1*A = B */ 864 | vli_modMult_fast(X2, X2, t5, NUM_ECC_DIGITS, curve_p); /* t3 = x2*A = C */ 865 | vli_modAdd(t5, Y2, Y1, curve_p, NUM_ECC_DIGITS); /* t4 = y2 + y1 */ 866 | vli_modSub(Y2, Y2, Y1, curve_p, NUM_ECC_DIGITS); /* t4 = y2 - y1 */ 867 | 868 | vli_modSub(t6, X2, X1, curve_p, NUM_ECC_DIGITS); /* t6 = C - B */ 869 | vli_modMult_fast(Y1, Y1, t6, NUM_ECC_DIGITS, curve_p); /* t2 = y1 * (C - B) */ 870 | vli_modAdd(t6, X1, X2, curve_p, NUM_ECC_DIGITS); /* t6 = B + C */ 871 | vli_modSquare_fast(X2, Y2, NUM_ECC_DIGITS, curve_p); /* t3 = (y2 - y1)^2 */ 872 | vli_modSub(X2, X2, t6, curve_p, NUM_ECC_DIGITS); /* t3 = x3 */ 873 | 874 | vli_modSub(t7, X1, X2, curve_p, NUM_ECC_DIGITS); /* t7 = B - x3 */ 875 | vli_modMult_fast(Y2, Y2, t7, NUM_ECC_DIGITS, curve_p); /* t4 = (y2 - y1)*(B - x3) */ 876 | vli_modSub(Y2, Y2, Y1, curve_p, NUM_ECC_DIGITS); /* t4 = y3 */ 877 | 878 | vli_modSquare_fast(t7, t5, NUM_ECC_DIGITS, curve_p); /* t7 = (y2 + y1)^2 = F */ 879 | vli_modSub(t7, t7, t6, curve_p, NUM_ECC_DIGITS); /* t7 = x3' */ 880 | vli_modSub(t6, t7, X1, curve_p, NUM_ECC_DIGITS); /* t6 = x3' - B */ 881 | vli_modMult_fast(t6, t6, t5, NUM_ECC_DIGITS, curve_p); /* t6 = (y2 + y1)*(x3' - B) */ 882 | vli_modSub(Y1, t6, Y1, curve_p, NUM_ECC_DIGITS); /* t2 = y3' */ 883 | 884 | vli_set(X1, t7, NUM_ECC_DIGITS); 885 | } 886 | 887 | static void EccPoint_mult(uint64_t *p_resultX, uint64_t *p_resultY, uint64_t *p_pointX, uint64_t *p_pointY, uint64_t *p_scalar, uint64_t *p_initialZ, int p_numBits, int NUM_ECC_DIGITS, uint64_t *curve_p) 888 | //static void EccPoint_mult(EccPoint *p_result, EccPoint *p_point, uint64_t *p_scalar, uint64_t *p_initialZ, int p_numBits) 889 | { 890 | /* R0 and R1 */ 891 | uint64_t Rx[2][NUM_ECC_DIGITS]; 892 | uint64_t Ry[2][NUM_ECC_DIGITS]; 893 | uint64_t z[NUM_ECC_DIGITS]; 894 | 895 | int i, nb; 896 | 897 | vli_set(Rx[1], p_pointX, NUM_ECC_DIGITS); 898 | vli_set(Ry[1], p_pointY, NUM_ECC_DIGITS); 899 | 900 | XYcZ_initial_double(Rx[1], Ry[1], Rx[0], Ry[0], p_initialZ, NUM_ECC_DIGITS, curve_p); 901 | 902 | for(i = p_numBits - 2; i > 0; --i) 903 | { 904 | nb = !vli_testBit(p_scalar, i); 905 | XYcZ_addC(Rx[1-nb], Ry[1-nb], Rx[nb], Ry[nb], NUM_ECC_DIGITS, curve_p); 906 | XYcZ_add(Rx[nb], Ry[nb], Rx[1-nb], Ry[1-nb], NUM_ECC_DIGITS, curve_p); 907 | } 908 | 909 | nb = !vli_testBit(p_scalar, 0); 910 | XYcZ_addC(Rx[1-nb], Ry[1-nb], Rx[nb], Ry[nb], NUM_ECC_DIGITS, curve_p); 911 | 912 | /* Find final 1/Z value. */ 913 | vli_modSub(z, Rx[1], Rx[0], curve_p, NUM_ECC_DIGITS); /* X1 - X0 */ 914 | vli_modMult_fast(z, z, Ry[1-nb], NUM_ECC_DIGITS, curve_p); /* Yb * (X1 - X0) */ 915 | vli_modMult_fast(z, z, p_pointX, NUM_ECC_DIGITS, curve_p); /* xP * Yb * (X1 - X0) */ 916 | vli_modInv(z, z, curve_p, NUM_ECC_DIGITS); /* 1 / (xP * Yb * (X1 - X0)) */ 917 | vli_modMult_fast(z, z, p_pointY, NUM_ECC_DIGITS, curve_p); /* yP / (xP * Yb * (X1 - X0)) */ 918 | vli_modMult_fast(z, z, Rx[1-nb], NUM_ECC_DIGITS, curve_p); /* Xb * yP / (xP * Yb * (X1 - X0)) */ 919 | /* End 1/Z calculation */ 920 | 921 | XYcZ_add(Rx[nb], Ry[nb], Rx[1-nb], Ry[1-nb], NUM_ECC_DIGITS, curve_p); 922 | 923 | apply_z(Rx[0], Ry[0], z, NUM_ECC_DIGITS, curve_p); 924 | 925 | vli_set(p_resultX, Rx[0], NUM_ECC_DIGITS); 926 | vli_set(p_resultY, Ry[0], NUM_ECC_DIGITS); 927 | } 928 | 929 | static void ecc_bytes2native(uint64_t *p_native, const uint8_t *p_bytes, int NUM_ECC_DIGITS) 930 | //static void ecc_bytes2native(uint64_t p_native[NUM_ECC_DIGITS], const uint8_t p_bytes[ECC_BYTES]) 931 | { 932 | unsigned i; 933 | for(i=0; i> 56; 949 | p_digit[1] = p_native[i] >> 48; 950 | p_digit[2] = p_native[i] >> 40; 951 | p_digit[3] = p_native[i] >> 32; 952 | p_digit[4] = p_native[i] >> 24; 953 | p_digit[5] = p_native[i] >> 16; 954 | p_digit[6] = p_native[i] >> 8; 955 | p_digit[7] = p_native[i]; 956 | } 957 | } 958 | 959 | /* Compute a = sqrt(a) (mod curve_p). */ 960 | static void mod_sqrt(uint64_t *a, int NUM_ECC_DIGITS, uint64_t *curve_p) 961 | //static void mod_sqrt(uint64_t a[NUM_ECC_DIGITS]) 962 | { 963 | unsigned i; 964 | uint64_t p1[NUM_ECC_DIGITS]; 965 | uint64_t l_result[NUM_ECC_DIGITS]; 966 | p1[0] = 1; 967 | l_result[0] = 1; 968 | for (int i = 1; i < NUM_ECC_DIGITS; i++) { 969 | p1[i] = l_result[i] = 0; 970 | } 971 | 972 | // uint64_t p1[NUM_ECC_DIGITS] = {1}; 973 | // uint64_t l_result[NUM_ECC_DIGITS] = {1}; 974 | 975 | /* Since curve_p == 3 (mod 4) for all supported curves, we can 976 | compute sqrt(a) = a^((curve_p + 1) / 4) (mod curve_p). */ 977 | vli_add(p1, curve_p, p1, NUM_ECC_DIGITS); /* p1 = curve_p + 1 */ 978 | for(i = vli_numBits(p1, NUM_ECC_DIGITS) - 1; i > 1; --i) 979 | { 980 | vli_modSquare_fast(l_result, l_result, NUM_ECC_DIGITS, curve_p); 981 | if(vli_testBit(p1, i)) 982 | { 983 | vli_modMult_fast(l_result, l_result, a, NUM_ECC_DIGITS, curve_p); 984 | } 985 | } 986 | vli_set(a, l_result, NUM_ECC_DIGITS); 987 | } 988 | 989 | static void ecc_point_decompress(uint64_t *p_pointX, uint64_t *p_pointY, const uint8_t *p_compressed, int NUM_ECC_DIGITS, uint64_t *curve_p, uint64_t *curve_b) 990 | //static void ecc_point_decompress(EccPoint *p_point, const uint8_t p_compressed[ECC_BYTES+1]) 991 | { 992 | uint64_t _3[NUM_ECC_DIGITS]; /* -a = 3 */ 993 | _3[0] = 3; 994 | for (int i = 1; i < NUM_ECC_DIGITS; i++) { 995 | _3[i] = 0; 996 | } 997 | 998 | //uint64_t _3[NUM_ECC_DIGITS] = {3}; /* -a = 3 */ 999 | ecc_bytes2native(p_pointX, p_compressed+1, NUM_ECC_DIGITS); 1000 | 1001 | vli_modSquare_fast(p_pointY, p_pointX, NUM_ECC_DIGITS, curve_p); /* y = x^2 */ 1002 | vli_modSub(p_pointY, p_pointY, _3, curve_p, NUM_ECC_DIGITS); /* y = x^2 - 3 */ 1003 | vli_modMult_fast(p_pointY, p_pointY, p_pointX, NUM_ECC_DIGITS, curve_p); /* y = x^3 - 3x */ 1004 | vli_modAdd(p_pointY, p_pointY, curve_b, curve_p, NUM_ECC_DIGITS); /* y = x^3 - 3x + b */ 1005 | 1006 | mod_sqrt(p_pointY, NUM_ECC_DIGITS, curve_p); 1007 | 1008 | if((p_pointY[0] & 0x01) != (p_compressed[0] & 0x01)) 1009 | { 1010 | vli_sub(p_pointY, curve_p, p_pointY, NUM_ECC_DIGITS); 1011 | } 1012 | } 1013 | 1014 | int ecc_make_key(uint8_t *p_publicKey, uint8_t *p_privateKey, int NUM_ECC_DIGITS, uint64_t *curve_p, uint64_t *curve_n, uint64_t *curve_GX, uint64_t *curve_GY) 1015 | //int ecc_make_key(uint8_t p_publicKey[ECC_BYTES+1], uint8_t p_privateKey[ECC_BYTES]) 1016 | { 1017 | uint64_t l_private[NUM_ECC_DIGITS]; 1018 | uint64_t l_publicX[NUM_ECC_DIGITS], l_publicY[NUM_ECC_DIGITS]; 1019 | //EccPoint l_public; 1020 | unsigned l_tries = 0; 1021 | 1022 | do 1023 | { 1024 | if(!getRandomNumber(l_private, NUM_ECC_DIGITS) || (l_tries++ >= MAX_TRIES)) 1025 | { 1026 | return 0; 1027 | } 1028 | if(vli_isZero(l_private, NUM_ECC_DIGITS)) 1029 | { 1030 | continue; 1031 | } 1032 | 1033 | /* Make sure the private key is in the range [1, n-1]. */ 1034 | if(vli_cmp(curve_n, l_private, NUM_ECC_DIGITS) != 1) 1035 | { 1036 | continue; 1037 | } 1038 | 1039 | EccPoint_mult(l_publicX, l_publicY, curve_GX, curve_GY, l_private, NULL, vli_numBits(l_private, NUM_ECC_DIGITS), NUM_ECC_DIGITS, curve_p); 1040 | //EccPoint_mult(&l_public, &curve_G, l_private, NULL, vli_numBits(l_private)); 1041 | 1042 | } while(EccPoint_isZero(l_publicX, l_publicY, NUM_ECC_DIGITS)); 1043 | //} while(EccPoint_isZero(&l_public, NUM_ECC_DIGITS)); 1044 | 1045 | ecc_native2bytes(p_privateKey, l_private, NUM_ECC_DIGITS); 1046 | ecc_native2bytes(p_publicKey + 1, l_publicX, NUM_ECC_DIGITS); 1047 | p_publicKey[0] = 2 + (l_publicY[0] & 0x01); 1048 | return 1; 1049 | } 1050 | 1051 | int ecdh_shared_secret(const uint8_t *p_publicKey, const uint8_t *p_privateKey, uint8_t *p_secret, int NUM_ECC_DIGITS, uint64_t *curve_p, uint64_t *curve_b) 1052 | //int ecdh_shared_secret(const uint8_t p_publicKey[ECC_BYTES+1], const uint8_t p_privateKey[ECC_BYTES], uint8_t p_secret[ECC_BYTES]) 1053 | { 1054 | uint64_t l_publicX[NUM_ECC_DIGITS], l_publicY[NUM_ECC_DIGITS]; 1055 | uint64_t l_private[NUM_ECC_DIGITS]; 1056 | uint64_t l_random[NUM_ECC_DIGITS]; 1057 | 1058 | if(!getRandomNumber(l_random, NUM_ECC_DIGITS)) 1059 | { 1060 | return 0; 1061 | } 1062 | 1063 | ecc_point_decompress(l_publicX, l_publicY, p_publicKey, NUM_ECC_DIGITS, curve_p, curve_b); 1064 | ecc_bytes2native(l_private, p_privateKey, NUM_ECC_DIGITS); 1065 | 1066 | uint64_t l_productX[NUM_ECC_DIGITS], l_productY[NUM_ECC_DIGITS]; 1067 | EccPoint_mult(l_productX, l_productY, l_publicX, l_publicY, l_private, l_random, vli_numBits(l_private, NUM_ECC_DIGITS), NUM_ECC_DIGITS, curve_p); 1068 | 1069 | ecc_native2bytes(p_secret, l_productX, NUM_ECC_DIGITS); 1070 | 1071 | return !EccPoint_isZero(l_productX, l_productY, NUM_ECC_DIGITS); 1072 | } 1073 | 1074 | /* -------- ECDSA code -------- */ 1075 | 1076 | /* Computes p_vli = p_vli >> 1. */ 1077 | static void vli2_rshift1(uint64_t *p_vli, int NUM_ECC_DIGITS) 1078 | { 1079 | uint64_t *l_end = p_vli; 1080 | uint64_t l_carry = 0; 1081 | 1082 | p_vli += NUM_ECC_DIGITS*2; 1083 | while(p_vli-- > l_end) 1084 | { 1085 | uint64_t l_temp = *p_vli; 1086 | *p_vli = (l_temp >> 1) | l_carry; 1087 | l_carry = l_temp << 63; 1088 | } 1089 | } 1090 | 1091 | /* Computes p_result = p_left - p_right, returning borrow. Can modify in place. */ 1092 | static uint64_t vli2_sub(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, int NUM_ECC_DIGITS) 1093 | { 1094 | uint64_t l_borrow = 0; 1095 | uint i; 1096 | for(i=0; i p_left[i]); 1102 | } 1103 | p_result[i] = l_diff; 1104 | } 1105 | return l_borrow; 1106 | } 1107 | 1108 | /* Computes p_result = (p_left * p_right) % p_mod. */ 1109 | static void vli_modMult(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, uint64_t *p_mod, int NUM_ECC_DIGITS) 1110 | { 1111 | uint64_t l_product[2 * NUM_ECC_DIGITS]; 1112 | uint64_t l_modMultiple[2 * NUM_ECC_DIGITS]; 1113 | uint64_t l_tmp[2 * NUM_ECC_DIGITS]; 1114 | uint64_t *v[2] = {l_tmp, l_product}; 1115 | 1116 | vli_mult(l_product, p_left, p_right, NUM_ECC_DIGITS); 1117 | vli_set(l_modMultiple + NUM_ECC_DIGITS, p_mod, NUM_ECC_DIGITS); 1118 | vli_clear(l_modMultiple, NUM_ECC_DIGITS); 1119 | 1120 | uint i; 1121 | uint l_index = 1; 1122 | for(i=0; i<=NUM_ECC_DIGITS * 64; ++i) 1123 | { 1124 | uint l_borrow = (uint)vli2_sub(v[1-l_index], v[l_index], l_modMultiple, NUM_ECC_DIGITS); /// Cast by RicMoo 1125 | l_index = !(l_index ^ l_borrow); /* Swap the index if there was no borrow */ 1126 | vli2_rshift1(l_modMultiple, NUM_ECC_DIGITS); 1127 | } 1128 | 1129 | vli_set(p_result, v[l_index], NUM_ECC_DIGITS); 1130 | } 1131 | 1132 | static uint umax(uint a, uint b) 1133 | { 1134 | return (a > b ? a : b); 1135 | } 1136 | 1137 | int ecdsa_sign(const uint8_t *p_privateKey, const uint8_t *p_hash, uint8_t *p_signature, int NUM_ECC_DIGITS, uint64_t *curve_p, uint64_t *curve_n, uint64_t *curve_GX, uint64_t *curve_GY) 1138 | //int ecdsa_sign(const uint8_t p_privateKey[ECC_BYTES], const uint8_t p_hash[ECC_BYTES], uint8_t p_signature[ECC_BYTES*2]) 1139 | { 1140 | int ECC_BYTES = NUM_ECC_DIGITS * 8; 1141 | 1142 | uint64_t k[NUM_ECC_DIGITS]; 1143 | uint64_t l_tmp[NUM_ECC_DIGITS]; 1144 | uint64_t s[NUM_ECC_DIGITS]; 1145 | uint64_t *k2[2] = {l_tmp, s}; 1146 | uint64_t pX[NUM_ECC_DIGITS], pY[NUM_ECC_DIGITS]; 1147 | unsigned l_tries = 0; 1148 | 1149 | do 1150 | { 1151 | if(!getRandomNumber(k, NUM_ECC_DIGITS) || (l_tries++ >= MAX_TRIES)) 1152 | { 1153 | return 0; 1154 | } 1155 | if(vli_isZero(k, NUM_ECC_DIGITS)) 1156 | { 1157 | continue; 1158 | } 1159 | 1160 | if(vli_cmp(curve_n, k, NUM_ECC_DIGITS) != 1) 1161 | { 1162 | continue; 1163 | } 1164 | 1165 | /* make sure that we don't leak timing information about k. See http://eprint.iacr.org/2011/232.pdf */ 1166 | uint64_t l_carry = vli_add(l_tmp, k, curve_n, NUM_ECC_DIGITS); 1167 | vli_add(s, l_tmp, curve_n, NUM_ECC_DIGITS); 1168 | 1169 | /* p = k * G */ 1170 | EccPoint_mult(pX, pY, curve_GX, curve_GY, k2[!l_carry], NULL, (ECC_BYTES * 8) + 1, NUM_ECC_DIGITS, curve_p); 1171 | 1172 | /* r = x1 (mod n) */ 1173 | if(vli_cmp(curve_n, pX, NUM_ECC_DIGITS) != 1) 1174 | { 1175 | vli_sub(pX, pX, curve_n, NUM_ECC_DIGITS); 1176 | } 1177 | } while(vli_isZero(pX, NUM_ECC_DIGITS)); 1178 | 1179 | do 1180 | { 1181 | if(!getRandomNumber(l_tmp, NUM_ECC_DIGITS) || (l_tries++ >= MAX_TRIES)) 1182 | { 1183 | return 0; 1184 | } 1185 | } while(vli_isZero(l_tmp, NUM_ECC_DIGITS)); 1186 | /* Prevent side channel analysis of vli_modInv() to determine 1187 | bits of k / the private key by premultiplying by a random number */ 1188 | vli_modMult(k, k, l_tmp, curve_n, NUM_ECC_DIGITS); /* k' = rand * k */ 1189 | vli_modInv(k, k, curve_n, NUM_ECC_DIGITS); /* k = 1 / k' */ 1190 | vli_modMult(k, k, l_tmp, curve_n, NUM_ECC_DIGITS); /* k = 1 / k */ 1191 | 1192 | ecc_native2bytes(p_signature, pX, NUM_ECC_DIGITS); /* store r */ 1193 | 1194 | ecc_bytes2native(l_tmp, p_privateKey, NUM_ECC_DIGITS); /* tmp = d */ 1195 | vli_modMult(s, l_tmp, pX, curve_n, NUM_ECC_DIGITS); /* s = r*d */ 1196 | 1197 | ecc_bytes2native(l_tmp, p_hash, NUM_ECC_DIGITS); 1198 | vli_modAdd(s, l_tmp, s, curve_n, NUM_ECC_DIGITS); /* s = e + r*d */ 1199 | vli_modMult(s, s, k, curve_n, NUM_ECC_DIGITS); /* s = (e + r*d) / k */ 1200 | ecc_native2bytes(p_signature + ECC_BYTES, s, NUM_ECC_DIGITS); 1201 | 1202 | return 1; 1203 | } 1204 | 1205 | static void clear_ecc_point(uint64_t *dstX, uint64_t *dstY, int length) { 1206 | for (int i = 0; i < length; i++) { 1207 | dstX[i] = 0; 1208 | dstY[i] = 0; 1209 | } 1210 | } 1211 | 1212 | static void copy_ecc_point(uint64_t *dstX, uint64_t *dstY, uint64_t *srcX, uint64_t *srcY, int length) { 1213 | for (int i = 0; i < length; i++) { 1214 | dstX[i] = srcX[i]; 1215 | dstY[i] = srcY[i]; 1216 | } 1217 | } 1218 | 1219 | int ecdsa_verify(const uint8_t *p_publicKey, const uint8_t *p_hash, const uint8_t *p_signature, int NUM_ECC_DIGITS, uint64_t *curve_p, uint64_t *curve_b, uint64_t *curve_n, uint64_t *curve_GX, uint64_t *curve_GY) 1220 | //int ecdsa_verify(const uint8_t p_publicKey[ECC_BYTES+1], const uint8_t p_hash[ECC_BYTES], const uint8_t p_signature[ECC_BYTES*2]) 1221 | { 1222 | int ECC_BYTES = NUM_ECC_DIGITS * 8; 1223 | 1224 | uint64_t u1[NUM_ECC_DIGITS], u2[NUM_ECC_DIGITS]; 1225 | uint64_t z[NUM_ECC_DIGITS]; 1226 | uint64_t l_publicX[NUM_ECC_DIGITS], l_publicY[NUM_ECC_DIGITS], l_sumX[NUM_ECC_DIGITS], l_sumY[NUM_ECC_DIGITS]; 1227 | uint64_t rx[NUM_ECC_DIGITS]; 1228 | uint64_t ry[NUM_ECC_DIGITS]; 1229 | uint64_t tx[NUM_ECC_DIGITS]; 1230 | uint64_t ty[NUM_ECC_DIGITS]; 1231 | uint64_t tz[NUM_ECC_DIGITS]; 1232 | 1233 | uint64_t l_r[NUM_ECC_DIGITS], l_s[NUM_ECC_DIGITS]; 1234 | 1235 | ecc_point_decompress(l_publicX, l_publicY, p_publicKey, NUM_ECC_DIGITS, curve_p, curve_b); 1236 | ecc_bytes2native(l_r, p_signature, NUM_ECC_DIGITS); 1237 | ecc_bytes2native(l_s, p_signature + ECC_BYTES, NUM_ECC_DIGITS); 1238 | 1239 | if(vli_isZero(l_r, NUM_ECC_DIGITS) || vli_isZero(l_s, NUM_ECC_DIGITS)) 1240 | { /* r, s must not be 0. */ 1241 | return 0; 1242 | } 1243 | 1244 | if(vli_cmp(curve_n, l_r, NUM_ECC_DIGITS) != 1 || vli_cmp(curve_n, l_s, NUM_ECC_DIGITS) != 1) 1245 | { /* r, s must be < n. */ 1246 | return 0; 1247 | } 1248 | 1249 | /* Calculate u1 and u2. */ 1250 | vli_modInv(z, l_s, curve_n, NUM_ECC_DIGITS); /* Z = s^-1 */ 1251 | ecc_bytes2native(u1, p_hash, NUM_ECC_DIGITS); 1252 | vli_modMult(u1, u1, z, curve_n, NUM_ECC_DIGITS); /* u1 = e/s */ 1253 | vli_modMult(u2, l_r, z, curve_n, NUM_ECC_DIGITS); /* u2 = r/s */ 1254 | 1255 | /* Calculate l_sum = G + Q. */ 1256 | vli_set(l_sumX, l_publicX, NUM_ECC_DIGITS); 1257 | vli_set(l_sumY, l_publicY, NUM_ECC_DIGITS); 1258 | vli_set(tx, curve_GX, NUM_ECC_DIGITS); 1259 | vli_set(ty, curve_GY, NUM_ECC_DIGITS); 1260 | vli_modSub(z, l_sumX, tx, curve_p, NUM_ECC_DIGITS); /* Z = x2 - x1 */ 1261 | XYcZ_add(tx, ty, l_sumX, l_sumY, NUM_ECC_DIGITS, curve_p); 1262 | vli_modInv(z, z, curve_p, NUM_ECC_DIGITS); /* Z = 1/Z */ 1263 | apply_z(l_sumX, l_sumY, z, NUM_ECC_DIGITS, curve_p); 1264 | 1265 | /* Use Shamir's trick to calculate u1*G + u2*Q */ 1266 | uint64_t l_pointsX[4 * NUM_ECC_DIGITS]; 1267 | uint64_t l_pointsY[4 * NUM_ECC_DIGITS]; 1268 | clear_ecc_point(&l_pointsX[0 * NUM_ECC_DIGITS], &l_pointsY[0 * NUM_ECC_DIGITS], NUM_ECC_DIGITS); 1269 | copy_ecc_point(&l_pointsX[1 * NUM_ECC_DIGITS], &l_pointsY[1 * NUM_ECC_DIGITS], curve_GX, curve_GY, NUM_ECC_DIGITS); 1270 | copy_ecc_point(&l_pointsX[2 * NUM_ECC_DIGITS], &l_pointsY[2 * NUM_ECC_DIGITS], l_publicX, l_publicY, NUM_ECC_DIGITS); 1271 | copy_ecc_point(&l_pointsX[3 * NUM_ECC_DIGITS], &l_pointsY[3 * NUM_ECC_DIGITS], l_sumX, l_sumY, NUM_ECC_DIGITS); 1272 | //EccPoint *l_points[4] = {NULL, &curve_G, &l_public, &l_sum}; 1273 | 1274 | uint l_numBits = umax(vli_numBits(u1, NUM_ECC_DIGITS), vli_numBits(u2, NUM_ECC_DIGITS)); 1275 | 1276 | uint64_t l_pointX[NUM_ECC_DIGITS]; 1277 | uint64_t l_pointY[NUM_ECC_DIGITS]; 1278 | int l_pointIndex = (!!vli_testBit(u1, l_numBits-1)) | ((!!vli_testBit(u2, l_numBits-1)) << 1); 1279 | copy_ecc_point(l_pointX, l_pointY, &l_pointsX[l_pointIndex * NUM_ECC_DIGITS], &l_pointsY[l_pointIndex * NUM_ECC_DIGITS], NUM_ECC_DIGITS); 1280 | //EccPoint *l_point = l_points[(!!vli_testBit(u1, l_numBits-1)) | ((!!vli_testBit(u2, l_numBits-1)) << 1)]; 1281 | 1282 | vli_set(rx, l_pointX, NUM_ECC_DIGITS); 1283 | vli_set(ry, l_pointY, NUM_ECC_DIGITS); 1284 | vli_clear(z, NUM_ECC_DIGITS); 1285 | z[0] = 1; 1286 | 1287 | int i; 1288 | for(i = l_numBits - 2; i >= 0; --i) 1289 | { 1290 | EccPoint_double_jacobian(rx, ry, z, NUM_ECC_DIGITS, curve_p); 1291 | 1292 | int l_index = (!!vli_testBit(u1, i)) | ((!!vli_testBit(u2, i)) << 1); 1293 | copy_ecc_point(l_pointX, l_pointY, &l_pointsX[l_index * NUM_ECC_DIGITS], &l_pointsY[l_index * NUM_ECC_DIGITS], NUM_ECC_DIGITS); 1294 | //l_point = l_points[l_index]; 1295 | if(l_index) 1296 | //if(l_point) 1297 | { 1298 | vli_set(tx, l_pointX, NUM_ECC_DIGITS); 1299 | vli_set(ty, l_pointY, NUM_ECC_DIGITS); 1300 | apply_z(tx, ty, z, NUM_ECC_DIGITS, curve_p); 1301 | vli_modSub(tz, rx, tx, curve_p, NUM_ECC_DIGITS); /* Z = x2 - x1 */ 1302 | XYcZ_add(tx, ty, rx, ry, NUM_ECC_DIGITS, curve_p); 1303 | vli_modMult_fast(z, z, tz, NUM_ECC_DIGITS, curve_p); 1304 | } 1305 | } 1306 | 1307 | vli_modInv(z, z, curve_p, NUM_ECC_DIGITS); /* Z = 1/Z */ 1308 | apply_z(rx, ry, z, NUM_ECC_DIGITS, curve_p); 1309 | 1310 | /* v = x1 (mod n) */ 1311 | if(vli_cmp(curve_n, rx, NUM_ECC_DIGITS) != 1) 1312 | { 1313 | vli_sub(rx, rx, curve_n, NUM_ECC_DIGITS); 1314 | } 1315 | 1316 | /* Accept only if v == r. */ 1317 | return (vli_cmp(rx, l_r, NUM_ECC_DIGITS) == 0); 1318 | } 1319 | 1320 | 1321 | // secp128r1 1322 | static uint64_t Curve_p_128[2] = {0xFFFFFFFFFFFFFFFF, 0xFFFFFFFDFFFFFFFF}; 1323 | static uint64_t Curve_b_128[2] = {0xD824993C2CEE5ED3, 0xE87579C11079F43D}; 1324 | static uint64_t Curve_Gx_128[2] = {0x0C28607CA52C5B86, 0x161FF7528B899B2D}; 1325 | static uint64_t Curve_Gy_128[2] = {0xC02DA292DDED7A83, 0xCF5AC8395BAFEB13}; 1326 | static uint64_t Curve_n_128[2] = {0x75A30D1B9038A115, 0xFFFFFFFE00000000}; 1327 | 1328 | // secp192r1 1329 | static uint64_t Curve_p_192[3] = {0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFFFFFFFFFEull, 0xFFFFFFFFFFFFFFFFull}; 1330 | static uint64_t Curve_b_192[3] = {0xFEB8DEECC146B9B1ull, 0x0FA7E9AB72243049ull, 0x64210519E59C80E7ull}; 1331 | static uint64_t Curve_Gx_192[3] = {0xF4FF0AFD82FF1012ull, 0x7CBF20EB43A18800ull, 0x188DA80EB03090F6ull}; 1332 | static uint64_t Curve_Gy_192[3] = {0x73F977A11E794811ull, 0x631011ED6B24CDD5ull, 0x07192B95FFC8DA78ull}; 1333 | static uint64_t Curve_n_192[3] = {0x146BC9B1B4D22831ull, 0xFFFFFFFF99DEF836ull, 0xFFFFFFFFFFFFFFFFull}; 1334 | 1335 | // secp256r1 1336 | static uint64_t Curve_p_256[4] = {0xFFFFFFFFFFFFFFFFull, 0x00000000FFFFFFFFull, 0x0000000000000000ull, 0xFFFFFFFF00000001ull}; 1337 | static uint64_t Curve_b_256[4] = {0x3BCE3C3E27D2604Bull, 0x651D06B0CC53B0F6ull, 0xB3EBBD55769886BCull, 0x5AC635D8AA3A93E7ull}; 1338 | static uint64_t Curve_Gx_256[4] = {0xF4A13945D898C296ull, 0x77037D812DEB33A0ull, 0xF8BCE6E563A440F2ull, 0x6B17D1F2E12C4247ull}; 1339 | static uint64_t Curve_Gy_256[4] = {0xCBB6406837BF51F5ull, 0x2BCE33576B315ECEull, 0x8EE7EB4A7C0F9E16ull, 0x4FE342E2FE1A7F9Bull}; 1340 | static uint64_t Curve_n_256[4] = {0xF3B9CAC2FC632551ull, 0xBCE6FAADA7179E84ull, 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFF00000000ull}; 1341 | 1342 | // secp384r1 1343 | static uint64_t Curve_p_384[6] = {0x00000000FFFFFFFF, 0xFFFFFFFF00000000, 0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}; 1344 | static uint64_t Curve_b_384[6] = {0x2A85C8EDD3EC2AEF, 0xC656398D8A2ED19D, 0x0314088F5013875A, 0x181D9C6EFE814112, 0x988E056BE3F82D19, 0xB3312FA7E23EE7E4}; 1345 | static uint64_t Curve_Gx_384[6] = {0x3A545E3872760AB7, 0x5502F25DBF55296C, 0x59F741E082542A38, 0x6E1D3B628BA79B98, 0x8EB1C71EF320AD74, 0xAA87CA22BE8B0537}; 1346 | static uint64_t Curve_Gy_384[6] = {0x7A431D7C90EA0E5F, 0x0A60B1CE1D7E819D, 0xE9DA3113B5F0B8C0, 0xF8F41DBD289A147C, 0x5D9E98BF9292DC29, 0x3617DE4A96262C6F}; 1347 | static uint64_t Curve_n_384[6] = {0xECEC196ACCC52973, 0x581A0DB248B0A77A, 0xC7634D81F4372DDF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}; 1348 | 1349 | 1350 | /* end of ecc.c */ 1351 | 1352 | 1353 | 1354 | @interface GMEllipticCurveCrypto () { 1355 | int _bytes, _numDigits; 1356 | uint64_t *_curve_p, *_curve_b, *_curve_Gx, *_curve_Gy, *_curve_n; 1357 | NSData *_publicKey; 1358 | } 1359 | 1360 | @end 1361 | 1362 | 1363 | @implementation GMEllipticCurveCrypto 1364 | 1365 | 1366 | + (GMEllipticCurveCrypto*)generateKeyPairForCurve:(GMEllipticCurve)curve { 1367 | GMEllipticCurveCrypto *crypto = [[self alloc] initWithCurve:curve]; 1368 | [crypto generateNewKeyPair]; 1369 | return crypto; 1370 | 1371 | } 1372 | 1373 | + (GMEllipticCurve)curveForKey:(NSData *)privateOrPublicKey { 1374 | 1375 | NSInteger length = [privateOrPublicKey length]; 1376 | 1377 | // We need at least 1 byte 1378 | if (length == 0) { 1379 | return GMEllipticCurveNone; 1380 | } 1381 | 1382 | const uint8_t *bytes = [privateOrPublicKey bytes]; 1383 | 1384 | // Odd-length, therefore a public key 1385 | if (length % 2) { 1386 | switch (bytes[0]) { 1387 | case 0x04: 1388 | length = (length - 1) / 2; 1389 | break; 1390 | case 0x02: case 0x03: 1391 | length--; 1392 | break; 1393 | default: 1394 | return GMEllipticCurveNone; 1395 | } 1396 | } 1397 | 1398 | switch (length) { 1399 | case 16: 1400 | return GMEllipticCurveSecp128r1; 1401 | case 24: 1402 | return GMEllipticCurveSecp192r1; 1403 | case 32: 1404 | return GMEllipticCurveSecp256r1; 1405 | case 48: 1406 | return GMEllipticCurveSecp384r1; 1407 | } 1408 | 1409 | return GMEllipticCurveNone; 1410 | } 1411 | 1412 | 1413 | + (GMEllipticCurve)curveForKeyBase64:(NSString *)privateOrPublicKey { 1414 | return [self curveForKey:[[NSData alloc] initWithBase64EncodedString:privateOrPublicKey options:0]]; 1415 | } 1416 | 1417 | 1418 | + (GMEllipticCurveCrypto*)cryptoForKey:(NSData *)privateOrPublicKey { 1419 | GMEllipticCurve curve = [self curveForKey:privateOrPublicKey]; 1420 | GMEllipticCurveCrypto *crypto = [[GMEllipticCurveCrypto alloc] initWithCurve:curve]; 1421 | if ([privateOrPublicKey length] % 2) { 1422 | crypto.publicKey = privateOrPublicKey; 1423 | } else { 1424 | crypto.privateKey = privateOrPublicKey; 1425 | } 1426 | return crypto; 1427 | } 1428 | 1429 | 1430 | + (GMEllipticCurveCrypto*)cryptoForKeyBase64:(NSString *)privateOrPublicKey { 1431 | return [self cryptoForKey:[[NSData alloc] initWithBase64EncodedString:privateOrPublicKey options:0]]; 1432 | } 1433 | 1434 | + (id)cryptoForCurve:(GMEllipticCurve)curve { 1435 | return [[self alloc] initWithCurve:curve]; 1436 | } 1437 | 1438 | - (id)initWithCurve:(GMEllipticCurve)curve { 1439 | self = [super init]; 1440 | if (self) { 1441 | _compressedPublicKey = YES; 1442 | 1443 | _bits = curve; 1444 | _bytes = _bits / 8; 1445 | _numDigits = _bytes / 8; 1446 | 1447 | switch (_bits) { 1448 | case 128: 1449 | _name = @"secp128r1"; 1450 | _curve_p = Curve_p_128; 1451 | _curve_b = Curve_b_128; 1452 | _curve_Gx = Curve_Gx_128; 1453 | _curve_Gy = Curve_Gy_128; 1454 | _curve_n = Curve_n_128; 1455 | break; 1456 | case 192: 1457 | _name = @"secp192r1"; 1458 | _curve_p = Curve_p_192; 1459 | _curve_b = Curve_b_192; 1460 | _curve_Gx = Curve_Gx_192; 1461 | _curve_Gy = Curve_Gy_192; 1462 | _curve_n = Curve_n_192; 1463 | break; 1464 | case 256: 1465 | _name = @"secp256r1"; 1466 | _curve_p = Curve_p_256; 1467 | _curve_b = Curve_b_256; 1468 | _curve_Gx = Curve_Gx_256; 1469 | _curve_Gy = Curve_Gy_256; 1470 | _curve_n = Curve_n_256; 1471 | break; 1472 | case 384: 1473 | _name = @"secp384r1"; 1474 | _curve_p = Curve_p_384; 1475 | _curve_b = Curve_b_384; 1476 | _curve_Gx = Curve_Gx_384; 1477 | _curve_Gy = Curve_Gy_384; 1478 | _curve_n = Curve_n_384; 1479 | break; 1480 | default: 1481 | NSLog(@"These are not the droids you are looking for."); 1482 | return nil; 1483 | break; 1484 | } 1485 | 1486 | } 1487 | return self; 1488 | } 1489 | 1490 | 1491 | - (BOOL)generateNewKeyPair { 1492 | uint8_t l_public[_bytes + 1]; 1493 | uint8_t l_private[_bytes]; 1494 | 1495 | BOOL success = ecc_make_key(l_public, l_private, _numDigits, _curve_p, _curve_n, _curve_Gx, _curve_Gy); 1496 | 1497 | _publicKey = [NSData dataWithBytes:l_public length:_bytes + 1]; 1498 | _privateKey = [NSData dataWithBytes:l_private length:_bytes]; 1499 | 1500 | return success; 1501 | } 1502 | 1503 | 1504 | - (NSData*)sharedSecretForPublicKey: (NSData*)otherPublicKey { 1505 | if (!_privateKey) { 1506 | [NSException raise:@"Missing Key" format:@"Cannot create shared secret without a private key"]; 1507 | } 1508 | 1509 | // Prepare the private key 1510 | uint8_t l_private[_bytes]; 1511 | if ([_privateKey length] != _bytes) { 1512 | [NSException raise:@"Invalid Key" format:@"Private key %@ is invalid", _privateKey]; 1513 | } 1514 | [_privateKey getBytes:&l_private length:[_privateKey length]]; 1515 | 1516 | // Prepare the public key 1517 | uint8_t l_other_public[_bytes + 1]; 1518 | if ([otherPublicKey length] != _bytes + 1) { 1519 | [NSException raise:@"Invalid Key" format:@"Public key %@ is invalid", otherPublicKey]; 1520 | } 1521 | [otherPublicKey getBytes:&l_other_public length:[otherPublicKey length]]; 1522 | 1523 | // Create the secret 1524 | uint8_t l_secret[_bytes]; 1525 | int success = ecdh_shared_secret(l_other_public, l_private, l_secret, _numDigits, _curve_p, _curve_b); 1526 | 1527 | if (!success) { return nil; } 1528 | 1529 | return [NSData dataWithBytes:l_secret length:_bytes]; 1530 | } 1531 | 1532 | 1533 | - (NSData*)sharedSecretForPublicKeyBase64: (NSString*)otherPublicKeyBase64 { 1534 | return [self sharedSecretForPublicKey:[[NSData alloc] initWithBase64EncodedString:otherPublicKeyBase64 options:0]]; 1535 | } 1536 | 1537 | 1538 | - (NSData*)signatureForHash:(NSData *)hash { 1539 | if (!_privateKey) { 1540 | [NSException raise:@"Missing Key" format:@"Cannot sign a hash without a private key"]; 1541 | } 1542 | 1543 | // Prepare the private key 1544 | uint8_t l_private[_bytes]; 1545 | if ([_privateKey length] != _bytes) { 1546 | [NSException raise:@"Invalid Key" format:@"Private key %@ is invalid", _privateKey]; 1547 | } 1548 | [_privateKey getBytes:&l_private length:[_privateKey length]]; 1549 | 1550 | // Prepare the hash 1551 | uint8_t l_hash[_bytes]; 1552 | if ([hash length] != _bytes) { 1553 | [NSException raise:@"Invalid hash" format:@"Signing requires a hash the same length as the curve"]; 1554 | } 1555 | [hash getBytes:&l_hash length:[hash length]]; 1556 | 1557 | // Create the signature 1558 | uint8_t l_signature[2 * _bytes]; 1559 | int success = ecdsa_sign(l_private, l_hash, l_signature, _numDigits, _curve_p, _curve_n, _curve_Gx, _curve_Gy); 1560 | 1561 | if (!success) { return nil; } 1562 | 1563 | return [NSData dataWithBytes:l_signature length:2 * _bytes]; 1564 | } 1565 | 1566 | 1567 | - (BOOL)verifySignature:(NSData *)signature forHash:(NSData *)hash { 1568 | if (!_publicKey) { 1569 | [NSException raise:@"Missing Key" format:@"Cannot verify signature without a public key"]; 1570 | } 1571 | 1572 | // Prepare the signature 1573 | uint8_t l_signature[2 * _bytes]; 1574 | if ([signature length] != 2 * _bytes) { 1575 | [NSException raise:@"Invalid signature" format:@"Signature must be twice the length of its curve"]; 1576 | } 1577 | [signature getBytes:&l_signature length:[signature length]]; 1578 | 1579 | // Prepare the public key 1580 | uint8_t l_public[_bytes + 1]; 1581 | if ([_publicKey length] != _bytes + 1) { 1582 | [NSException raise:@"Invalid Key" format:@"Public key %@ is invalid", _publicKey]; 1583 | } 1584 | [_publicKey getBytes:&l_public length:[_publicKey length]]; 1585 | 1586 | // Prepare the hash 1587 | uint8_t l_hash[_bytes]; 1588 | if ([hash length] != _bytes) { 1589 | [NSException raise:@"Invalid hash" format:@"Verifying requires a hash the same length as the curve"]; 1590 | } 1591 | [hash getBytes:&l_hash length:[hash length]]; 1592 | 1593 | // Check the signature 1594 | return ecdsa_verify(l_public, l_hash, l_signature, _numDigits, _curve_p, _curve_b, _curve_n, _curve_Gx, _curve_Gy); 1595 | } 1596 | 1597 | 1598 | - (int)hashLength { 1599 | return _bytes; 1600 | } 1601 | 1602 | 1603 | - (int)sharedSecretLength { 1604 | return _bytes; 1605 | } 1606 | 1607 | 1608 | - (int)signatureLength { 1609 | return 2 * _bytes; 1610 | } 1611 | 1612 | 1613 | - (NSData*)publicKeyForPrivateKey: (NSData*)privateKey { 1614 | 1615 | // Prepare the private key 1616 | uint8_t l_privateBytes[_bytes]; 1617 | if ([privateKey length] != _bytes) { 1618 | [NSException raise:@"Invalid Key" format:@"Private key %@ is invalid", privateKey]; 1619 | } 1620 | [privateKey getBytes:&l_privateBytes length:[privateKey length]]; 1621 | uint64_t l_private[_numDigits]; 1622 | ecc_bytes2native(l_private, l_privateBytes, _numDigits); 1623 | 1624 | // The (x, y) public point 1625 | uint64_t l_publicX[_numDigits], l_publicY[_numDigits]; 1626 | EccPoint_mult(l_publicX, l_publicY, _curve_Gx, _curve_Gy, l_private, NULL, vli_numBits(l_private, _numDigits), _numDigits, _curve_p); 1627 | 1628 | // Now compress the point into our public key 1629 | uint8_t l_public[_bytes + 1]; 1630 | ecc_native2bytes(l_public + 1, l_publicX, _numDigits); 1631 | l_public[0] = 2 + (l_publicY[0] & 0x01); 1632 | 1633 | return [NSData dataWithBytes:l_public length:_bytes + 1]; 1634 | } 1635 | 1636 | - (NSData*)compressPublicKey: (NSData*)publicKey { 1637 | 1638 | NSInteger length = [publicKey length]; 1639 | 1640 | if (length == 0) { 1641 | return nil; 1642 | } 1643 | 1644 | const uint8_t *bytes = [publicKey bytes]; 1645 | 1646 | switch (bytes[0]) { 1647 | 1648 | // Already compressed 1649 | case 0x02: case 0x03: 1650 | if (length != (1 + _bytes)) { 1651 | return nil; 1652 | } 1653 | 1654 | return publicKey; 1655 | 1656 | // Compress! 1657 | case 0x04: { 1658 | if (length != (1 + 2 * _bytes)) { 1659 | return nil; 1660 | } 1661 | 1662 | // Get the (x, y) point from the public key 1663 | uint64_t l_publicX[_numDigits], l_publicY[_numDigits]; 1664 | ecc_bytes2native(l_publicX, &bytes[1], _numDigits); 1665 | ecc_bytes2native(l_publicY, &bytes[1 + _bytes], _numDigits); 1666 | 1667 | // And compress 1668 | uint8_t l_public[_bytes + 1]; 1669 | ecc_native2bytes(l_public + 1, l_publicX, _numDigits); 1670 | l_public[0] = 2 + (l_publicY[0] & 0x01); 1671 | 1672 | return [NSData dataWithBytes:l_public length:_bytes + 1]; 1673 | } 1674 | } 1675 | 1676 | return nil; 1677 | } 1678 | 1679 | - (NSData*)decompressPublicKey: (NSData*)publicKey { 1680 | NSInteger length = [publicKey length]; 1681 | 1682 | if (length == 0) { 1683 | return nil; 1684 | } 1685 | 1686 | const uint8_t *bytes = [publicKey bytes]; 1687 | 1688 | switch (bytes[0]) { 1689 | 1690 | // Already uncompressed 1691 | case 0x04: 1692 | if (length != (1 + 2 * _bytes)) { 1693 | return nil; 1694 | } 1695 | return publicKey; 1696 | 1697 | case 0x02: case 0x03: { 1698 | if (length != (1 + _bytes)) { 1699 | return nil; 1700 | } 1701 | 1702 | // Decompress to get the (x, y) point 1703 | uint64_t l_publicX[_numDigits], l_publicY[_numDigits]; 1704 | ecc_point_decompress(l_publicX, l_publicY, [_publicKey bytes], _numDigits, _curve_p, _curve_b); 1705 | 1706 | // Compose the public key (0x04 + x + y) 1707 | uint8_t l_public[2 * _bytes + 1]; 1708 | l_public[0] = 0x04; 1709 | ecc_native2bytes(l_public + 1, l_publicX, _numDigits); 1710 | ecc_native2bytes(l_public + 1 + _bytes, l_publicY, _numDigits); 1711 | 1712 | uint8_t l_publicXBytes[_bytes]; 1713 | uint8_t l_publicYBytes[_bytes]; 1714 | ecc_native2bytes(l_publicXBytes, l_publicX, _numDigits); 1715 | ecc_native2bytes(l_publicYBytes, l_publicY, _numDigits); 1716 | 1717 | self.publicKeyXBase64 = [[NSData dataWithBytes:l_publicXBytes length:_bytes] base64EncodedStringWithOptions:NSUTF8StringEncoding]; 1718 | 1719 | self.publicKeyYBase64 = [[NSData dataWithBytes:l_publicYBytes length:_bytes] base64EncodedStringWithOptions:NSUTF8StringEncoding]; 1720 | 1721 | return [NSData dataWithBytes:l_public length:2 * _bytes + 1]; 1722 | } 1723 | } 1724 | 1725 | return nil; 1726 | } 1727 | 1728 | - (NSString*)privateKeyBase64 { 1729 | return [_privateKey base64EncodedStringWithOptions:0]; 1730 | } 1731 | 1732 | 1733 | - (void)setPrivateKey: (NSData*)privateKey { 1734 | int keyBits = [GMEllipticCurveCrypto curveForKey:privateKey]; 1735 | if (keyBits != _bits) { 1736 | [NSException raise:@"Invalid Key" format:@"Private key %@ is %d bits; curve is %d bits", privateKey, keyBits, _bits]; 1737 | } 1738 | 1739 | NSData *checkPublicKey = [self publicKeyForPrivateKey:privateKey]; 1740 | if (_publicKey && ![_publicKey isEqual:checkPublicKey]) { 1741 | [NSException raise:@"Key mismatch" format:@"Private key %@ does not match public key %@", privateKey, _publicKey]; 1742 | } 1743 | 1744 | _publicKey = checkPublicKey; 1745 | _privateKey = privateKey; 1746 | } 1747 | 1748 | 1749 | - (void)setPrivateKeyBase64:(NSString *)privateKeyBase64 { 1750 | [self setPrivateKey:[[NSData alloc] initWithBase64EncodedString:privateKeyBase64 options:0]]; 1751 | } 1752 | 1753 | 1754 | - (NSString*)publicKeyBase64 { 1755 | return [self.publicKey base64EncodedStringWithOptions:0]; 1756 | } 1757 | 1758 | 1759 | - (NSData*)publicKey { 1760 | if (_compressedPublicKey) { 1761 | return _publicKey; 1762 | } 1763 | return [self decompressPublicKey:_publicKey]; 1764 | } 1765 | 1766 | - (void)setPublicKey: (NSData*)publicKey { 1767 | int keyBits = [GMEllipticCurveCrypto curveForKey:publicKey]; 1768 | if (keyBits != _bits) { 1769 | [NSException raise:@"Invalid Key" format:@"Public key %@ is %d bits; curve is %d bits", publicKey, keyBits, _bits]; 1770 | } 1771 | 1772 | const uint8_t *bytes = [publicKey bytes]; 1773 | BOOL compressedPublicKey = (bytes[0] != (uint8_t)0x04); 1774 | 1775 | // Ensure the key is compressed (we only store compressed keys internally) 1776 | publicKey = [self compressPublicKey:publicKey]; 1777 | 1778 | // If the private key has already been set, and it doesn't match, complain 1779 | if (_privateKey && ![publicKey isEqual:_publicKey]) { 1780 | [NSException raise:@"Key mismatch" format:@"Private key %@ does not match public key %@", _privateKey, publicKey]; 1781 | } 1782 | 1783 | _compressedPublicKey = compressedPublicKey; 1784 | _publicKey = publicKey; 1785 | } 1786 | 1787 | 1788 | - (void)setPublicKeyBase64:(NSString *)publicKeyBase64 { 1789 | [self setPublicKey:[[NSData alloc] initWithBase64EncodedString:publicKeyBase64 options:0]]; 1790 | } 1791 | 1792 | 1793 | - (NSString*)description { 1794 | return [NSString stringWithFormat:@"", _name, self.publicKeyBase64, self.privateKeyBase64]; 1795 | } 1796 | 1797 | 1798 | @end 1799 | --------------------------------------------------------------------------------