Skip to content

Commit

Permalink
[Core] Feature/#273 envelope types (#279)
Browse files Browse the repository at this point in the history
* Add SerializationPolicy type

* savepoint

* savepoint

* Add envelope

* update serialize method

* update envelope

* handle envelope in Sign sdk

* extract envelope to a new file

* add getPublicKey method to kms

* fix kms errors

* fix kms tests

* add kms tests scheme
fix codec tests

* fix serializer tests

* update kms test, fix type 0 envelope key size issue

* simplify serialiser, add envelope init

* Fix envelope type, fix serialiser tests

* remove debugging prints

* update codec docs

* remove unused error

* remove unused error

* move serializing protocol to serializer

* prevent potential crash on envelope init

* add serializing file

* run lint
  • Loading branch information
llbartekll authored Jun 21, 2022
1 parent 9412506 commit 1773af7
Show file tree
Hide file tree
Showing 24 changed files with 382 additions and 185 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1340"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "WalletConnectKMSTests"
BuildableName = "WalletConnectKMSTests"
BlueprintName = "WalletConnectKMSTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
5 changes: 2 additions & 3 deletions Sources/Chat/Chat.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,14 @@ class Chat {
jsonRpcHistory: jsonRpcHistory)
let invitePayloadStore = CodableStore<RequestSubscriptionPayload>(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.invite.rawValue)
self.registryService = RegistryService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToInvitationPubKeyStore: topicToInvitationPubKeyStore)
let codec = ChaChaPolyCodec()
self.invitationHandlingService = InvitationHandlingService(registry: registry,
networkingInteractor: networkingInteractor,
kms: kms,
logger: logger,
topicToInvitationPubKeyStore: topicToInvitationPubKeyStore,
invitePayloadStore: invitePayloadStore,
threadsStore: CodableStore<Thread>(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.threads.rawValue), codec: codec)
self.inviteService = InviteService(networkingInteractor: networkingInteractor, kms: kms, logger: logger, codec: codec)
threadsStore: CodableStore<Thread>(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.threads.rawValue))
self.inviteService = InviteService(networkingInteractor: networkingInteractor, kms: kms, logger: logger)
socketConnectionStatusPublisher = relayClient.socketConnectionStatusPublisher
setUpEnginesCallbacks()
}
Expand Down
23 changes: 14 additions & 9 deletions Sources/Chat/NetworkingInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@ import Foundation
import Combine
import WalletConnectRelay
import WalletConnectUtils
import WalletConnectKMS

protocol NetworkInteracting {
var requestPublisher: AnyPublisher<RequestSubscriptionPayload, Never> {get}
var responsePublisher: AnyPublisher<ChatResponse, Never> {get}
func subscribe(topic: String) async throws
func requestUnencrypted(_ request: JSONRPCRequest<ChatRequestParams>, topic: String) async throws
func request(_ request: JSONRPCRequest<ChatRequestParams>, topic: String) async throws
func request(_ request: JSONRPCRequest<ChatRequestParams>, topic: String, envelopeType: Envelope.EnvelopeType) async throws
func respond(topic: String, response: JsonRpcResult) async throws
}

extension NetworkInteracting {
func request(_ request: JSONRPCRequest<ChatRequestParams>, topic: String, envelopeType: Envelope.EnvelopeType = .type0) async throws {
try await self.request(request, topic: topic, envelopeType: envelopeType)
}
}

class NetworkingInteractor: NetworkInteracting {
Expand Down Expand Up @@ -52,9 +58,9 @@ class NetworkingInteractor: NetworkInteracting {
try await relayClient.publish(topic: topic, payload: message)
}

func request(_ request: JSONRPCRequest<ChatRequestParams>, topic: String) async throws {
func request(_ request: JSONRPCRequest<ChatRequestParams>, topic: String, envelopeType: Envelope.EnvelopeType) async throws {
try jsonRpcHistory.set(topic: topic, request: request)
let message = try! serializer.serialize(topic: topic, encodable: request)
let message = try! serializer.serialize(topic: topic, encodable: request, envelopeType: envelopeType)
try await relayClient.publish(topic: topic, payload: message)
}

Expand All @@ -67,15 +73,14 @@ class NetworkingInteractor: NetworkInteracting {
try await relayClient.subscribe(topic: topic)
}

private func manageSubscription(_ topic: String, _ message: String) {
if let deserializedJsonRpcRequest: JSONRPCRequest<ChatRequestParams> = serializer.tryDeserialize(topic: topic, message: message) {
private func manageSubscription(_ topic: String, _ encodedEnvelope: String) {
if let deserializedJsonRpcRequest: JSONRPCRequest<ChatRequestParams> = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) {
handleWCRequest(topic: topic, request: deserializedJsonRpcRequest)
} else if let decodedJsonRpcRequest: JSONRPCRequest<ChatRequestParams> = tryDecodeRequest(message: message) {
} else if let decodedJsonRpcRequest: JSONRPCRequest<ChatRequestParams> = tryDecodeRequest(message: encodedEnvelope) {
handleWCRequest(topic: topic, request: decodedJsonRpcRequest)

} else if let deserializedJsonRpcResponse: JSONRPCResponse<AnyCodable> = serializer.tryDeserialize(topic: topic, message: message) {
} else if let deserializedJsonRpcResponse: JSONRPCResponse<AnyCodable> = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) {
handleJsonRpcResponse(response: deserializedJsonRpcResponse)
} else if let deserializedJsonRpcError: JSONRPCErrorResponse = serializer.tryDeserialize(topic: topic, message: message) {
} else if let deserializedJsonRpcError: JSONRPCErrorResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) {
handleJsonRpcErrorResponse(response: deserializedJsonRpcError)
} else {
print("Warning: WalletConnect Relay - Received unknown object type from networking relay")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ class InvitationHandlingService {
enum Error: Swift.Error {
case inviteForIdNotFound
}
var onInvite: ((InviteEnvelope)->Void)?
var onNewThread: ((String)->Void)?
var onInvite: ((InviteEnvelope) -> Void)?
var onNewThread: ((String) -> Void)?
private let networkingInteractor: NetworkInteracting
private let invitePayloadStore: CodableStore<(RequestSubscriptionPayload)>
private let topicToInvitationPubKeyStore: CodableStore<String>
Expand All @@ -18,24 +18,21 @@ class InvitationHandlingService {
private let kms: KeyManagementService
private let threadsStore: CodableStore<Thread>
private var publishers = [AnyCancellable]()
private let codec: Codec

init(registry: Registry,
networkingInteractor: NetworkInteracting,
kms: KeyManagementService,
logger: ConsoleLogging,
topicToInvitationPubKeyStore: CodableStore<String>,
invitePayloadStore: CodableStore<RequestSubscriptionPayload>,
threadsStore: CodableStore<Thread>,
codec: Codec) {
threadsStore: CodableStore<Thread>) {
self.registry = registry
self.kms = kms
self.networkingInteractor = networkingInteractor
self.logger = logger
self.topicToInvitationPubKeyStore = topicToInvitationPubKeyStore
self.invitePayloadStore = invitePayloadStore
self.threadsStore = threadsStore
self.codec = codec
setUpRequestHandling()
}

Expand Down Expand Up @@ -90,14 +87,15 @@ class InvitationHandlingService {

let agreementKeysI = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: inviteParams.pubKey)

let decryptedData = try codec.decode(sealboxString: inviteParams.invite, symmetricKey: agreementKeysI.sharedKey.rawRepresentation)

let invite = try JSONDecoder().decode(Invite.self, from: decryptedData)

try kms.setSymmetricKey(agreementKeysI.sharedKey, for: payload.topic)

invitePayloadStore.set(payload, forKey: inviteParams.id)

onInvite?(InviteEnvelope(pubKey: inviteParams.pubKey, invite: invite))
// TODO - fix with new specs
// let decryptedData = try codec.decode(sealboxString: inviteParams.invite, symmetricKey: agreementKeysI.sharedKey.rawRepresentation)
//
// let invite = try JSONDecoder().decode(Invite.self, from: decryptedData)
//
// try kms.setSymmetricKey(agreementKeysI.sharedKey, for: payload.topic)
//
// invitePayloadStore.set(payload, forKey: inviteParams.id)
//
// onInvite?(InviteEnvelope(pubKey: inviteParams.pubKey, invite: invite))
}
}
16 changes: 6 additions & 10 deletions Sources/Chat/ProtocolServices/Inviter/InviteService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,35 @@ class InviteService {
let networkingInteractor: NetworkInteracting
let logger: ConsoleLogging
let kms: KeyManagementService
let codec: Codec

var onNewThread: ((String)->Void)?
var onInvite: ((InviteParams)->Void)?
var onNewThread: ((String) -> Void)?
var onInvite: ((InviteParams) -> Void)?

init(networkingInteractor: NetworkInteracting,
kms: KeyManagementService,
logger: ConsoleLogging,
codec: Codec) {
logger: ConsoleLogging) {
self.kms = kms
self.networkingInteractor = networkingInteractor
self.logger = logger
self.codec = codec
setUpResponseHandling()
}

func invite(peerPubKey: String, openingMessage: String, account: Account) async throws {
let selfPubKeyY = try kms.createX25519KeyPair()
let invite = Invite(message: openingMessage, account: account)

let peerPublicKeyRaw = Data(hex: peerPubKey)
let symKeyI = try kms.performKeyAgreement(selfPublicKey: selfPubKeyY, peerPublicKey: peerPubKey)
let inviteTopic = try AgreementPublicKey(hex: peerPubKey).rawRepresentation.sha256().toHexString()

try kms.setSymmetricKey(symKeyI.sharedKey, for: inviteTopic)

let encodedInvite = try codec.encode(plaintext: invite.json(), symmetricKey: symKeyI.sharedKey.rawRepresentation)
let inviteRequestParams = InviteParams(pubKey: selfPubKeyY.hexRepresentation, invite: encodedInvite)
let inviteRequestParams = InviteParams(pubKey: selfPubKeyY.hexRepresentation, invite: invite)

let request = JSONRPCRequest<ChatRequestParams>(params: .invite(inviteRequestParams))

try await networkingInteractor.subscribe(topic: inviteTopic)

try await networkingInteractor.requestUnencrypted(request, topic: inviteTopic)
try await networkingInteractor.request(request, topic: inviteTopic, envelopeType: .type1(pubKey: peerPublicKeyRaw))

logger.debug("invite sent on topic: \(inviteTopic)")
}
Expand Down
9 changes: 0 additions & 9 deletions Sources/Chat/Serializing.swift

This file was deleted.

4 changes: 2 additions & 2 deletions Sources/Chat/Types/InviteParams.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Foundation

struct InviteParams: Codable, Equatable {
let pubKey: String
let invite: String
let invite: Invite

var id: String {
return pubKey
Expand All @@ -14,7 +14,7 @@ struct InviteResponse: Codable {
let pubKey: String
}

struct Invite: Codable {
struct Invite: Codable, Equatable {
let message: String
let account: Account
}
Expand Down
44 changes: 27 additions & 17 deletions Sources/WalletConnectKMS/Codec/ChaChaPolyCodec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,51 @@
import Foundation
import CryptoKit

public protocol Codec {
func encode(plaintext: String, symmetricKey: Data, nonce: ChaChaPoly.Nonce) throws -> String
func decode(sealboxString: String, symmetricKey: Data) throws -> Data
protocol Codec {
func encode(plaintext: String, symmetricKey: Data, nonce: ChaChaPoly.Nonce) throws -> Data
func decode(sealbox: Data, symmetricKey: Data) throws -> Data
}
public extension Codec {
func encode(plaintext: String, symmetricKey: Data, nonce: ChaChaPoly.Nonce = ChaChaPoly.Nonce()) throws -> String {
extension Codec {
func encode(plaintext: String, symmetricKey: Data, nonce: ChaChaPoly.Nonce = ChaChaPoly.Nonce()) throws -> Data {
try encode(plaintext: plaintext, symmetricKey: symmetricKey, nonce: nonce)
}
}

public class ChaChaPolyCodec: Codec {

public init() {}
/// nonce should always be random, exposed in parameter for testing purpose only
public func encode(plaintext: String, symmetricKey: Data, nonce: ChaChaPoly.Nonce) throws -> String {
class ChaChaPolyCodec: Codec {
enum Errors: Error {
case stringToDataFailed(String)
}
/// Secures the given plaintext message with encryption and an authentication tag.
/// - Parameters:
/// - plaintext: plaintext to to encrypt
/// - symmetricKey: symmetric key for encryption
/// - nonce: nonce should always be random, exposed in parameter for testing purpose only
/// - Returns: A combined element composed of the tag, the nonce, and the ciphertext.
/// The data layout of the combined representation is: nonce, ciphertext, then tag.
func encode(plaintext: String, symmetricKey: Data, nonce: ChaChaPoly.Nonce) throws -> Data {
let key = CryptoKit.SymmetricKey(data: symmetricKey)
let dataToSeal = try data(string: plaintext)
let sealBox = try ChaChaPoly.seal(dataToSeal, using: key, nonce: nonce)
return sealBox.combined.base64EncodedString()
return sealBox.combined
}

public func decode(sealboxString: String, symmetricKey: Data) throws -> Data {
guard let sealboxData = Data(base64Encoded: sealboxString) else {
throw CodecError.malformedSealbox
}
/// Decrypts the message and verifies its authenticity.
/// - Parameters:
/// - sealbox: The sealed box to open.
/// - symmetricKey: The cryptographic key that was used to seal the message.
/// - Returns: The original plaintext message that was sealed in the box, as long as the correct key is used and authentication succeeds. The call throws an error if decryption or authentication fail.
func decode(sealbox: Data, symmetricKey: Data) throws -> Data {
let sealboxCombined = sealbox
let key = CryptoKit.SymmetricKey(data: symmetricKey)
let sealBox = try ChaChaPoly.SealedBox(combined: sealboxData)
let sealBox = try ChaChaPoly.SealedBox(combined: sealboxCombined)
return try ChaChaPoly.open(sealBox, using: key)
}

private func data(string: String) throws -> Data {
if let data = string.data(using: .utf8) {
return data
} else {
throw CodecError.stringToDataFailed(string)
throw Errors.stringToDataFailed(string)
}
}
}
8 changes: 0 additions & 8 deletions Sources/WalletConnectKMS/Codec/CodecError.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ extension Curve25519.KeyAgreement.PrivateKey: Equatable {

// MARK: - Public Key

public struct AgreementPublicKey: Equatable {
public struct AgreementPublicKey: GenericPasswordConvertible, Equatable {

fileprivate let key: Curve25519.KeyAgreement.PublicKey

Expand Down
Loading

0 comments on commit 1773af7

Please sign in to comment.