Skip to content

Commit 3659a35

Browse files
authored
Signed private key bundle (#175)
feat: private key bundle v2 Introducting PrivateKeyBundleV2 with signed private keys. Also moving wallet encryption to EncryptedKeyStore.
1 parent 1e011d8 commit 3659a35

24 files changed

+424
-315
lines changed

src/Client.ts

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { PublicKeyBundle, PrivateKeyBundle } from './crypto'
1+
import { PublicKeyBundle, PrivateKeyBundleV1 } from './crypto'
22
import Message from './Message'
33
import {
44
buildDirectMessageTopic,
@@ -8,7 +8,7 @@ import {
88
import Stream, { MessageFilter, noTransformation } from './Stream'
99
import { Signer } from 'ethers'
1010
import {
11-
EncryptedStore,
11+
EncryptedKeyStore,
1212
KeyStore,
1313
LocalStorageStore,
1414
PrivateTopicStore,
@@ -24,7 +24,7 @@ import {
2424
} from './MessageContent'
2525
import { decompress, compress } from './Compression'
2626
import { xmtpEnvelope, messageApi, fetcher } from '@xmtp/proto'
27-
import { DecodeContactBundle } from './ContactBundle'
27+
import { decodeContactBundle } from './ContactBundle'
2828
import ApiClient, { SortDirection } from './ApiClient'
2929
import { Authenticator } from './authn'
3030
const { Compression } = xmtpEnvelope
@@ -125,15 +125,15 @@ export function defaultOptions(opts?: Partial<ClientOptions>): ClientOptions {
125125
*/
126126
export default class Client {
127127
address: string
128-
keys: PrivateKeyBundle
128+
keys: PrivateKeyBundleV1
129129
apiClient: ApiClient
130130
private contacts: Set<string> // address which we have connected to
131131
private knownPublicKeyBundles: Map<string, PublicKeyBundle> // addresses and key bundles that we have witnessed
132132
private _conversations: Conversations
133133
private _codecs: Map<string, ContentCodec<any>>
134134
private _maxContentSize: number
135135

136-
constructor(keys: PrivateKeyBundle, apiClient: ApiClient) {
136+
constructor(keys: PrivateKeyBundleV1, apiClient: ApiClient) {
137137
this.contacts = new Set<string>()
138138
this.knownPublicKeyBundles = new Map<string, PublicKeyBundle>()
139139
this.keys = keys
@@ -487,13 +487,13 @@ function createKeyStoreFromConfig(
487487
function createNetworkPrivateKeyStore(
488488
wallet: Signer,
489489
apiClient: ApiClient
490-
): EncryptedStore {
491-
return new EncryptedStore(wallet, new PrivateTopicStore(apiClient))
490+
): EncryptedKeyStore {
491+
return new EncryptedKeyStore(wallet, new PrivateTopicStore(apiClient))
492492
}
493493

494494
// Create Encrypted store which uses LocalStorage to store KeyBundles
495-
function createLocalPrivateKeyStore(wallet: Signer): EncryptedStore {
496-
return new EncryptedStore(wallet, new LocalStorageStore())
495+
function createLocalPrivateKeyStore(wallet: Signer): EncryptedKeyStore {
496+
return new EncryptedKeyStore(wallet, new LocalStorageStore())
497497
}
498498

499499
function createStaticStore(privateKeyOverride: Uint8Array): KeyStore {
@@ -505,15 +505,15 @@ function createStaticStore(privateKeyOverride: Uint8Array): KeyStore {
505505
async function loadOrCreateKeysFromStore(
506506
wallet: Signer | null,
507507
store: KeyStore
508-
): Promise<PrivateKeyBundle> {
508+
): Promise<PrivateKeyBundleV1> {
509509
let keys = await store.loadPrivateKeyBundle()
510510
if (keys) {
511511
return keys
512512
}
513513
if (!wallet) {
514514
throw new Error('No wallet found')
515515
}
516-
keys = await PrivateKeyBundle.generate(wallet)
516+
keys = await PrivateKeyBundleV1.generate(wallet)
517517
await store.storePrivateKeyBundle(keys)
518518
return keys
519519
}
@@ -565,7 +565,7 @@ async function getUserContactFromNetwork(
565565

566566
for await (const env of stream) {
567567
if (!env.message) continue
568-
const bundle = DecodeContactBundle(b64Decode(env.message.toString()))
568+
const bundle = decodeContactBundle(b64Decode(env.message.toString()))
569569
const keyBundle = bundle.keyBundle
570570

571571
const address = keyBundle?.walletSignatureAddress()

src/ContactBundle.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,19 @@ export class ContactBundleV2 implements contact.ContactBundleV2 {
4343
},
4444
}).finish()
4545
}
46+
47+
static fromLegacyBundle(bundle: ContactBundleV1): ContactBundleV2 {
48+
return new ContactBundleV2({
49+
keyBundle: SignedPublicKeyBundle.fromLegacyBundle(bundle.keyBundle),
50+
})
51+
}
4652
}
4753

4854
// This is the union of all supported bundle versions.
4955
export type ContactBundle = ContactBundleV1 | ContactBundleV2
5056

5157
// This is the primary function for reading contact bundles off the wire.
52-
export function DecodeContactBundle(bytes: Uint8Array): ContactBundle {
58+
export function decodeContactBundle(bytes: Uint8Array): ContactBundle {
5359
let cb: contact.ContactBundle
5460
try {
5561
cb = contact.ContactBundle.decode(bytes)

src/Message.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Long from 'long'
33
import Ciphertext from './crypto/Ciphertext'
44
import {
55
PublicKeyBundle,
6-
PrivateKeyBundle,
6+
PrivateKeyBundleV1,
77
PublicKey,
88
decrypt,
99
encrypt,
@@ -105,7 +105,7 @@ export default class Message implements proto.MessageV1 {
105105

106106
// encrypt and serialize the message
107107
static async encode(
108-
sender: PrivateKeyBundle,
108+
sender: PrivateKeyBundleV1,
109109
recipient: PublicKeyBundle,
110110
message: Uint8Array,
111111
timestamp: Date
@@ -137,7 +137,7 @@ export default class Message implements proto.MessageV1 {
137137
// throws if any part of the messages (including the header) was tampered with
138138
// or the recipient preKey used to encrypt the message is not recognized
139139
static async decode(
140-
viewer: PrivateKeyBundle,
140+
viewer: PrivateKeyBundleV1,
141141
bytes: Uint8Array
142142
): Promise<Message> {
143143
const message = proto.Message.decode(bytes)

0 commit comments

Comments
 (0)