Skip to content

Commit

Permalink
fix: use stable hash in future payload to avoid unstable hash
Browse files Browse the repository at this point in the history
  • Loading branch information
Jack-Works committed Oct 9, 2019
1 parent d2ccccf commit 95b05f1
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 14 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@types/classnames": "^2.2.7",
"@types/gun": "^0.9.1",
"@types/jest": "^24.0.13",
"@types/json-stable-stringify": "^1.0.32",
"@types/lodash-es": "^4.17.3",
"@types/node": "^12.7.11",
"@types/qrcode": "^1.3.3",
Expand All @@ -60,6 +61,7 @@
"gun": "0.2019.422",
"husky": "^3.0.5",
"idb": "^4.0.3",
"json-stable-stringify": "^1.0.1",
"jsqr": "^1.2.0",
"lint-staged": "^9.2.5",
"lodash-es": "^4.17.11",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export async function appendShareTarget(
if (version === -39) {
const toKey = await prepareOthersKeyForEncryptionV39(people)
const othersAESKeyEncrypted = await Alpha39.generateOthersAESKeyEncrypted(-39, AESKey, myPrivateKey, toKey)
Gun2.publishPostAESKeyOnGun2(iv, othersAESKeyEncrypted)
Gun2.publishPostAESKeyOnGun2(-39, iv, othersAESKeyEncrypted)
} else if (version === -40) {
const toKey = await prepareOthersKeyForEncryptionV40(people)
const othersAESKeyEncrypted = await Alpha40.generateOthersAESKeyEncrypted(-40, AESKey, myPrivateKey, toKey)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export async function debugShowAllPossibleHashForPost(post: PostIVIdentifier) {
async x =>
[
x.identifier.toText(),
(await hashPostSalt(post.postIV)) + '-' + (await hashCryptoKey(x.publicKey!)),
(await hashPostSalt(post.postIV)) + '-' + (await hashCryptoKey(x.publicKey!, true)),
] as [string, string],
),
)
Expand Down
6 changes: 3 additions & 3 deletions src/extension/background-script/CryptoServices/decryptFrom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export async function* decryptFromMessageWithProgress(
}
} else {
if (cachedPostResult) {
const { keyHash, postHash } = await Gun2.queryPostKeysOnGun2(iv, mine.publicKey)
const { keyHash, postHash } = await Gun2.queryPostKeysOnGun2(-39, iv, mine.publicKey)
yield { debug: 'debug_finding_hash', hash: [postHash, keyHash] }
return {
signatureVerifyResult: await cryptoProvider.verify(
Expand All @@ -171,7 +171,7 @@ export async function* decryptFromMessageWithProgress(
if (result === undefined) return { error: geti18nString('service_not_share_target') }
aesKeyEncrypted.push(result)
} else if (version === -39) {
const { keyHash, keys, postHash } = await Gun2.queryPostKeysOnGun2(iv, mine.publicKey)
const { keyHash, keys, postHash } = await Gun2.queryPostKeysOnGun2(-39, iv, mine.publicKey)
yield { debug: 'debug_finding_hash', hash: [postHash, keyHash] }
aesKeyEncrypted.push(...keys)
}
Expand All @@ -196,7 +196,7 @@ export async function* decryptFromMessageWithProgress(

// Failed, we have to wait for the future info from gun.
return new Promise<Success>((resolve, reject) => {
const undo = Gun2.subscribePostKeysOnGun2(iv, mine.publicKey, async key => {
const undo = Gun2.subscribePostKeysOnGun2(-39, iv, mine.publicKey, async key => {
console.log('New key received, trying', key)
try {
const result = await decryptWith(key)
Expand Down
4 changes: 3 additions & 1 deletion src/extension/background-script/CryptoServices/encryptTo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ export async function encryptTo(

/**
* MUST call before send post, or othersAESKeyEncrypted will not be published to the internet!
* TODO: If we can use PostIVIdentifier to avoid this hacking way to publish PostAESKey?
* @param iv Token that returns in the encryptTo
*/
export async function publishPostAESKey(iv: string, whoAmI: PersonIdentifier) {
if (!OthersAESKeyEncryptedMap.has(iv)) throw new Error(geti18nString('service_publish_post_aes_key_failed'))
return Gun2.publishPostAESKeyOnGun2(iv, OthersAESKeyEncryptedMap.get(iv)!)
// Use the latest payload version here since we do not accept new post for older version.
return Gun2.publishPostAESKeyOnGun2(-39, iv, OthersAESKeyEncryptedMap.get(iv)!)
}
11 changes: 9 additions & 2 deletions src/network/gun/version.2/hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Gun from 'gun'
import { PersonIdentifier } from '../../../database/type'
import { memoizePromise } from '../../../utils/memoize'
import { CryptoKeyToJsonWebKey } from '../../../utils/type-transform/CryptoKey-JsonWebKey'
import stableJSONStringify from 'json-stable-stringify'

export const hashPersonIdentifier = memoizePromise(
async function hashPersonIdentifier(id: PersonIdentifier) {
Expand All @@ -24,11 +25,17 @@ export const hashPostSalt = memoizePromise(async function(postSalt: string) {
return hash.substring(0, N)
})

export const hashCryptoKey = memoizePromise(async function(key: CryptoKey) {
/**
* @param key - The key need to be hashed
* @param stableHash - Set this to true if you're writing new code.
* Unstable hash may cause problem but we cannot just switch to stable hash
* because it may breaks current data.
*/
export const hashCryptoKey = memoizePromise(async function(key: CryptoKey, stableHash: boolean) {
const hashPair = `10198a2f-205f-45a6-9987-3488c80113d0`
const N = 2

const jwk = JSON.stringify(await CryptoKeyToJsonWebKey(key))
const jwk = (stableHash ? stableJSONStringify : JSON.stringify)(await CryptoKeyToJsonWebKey(key))
const hash = (await Gun.SEA.work(jwk, hashPair))!
return hash.substring(0, N)
})
19 changes: 15 additions & 4 deletions src/network/gun/version.2/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ OnlyRunInContext('background', 'gun')

/**
* Query all possible keys stored on the gun
* @param version current payload version
* @param postSalt Post iv
* @param partitionByCryptoKey Public key of the current user (receiver)
*/
export async function queryPostKeysOnGun2(
version: -39,
postSalt: string,
partitionByCryptoKey: CryptoKey,
): Promise<{ keys: SharedAESKeyGun2[]; postHash: string; keyHash: string }> {
const postHash = await hashPostSalt(postSalt)
const keyHash = await hashCryptoKey(partitionByCryptoKey)
// In version > -39, we will use stable hash to prevent unstable result for key hashing
const keyHash = await hashCryptoKey(partitionByCryptoKey, version > -39)

// ? here we get the internal node names of gun2[postHash][keyHash]
// ? where gun2[postHash][keyHash] is a list
Expand All @@ -36,17 +39,20 @@ export async function queryPostKeysOnGun2(

/**
* Listen on the changes of all possible keys on the gun
* @param version current payload version
* @param postSalt Post iv
* @param partitionByCryptoKey Public key of the current user (receiver)
* @param callback
*/
export function subscribePostKeysOnGun2(
version: -39,
postSalt: string,
partitionByCryptoKey: CryptoKey,
callback: (data: SharedAESKeyGun2) => void,
) {
hashPostSalt(postSalt).then(postHash => {
hashCryptoKey(partitionByCryptoKey).then(keyHash => {
// In version > -39, we will use stable hash to prevent unstable result for key hashing
hashCryptoKey(partitionByCryptoKey, version > -39).then(keyHash => {
gun2.get(postHash)
// @ts-ignore
.get(keyHash)
Expand All @@ -63,14 +69,19 @@ export function subscribePostKeysOnGun2(

/**
* Publish post keys on the gun
* @param version current payload
* @param postSalt Post iv
* @param receiversKeys Keys needs to publish
*/
export async function publishPostAESKeyOnGun2(postSalt: string, receiversKeys: PublishedAESKeyRecordV39[]) {
export async function publishPostAESKeyOnGun2(
version: -39,
postSalt: string,
receiversKeys: PublishedAESKeyRecordV39[],
) {
const postHash = await hashPostSalt(postSalt)
// Store AES key to gun
receiversKeys.forEach(async ({ aesKey, receiverKey }) => {
const keyHash = await hashCryptoKey(receiverKey)
const keyHash = await hashCryptoKey(receiverKey, version > -39)
console.log(`gun[${postHash}][${keyHash}].push(`, aesKey, `)`)
gun2.get(postHash)
// @ts-ignore
Expand Down
6 changes: 4 additions & 2 deletions src/utils/type-transform/CryptoKey-JsonWebKey.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import stableStringify from 'json-stable-stringify'
const CryptoKeyCache = new Map<string, CryptoKey>()
const JsonWebKeyCache = new WeakMap<CryptoKey, JsonWebKey>()

Expand All @@ -21,7 +22,7 @@ export async function JsonWebKeyToCryptoKey(
usage: (Usages)[] = ['deriveKey'],
algorithm: Algorithms = { name: 'ECDH', namedCurve: 'K-256' },
) {
const _key = JSON.stringify(key) + usage.join(',')
const _key = stableStringify(key) + usage.sort().join(',')
if (CryptoKeyCache.has(_key)) return CryptoKeyCache.get(_key)
const cryptoKey = await crypto.subtle.importKey('jwk', key, algorithm, true, usage)
CryptoKeyCache.set(_key, cryptoKey)
Expand All @@ -37,6 +38,7 @@ export async function CryptoKeyToJsonWebKey(key: CryptoKey) {
if (JsonWebKeyCache.has(key)) return JsonWebKeyCache.get(key)!
const jwk = await crypto.subtle.exportKey('jwk', key)
JsonWebKeyCache.set(key, jwk)
CryptoKeyCache.set(JSON.stringify(jwk) + key.usages.join(','), key)
const hash = stableStringify(jwk) + key.usages.sort().join(',')
CryptoKeyCache.set(hash, key)
return jwk
}
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2421,6 +2421,11 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636"
integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==

"@types/json-stable-stringify@^1.0.32":
version "1.0.32"
resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.32.tgz#121f6917c4389db3923640b2e68de5fa64dda88e"
integrity sha512-q9Q6+eUEGwQkv4Sbst3J4PNgDOvpuVuKj79Hl/qnmBMEIPzB5QoFRUtjcgcg2xNUZyYUGXBk5wYIBKHt0A+Mxw==

"@types/lodash-es@^4.1.4", "@types/lodash-es@^4.17.3":
version "4.17.3"
resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.3.tgz#87eb0b3673b076b8ee655f1890260a136af09a2d"
Expand Down

0 comments on commit 95b05f1

Please sign in to comment.