diff --git a/Package.resolved b/Package.resolved index 38ae8640..fe965630 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "6a7b6f94e12ad64740435b6a1f32724d65be46fe7a993e5ced1b75ca71949624", + "originHash" : "b32ba4976231931ba232922d2a9641335f577e4e7cbe50182cb15282578b0c9a", "pins" : [ { "identity" : "bigint", "kind" : "remoteSourceControl", "location" : "https://github.com/attaswift/BigInt", "state" : { - "revision" : "0ed110f7555c34ff468e72e1686e59721f2b0da6", - "version" : "5.3.0" + "revision" : "e07e00fa1fd435143a2dcf8b7eec9a7710b2fdfe", + "version" : "5.7.0" } }, { @@ -20,21 +20,30 @@ } }, { - "identity" : "secp256k1.swift", + "identity" : "swift-algorithms", "kind" : "remoteSourceControl", - "location" : "https://github.com/GigaBitcoin/secp256k1.swift.git", + "location" : "https://github.com/apple/swift-algorithms.git", "state" : { - "revision" : "1a14e189def5eaa92f839afdd2faad8e43b61a6e", - "version" : "0.12.2" + "revision" : "87e50f483c54e6efd60e885f7f5aa946cee68023", + "version" : "1.2.1" } }, { - "identity" : "swift-algorithms", + "identity" : "swift-asn1", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-algorithms.git", + "location" : "https://github.com/apple/swift-asn1.git", "state" : { - "revision" : "87e50f483c54e6efd60e885f7f5aa946cee68023", - "version" : "1.2.1" + "revision" : "f70225981241859eb4aa1a18a75531d26637c8cc", + "version" : "1.4.0" + } + }, + { + "identity" : "swift-async-algorithms", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-async-algorithms.git", + "state" : { + "revision" : "042e1c4d9d19748c9c228f8d4ebc97bb1e339b0b", + "version" : "1.0.4" } }, { @@ -46,13 +55,31 @@ "version" : "1.3.0" } }, + { + "identity" : "swift-certificates", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-certificates.git", + "state" : { + "revision" : "4b092f15164144c24554e0a75e080a960c5190a6", + "version" : "1.14.0" + } + }, { "identity" : "swift-collections", "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections.git", "state" : { - "revision" : "8c0c0a8b49e080e54e5e328cc552821ff07cd341", - "version" : "1.2.1" + "revision" : "7b847a3b7008b2dc2f47ca3110d8c782fb2e5c7e", + "version" : "1.3.0" + } + }, + { + "identity" : "swift-crypto", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-crypto.git", + "state" : { + "revision" : "95ba0316a9b733e92bb6b071255ff46263bbe7dc", + "version" : "3.15.1" } }, { @@ -60,8 +87,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-http-structured-headers.git", "state" : { - "revision" : "db6eea3692638a65e2124990155cd220c2915903", - "version" : "1.3.0" + "revision" : "1625f271afb04375bf48737a5572613248d0e7a0", + "version" : "1.4.0" } }, { @@ -87,8 +114,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio.git", "state" : { - "revision" : "a5fea865badcb1c993c85b0f0e8d05a4bd2270fb", - "version" : "2.85.0" + "revision" : "a18bddb0acf7a40d982b2f128ce73ce4ee31f352", + "version" : "2.86.2" } }, { @@ -96,8 +123,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-extras.git", "state" : { - "revision" : "f1f6f772198bee35d99dd145f1513d8581a54f2c", - "version" : "1.26.0" + "revision" : "a55c3dd3a81d035af8a20ce5718889c0dcab073d", + "version" : "1.29.0" } }, { @@ -114,8 +141,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-ssl.git", "state" : { - "revision" : "385f5bd783ffbfff46b246a7db7be8e4f04c53bd", - "version" : "2.33.0" + "revision" : "b2b043a8810ab6d51b3ff4df17f057d87ef1ec7c", + "version" : "2.34.1" } }, { @@ -123,8 +150,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-transport-services.git", "state" : { - "revision" : "decfd235996bc163b44e10b8a24997a3d2104b90", - "version" : "1.25.0" + "revision" : "df6c28355051c72c884574a6c858bc54f7311ff9", + "version" : "1.25.2" } }, { @@ -132,8 +159,26 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-numerics.git", "state" : { - "revision" : "e0ec0f5f3af6f3e4d5e7a19d2af26b481acb6ba8", - "version" : "1.0.3" + "revision" : "0c0290ff6b24942dadb83a929ffaaa1481df04a2", + "version" : "1.1.1" + } + }, + { + "identity" : "swift-secp256k1", + "kind" : "remoteSourceControl", + "location" : "https://github.com/21-DOT-DEV/swift-secp256k1", + "state" : { + "revision" : "8c62aba8a3011c9bcea232e5ee007fb0b34a15e2", + "version" : "0.21.1" + } + }, + { + "identity" : "swift-service-lifecycle", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swift-server/swift-service-lifecycle.git", + "state" : { + "revision" : "0fcc4c9c2d58dd98504c06f7308c86de775396ff", + "version" : "2.9.0" } }, { @@ -141,8 +186,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-system.git", "state" : { - "revision" : "b63d24d465e237966c3f59f47dcac6c70fb0bca3", - "version" : "1.6.1" + "revision" : "395a77f0aa927f0ff73941d7ac35f2b46d47c9db", + "version" : "1.6.3" } }, { diff --git a/Package.swift b/Package.swift index bd4a0e03..de75a8bf 100644 --- a/Package.swift +++ b/Package.swift @@ -13,9 +13,9 @@ let package = Package( .library(name: "web3-zksync.swift", targets: ["web3-zksync"]) ], dependencies: [ - .package(url: "https://github.com/attaswift/BigInt", from: "5.3.0"), + .package(url: "https://github.com/attaswift/BigInt", from: "5.7.0"), .package(url: "https://github.com/iwill/generic-json-swift", .upToNextMajor(from: "2.0.2")), - .package(url: "https://github.com/GigaBitcoin/secp256k1.swift.git", .upToNextMajor(from: "0.6.0")), + .package(url: "https://github.com/21-DOT-DEV/swift-secp256k1", exact: "0.21.1"), .package(url: "https://github.com/vapor/websocket-kit.git", from: "2.16.1"), .package(url: "https://github.com/apple/swift-log.git", from: "1.6.4") ], @@ -29,7 +29,7 @@ let package = Package( .target(name: "Internal_CryptoSwift_PBDKF2"), "BigInt", .product(name: "GenericJSON", package: "generic-json-swift"), - .product(name: "secp256k1", package: "secp256k1.swift"), + .product(name: "P256K", package: "swift-secp256k1"), .product(name: "WebSocketKit", package: "websocket-kit"), .product(name: "Logging", package: "swift-log") ], diff --git a/Package@swift-6.1.swift b/Package@swift-6.1.swift index 7362ba50..c4790339 100644 --- a/Package@swift-6.1.swift +++ b/Package@swift-6.1.swift @@ -13,9 +13,9 @@ let package = Package( .library(name: "web3-zksync.swift", targets: ["web3-zksync"]) ], dependencies: [ - .package(url: "https://github.com/attaswift/BigInt", from: "5.3.0"), + .package(url: "https://github.com/attaswift/BigInt", from: "5.7.0"), .package(url: "https://github.com/iwill/generic-json-swift", .upToNextMajor(from: "2.0.2")), - .package(url: "https://github.com/GigaBitcoin/secp256k1.swift.git", .upToNextMajor(from: "0.6.0")), + .package(url: "https://github.com/21-DOT-DEV/swift-secp256k1", exact: "0.21.1"), .package(url: "https://github.com/vapor/websocket-kit.git", from: "2.16.1"), .package(url: "https://github.com/apple/swift-log.git", from: "1.6.4") ], @@ -29,7 +29,7 @@ let package = Package( .target(name: "Internal_CryptoSwift_PBDKF2"), "BigInt", .product(name: "GenericJSON", package: "generic-json-swift"), - .product(name: "secp256k1", package: "secp256k1.swift"), + .product(name: "P256K", package: "swift-secp256k1"), .product(name: "WebSocketKit", package: "websocket-kit"), .product(name: "Logging", package: "swift-log") ], diff --git a/web3sTests/Account/EthereumKeyStorageTests.swift b/web3sTests/Account/EthereumKeyStorageTests.swift index 92b5f824..828d9848 100644 --- a/web3sTests/Account/EthereumKeyStorageTests.swift +++ b/web3sTests/Account/EthereumKeyStorageTests.swift @@ -17,7 +17,7 @@ class EthereumKeyStorageTests: XCTestCase { } func testStoreLocalPrivateKey() { - let randomData = Data.randomOfLength(256)! + let randomData = Data.randomOfLength(32)! let keyStorage = EthereumKeyLocalStorage() do { @@ -29,7 +29,7 @@ class EthereumKeyStorageTests: XCTestCase { } func testStoreAndLoadLocalPrivateKey() { - let randomData = Data.randomOfLength(256)! + let randomData = Data.randomOfLength(32)! let keyStorage = EthereumKeyLocalStorage() let ethereumAddress = EthereumAddress(TestConfig.publicKey) do { diff --git a/web3swift/src/Account/EthereumKeyStorage.swift b/web3swift/src/Account/EthereumKeyStorage.swift index 0a4db78d..157e6f18 100644 --- a/web3swift/src/Account/EthereumKeyStorage.swift +++ b/web3swift/src/Account/EthereumKeyStorage.swift @@ -31,16 +31,18 @@ public class EthereumKeyLocalStorage: EthereumSingleKeyStorageProtocol, @uncheck private let address: ThreadSafeBox = ThreadSafeBox(nil) private let localFileName = "ethereumkey" - private var addressPath: String? { + private var addressURL: URL? { guard let address = address.value else { return nil } if let url = folderPath { - return url.appendingPathComponent(address.asString()).path + return url.appendingPathComponent(address.asString()) } return nil } + private var addressPath: String? { addressURL?.path } + private var folderPath: URL? { if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first { return url @@ -48,33 +50,40 @@ public class EthereumKeyLocalStorage: EthereumSingleKeyStorageProtocol, @uncheck return nil } + private var localURL: URL? { + fileManager + .urls(for: .cachesDirectory, in: .userDomainMask) + .first? + .appendingPathComponent(localFileName) + } + private var localPath: String? { - if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first { - return url.appendingPathComponent(localFileName).path - } - return nil + localURL?.path } private let fileManager = FileManager.default public func storePrivateKey(key: Data) throws { - guard let localPath else { + guard let localURL else { throw EthereumKeyStorageError.failedToSave } - let success = NSKeyedArchiver.archiveRootObject(key, toFile: localPath) - - if !success { + do { + try NSKeyedArchiver + .archivedData(withRootObject: key, requiringSecureCoding: false) + .write(to: localURL) + } catch { throw EthereumKeyStorageError.failedToSave } } public func loadPrivateKey() throws -> Data { - guard let localPath else { + guard let localURL else { throw EthereumKeyStorageError.failedToLoad } - guard let data = NSKeyedUnarchiver.unarchiveObject(withFile: localPath) as? Data else { + guard let archivedData = try? Data(contentsOf: localURL), + let data = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSData.self, from: archivedData) as? Data else { throw EthereumKeyStorageError.failedToLoad } @@ -112,13 +121,15 @@ extension EthereumKeyLocalStorage: EthereumMultipleKeyStorageProtocol { } } - guard let localPath = self.addressPath else { + guard let localURL = self.addressURL else { throw EthereumKeyStorageError.failedToSave } - let success = NSKeyedArchiver.archiveRootObject(key, toFile: localPath) - - if !success { + do { + try NSKeyedArchiver + .archivedData(withRootObject: key, requiringSecureCoding: false) + .write(to: localURL) + } catch { throw EthereumKeyStorageError.failedToSave } } @@ -134,11 +145,12 @@ extension EthereumKeyLocalStorage: EthereumMultipleKeyStorageProtocol { } } - guard let localPath = self.addressPath else { + guard let localURL = self.addressURL else { throw EthereumKeyStorageError.failedToLoad } - guard let data = NSKeyedUnarchiver.unarchiveObject(withFile: localPath) as? Data else { + guard let archivedData = try? Data(contentsOf: localURL), + let data = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSData.self, from: archivedData) as? Data else { throw EthereumKeyStorageError.failedToLoad } diff --git a/web3swift/src/Contract/Statically Typed/ABIRawType+Static.swift b/web3swift/src/Contract/Statically Typed/ABIRawType+Static.swift index f21571cf..f417bde6 100644 --- a/web3swift/src/Contract/Statically Typed/ABIRawType+Static.swift +++ b/web3swift/src/Contract/Statically Typed/ABIRawType+Static.swift @@ -44,9 +44,6 @@ extension EthereumAddress: ABIType { } } -extension BigInt: @unchecked Sendable {} -extension BigUInt: @unchecked Sendable {} - extension BigInt: ABIType { public static var rawType: ABIRawType { .FixedInt(256) } public static var parser: ParserFunction { diff --git a/web3swift/src/Extensions/HexExtensions.swift b/web3swift/src/Extensions/HexExtensions.swift index a07537a2..92cc66fb 100644 --- a/web3swift/src/Extensions/HexExtensions.swift +++ b/web3swift/src/Extensions/HexExtensions.swift @@ -8,7 +8,7 @@ import Foundation public extension BigUInt { init?(hex: String) { - self.init(hex.web3.noHexPrefix.lowercased(), radix: 16) + self.init(hex.web3.noHexPrefix.web3.orZero.lowercased(), radix: 16) } } @@ -24,7 +24,7 @@ public extension Web3Extensions where Base == BigUInt { public extension BigInt { init?(hex: String) { - self.init(hex.web3.noHexPrefix.lowercased(), radix: 16) + self.init(hex.web3.noHexPrefix.web3.orZero.lowercased(), radix: 16) } } @@ -79,6 +79,13 @@ public extension Web3Extensions where Base == String { return base } + var orZero: String { + if base.isEmpty { + return "0" + } + return base + } + var stringValue: String { if let byteArray = try? HexUtil.byteArray(fromHex: base.web3.noHexPrefix), let str = String(bytes: byteArray, encoding: .utf8) { return str diff --git a/web3swift/src/Utils/KeyUtil.swift b/web3swift/src/Utils/KeyUtil.swift index a9d1ac6e..34735786 100644 --- a/web3swift/src/Utils/KeyUtil.swift +++ b/web3swift/src/Utils/KeyUtil.swift @@ -3,8 +3,8 @@ // Copyright © Argent Labs Limited. All rights reserved. // +import P256K import Logging -import secp256k1 import Foundation public enum KeyUtilError: Error { @@ -26,40 +26,12 @@ public class KeyUtil { } public static func generatePublicKey(from privateKey: Data) throws -> Data { - guard let ctx = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)) else { - logger.warning("Failed to generate a public key: invalid context.") - throw KeyUtilError.invalidContext - } - - defer { - secp256k1_context_destroy(ctx) - } - - let privateKeyPtr = (privateKey as NSData).bytes.assumingMemoryBound(to: UInt8.self) - guard secp256k1_ec_seckey_verify(ctx, privateKeyPtr) == 1 else { - logger.warning("Failed to generate a public key: private key is not valid.") - throw KeyUtilError.privateKeyInvalid - } - - let publicKeyPtr = UnsafeMutablePointer.allocate(capacity: 1) - defer { - publicKeyPtr.deallocate() - } - guard secp256k1_ec_pubkey_create(ctx, publicKeyPtr, privateKeyPtr) == 1 else { - logger.warning("Failed to generate a public key: public key could not be created.") - throw KeyUtilError.unknownError - } + let privateKey = try P256K.Recovery.PrivateKey(dataRepresentation: privateKey, format: .uncompressed) - var publicKeyLength = 65 - let outputPtr = UnsafeMutablePointer.allocate(capacity: publicKeyLength) - defer { - outputPtr.deallocate() - } - secp256k1_ec_pubkey_serialize(ctx, outputPtr, &publicKeyLength, publicKeyPtr, UInt32(SECP256K1_EC_UNCOMPRESSED)) - - let publicKey = Data(bytes: outputPtr, count: publicKeyLength).subdata(in: 1 ..< publicKeyLength) - - return publicKey + let publicKey = privateKey.publicKey.dataRepresentation + let publicKeyLength = publicKey.count + let finalPublicKey = publicKey.subdata(in: 1 ..< publicKeyLength) + return finalPublicKey } public static func generateAddress(from publicKey: Data) -> EthereumAddress { @@ -69,44 +41,18 @@ public class KeyUtil { } public static func sign(message: Data, with privateKey: Data, hashing: Bool) throws -> Data { - guard let ctx = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)) else { - logger.warning("Failed to sign message: invalid context.") - throw KeyUtilError.invalidContext - } - - defer { - secp256k1_context_destroy(ctx) - } - + let privateKey = try P256K.Recovery.PrivateKey(dataRepresentation: privateKey) let msgData = hashing ? message.web3.keccak256 : message - let msg = (msgData as NSData).bytes.assumingMemoryBound(to: UInt8.self) - let privateKeyPtr = (privateKey as NSData).bytes.assumingMemoryBound(to: UInt8.self) - let signaturePtr = UnsafeMutablePointer.allocate(capacity: 1) - defer { - signaturePtr.deallocate() - } - guard secp256k1_ecdsa_sign_recoverable(ctx, signaturePtr, msg, privateKeyPtr, nil, nil) == 1 else { - logger.warning("Failed to sign message: recoverable ECDSA signature creation failed.") - throw KeyUtilError.signatureFailure - } - - let outputPtr = UnsafeMutablePointer.allocate(capacity: 64) - defer { - outputPtr.deallocate() - } - var recid: Int32 = 0 - secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, outputPtr, &recid, signaturePtr) + let digest = HashDigest(msgData.bytes) + let signature = try privateKey.signature(for: digest) - let outputWithRecidPtr = UnsafeMutablePointer.allocate(capacity: 65) - defer { - outputWithRecidPtr.deallocate() - } - outputWithRecidPtr.update(from: outputPtr, count: 64) - outputWithRecidPtr.advanced(by: 64).pointee = UInt8(recid) - - let signature = Data(bytes: outputWithRecidPtr, count: 65) + let compactSignature = try signature.compactRepresentation.signature + let recoveryId = try signature.compactRepresentation.recoveryId - return signature + var resultSignature = Data(compactSignature) + let v = UInt8(recoveryId & 0xFF) + resultSignature.append(v) + return resultSignature } public static func recoverPublicKey(message: Data, signature: Data) throws -> String { @@ -114,45 +60,23 @@ public class KeyUtil { throw KeyUtilError.badArguments } - guard let ctx = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)) else { - logger.warning("Failed to sign message: invalid context.") - throw KeyUtilError.invalidContext - } - defer { secp256k1_context_destroy(ctx) } - - // get recoverable signature - let signaturePtr = UnsafeMutablePointer.allocate(capacity: 1) - defer { signaturePtr.deallocate() } - let serializedSignature = Data(signature[0 ..< 64]) - var v = Int32(signature[64]) - if v >= 27, v <= 30 { - v -= 27 - } else if v >= 31, v <= 34 { - v -= 31 - } else if v >= 35, v <= 38 { - v -= 35 - } - - try serializedSignature.withUnsafeBytes { - guard secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, signaturePtr, $0.bindMemory(to: UInt8.self).baseAddress!, v) == 1 else { - logger.warning("Failed to parse signature: recoverable ECDSA signature parse failed.") - throw KeyUtilError.signatureParseFailure - } - } - let pubkey = UnsafeMutablePointer.allocate(capacity: 1) - defer { pubkey.deallocate() } - - try message.withUnsafeBytes { - guard secp256k1_ecdsa_recover(ctx, pubkey, signaturePtr, $0.bindMemory(to: UInt8.self).baseAddress!) == 1 else { - throw KeyUtilError.signatureFailure - } - } - var size = 65 - var rv = Data(count: size) - _ = rv.withUnsafeMutableBytes { - secp256k1_ec_pubkey_serialize(ctx, $0.bindMemory(to: UInt8.self).baseAddress!, &size, pubkey, UInt32(SECP256K1_EC_UNCOMPRESSED)) - } - return "0x\(rv[1...].web3.keccak256.web3.hexString.suffix(40))" + var recoveryId = Int32(signature[64]) + if recoveryId >= 27, recoveryId <= 30 { + recoveryId -= 27 + } else if recoveryId >= 31, recoveryId <= 34 { + recoveryId -= 31 + } else if recoveryId >= 35, recoveryId <= 38 { + recoveryId -= 35 + } + + let digest = HashDigest(message.bytes) + let publicKey = try P256K.Recovery.PublicKey( + digest, + signature: P256K.Recovery.ECDSASignature(compactRepresentation: serializedSignature, recoveryId: recoveryId), + format: .uncompressed + ) + + return "0x\(publicKey.dataRepresentation[1...].web3.keccak256.web3.hexString.suffix(40))" } }