Skip to content

Commit

Permalink
feat: add method to get conversation HMAC keys
Browse files Browse the repository at this point in the history
  • Loading branch information
rygine committed Jan 18, 2024
1 parent 19b7bd7 commit 7c4fe55
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 9 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
},
"dependencies": {
"@noble/secp256k1": "^1.5.2",
"@xmtp/proto": "^3.37.0-beta.1",
"@xmtp/proto": "^3.37.0-beta.2",
"@xmtp/user-preferences-bindings-wasm": "^0.3.4",
"async-mutex": "^0.4.0",
"elliptic": "^6.5.4",
Expand Down
2 changes: 1 addition & 1 deletion src/crypto/encryption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ async function hkdf(secret: Uint8Array, salt: Uint8Array): Promise<CryptoKey> {
)
}

async function hkdfHmacKey(
export async function hkdfHmacKey(
secret: Uint8Array,
salt: Uint8Array
): Promise<CryptoKey> {
Expand Down
42 changes: 39 additions & 3 deletions src/keystore/InMemoryKeystore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
userPreferencesEncrypt,
generateUserPreferencesTopic,
} from '../crypto/selfEncryption'
import { generateHmacSignature } from '../crypto/encryption'
import { generateHmacSignature, hkdfHmacKey } from '../crypto/encryption'

const { ErrorCode } = keystore

Expand Down Expand Up @@ -602,7 +602,43 @@ export default class InMemoryKeystore implements Keystore {
return this.v2Store.lookup(topic)
}

getTopicHmacs() {
const topics = this.v2Store.topics.map((topic) => topic.invitation?.topic)
async getV2ConversationHmacKeys(): Promise<keystore.GetConversationHmacKeysResponse> {
const thirtyDayPeriodsSinceEpoch = Math.floor(
Date.now() / 1000 / 60 / 60 / 24 / 30
)

const hmacKeys: keystore.GetConversationHmacKeysResponse['hmacKeys'] = {}

this.v2Store.topics.forEach(async (topicData) => {
if (topicData.invitation?.topic) {
const keyMaterial = getKeyMaterial(topicData.invitation)
const values = await Promise.all(
[
thirtyDayPeriodsSinceEpoch - 1,
thirtyDayPeriodsSinceEpoch,
thirtyDayPeriodsSinceEpoch + 1,
].map(async (value) => {
const salt = `${value}-${this.accountAddress}`
const hmacKey = await hkdfHmacKey(
keyMaterial,
new TextEncoder().encode(salt)
)
return {
thirtyDayPeriodsSinceEpoch: value,
// convert CryptoKey to Uint8Array to match the proto
hmacKey: new Uint8Array(
await crypto.subtle.exportKey('raw', hmacKey)
),
}
})
)

hmacKeys[topicData.invitation.topic] = {
values,
}
}
})

return { hmacKeys }
}
}
5 changes: 5 additions & 0 deletions src/keystore/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ export interface Keystore {
* Get the private preferences topic identifier
*/
getPrivatePreferencesTopicIdentifier(): Promise<keystore.GetPrivatePreferencesTopicIdentifierResponse>
/**
* Returns the conversation HMAC keys for the current, previous, and next
* 30 day periods since the epoch
*/
getV2ConversationHmacKeys(): Promise<keystore.GetConversationHmacKeysResponse>
}

export type TopicData = WithoutUndefined<keystore.TopicMap_TopicData>
4 changes: 4 additions & 0 deletions src/keystore/rpcDefinitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,8 @@ export const apiDefs: ApiDefs = {
req: null,
res: keystore.GetPrivatePreferencesTopicIdentifierResponse,
},
getV2ConversationHmacKeys: {
req: null,
res: keystore.GetConversationHmacKeysResponse,
},
} as const

0 comments on commit 7c4fe55

Please sign in to comment.