Skip to content

Commit

Permalink
Merge pull request #540 from xmtp/np/fix-tls-installation-signer
Browse files Browse the repository at this point in the history
Add ability to sign with an installation key
  • Loading branch information
nplasterer authored Nov 19, 2024
2 parents 698fe3e + 4a79d7d commit 2192f53
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 18 deletions.
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ repositories {
dependencies {
implementation project(':expo-modules-core')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
implementation "org.xmtp:android:3.0.5"
implementation "org.xmtp:android:3.0.6"
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.facebook.react:react-native:0.71.3'
implementation "com.daveanthonythomas.moshipack:moshipack:1.0.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,15 @@ class XMTPModule : Module() {
}
}

AsyncFunction("signWithInstallationKey") Coroutine { inboxId: String, message: String ->
withContext(Dispatchers.IO) {
val client = clients[inboxId] ?: throw XMTPException("No client")

val signature = client.signWithInstallationKey(message)
signature.map { it.toInt() and 0xFF }
}
}

AsyncFunction("canMessage") Coroutine { inboxId: String, peerAddresses: List<String> ->
withContext(Dispatchers.IO) {
logV("canMessage")
Expand Down Expand Up @@ -445,7 +454,11 @@ class XMTPModule : Module() {
val params = ConversationParamsWrapper.conversationParamsFromJson(groupParams ?: "")
val order = getConversationSortOrder(sortOrder ?: "")
val consent = consentState?.let { getConsentState(it) }
val groups = client.conversations.listGroups(order = order, limit = limit, consentState = consent)
val groups = client.conversations.listGroups(
order = order,
limit = limit,
consentState = consent
)
groups.map { group ->
GroupWrapper.encode(client, group, params)
}
Expand All @@ -459,7 +472,11 @@ class XMTPModule : Module() {
val params = ConversationParamsWrapper.conversationParamsFromJson(groupParams ?: "")
val order = getConversationSortOrder(sortOrder ?: "")
val consent = consentState?.let { getConsentState(it) }
val dms = client.conversations.listDms(order = order, limit = limit, consentState = consent)
val dms = client.conversations.listDms(
order = order,
limit = limit,
consentState = consent
)
dms.map { dm ->
DmWrapper.encode(client, dm, params)
}
Expand All @@ -474,7 +491,8 @@ class XMTPModule : Module() {
ConversationParamsWrapper.conversationParamsFromJson(conversationParams ?: "")
val order = getConversationSortOrder(sortOrder ?: "")
val consent = consentState?.let { getConsentState(it) }
val conversations = client.conversations.list(order = order, limit = limit, consentState = consent)
val conversations =
client.conversations.list(order = order, limit = limit, consentState = consent)
conversations.map { conversation ->
ConversationWrapper.encode(client, conversation, params)
}
Expand Down
14 changes: 7 additions & 7 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ PODS:
- hermes-engine/Pre-built (= 0.71.14)
- hermes-engine/Pre-built (0.71.14)
- libevent (2.1.12)
- LibXMTP (3.0.1)
- LibXMTP (3.0.3)
- Logging (1.0.0)
- MessagePacker (0.4.7)
- MMKV (2.0.0):
Expand Down Expand Up @@ -449,16 +449,16 @@ PODS:
- GenericJSON (~> 2.0)
- Logging (~> 1.0.0)
- secp256k1.swift (~> 0.1)
- XMTP (3.0.5):
- XMTP (3.0.6):
- Connect-Swift (= 0.12.0)
- GzipSwift
- LibXMTP (= 3.0.1)
- LibXMTP (= 3.0.3)
- web3.swift
- XMTPReactNative (0.1.0):
- ExpoModulesCore
- MessagePacker
- secp256k1.swift
- XMTP (= 3.0.5)
- XMTP (= 3.0.6)
- Yoga (1.14.0)

DEPENDENCIES:
Expand Down Expand Up @@ -711,7 +711,7 @@ SPEC CHECKSUMS:
GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa
hermes-engine: d7cc127932c89c53374452d6f93473f1970d8e88
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
LibXMTP: b23a18d05d458fee72f0a96a114b1eb1e6d77d3b
LibXMTP: 948d39cf5b978adaa7d0f6ea5c6c0995a0b9e63f
Logging: 9ef4ecb546ad3169398d5a723bc9bea1c46bef26
MessagePacker: ab2fe250e86ea7aedd1a9ee47a37083edd41fd02
MMKV: f7d1d5945c8765f97f39c3d121f353d46735d801
Expand Down Expand Up @@ -763,8 +763,8 @@ SPEC CHECKSUMS:
secp256k1.swift: a7e7a214f6db6ce5db32cc6b2b45e5c4dd633634
SwiftProtobuf: 407a385e97fd206c4fbe880cc84123989167e0d1
web3.swift: 2263d1e12e121b2c42ffb63a5a7beb1acaf33959
XMTP: 7b9105a3549427a294fb991c5892ebec73d2388d
XMTPReactNative: 0862a746eaddb7d643ad4cbdc2727d02863d9a18
XMTP: 48d0c71ef732ac4d79c2942902a132bf71661029
XMTPReactNative: 406f92e777c9d2891404956309d926e7b2f25c1c
Yoga: e71803b4c1fff832ccf9b92541e00f9b873119b9

PODFILE CHECKSUM: 0e6fe50018f34e575d38dc6a1fdf1f99c9596cdd
Expand Down
12 changes: 12 additions & 0 deletions example/src/tests/clientTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ test('can make a client', async () => {
return true
})

test('can sign with installation key', async () => {
const [client] = await createClients(1)

const signature = await client.signWithInstallationKey('A digest message')

assert(
signature !== undefined && signature.length > 0,
`Signature should not be empty but was: ${signature}`
)
return true
})

test('can revoke all other installations', async () => {
const keyBytes = new Uint8Array([
233, 120, 198, 96, 154, 65, 132, 17, 132, 96, 250, 40, 103, 35, 125, 64,
Expand Down
10 changes: 10 additions & 0 deletions ios/XMTPModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,16 @@ public class XMTPModule: Module {
await clientsManager.dropClient(key: inboxId)
}

AsyncFunction("signWithInstallationKey") {
(inboxId: String, message: String) -> [UInt8] in
guard let client = await clientsManager.getClient(key: inboxId)
else {
throw Error.noClient
}
let signature = try client.signWithInstallationKey(message: message)
return [UInt8](signature)
}

AsyncFunction("canMessage") {
(inboxId: String, peerAddresses: [String]) -> [String: Bool] in
guard let client = await clientsManager.getClient(key: inboxId)
Expand Down
2 changes: 1 addition & 1 deletion ios/XMTPReactNative.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ Pod::Spec.new do |s|
s.source_files = "**/*.{h,m,swift}"
s.dependency 'secp256k1.swift'
s.dependency "MessagePacker"
s.dependency "XMTP", "= 3.0.5"
s.dependency "XMTP", "= 3.0.6"
end
11 changes: 11 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,17 @@ export async function dropClient(inboxId: InboxId) {
return await XMTPModule.dropClient(inboxId)
}

export async function signWithInstallationKey(
inboxId: InboxId,
message: string
): Promise<Uint8Array> {
const signatureArray = await XMTPModule.signWithInstallationKey(
inboxId,
message
)
return new Uint8Array(signatureArray)
}

export async function canMessage(
inboxId: InboxId,
peerAddresses: Address[]
Expand Down
25 changes: 19 additions & 6 deletions src/lib/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ import * as XMTPModule from '../index'

declare const Buffer

export type GetMessageContentTypeFromClient<C> =
C extends Client<infer T> ? T : never
export type GetMessageContentTypeFromClient<C> = C extends Client<infer T>
? T
: never

export type ExtractDecodedType<C> =
C extends XMTPModule.ContentCodec<infer T> ? T : never
export type ExtractDecodedType<C> = C extends XMTPModule.ContentCodec<infer T>
? T
: never

export type InboxId = string & { readonly brand: unique symbol }
export type Address = string
Expand Down Expand Up @@ -316,6 +318,10 @@ export class Client<
this.codecRegistry[id] = contentCodec
}

async signWithInstallationKey(message: string): Promise<Uint8Array> {
return XMTPModule.signWithInstallationKey(this.inboxId, message)
}

/**
* Find the Address associated with this address
*
Expand Down Expand Up @@ -409,8 +415,15 @@ export class Client<
* @param {boolean} refreshFromNetwork - If you want to refresh the current state the inbox from the network or not.
* @returns {Promise<InboxState[]>} A Promise resolving to a list of InboxState.
*/
async inboxStates(refreshFromNetwork: boolean, inboxIds: InboxId[]): Promise<InboxState[]> {
return await XMTPModule.getInboxStates(this.inboxId, refreshFromNetwork, inboxIds)
async inboxStates(
refreshFromNetwork: boolean,
inboxIds: InboxId[]
): Promise<InboxState[]> {
return await XMTPModule.getInboxStates(
this.inboxId,
refreshFromNetwork,
inboxIds
)
}

/**
Expand Down

0 comments on commit 2192f53

Please sign in to comment.