diff --git a/Podfile b/Podfile index d4dcaf516..2e63b2ca1 100644 --- a/Podfile +++ b/Podfile @@ -99,6 +99,7 @@ target 'SignalMessaging' do end target 'SessionMessagingKit' do + pod 'CryptoSwift', :inhibit_warnings => true pod 'PromiseKit', :inhibit_warnings => true pod 'SwiftProtobuf', '~> 1.5.0', :inhibit_warnings => true end diff --git a/Podfile.lock b/Podfile.lock index 681d0317a..4e66a84a4 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -332,6 +332,6 @@ SPEC CHECKSUMS: YYImage: 6db68da66f20d9f169ceb94dfb9947c3867b9665 ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: 58acf63b2164dadf743187b6a52154721c1c01dd +PODFILE CHECKSUM: 4d6ed1d0ef2a98ce221ebfffc9ae860d4703ec47 COCOAPODS: 1.10.0.rc.1 diff --git a/Pods b/Pods index 03a39c064..855baeb2e 160000 --- a/Pods +++ b/Pods @@ -1 +1 @@ -Subproject commit 03a39c064860412e9e69b99f19dd85cd5a9b8aad +Subproject commit 855baeb2e72e3f90eb0cdcbc682162175db2bec8 diff --git a/SessionMessagingKit/Pipelines/SendingPipeline.swift b/SessionMessagingKit/Pipelines/SendingPipeline.swift index 6827f3671..108f857ca 100644 --- a/SessionMessagingKit/Pipelines/SendingPipeline.swift +++ b/SessionMessagingKit/Pipelines/SendingPipeline.swift @@ -13,11 +13,13 @@ public enum SendingPipeline { public enum Error : LocalizedError { case protoConversionFailed case protoSerializationFailed + case proofOfWorkCalculationFailed public var errorDescription: String? { switch self { case .protoConversionFailed: return "Couldn't convert message to proto." case .protoSerializationFailed: return "Couldn't serialize proto." + case .proofOfWorkCalculationFailed: return "Proof of work calculation failed." } } } @@ -35,8 +37,10 @@ public enum SendingPipeline { let recipient = "" let base64EncodedData = data.base64EncodedString() let ttl: UInt64 = 2 * 24 * 60 * 60 * 1000 - let timestamp: UInt64 = 0 - let nonce = "" + guard let (timestamp, nonce) = ProofOfWork.calculate(ttl: ttl, publicKey: recipient, data: base64EncodedData) else { + SNLog("Proof of work calculation failed.") + return Promise(error: Error.proofOfWorkCalculationFailed) + } let snodeMessage = SnodeMessage(recipient: recipient, data: base64EncodedData, ttl: ttl, timestamp: timestamp, nonce: nonce) let _ = SnodeAPI.sendMessage(snodeMessage) return Promise.value(()) diff --git a/SessionMessagingKit/Utilities/FixedWidthInteger+BigEndian.swift b/SessionMessagingKit/Utilities/FixedWidthInteger+BigEndian.swift new file mode 100644 index 000000000..6461e06ad --- /dev/null +++ b/SessionMessagingKit/Utilities/FixedWidthInteger+BigEndian.swift @@ -0,0 +1,13 @@ + + +internal extension FixedWidthInteger { + + init?(fromBigEndianBytes bytes: [UInt8]) { + guard bytes.count == MemoryLayout.size else { return nil } + self = bytes.reduce(0) { ($0 << 8) | Self($1) } + } + + var bigEndianBytes: [UInt8] { + return withUnsafeBytes(of: bigEndian) { [UInt8]($0) } + } +} diff --git a/SessionMessagingKit/Utilities/ProofOfWork.swift b/SessionMessagingKit/Utilities/ProofOfWork.swift new file mode 100644 index 000000000..80d4d582e --- /dev/null +++ b/SessionMessagingKit/Utilities/ProofOfWork.swift @@ -0,0 +1,36 @@ +import SessionUtilities +import SessionSnodeKit + +enum ProofOfWork { + + /// A modified version of [Bitmessage's Proof of Work Implementation](https://bitmessage.org/wiki/Proof_of_work). + static func calculate(ttl: UInt64, publicKey: String, data: String) -> (timestamp: UInt64, base64EncodedNonce: String)? { + let nonceSize = MemoryLayout.size + // Get millisecond timestamp + let timestamp = NSDate.millisecondTimestamp() + // Construct payload + let payloadAsString = String(timestamp) + String(ttl) + publicKey + data + let payload = payloadAsString.bytes + // Calculate target + let numerator = UInt64.max + let difficulty = UInt64(SnodeAPI.powDifficulty) + let totalSize = UInt64(payload.count + nonceSize) + let ttlInSeconds = ttl / 1000 + let denominator = difficulty * (totalSize + (ttlInSeconds * totalSize) / UInt64(UInt16.max)) + let target = numerator / denominator + // Calculate proof of work + var value = UInt64.max + let payloadHash = payload.sha512() + var nonce = UInt64(0) + while value > target { + nonce = nonce &+ 1 + let hash = (nonce.bigEndianBytes + payloadHash).sha512() + guard let newValue = UInt64(fromBigEndianBytes: [UInt8](hash[0..