[web] Use our standard crypto layer in accounts app (#2021)
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
import EnteButton from "@ente/shared/components/EnteButton";
|
||||
import EnteSpinner from "@ente/shared/components/EnteSpinner";
|
||||
import FormPaper from "@ente/shared/components/Form/FormPaper";
|
||||
import { fromB64URLSafeNoPadding } from "@ente/shared/crypto/internal/libsodium";
|
||||
import HTTPService from "@ente/shared/network/HTTPService";
|
||||
import { LS_KEYS, setData } from "@ente/shared/storage/localStorage";
|
||||
import InfoIcon from "@mui/icons-material/Info";
|
||||
@@ -125,6 +126,7 @@ const PasskeysFlow = () => {
|
||||
|
||||
const encodedResponse = _sodium.to_base64(JSON.stringify(finishData));
|
||||
|
||||
// TODO-PK: Shouldn't this be URL encoded?
|
||||
window.location.href = `${redirect}?response=${encodedResponse}`;
|
||||
};
|
||||
|
||||
@@ -144,20 +146,16 @@ const PasskeysFlow = () => {
|
||||
publicKey: any,
|
||||
timeoutMillis: number = 60000, // Default timeout of 60 seconds
|
||||
): Promise<Credential | null> => {
|
||||
publicKey.challenge = _sodium.from_base64(
|
||||
publicKey.challenge = await fromB64URLSafeNoPadding(
|
||||
publicKey.challenge,
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING,
|
||||
);
|
||||
publicKey.allowCredentials?.forEach(function (listItem: any) {
|
||||
listItem.id = _sodium.from_base64(
|
||||
listItem.id,
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING,
|
||||
);
|
||||
for (const listItem of publicKey.allowCredentials ?? []) {
|
||||
listItem.id = await fromB64URLSafeNoPadding(listItem.id);
|
||||
// note: we are orverwriting the transports array with all possible values.
|
||||
// This is because the browser will only prompt the user for the transport that is available.
|
||||
// Warning: In case of invalid transport value, the webauthn will fail on Safari & iOS browsers
|
||||
listItem.transports = ["usb", "nfc", "ble", "internal"];
|
||||
});
|
||||
}
|
||||
publicKey.timeout = timeoutMillis;
|
||||
const publicKeyCredentialCreationOptions: CredentialRequestOptions = {
|
||||
publicKey: publicKey,
|
||||
|
||||
@@ -101,6 +101,7 @@ const Passkeys = () => {
|
||||
|
||||
const options = response.options;
|
||||
|
||||
// TODO-PK: The types don't match.
|
||||
options.publicKey.challenge = _sodium.from_base64(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import log from "@/next/log";
|
||||
import { toB64URLSafeNoPadding } from "@ente/shared/crypto/internal/libsodium";
|
||||
import HTTPService from "@ente/shared/network/HTTPService";
|
||||
import { getEndpoint } from "@ente/shared/network/api";
|
||||
import { getToken } from "@ente/shared/storage/localStorage/helpers";
|
||||
import _sodium from "libsodium-wrappers";
|
||||
|
||||
const ENDPOINT = getEndpoint();
|
||||
|
||||
@@ -87,17 +87,15 @@ export const finishPasskeyRegistration = async (
|
||||
sessionId: string,
|
||||
) => {
|
||||
try {
|
||||
const attestationObjectB64 = _sodium.to_base64(
|
||||
const attestationObjectB64 = await toB64URLSafeNoPadding(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
new Uint8Array(credential.response.attestationObject),
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING,
|
||||
);
|
||||
const clientDataJSONB64 = _sodium.to_base64(
|
||||
const clientDataJSONB64 = await toB64URLSafeNoPadding(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
new Uint8Array(credential.response.clientDataJSON),
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING,
|
||||
);
|
||||
|
||||
const token = getToken();
|
||||
@@ -168,29 +166,25 @@ export const finishPasskeyAuthentication = async (
|
||||
rawId: credential.id,
|
||||
type: credential.type,
|
||||
response: {
|
||||
authenticatorData: _sodium.to_base64(
|
||||
authenticatorData: await toB64URLSafeNoPadding(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
new Uint8Array(credential.response.authenticatorData),
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING,
|
||||
),
|
||||
clientDataJSON: _sodium.to_base64(
|
||||
clientDataJSON: await toB64URLSafeNoPadding(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
new Uint8Array(credential.response.clientDataJSON),
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING,
|
||||
),
|
||||
signature: _sodium.to_base64(
|
||||
signature: await toB64URLSafeNoPadding(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
new Uint8Array(credential.response.signature),
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING,
|
||||
),
|
||||
userHandle: _sodium.to_base64(
|
||||
userHandle: await toB64URLSafeNoPadding(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
new Uint8Array(credential.response.userHandle),
|
||||
_sodium.base64_variants.URLSAFE_NO_PADDING,
|
||||
),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import log from "@/next/log";
|
||||
import { wait } from "@/utils/promise";
|
||||
import { boxSealOpen, toB64 } from "@ente/shared/crypto/internal/libsodium";
|
||||
import {
|
||||
boxSealOpen,
|
||||
generateKeyPair,
|
||||
} from "@ente/shared/crypto/internal/libsodium";
|
||||
import castGateway from "@ente/shared/network/cast";
|
||||
import _sodium from "libsodium-wrappers";
|
||||
|
||||
export interface Registration {
|
||||
/** A pairing code shown on the screen. A client can use this to connect. */
|
||||
@@ -75,9 +77,8 @@ export interface Registration {
|
||||
*/
|
||||
export const register = async (): Promise<Registration> => {
|
||||
// Generate keypair.
|
||||
const keypair = await generateKeyPair();
|
||||
const publicKeyB64 = await toB64(keypair.publicKey);
|
||||
const privateKeyB64 = await toB64(keypair.privateKey);
|
||||
const { publicKey: publicKeyB64, privateKey: privateKeyB64 } =
|
||||
await generateKeyPair();
|
||||
|
||||
// Register keypair with museum to get a pairing code.
|
||||
let pairingCode: string;
|
||||
@@ -127,8 +128,3 @@ export const getCastData = async (registration: Registration) => {
|
||||
|
||||
return JSON.parse(atob(decryptedCastData));
|
||||
};
|
||||
|
||||
const generateKeyPair = async () => {
|
||||
await _sodium.ready;
|
||||
return _sodium.crypto_box_keypair();
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ export async function decryptAndStoreToken(
|
||||
const decryptedTokenBytes = await cryptoWorker.fromB64(
|
||||
urlUnsafeB64DecryptedToken,
|
||||
);
|
||||
decryptedToken = await cryptoWorker.toURLSafeB64(decryptedTokenBytes);
|
||||
decryptedToken = await cryptoWorker.toB64URLSafe(decryptedTokenBytes);
|
||||
setData(LS_KEYS.USER, {
|
||||
...user,
|
||||
token: decryptedToken,
|
||||
|
||||
@@ -195,8 +195,8 @@ export class DedicatedCryptoWorker {
|
||||
return libsodium.toB64(data);
|
||||
}
|
||||
|
||||
async toURLSafeB64(data: Uint8Array) {
|
||||
return libsodium.toURLSafeB64(data);
|
||||
async toB64URLSafe(data: Uint8Array) {
|
||||
return libsodium.toB64URLSafe(data);
|
||||
}
|
||||
|
||||
async fromB64(string: string) {
|
||||
|
||||
@@ -340,14 +340,18 @@ export async function generateSaltToDeriveKey() {
|
||||
return await toB64(sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES));
|
||||
}
|
||||
|
||||
export async function generateKeyPair() {
|
||||
/**
|
||||
* Generate a new public/private keypair, and return their Base64
|
||||
* representations.
|
||||
*/
|
||||
export const generateKeyPair = async () => {
|
||||
await sodium.ready;
|
||||
const keyPair: sodium.KeyPair = sodium.crypto_box_keypair();
|
||||
const keyPair = sodium.crypto_box_keypair();
|
||||
return {
|
||||
privateKey: await toB64(keyPair.privateKey),
|
||||
publicKey: await toB64(keyPair.publicKey),
|
||||
privateKey: await toB64(keyPair.privateKey),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export async function boxSealOpen(
|
||||
input: string,
|
||||
@@ -398,10 +402,34 @@ export async function toB64(input: Uint8Array) {
|
||||
return sodium.to_base64(input, sodium.base64_variants.ORIGINAL);
|
||||
}
|
||||
|
||||
export async function toURLSafeB64(input: Uint8Array) {
|
||||
/** Convert a {@link Uint8Array} to a URL safe Base64 encoded string. */
|
||||
export const toB64URLSafe = async (input: Uint8Array) => {
|
||||
await sodium.ready;
|
||||
return sodium.to_base64(input, sodium.base64_variants.URLSAFE);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a {@link Uint8Array} to a URL safe Base64 encoded string.
|
||||
*
|
||||
* This differs from {@link toB64URLSafe} in that it does not append any
|
||||
* trailing padding character(s) "=" to make the resultant string's length be an
|
||||
* integer multiple of 4.
|
||||
*/
|
||||
export const toB64URLSafeNoPadding = async (input: Uint8Array) => {
|
||||
await sodium.ready;
|
||||
return sodium.to_base64(input, sodium.base64_variants.URLSAFE_NO_PADDING);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a Base64 encoded string to a {@link Uint8Array}.
|
||||
*
|
||||
* This is the converse of {@link toB64URLSafeNoPadding}, and does not expect
|
||||
* its input string's length to be a an integer multiple of 4.
|
||||
*/
|
||||
export const fromB64URLSafeNoPadding = async (input: string) => {
|
||||
await sodium.ready;
|
||||
return sodium.from_base64(input, sodium.base64_variants.URLSAFE_NO_PADDING);
|
||||
};
|
||||
|
||||
export async function fromUTF8(input: string) {
|
||||
await sodium.ready;
|
||||
|
||||
Reference in New Issue
Block a user