From abe6119923531374f34d54a4d74aeaa8e5a499e3 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar Date: Tue, 23 Aug 2022 16:56:39 -0700 Subject: [PATCH 1/7] feat: add key manager --- src/KeyManager.ts | 97 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/KeyManager.ts diff --git a/src/KeyManager.ts b/src/KeyManager.ts new file mode 100644 index 000000000..b93b2fb44 --- /dev/null +++ b/src/KeyManager.ts @@ -0,0 +1,97 @@ +import PrivateKeyBundle from './crypto/PrivateKeyBundle' +import PublicKeyBundle from './crypto/PublicKeyBundle' + +enum EncryptionAlgorithm { + AES_256_GCM_HKDF_SHA_256, +} + +type TopicKeyRecord = { + keyMaterial: Uint8Array + encryptionAlgorithm: EncryptionAlgorithm +} + +type PrivateKeyRecord = { + bundle: PrivateKeyBundle + keySentBy: PublicKeyBundle +} + +type TopicResult = { + // This would never include the client. We can assume you are in every topic available + participants: PublicKeyBundle[] + topicKey: TopicKeyRecord + contentTopic: string +} + +type WalletTopicRecord = { + contentTopic: string + createdAt: Date +} + +export default class KeyManager { + topicKeys = new Map() + topicParticipants = new Map() + dmTopics = new Map() + delegateKeys: PrivateKeyRecord[] = [] + + addDirectMessageTopic( + contentTopic: string, + key: TopicKeyRecord, + counterparty: PublicKeyBundle, + createdAt: Date + ): void { + if ( + this.topicKeys.has(contentTopic) || + this.topicParticipants.has(contentTopic) + ) { + throw new Error('Topic key has already been set') + } + this.topicKeys.set(contentTopic, key) + this.topicParticipants.set(contentTopic, [counterparty]) + + const walletAddress = counterparty.identityKey.walletSignatureAddress() + const counterpartyTopicList = this.dmTopics.get(walletAddress) || [] + counterpartyTopicList.push({ contentTopic, createdAt }) + this.dmTopics.set(walletAddress, counterpartyTopicList) + } + + // Would be used to get all information required to decrypt/validate a given message + getTopicResult(contentTopic: string): TopicResult | undefined { + const participants = this.topicParticipants.get(contentTopic) + const topicKey = this.topicKeys.get(contentTopic) + if (!participants || !participants.length || !topicKey) { + return undefined + } + return { + participants, + topicKey, + contentTopic, + } + } + + // Would be used to know which topic/key to use to send to a given wallet address + getDirectMessageTopic(walletAddress: string): TopicResult | undefined { + const walletTopics = this.dmTopics.get(walletAddress) + if (!walletTopics || !walletTopics.length) { + return undefined + } + const newestTopic = this.findLatestTopic(walletTopics) + return this.getTopicResult(newestTopic.contentTopic) + } + + addDelegateKey(record: PrivateKeyRecord): void { + this.delegateKeys.push(record) + } + + private findLatestTopic(records: WalletTopicRecord[]): WalletTopicRecord { + let latestRecord: WalletTopicRecord | undefined + for (const record of records) { + if (!latestRecord || record.createdAt > latestRecord.createdAt) { + latestRecord = record + } + } + if (!latestRecord) { + throw new Error('No record found') + } + return latestRecord + } +} From 0cfd9de47bafb59bc1ca27d17f56fcaabbc53990 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar Date: Thu, 1 Sep 2022 12:40:57 -0700 Subject: [PATCH 2/7] refactor: update to V3 of proto --- package-lock.json | 14 +++++++------- package.json | 2 +- src/ContactBundle.ts | 13 +++++++------ src/Message.ts | 23 +++++++++++++---------- src/authn/Authenticator.ts | 4 ++-- src/authn/Token.ts | 6 +++--- src/crypto/Ciphertext.ts | 2 +- src/crypto/PrivateKey.ts | 12 ++++++------ src/crypto/PublicKey.ts | 14 +++++++------- src/crypto/PublicKeyBundle.ts | 8 ++++---- src/crypto/Signature.ts | 5 +++-- 11 files changed, 54 insertions(+), 49 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1d6bff5ab..6f6ebb281 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@noble/secp256k1": "^1.5.2", "@stardazed/streams-polyfill": "^2.4.0", - "@xmtp/proto": "^1.3.1", + "@xmtp/proto": "~3.0.0", "ethers": "^5.5.3", "long": "^5.2.0", "node-localstorage": "^2.2.1" @@ -3134,9 +3134,9 @@ } }, "node_modules/@xmtp/proto": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@xmtp/proto/-/proto-1.3.1.tgz", - "integrity": "sha512-qpZd1/gcjz9I8rB7wAsriKfpu41rpomuIsSVyDg0yGOOPJV+thNSA6UqAs2XNDO68dPSOhFWkROHEdixVnMJPg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@xmtp/proto/-/proto-3.0.0.tgz", + "integrity": "sha512-JJ2h+1SxzocSBnex5BN900HVglRyGC5M7eypl0rFpugVj4Q4//A67ZtB5rSul74MVRrypNbcYXRZRAwI8U9TBg==", "dependencies": { "long": "^5.2.0", "protobufjs": "^7.0.0", @@ -15938,9 +15938,9 @@ "requires": {} }, "@xmtp/proto": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@xmtp/proto/-/proto-1.3.1.tgz", - "integrity": "sha512-qpZd1/gcjz9I8rB7wAsriKfpu41rpomuIsSVyDg0yGOOPJV+thNSA6UqAs2XNDO68dPSOhFWkROHEdixVnMJPg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@xmtp/proto/-/proto-3.0.0.tgz", + "integrity": "sha512-JJ2h+1SxzocSBnex5BN900HVglRyGC5M7eypl0rFpugVj4Q4//A67ZtB5rSul74MVRrypNbcYXRZRAwI8U9TBg==", "requires": { "long": "^5.2.0", "protobufjs": "^7.0.0", diff --git a/package.json b/package.json index db8c2e634..0804bf8b3 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "dependencies": { "@noble/secp256k1": "^1.5.2", "@stardazed/streams-polyfill": "^2.4.0", - "@xmtp/proto": "^1.3.1", + "@xmtp/proto": "~3.0.0", "ethers": "^5.5.3", "long": "^5.2.0", "node-localstorage": "^2.2.1" diff --git a/src/ContactBundle.ts b/src/ContactBundle.ts index 6ad800879..c99023a36 100644 --- a/src/ContactBundle.ts +++ b/src/ContactBundle.ts @@ -1,9 +1,9 @@ -import { xmtpEnvelope as proto } from '@xmtp/proto' +import { xmtpEnvelope as proto, contact, publicKey } from '@xmtp/proto' import { PublicKeyBundle } from './crypto' import PublicKey from './crypto/PublicKey' // ContactBundle packages all the infromation which a client uses to advertise on the network. -export default class ContactBundle implements proto.ContactBundleV1 { +export default class ContactBundle implements contact.ContactBundleV1 { keyBundle: PublicKeyBundle constructor(publicKeyBundle: PublicKeyBundle) { @@ -14,10 +14,11 @@ export default class ContactBundle implements proto.ContactBundleV1 { } toBytes(): Uint8Array { - return proto.ContactBundle.encode({ + return contact.ContactBundle.encode({ v1: { keyBundle: this.keyBundle, }, + v2: undefined, }).finish() } @@ -42,9 +43,9 @@ export default class ContactBundle implements proto.ContactBundleV1 { ) } - static decodeV1(bytes: Uint8Array): proto.PublicKeyBundle | undefined { + static decodeV1(bytes: Uint8Array): publicKey.PublicKeyBundle | undefined { try { - const b = proto.ContactBundle.decode(bytes) + const b = contact.ContactBundle.decode(bytes) return b.v1?.keyBundle } catch (e) { if ( @@ -53,7 +54,7 @@ export default class ContactBundle implements proto.ContactBundleV1 { ) { // Adds a default fallback for older versions of the proto (Which may also fail) try { - return proto.PublicKeyBundle.decode(bytes) + return publicKey.PublicKeyBundle.decode(bytes) } catch (e) { throw new Error("Couldn't decode contact bundle: " + e) } diff --git a/src/Message.ts b/src/Message.ts index a83660da1..706b1e3ed 100644 --- a/src/Message.ts +++ b/src/Message.ts @@ -13,7 +13,7 @@ import { bytesToHex } from './crypto/utils' import { sha256 } from './crypto/encryption' import { ContentTypeId } from './MessageContent' -const extractV1Message = (msg: proto.Message): proto.V1Message => { +const extractV1Message = (msg: proto.Message): proto.MessageV1 => { if (!msg.v1) { throw new Error('Message is not of type v1') } @@ -23,8 +23,8 @@ const extractV1Message = (msg: proto.Message): proto.V1Message => { // Message is basic unit of communication on the network. // Message header carries the sender and recipient keys used to protect message. // Message timestamp is set by the sender. -export default class Message implements proto.V1Message { - header: proto.MessageHeader // eslint-disable-line camelcase +export default class Message implements proto.MessageV1 { + header: proto.MessageHeaderV1 // eslint-disable-line camelcase headerBytes: Uint8Array // encoded header bytes ciphertext: Ciphertext decrypted?: Uint8Array @@ -46,7 +46,7 @@ export default class Message implements proto.V1Message { id: string, bytes: Uint8Array, obj: proto.Message, - header: proto.MessageHeader + header: proto.MessageHeaderV1 ) { const msg = extractV1Message(obj) this.id = id @@ -65,7 +65,7 @@ export default class Message implements proto.V1Message { static async create( obj: proto.Message, - header: proto.MessageHeader, + header: proto.MessageHeaderV1, bytes: Uint8Array ): Promise { const id = bytesToHex(await sha256(bytes)) @@ -75,7 +75,7 @@ export default class Message implements proto.V1Message { static async fromBytes(bytes: Uint8Array): Promise { const msg = proto.Message.decode(bytes) const innerMessage = extractV1Message(msg) - const header = proto.MessageHeader.decode(innerMessage.headerBytes) + const header = proto.MessageHeaderV1.decode(innerMessage.headerBytes) return Message.create(msg, header, bytes) } @@ -116,14 +116,17 @@ export default class Message implements proto.V1Message { false ) // eslint-disable-next-line camelcase - const header: proto.MessageHeader = { + const header: proto.MessageHeaderV1 = { sender: sender.getPublicKeyBundle(), recipient, timestamp: Long.fromNumber(timestamp.getTime()), } - const headerBytes = proto.MessageHeader.encode(header).finish() + const headerBytes = proto.MessageHeaderV1.encode(header).finish() const ciphertext = await encrypt(message, secret, headerBytes) - const protoMsg = { v1: { headerBytes: headerBytes, ciphertext } } + const protoMsg = { + v1: { headerBytes: headerBytes, ciphertext }, + v2: undefined, + } const bytes = proto.Message.encode(protoMsg).finish() const msg = await Message.create(protoMsg, header, bytes) msg.decrypted = message @@ -139,7 +142,7 @@ export default class Message implements proto.V1Message { ): Promise { const message = proto.Message.decode(bytes) const v1Message = extractV1Message(message) - const header = proto.MessageHeader.decode(v1Message.headerBytes) + const header = proto.MessageHeaderV1.decode(v1Message.headerBytes) if (!header) { throw new Error('missing message header') } diff --git a/src/authn/Authenticator.ts b/src/authn/Authenticator.ts index c8a26bd9a..544147cea 100644 --- a/src/authn/Authenticator.ts +++ b/src/authn/Authenticator.ts @@ -1,5 +1,5 @@ import { keccak256 } from 'js-sha3' -import { authn, xmtpEnvelope } from '@xmtp/proto' +import { authn, xmtpEnvelope, publicKey } from '@xmtp/proto' import AuthData from './AuthData' import { PrivateKey } from '../crypto' import { hexToBytes } from '../crypto/utils' @@ -26,7 +26,7 @@ export default class Authenticator { return new Token( authn.Token.fromPartial({ - identityKey: xmtpEnvelope.PublicKey.fromPartial( + identityKey: publicKey.PublicKey.fromPartial( // The generated types are overly strict and don't like our additional methods // eslint-disable-next-line // @ts-ignore diff --git a/src/authn/Token.ts b/src/authn/Token.ts index 7bf70a349..472101eb1 100644 --- a/src/authn/Token.ts +++ b/src/authn/Token.ts @@ -1,10 +1,10 @@ -import { authn, xmtpEnvelope } from '@xmtp/proto' +import { authn, signature, publicKey } from '@xmtp/proto' import AuthData from './AuthData' export default class Token implements authn.Token { - identityKey: xmtpEnvelope.PublicKey + identityKey: publicKey.PublicKey authDataBytes: Uint8Array - authDataSignature: xmtpEnvelope.Signature + authDataSignature: signature.Signature private _authData?: AuthData constructor({ identityKey, authDataBytes, authDataSignature }: authn.Token) { diff --git a/src/crypto/Ciphertext.ts b/src/crypto/Ciphertext.ts index a69ed845c..11a151171 100644 --- a/src/crypto/Ciphertext.ts +++ b/src/crypto/Ciphertext.ts @@ -1,4 +1,4 @@ -import { xmtpEnvelope as proto } from '@xmtp/proto' +import { ciphertext as proto } from '@xmtp/proto' export const AESKeySize = 32 // bytes export const KDFSaltSize = 32 // bytes diff --git a/src/crypto/PrivateKey.ts b/src/crypto/PrivateKey.ts index be0f3b1b0..00f2df33b 100644 --- a/src/crypto/PrivateKey.ts +++ b/src/crypto/PrivateKey.ts @@ -1,4 +1,4 @@ -import { privateKey as proto } from '@xmtp/proto' +import { privateKey, publicKey } from '@xmtp/proto' import * as secp from '@noble/secp256k1' import Long from 'long' import Signature from './Signature' @@ -7,12 +7,12 @@ import Ciphertext from './Ciphertext' import { decrypt, encrypt, sha256 } from './encryption' // PrivateKey represents a secp256k1 private key. -export default class PrivateKey implements proto.PrivateKey { +export default class PrivateKey implements privateKey.PrivateKey { timestamp: Long - secp256k1: proto.PrivateKey_Secp256k1 | undefined // eslint-disable-line camelcase + secp256k1: privateKey.PrivateKey_Secp256k1 | undefined // eslint-disable-line camelcase publicKey: PublicKey // caches corresponding PublicKey - constructor(obj: proto.PrivateKey) { + constructor(obj: privateKey.PrivateKey) { if (!obj.secp256k1) { throw new Error('invalid private key') } @@ -127,10 +127,10 @@ export default class PrivateKey implements proto.PrivateKey { } toBytes(): Uint8Array { - return proto.PrivateKey.encode(this).finish() + return privateKey.PrivateKey.encode(this).finish() } static fromBytes(bytes: Uint8Array): PrivateKey { - return new PrivateKey(proto.PrivateKey.decode(bytes)) + return new PrivateKey(privateKey.PrivateKey.decode(bytes)) } } diff --git a/src/crypto/PublicKey.ts b/src/crypto/PublicKey.ts index 9cb3d3f07..6dea5cd2e 100644 --- a/src/crypto/PublicKey.ts +++ b/src/crypto/PublicKey.ts @@ -1,4 +1,4 @@ -import { xmtpEnvelope as proto } from '@xmtp/proto' +import { publicKey } from '@xmtp/proto' import * as secp from '@noble/secp256k1' import Long from 'long' import Signature from './Signature' @@ -9,12 +9,12 @@ import { sha256 } from './encryption' // PublicKey represents uncompressed secp256k1 public key, // that can optionally be signed with another trusted key pair. // PublicKeys can be generated through PrivateKey.generate() -export default class PublicKey implements proto.PublicKey { +export default class PublicKey implements publicKey.PublicKey { timestamp: Long - secp256k1Uncompressed: proto.PublicKey_Secp256k1Uncompressed // eslint-disable-line camelcase + secp256k1Uncompressed: publicKey.PublicKey_Secp256k1Uncompressed // eslint-disable-line camelcase signature?: Signature - constructor(obj: proto.PublicKey) { + constructor(obj: publicKey.PublicKey) { if (!obj?.secp256k1Uncompressed?.bytes) { throw new Error('invalid public key') } @@ -58,7 +58,7 @@ export default class PublicKey implements proto.PublicKey { } bytesToSign(): Uint8Array { - return proto.PublicKey.encode({ + return publicKey.PublicKey.encode({ timestamp: this.timestamp, secp256k1Uncompressed: this.secp256k1Uncompressed, }).finish() @@ -154,10 +154,10 @@ export default class PublicKey implements proto.PublicKey { } toBytes(): Uint8Array { - return proto.PublicKey.encode(this).finish() + return publicKey.PublicKey.encode(this).finish() } static fromBytes(bytes: Uint8Array): PublicKey { - return new PublicKey(proto.PublicKey.decode(bytes)) + return new PublicKey(publicKey.PublicKey.decode(bytes)) } } diff --git a/src/crypto/PublicKeyBundle.ts b/src/crypto/PublicKeyBundle.ts index 65412e118..0b0ad7167 100644 --- a/src/crypto/PublicKeyBundle.ts +++ b/src/crypto/PublicKeyBundle.ts @@ -1,10 +1,10 @@ -import { xmtpEnvelope as proto } from '@xmtp/proto' +import { publicKey } from '@xmtp/proto' import PublicKey from './PublicKey' // PublicKeyBundle packages all the keys that a participant should advertise. // The PreKey must be signed by the IdentityKey. // The IdentityKey can be signed by the wallet to authenticate it. -export default class PublicKeyBundle implements proto.PublicKeyBundle { +export default class PublicKeyBundle implements publicKey.PublicKeyBundle { identityKey: PublicKey preKey: PublicKey @@ -24,11 +24,11 @@ export default class PublicKeyBundle implements proto.PublicKeyBundle { } toBytes(): Uint8Array { - return proto.PublicKeyBundle.encode(this).finish() + return publicKey.PublicKeyBundle.encode(this).finish() } static fromBytes(bytes: Uint8Array): PublicKeyBundle { - const decoded = proto.PublicKeyBundle.decode(bytes) + const decoded = publicKey.PublicKeyBundle.decode(bytes) if (!decoded.identityKey) { throw new Error('missing identity key') } diff --git a/src/crypto/Signature.ts b/src/crypto/Signature.ts index 565ff2fe7..11352a4b9 100644 --- a/src/crypto/Signature.ts +++ b/src/crypto/Signature.ts @@ -1,4 +1,4 @@ -import { xmtpEnvelope as proto } from '@xmtp/proto' +import { signature as proto } from '@xmtp/proto' import Long from 'long' import * as secp from '@noble/secp256k1' import PublicKey from './PublicKey' @@ -6,8 +6,9 @@ import PublicKey from './PublicKey' // Signature represents an ECDSA signature with recovery bit. export default class Signature implements proto.Signature { ecdsaCompact: proto.Signature_ECDSACompact | undefined // eslint-disable-line camelcase + walletEcdsaCompact: proto.Signature_WalletECDSACompact | undefined // eslint-disable-line camelcase - constructor(obj: proto.Signature) { + constructor(obj: Partial) { if (!obj.ecdsaCompact) { throw new Error('invalid signature') } From 6a213f7d26e4bfe2e6e18fa078b069879c488029 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar Date: Thu, 1 Sep 2022 12:57:39 -0700 Subject: [PATCH 3/7] fix: move around publickey imports --- test/ContentTypeTestKey.ts | 6 +++--- test/helpers.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/ContentTypeTestKey.ts b/test/ContentTypeTestKey.ts index 85d3b8505..1a66ac1a9 100644 --- a/test/ContentTypeTestKey.ts +++ b/test/ContentTypeTestKey.ts @@ -1,4 +1,4 @@ -import { xmtpEnvelope as proto } from '@xmtp/proto' +import { publicKey } from '@xmtp/proto' import { ContentTypeId, ContentCodec, PublicKey, EncodedContent } from '../src' export const ContentTypeTestKey = new ContentTypeId({ @@ -17,11 +17,11 @@ export class TestKeyCodec implements ContentCodec { return { type: ContentTypeTestKey, parameters: {}, - content: proto.PublicKey.encode(key).finish(), + content: publicKey.PublicKey.encode(key).finish(), } } decode(content: EncodedContent): PublicKey { - return new PublicKey(proto.PublicKey.decode(content.content)) + return new PublicKey(publicKey.PublicKey.decode(content.content)) } } diff --git a/test/helpers.ts b/test/helpers.ts index a582cec91..9a143395c 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -11,8 +11,8 @@ import { import Stream from '../src/Stream' import { promiseWithTimeout } from '../src/utils' import assert from 'assert' -import { xmtpEnvelope } from '@xmtp/proto' -type PublicKeyBundle = xmtpEnvelope.PublicKeyBundle +import { publicKey } from '@xmtp/proto' +type PublicKeyBundle = publicKey.PublicKeyBundle export const sleep = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)) From 3ed62e7623978b0f1e91d06c3462c5d47471b7e7 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar Date: Thu, 1 Sep 2022 12:59:21 -0700 Subject: [PATCH 4/7] fix: make tests pass --- src/authn/Authenticator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/authn/Authenticator.ts b/src/authn/Authenticator.ts index 544147cea..f073361ba 100644 --- a/src/authn/Authenticator.ts +++ b/src/authn/Authenticator.ts @@ -1,5 +1,5 @@ import { keccak256 } from 'js-sha3' -import { authn, xmtpEnvelope, publicKey } from '@xmtp/proto' +import { authn, signature, publicKey } from '@xmtp/proto' import AuthData from './AuthData' import { PrivateKey } from '../crypto' import { hexToBytes } from '../crypto/utils' @@ -36,7 +36,7 @@ export default class Authenticator { // The generated types are overly strict and don't like our additional methods // eslint-disable-next-line // @ts-ignore - authDataSignature: xmtpEnvelope.Signature.fromPartial(authSig), + authDataSignature: signature.Signature.fromPartial(authSig), }) ) } From 889882a8e82db25bc861b20e264d70593b9b1d95 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar Date: Tue, 6 Sep 2022 11:25:24 -0700 Subject: [PATCH 5/7] fix: remove key manager --- src/KeyManager.ts | 97 ----------------------------------------------- 1 file changed, 97 deletions(-) delete mode 100644 src/KeyManager.ts diff --git a/src/KeyManager.ts b/src/KeyManager.ts deleted file mode 100644 index b93b2fb44..000000000 --- a/src/KeyManager.ts +++ /dev/null @@ -1,97 +0,0 @@ -import PrivateKeyBundle from './crypto/PrivateKeyBundle' -import PublicKeyBundle from './crypto/PublicKeyBundle' - -enum EncryptionAlgorithm { - AES_256_GCM_HKDF_SHA_256, -} - -type TopicKeyRecord = { - keyMaterial: Uint8Array - encryptionAlgorithm: EncryptionAlgorithm -} - -type PrivateKeyRecord = { - bundle: PrivateKeyBundle - keySentBy: PublicKeyBundle -} - -type TopicResult = { - // This would never include the client. We can assume you are in every topic available - participants: PublicKeyBundle[] - topicKey: TopicKeyRecord - contentTopic: string -} - -type WalletTopicRecord = { - contentTopic: string - createdAt: Date -} - -export default class KeyManager { - topicKeys = new Map() - topicParticipants = new Map() - dmTopics = new Map() - delegateKeys: PrivateKeyRecord[] = [] - - addDirectMessageTopic( - contentTopic: string, - key: TopicKeyRecord, - counterparty: PublicKeyBundle, - createdAt: Date - ): void { - if ( - this.topicKeys.has(contentTopic) || - this.topicParticipants.has(contentTopic) - ) { - throw new Error('Topic key has already been set') - } - this.topicKeys.set(contentTopic, key) - this.topicParticipants.set(contentTopic, [counterparty]) - - const walletAddress = counterparty.identityKey.walletSignatureAddress() - const counterpartyTopicList = this.dmTopics.get(walletAddress) || [] - counterpartyTopicList.push({ contentTopic, createdAt }) - this.dmTopics.set(walletAddress, counterpartyTopicList) - } - - // Would be used to get all information required to decrypt/validate a given message - getTopicResult(contentTopic: string): TopicResult | undefined { - const participants = this.topicParticipants.get(contentTopic) - const topicKey = this.topicKeys.get(contentTopic) - if (!participants || !participants.length || !topicKey) { - return undefined - } - return { - participants, - topicKey, - contentTopic, - } - } - - // Would be used to know which topic/key to use to send to a given wallet address - getDirectMessageTopic(walletAddress: string): TopicResult | undefined { - const walletTopics = this.dmTopics.get(walletAddress) - if (!walletTopics || !walletTopics.length) { - return undefined - } - const newestTopic = this.findLatestTopic(walletTopics) - return this.getTopicResult(newestTopic.contentTopic) - } - - addDelegateKey(record: PrivateKeyRecord): void { - this.delegateKeys.push(record) - } - - private findLatestTopic(records: WalletTopicRecord[]): WalletTopicRecord { - let latestRecord: WalletTopicRecord | undefined - for (const record of records) { - if (!latestRecord || record.createdAt > latestRecord.createdAt) { - latestRecord = record - } - } - if (!latestRecord) { - throw new Error('No record found') - } - return latestRecord - } -} From a74e97bc8a20e81b0dc585d53f23bc8a1baa36df Mon Sep 17 00:00:00 2001 From: Nicholas Molnar Date: Tue, 6 Sep 2022 11:39:28 -0700 Subject: [PATCH 6/7] fix: normalize imports --- src/ContactBundle.ts | 2 +- src/crypto/Ciphertext.ts | 12 ++++++------ src/crypto/Signature.ts | 14 +++++++------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/ContactBundle.ts b/src/ContactBundle.ts index c99023a36..194c84896 100644 --- a/src/ContactBundle.ts +++ b/src/ContactBundle.ts @@ -1,4 +1,4 @@ -import { xmtpEnvelope as proto, contact, publicKey } from '@xmtp/proto' +import { contact, publicKey } from '@xmtp/proto' import { PublicKeyBundle } from './crypto' import PublicKey from './crypto/PublicKey' diff --git a/src/crypto/Ciphertext.ts b/src/crypto/Ciphertext.ts index 11a151171..941ebbc14 100644 --- a/src/crypto/Ciphertext.ts +++ b/src/crypto/Ciphertext.ts @@ -1,4 +1,4 @@ -import { ciphertext as proto } from '@xmtp/proto' +import { ciphertext } from '@xmtp/proto' export const AESKeySize = 32 // bytes export const KDFSaltSize = 32 // bytes @@ -8,10 +8,10 @@ export const AESGCMTagLength = 16 // property tagLength // Ciphertext packages the encrypted ciphertext with the salt and nonce used to produce it. // salt and nonce are not secret, and should be transmitted/stored along with the encrypted ciphertext. -export default class Ciphertext implements proto.Ciphertext { - aes256GcmHkdfSha256: proto.Ciphertext_Aes256gcmHkdfsha256 | undefined // eslint-disable-line camelcase +export default class Ciphertext implements ciphertext.Ciphertext { + aes256GcmHkdfSha256: ciphertext.Ciphertext_Aes256gcmHkdfsha256 | undefined // eslint-disable-line camelcase - constructor(obj: proto.Ciphertext) { + constructor(obj: ciphertext.Ciphertext) { if (!obj.aes256GcmHkdfSha256) { throw new Error('invalid ciphertext') } @@ -34,10 +34,10 @@ export default class Ciphertext implements proto.Ciphertext { } toBytes(): Uint8Array { - return proto.Ciphertext.encode(this).finish() + return ciphertext.Ciphertext.encode(this).finish() } static fromBytes(bytes: Uint8Array): Ciphertext { - return new Ciphertext(proto.Ciphertext.decode(bytes)) + return new Ciphertext(ciphertext.Ciphertext.decode(bytes)) } } diff --git a/src/crypto/Signature.ts b/src/crypto/Signature.ts index 11352a4b9..dfe760850 100644 --- a/src/crypto/Signature.ts +++ b/src/crypto/Signature.ts @@ -1,14 +1,14 @@ -import { signature as proto } from '@xmtp/proto' +import { signature } from '@xmtp/proto' import Long from 'long' import * as secp from '@noble/secp256k1' import PublicKey from './PublicKey' // Signature represents an ECDSA signature with recovery bit. -export default class Signature implements proto.Signature { - ecdsaCompact: proto.Signature_ECDSACompact | undefined // eslint-disable-line camelcase - walletEcdsaCompact: proto.Signature_WalletECDSACompact | undefined // eslint-disable-line camelcase +export default class Signature implements signature.Signature { + ecdsaCompact: signature.Signature_ECDSACompact | undefined // eslint-disable-line camelcase + walletEcdsaCompact: signature.Signature_WalletECDSACompact | undefined // eslint-disable-line camelcase - constructor(obj: Partial) { + constructor(obj: Partial) { if (!obj.ecdsaCompact) { throw new Error('invalid signature') } @@ -44,10 +44,10 @@ export default class Signature implements proto.Signature { } toBytes(): Uint8Array { - return proto.Signature.encode(this).finish() + return signature.Signature.encode(this).finish() } static fromBytes(bytes: Uint8Array): Signature { - return new Signature(proto.Signature.decode(bytes)) + return new Signature(signature.Signature.decode(bytes)) } } From 8a3f4fa37f7b7507d2df53d346ada29d4d4ebbc4 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar Date: Tue, 6 Sep 2022 14:05:26 -0700 Subject: [PATCH 7/7] fix: remove unused publicKey import --- src/crypto/PrivateKey.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/PrivateKey.ts b/src/crypto/PrivateKey.ts index 00f2df33b..58cfd61f8 100644 --- a/src/crypto/PrivateKey.ts +++ b/src/crypto/PrivateKey.ts @@ -1,4 +1,4 @@ -import { privateKey, publicKey } from '@xmtp/proto' +import { privateKey } from '@xmtp/proto' import * as secp from '@noble/secp256k1' import Long from 'long' import Signature from './Signature'