· 6 years ago · Nov 20, 2019, 03:10 AM
1// RallyWeb3Sign.swift
2// Copyright © 2019 Rally Budget Inc. All rights reserved.
3
4import BigInt
5import CryptoSwift
6import Foundation
7import secp256k1
8
9
10//MARK: - API
11
12public func RallySecp256k1Sign(message: String,
13 stringPrivateKey: String,
14 prefixWith0x: Bool = false) throws -> String {
15 return try RallySecp256k1SignBytes(bytes: message.makeBytes(), stringPrivateKey: stringPrivateKey, prefixWith0x: prefixWith0x)
16}
17
18public func RallySecp256k1SignData(data: Data,
19 stringPrivateKey: String,
20 prefixWith0x: Bool = false) throws -> String {
21 return try RallySecp256k1SignBytes(bytes: data.bytes, stringPrivateKey: stringPrivateKey, prefixWith0x: prefixWith0x)
22}
23
24public func RallySecp256k1SignBytes(bytes: Bytes,
25 stringPrivateKey: String,
26 prefixWith0x: Bool = false) throws -> String {
27 let messageSigner = try RallySecp256k1Signer(hexPrivateKey: stringPrivateKey)
28 let signature = try messageSigner.sign(message: bytes)
29
30 let v: BigUInt = BigUInt(signature.v) + BigUInt(27)
31 let recoveryId = v == 1 ? "1c" : "1b"
32 let prefix = prefixWith0x ? "0x" : ""
33 return prefix
34 + signature.r.toHexString()
35 + signature.s.toHexString()
36 + recoveryId
37}
38
39
40//MARK: - Typealiases
41
42public typealias Byte = UInt8
43public typealias Bytes = [Byte]
44
45
46//MARK: - Signer
47
48public final class RallySecp256k1Signer {
49
50 // MARK: - Properties
51
52 /// The raw private key bytes
53 public let rawPrivateKey: Bytes
54
55 /// True iff ctx should not be freed on deinit
56 private let ctxSelfManaged: Bool
57
58 /// Internal context for secp256k1 library calls
59 private let ctx: OpaquePointer
60
61 // MARK: - Initialization
62
63 /**
64 * Initializes a new instance of `RallySecp256k1Signer` with the given `privateKey` Bytes.
65 *
66 * `privateKey` must be exactly a big endian 32 Byte array representing the private key.
67 *
68 * The number must be in the secp256k1 range as described in: https://en.bitcoin.it/wiki/Private_key
69 *
70 * So any number between
71 *
72 * 0x0000000000000000000000000000000000000000000000000000000000000001
73 *
74 * and
75 *
76 * 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140
77 *
78 * is considered to be a valid secp256k1 private key.
79 *
80 * - parameter privateKey: The private key bytes.
81 *
82 * - parameter ctx: An optional self managed context. If you have specific requirements and
83 * your app performs not as fast as you want it to, you can manage the
84 * `secp256k1_context` yourself with the public methods
85 * `secp256k1_default_ctx_create` and `secp256k1_default_ctx_destroy`.
86 * If you do this, we will not be able to free memory automatically and you
87 * __have__ to destroy the context yourself once your app is closed or
88 * you are sure it will not be used any longer. Only use this optional
89 * context management if you know exactly what you are doing and you really
90 * need it.
91 *
92 * - throws: RallySecp256k1Signer.Error.keyMalformed if the restrictions described above are not met.
93 * RallySecp256k1Signer.Error.internalError if a secp256k1 library call or another internal call fails.
94 * RallySecp256k1Signer.Error.pubKeyGenerationFailed if the public key extraction from the private key fails.
95 */
96 public init(privateKey: Bytes, ctx: OpaquePointer? = nil) throws {
97 guard privateKey.count == 32 else {
98 throw Error.keyMalformed
99 }
100
101 self.rawPrivateKey = privateKey
102
103 let finalCtx: OpaquePointer
104 if let ctx = ctx {
105 finalCtx = ctx
106 self.ctxSelfManaged = true
107 } else {
108 let ctx = try secp256k1_default_ctx_create(errorThrowable: Error.internalError)
109 finalCtx = ctx
110 self.ctxSelfManaged = false
111 }
112 self.ctx = finalCtx
113
114 // Verify private key
115 try verifyPrivateKey()
116 }
117
118 /**
119 * Initializes a new instance of `RallySecp256k1Signer` with the given `hexPrivateKey` hex string.
120 *
121 * `hexPrivateKey` must be either 64 characters long or 66 characters (with the hex prefix 0x).
122 *
123 * The number must be in the secp256k1 range as described in: https://en.bitcoin.it/wiki/Private_key
124 *
125 * So any number between
126 *
127 * 0x0000000000000000000000000000000000000000000000000000000000000001
128 *
129 * and
130 *
131 * 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140
132 *
133 * is considered to be a valid secp256k1 private key.
134 *
135 * - parameter hexPrivateKey: The private key bytes.
136 *
137 * - parameter ctx: An optional self managed context. If you have specific requirements and
138 * your app performs not as fast as you want it to, you can manage the
139 * `secp256k1_context` yourself with the public methods
140 * `secp256k1_default_ctx_create` and `secp256k1_default_ctx_destroy`.
141 * If you do this, we will not be able to free memory automatically and you
142 * __have__ to destroy the context yourself once your app is closed or
143 * you are sure it will not be used any longer. Only use this optional
144 * context management if you know exactly what you are doing and you really
145 * need it.
146 *
147 * - throws: RallySecp256k1Signer.Error.keyMalformed if the restrictions described above are not met.
148 * RallySecp256k1Signer.Error.internalError if a secp256k1 library call or another internal call fails.
149 * RallySecp256k1Signer.Error.pubKeyGenerationFailed if the public key extraction from the private key fails.
150 */
151 public convenience init(hexPrivateKey: String, ctx: OpaquePointer? = nil) throws {
152 guard hexPrivateKey.count == 64 || hexPrivateKey.count == 66 else {
153 throw Error.keyMalformed
154 }
155
156 var hexPrivateKey = hexPrivateKey
157
158 if hexPrivateKey.count == 66 {
159 let s = hexPrivateKey.index(hexPrivateKey.startIndex, offsetBy: 0)
160 let e = hexPrivateKey.index(hexPrivateKey.startIndex, offsetBy: 2)
161 let prefix = String(hexPrivateKey[s..<e])
162
163 guard prefix == "0x" else {
164 throw Error.keyMalformed
165 }
166
167 // Remove prefix
168 hexPrivateKey = String(hexPrivateKey[e...])
169 }
170
171 var raw = Bytes()
172 for i in stride(from: 0, to: hexPrivateKey.count, by: 2) {
173 let s = hexPrivateKey.index(hexPrivateKey.startIndex, offsetBy: i)
174 let e = hexPrivateKey.index(hexPrivateKey.startIndex, offsetBy: i + 2)
175
176 guard let b = Byte(String(hexPrivateKey[s..<e]), radix: 16) else {
177 throw Error.keyMalformed
178 }
179 raw.append(b)
180 }
181
182 try self.init(privateKey: raw, ctx: ctx)
183 }
184
185 // MARK: - Convenient functions
186
187 public func sign(message: Bytes) throws -> (v: UInt, r: Bytes, s: Bytes) {
188 var hash = SHA3(variant: .keccak256).calculate(for: message)
189 guard hash.count == 32 else {
190 throw Error.internalError
191 }
192 guard let sig = malloc(MemoryLayout<secp256k1_ecdsa_recoverable_signature>.size)?.assumingMemoryBound(to: secp256k1_ecdsa_recoverable_signature.self) else {
193 throw Error.internalError
194 }
195 defer {
196 free(sig)
197 }
198
199 var seckey = rawPrivateKey
200 guard secp256k1_ecdsa_sign_recoverable(ctx, sig, &hash, &seckey, nil, nil) == 1 else {
201 throw Error.internalError
202 }
203
204 var output64 = Bytes(repeating: 0, count: 64)
205 var recid: Int32 = 0
206 secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, &output64, &recid, sig)
207
208 guard recid == 0 || recid == 1 else {
209 // Well I guess this one should never happen but to avoid bigger problems...
210 throw Error.internalError
211 }
212
213 return (v: UInt(recid), r: Array(output64[0..<32]), s: Array(output64[32..<64]))
214 }
215
216 // MARK: - Helper functions
217
218 private func verifyPrivateKey() throws {
219 var secret = rawPrivateKey
220 guard secp256k1_ec_seckey_verify(ctx, &secret) == 1 else {
221 throw Error.keyMalformed
222 }
223 }
224
225 // MARK: - Errors
226
227 public enum Error: Swift.Error {
228
229 case internalError
230 case keyMalformed
231 case pubKeyGenerationFailed
232 }
233
234 // MARK: - Deinitialization
235
236 deinit {
237 if !ctxSelfManaged {
238 secp256k1_context_destroy(ctx)
239 }
240 }
241}
242
243
244
245//MARK: - String To Bytes
246
247extension String {
248 /**
249 * UTF8 Byte Array representation of self
250 */
251 public func makeBytes() -> Bytes {
252 return Bytes(utf8)
253 }
254}
255
256
257//MAR: - Uint Array
258
259public extension Array where Element == UInt8 {
260 static func secureRandom(count: Int) -> [UInt8]? {
261 var array = [UInt8](repeating: 0, count: count)
262
263 let fd = open("/dev/urandom", O_RDONLY)
264 guard fd != -1 else {
265 return nil
266 }
267 defer {
268 close(fd)
269 }
270
271 let ret = read(fd, &array, MemoryLayout<UInt8>.size * array.count)
272 guard ret > 0 else {
273 return nil
274 }
275
276 return array
277 }
278}
279
280
281//MARK: - Ctx Create
282
283public func secp256k1_default_ctx_create(errorThrowable: Error) throws -> OpaquePointer {
284 let c = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_SIGN) | UInt32(SECP256K1_CONTEXT_VERIFY))
285 guard let ctx = c else {
286 throw errorThrowable
287 }
288
289 guard var rand = Bytes.secureRandom(count: 32) else {
290 throw errorThrowable
291 }
292
293 guard secp256k1_context_randomize(ctx, &rand) == 1 else {
294 throw errorThrowable
295 }
296
297 return ctx
298}
299
300
301//MARK: - Ctx Destroy
302
303public func secp256k1_default_ctx_destroy(ctx: OpaquePointer) {
304 secp256k1_context_destroy(ctx)
305}