Skip to content

Commit

Permalink
feat: validate length and checksum of fetched masp param
Browse files Browse the repository at this point in the history
  • Loading branch information
jurevans committed Sep 30, 2024
1 parent 1def6e0 commit a0830bd
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 17 deletions.
2 changes: 1 addition & 1 deletion apps/namadillo/src/hooks/useSdk.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const SdkProvider: FunctionComponent<PropsWithChildren> = ({
masp
.fetchAndStoreMaspParams(paramsUrl)
.then(() => masp.loadMaspParams(""))
.catch((e) => console.error(`${e}`));
.catch((e) => console.error(`Error downloading MASP params ${e}`));
});
});
}
Expand Down
107 changes: 91 additions & 16 deletions packages/shared/lib/src/sdk/mod.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,121 @@
const PREFIX = "Namada::SDK";
const MASP_MPC_RELEASE_URL =
"https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/";

const sha256Hash = async (msg: Uint8Array): Promise<string> => {
const hashBuffer = await crypto.subtle.digest("SHA-256", msg);
const hashArray = Array.from(new Uint8Array(hashBuffer));
// Return hash as hex
return hashArray.map((byte) => byte.toString(16).padStart(2, "0")).join("");
};

enum MaspParam {
Output = "masp-output.params",
Convert = "masp-convert.params",
Spend = "masp-spend.params",
}

type MaspParamBytes = {
param: MaspParam;
bytes: Uint8Array;
};

const MASP_PARAM_ATTR: Record<
MaspParam,
{ length: number; sha256sum: string }
> = {
[MaspParam.Output]: {
length: 16398620,
sha256sum:
"ed8b5d354017d808cfaf7b31eca5c511936e65ef6d276770251f5234ec5328b8",
},
[MaspParam.Spend]: {
length: 49848572,
sha256sum:
"62b3c60ca54bd99eb390198e949660624612f7db7942db84595fa9f1b4a29fd8",
},
[MaspParam.Convert]: {
length: 22570940,
sha256sum:
"8e049c905e0e46f27662c7577a4e3480c0047ee1171f7f6d9c5b0de757bf71f1",
},
};

const validateMaspParamBytes = async ({
param,
bytes,
}: MaspParamBytes): Promise<Uint8Array> => {
const { length, sha256sum } = MASP_PARAM_ATTR[param];

// Reject if invalid length (incomplete download or invalid)
console.info(`Validating data length for ${param}, expecting ${length}...`);

if (length !== bytes.length) {
return Promise.reject(
`[${param}]: Invalid data length! Expected ${length}, received ${bytes.length}!`
);
}

// Reject if invalid hash (otherwise invalid data)
console.info(`Validating sha256sum for ${param}, expecting ${sha256sum}...`);
const hash = await sha256Hash(bytes);

if (hash !== sha256sum) {
return Promise.reject(
`[${param}]: Invalid sha256sum! Expected ${sha256sum}, received ${hash}!`
);
}

return bytes;
};

export async function hasMaspParams(): Promise<boolean> {
return (
(await has("masp-spend.params")) &&
(await has("masp-output.params")) &&
(await has("masp-convert.params"))
(await has(MaspParam.Spend)) &&
(await has(MaspParam.Output)) &&
(await has(MaspParam.Convert))
);
}

export async function fetchAndStoreMaspParams(
url?: string
): Promise<[void, void, void]> {
return Promise.all([
fetchAndStore("masp-spend.params", url),
fetchAndStore("masp-output.params", url),
fetchAndStore("masp-convert.params", url),
fetchAndStore(MaspParam.Spend, url),
fetchAndStore(MaspParam.Output, url),
fetchAndStore(MaspParam.Convert, url),
]);
}

export async function getMaspParams(): Promise<[unknown, unknown, unknown]> {
return Promise.all([
get("masp-spend.params"),
get("masp-output.params"),
get("masp-convert.params"),
get(MaspParam.Spend),
get(MaspParam.Output),
get(MaspParam.Convert),
]);
}

export async function fetchAndStore(
params: string,
param: MaspParam,
url?: string
): Promise<void> {
const data = await fetchParams(params, url);
await set(params, data);
return await fetchParams(param, url)
.then((data) => set(param, data))
.catch((e) => {
console.warn(`Encountered errors fetching ${param}: ${e}`);
});
}

export async function fetchParams(
params: string,
url: string = "https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/"
param: MaspParam,
url: string = MASP_MPC_RELEASE_URL
): Promise<Uint8Array> {
return fetch(`${url}${params}`)
return fetch(`${url}${param}`)
.then((response) => response.arrayBuffer())
.then((ab) => new Uint8Array(ab));
.then((ab) => {
const bytes = new Uint8Array(ab);
return validateMaspParamBytes({ param, bytes });
});
}

function getDB(): Promise<IDBDatabase> {
Expand Down

0 comments on commit a0830bd

Please sign in to comment.