Skip to content

Commit

Permalink
encrypt
Browse files Browse the repository at this point in the history
  • Loading branch information
nakajima committed Oct 13, 2023
1 parent 0d00b17 commit 87c1df3
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 38 deletions.
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/xmtp/xmtp-rust-swift",
"state" : {
"revision" : "d1aaac47fc7c57645a6fe9e06972b957b3efa33c",
"version" : "0.3.5-beta0"
"branch" : "main",
"revision" : "d1aaac47fc7c57645a6fe9e06972b957b3efa33c"
}
}
],
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ let package = Package(
.package(url: "https://github.com/1024jp/GzipSwift", from: "5.2.0"),
.package(url: "https://github.com/bufbuild/connect-swift", from: "0.3.0"),
.package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.0.0"),
.package(url: "https://github.com/xmtp/xmtp-rust-swift", from: "0.3.5-beta0"),
.package(url: "https://github.com/xmtp/xmtp-rust-swift", branch: "main"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand Down
105 changes: 75 additions & 30 deletions Sources/XMTP/Contacts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//

import Foundation
import XMTPRust

public enum AllowState: String, Codable {
case allowed, blocked, unknown
Expand All @@ -16,23 +17,81 @@ struct AllowListEntry: Codable, Hashable {
case address
}

static func address(_ address: String, type: AllowState) -> AllowListEntry {
static func address(_ address: String, type: AllowState = .unknown) -> AllowListEntry {
AllowListEntry(value: address, entryType: .address, permissionType: type)
}

var value: String
var entryType: EntryType
var permissionType: AllowState

var key: String {
"\(entryType)-\(value)"
}
}

struct AllowList {
var allowedAddresses: Set<AllowListEntry> = []
var blockedAddresses: Set<AllowListEntry> = []
class AllowList {
var entries: [String: AllowState] = [:]

static func load(from client: Client) async throws -> AllowList {
let envelopes = try await client.query(topic: .allowList(client.address))
let allowList = AllowList()

for envelope in envelopes.envelopes {
let publicKey = client.privateKeyBundleV1.identityKey.publicKey.secp256K1Uncompressed.bytes
let privateKey = client.privateKeyBundleV1.identityKey.secp256K1.bytes

let payload = try XMTPRust.ecies_decrypt_k256_sha3_256(
RustVec(publicKey),
RustVec(privateKey),
RustVec(envelope.message)
)

let entry = try JSONDecoder().decode(AllowListEntry.self, from: Data(payload))

allowList.entries[entry.key] = entry.permissionType
}

return allowList
}

static func publish(entry: AllowListEntry, to client: Client) async throws {
let payload = try JSONEncoder().encode(entry)

let publicKey = client.privateKeyBundleV1.identityKey.publicKey.secp256K1Uncompressed.bytes
let privateKey = client.privateKeyBundleV1.identityKey.secp256K1.bytes

let message = try XMTPRust.ecies_encrypt_k256_sha3_256(
RustVec(publicKey),
RustVec(privateKey),
RustVec(payload)
)

let envelope = Envelope(
topic: Topic.allowList(client.address),
timestamp: Date(),
message: Data(message)
)

try await client.publish(envelopes: [envelope])
}

func allow(address: String) -> AllowListEntry {
entries[AllowListEntry.address(address).key] = .allowed

return .address(address, type: .allowed)
}

var entries: Set<AllowListEntry> = []
func block(address: String) -> AllowListEntry {
entries[AllowListEntry.address(address).key] = .blocked

return .address(address, type: .blocked)
}

func state(address: String) -> AllowState {
entries.first(where: { $0.entryType == .address && $0.value == address })?.permissionType ?? AllowState.unknown
let state = entries[AllowListEntry.address(address).key]

return state ?? .unknown
}
}

Expand All @@ -52,41 +111,27 @@ public actor Contacts {
self.client = client
}

public func isAllowed(_ address: String) -> Bool {
for entry in allowList.entries {
switch entry.entryType {
case .address:
if address == entry.value {
return entry.permissionType == .allowed
}
}
}
public func refreshAllowList() async throws {
self.allowList = try await AllowList.load(from: client)
}

return false
public func isAllowed(_ address: String) -> Bool {
return allowList.state(address: address) == .allowed
}

public func isBlocked(_ address: String) -> Bool {
for entry in allowList.entries {
switch entry.entryType {
case .address:
if address == entry.value {
return entry.permissionType == .blocked
}
}
}

return false
return allowList.state(address: address) == .blocked
}

public func allow(addresses: [String]) {
public func allow(addresses: [String]) async throws {
for address in addresses {
allowList.entries.insert(.address(address, type: .allowed))
try await AllowList.publish(entry: allowList.allow(address: address), to: client)
}
}

public func block(addresses: [String]) {
public func block(addresses: [String]) async throws {
for address in addresses {
allowList.entries.insert(.address(address, type: .blocked))
try await AllowList.publish(entry: allowList.block(address: address), to: client)
}
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/XMTP/Conversations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public actor Conversations {
let sealedInvitation = try await sendInvitation(recipient: recipient, invitation: invitation, created: Date())
let conversationV2 = try ConversationV2.create(client: client, invitation: invitation, header: sealedInvitation.v1.header)

await client.contacts.allow(addresses: [peerAddress])
try await client.contacts.allow(addresses: [peerAddress])

let conversation: Conversation = .v2(conversationV2)
conversationsByTopic[conversation.topic] = conversation
Expand Down
5 changes: 4 additions & 1 deletion Sources/XMTP/Messages/Topic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ public enum Topic {
userIntro(String),
userInvite(String),
directMessageV1(String, String),
directMessageV2(String)
directMessageV2(String),
allowList(String)

var description: String {
switch self {
Expand All @@ -30,6 +31,8 @@ public enum Topic {
return wrap("dm-\(addresses)")
case let .directMessageV2(randomString):
return wrap("m-\(randomString)")
case let .allowList(identifier):
return wrap("privatestore-\(identifier)/allowlist")
}
}

Expand Down
4 changes: 2 additions & 2 deletions Tests/XMTPTests/ContactsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class ContactsTests: XCTestCase {

XCTAssertFalse(result)

await contacts.allow(addresses: [fixtures.alice.address])
try await contacts.allow(addresses: [fixtures.alice.address])

result = await contacts.isAllowed(fixtures.alice.address)
XCTAssertTrue(result)
Expand All @@ -76,7 +76,7 @@ class ContactsTests: XCTestCase {

XCTAssertFalse(result)

await contacts.block(addresses: [fixtures.alice.address])
try await contacts.block(addresses: [fixtures.alice.address])

result = await contacts.isBlocked(fixtures.alice.address)
XCTAssertTrue(result)
Expand Down
5 changes: 4 additions & 1 deletion Tests/XMTPTests/ConversationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -619,16 +619,19 @@ class ConversationTests: XCTestCase {
// Conversations started with you should start as unknown
XCTAssertTrue(isUnknown)

await aliceClient.contacts.allow(addresses: [bob.address])
try await aliceClient.contacts.allow(addresses: [bob.address])

let isBobAllowed = (await aliceConversation.allowState()) == .allowed
XCTAssertTrue(isBobAllowed)

let aliceClient2 = try await Client.create(account: alice, apiClient: fakeApiClient)
let aliceConversation2 = (try await aliceClient2.conversations.list())[0]

try await aliceClient2.contacts.refreshAllowList()

// Allow state should sync across clients
let isBobAllowed2 = (await aliceConversation2.allowState()) == .allowed

XCTAssertTrue(isBobAllowed2)
}
}

0 comments on commit 87c1df3

Please sign in to comment.