Skip to content

Commit

Permalink
feat: add senderHmac to MessageV2
Browse files Browse the repository at this point in the history
  • Loading branch information
rygine committed Mar 12, 2024
1 parent d14f677 commit f99d661
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 18 deletions.
12 changes: 8 additions & 4 deletions src/Message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,26 +188,30 @@ export class MessageV1 extends MessageBase implements proto.MessageV1 {

export class MessageV2 extends MessageBase implements proto.MessageV2 {
senderAddress: string | undefined
private header: proto.MessageHeaderV2 // eslint-disable-line camelcase
private header: proto.MessageHeaderV2
senderHmac: Uint8Array

constructor(
id: string,
bytes: Uint8Array,
obj: proto.Message,
header: proto.MessageHeaderV2
header: proto.MessageHeaderV2,
senderHmac: Uint8Array
) {
super(id, bytes, obj)
this.header = header
this.senderHmac = senderHmac
}

static async create(
obj: proto.Message,
header: proto.MessageHeaderV2,
bytes: Uint8Array
bytes: Uint8Array,
senderHmac: Uint8Array
): Promise<MessageV2> {
const id = bytesToHex(await sha256(bytes))

return new MessageV2(id, bytes, obj, header)
return new MessageV2(id, bytes, obj, header, senderHmac)
}

get sent(): Date {
Expand Down
22 changes: 11 additions & 11 deletions src/conversations/Conversation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type {
import type Client from '../Client'
import type { InvitationContext } from '../Invitation'
import { DecodedMessage, MessageV1, MessageV2 } from '../Message'
import type { messageApi, keystore, ciphertext } from '@xmtp/proto'
import type { messageApi, keystore } from '@xmtp/proto'
import { message, content as proto } from '@xmtp/proto'
import {
SignedPublicKey,
Expand Down Expand Up @@ -658,14 +658,17 @@ export class ConversationV2<ContentTypes>
}
const signedBytes = proto.SignedContent.encode(signed).finish()

const ciphertext = await this.encryptMessage(signedBytes, headerBytes)
const { encrypted: ciphertext, senderHmac } = await this.encryptMessage(
signedBytes,
headerBytes
)
const protoMsg = {
v1: undefined,
v2: { headerBytes, ciphertext },
v2: { headerBytes, ciphertext, senderHmac },
}
const bytes = message.Message.encode(protoMsg).finish()

return MessageV2.create(protoMsg, header, bytes)
return MessageV2.create(protoMsg, header, bytes, senderHmac)
}

private async decryptBatch(
Expand Down Expand Up @@ -709,10 +712,7 @@ export class ConversationV2<ContentTypes>
}
}

private async encryptMessage(
payload: Uint8Array,
headerBytes: Uint8Array
): Promise<ciphertext.Ciphertext> {
private async encryptMessage(payload: Uint8Array, headerBytes: Uint8Array) {
const { responses } = await this.client.keystore.encryptV2({
requests: [
{
Expand All @@ -725,8 +725,8 @@ export class ConversationV2<ContentTypes>
if (responses.length !== 1) {
throw new Error('Invalid response length')
}
const { encrypted } = getResultOrThrow(responses[0])
return encrypted
const { encrypted, senderHmac } = getResultOrThrow(responses[0])
return { encrypted, senderHmac }
}

private async buildDecodedMessage(
Expand Down Expand Up @@ -829,7 +829,7 @@ export class ConversationV2<ContentTypes>
throw new Error('topic mismatch')
}

return MessageV2.create(msg, header, env.message)
return MessageV2.create(msg, header, env.message, msg.v2.senderHmac)
}

async decodeMessage(
Expand Down
11 changes: 8 additions & 3 deletions src/keystore/InMemoryKeystore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
generateUserPreferencesTopic,
} from '../crypto/selfEncryption'
import type { KeystoreInterface } from '..'
import { generateHmacSignature } from '../crypto/encryption'

const { ErrorCode } = keystore

Expand Down Expand Up @@ -295,10 +296,14 @@ export default class InMemoryKeystore implements KeystoreInterface {
)
}

const keyMaterial = getKeyMaterial(topicData.invitation)
const ciphertext = await encryptV2(payload, keyMaterial, headerBytes)

return {
encrypted: await encryptV2(
payload,
getKeyMaterial(topicData.invitation),
encrypted: ciphertext,
senderHmac: await generateHmacSignature(
keyMaterial,
new TextEncoder().encode(contentTopic),
headerBytes
),
}
Expand Down

0 comments on commit f99d661

Please sign in to comment.