mirror of
https://github.com/remotely-save/remotely-save.git
synced 2024-06-07 21:10:45 +00:00
use base64url not base32
This commit is contained in:
parent
792703f8b6
commit
1bc6ba4b01
@ -1,4 +1,4 @@
|
|||||||
import { base32, base64 } from "rfc4648";
|
import { base32, base64url } from "rfc4648";
|
||||||
import {
|
import {
|
||||||
bufferToArrayBuffer,
|
bufferToArrayBuffer,
|
||||||
arrayBufferToBuffer,
|
arrayBufferToBuffer,
|
||||||
@ -10,6 +10,8 @@ const DEFAULT_ITER = 20000;
|
|||||||
|
|
||||||
// base32.stringify(Buffer.from('Salted__'))
|
// base32.stringify(Buffer.from('Salted__'))
|
||||||
export const MAGIC_ENCRYPTED_PREFIX_BASE32 = "KNQWY5DFMRPV";
|
export const MAGIC_ENCRYPTED_PREFIX_BASE32 = "KNQWY5DFMRPV";
|
||||||
|
// base64.stringify(Buffer.from('Salted__'))
|
||||||
|
export const MAGIC_ENCRYPTED_PREFIX_BASE64URL = "U2FsdGVkX";
|
||||||
|
|
||||||
const getKeyIVFromPassword = async (
|
const getKeyIVFromPassword = async (
|
||||||
salt: Uint8Array,
|
salt: Uint8Array,
|
||||||
@ -120,7 +122,7 @@ export const encryptStringToBase32 = async (
|
|||||||
rounds,
|
rounds,
|
||||||
saltHex
|
saltHex
|
||||||
);
|
);
|
||||||
return base32.stringify(new Uint8Array(enc));
|
return base32.stringify(new Uint8Array(enc), { pad: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const decryptBase32ToString = async (
|
export const decryptBase32ToString = async (
|
||||||
@ -130,7 +132,36 @@ export const decryptBase32ToString = async (
|
|||||||
) => {
|
) => {
|
||||||
return new TextDecoder().decode(
|
return new TextDecoder().decode(
|
||||||
await decryptArrayBuffer(
|
await decryptArrayBuffer(
|
||||||
bufferToArrayBuffer(base32.parse(text)),
|
bufferToArrayBuffer(base32.parse(text, { loose: true })),
|
||||||
|
password,
|
||||||
|
rounds
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const encryptStringToBase64url = async (
|
||||||
|
text: string,
|
||||||
|
password: string,
|
||||||
|
rounds: number = DEFAULT_ITER,
|
||||||
|
saltHex: string = ""
|
||||||
|
) => {
|
||||||
|
const enc = await encryptArrayBuffer(
|
||||||
|
bufferToArrayBuffer(new TextEncoder().encode(text)),
|
||||||
|
password,
|
||||||
|
rounds,
|
||||||
|
saltHex
|
||||||
|
);
|
||||||
|
return base64url.stringify(new Uint8Array(enc), { pad: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const decryptBase64urlToString = async (
|
||||||
|
text: string,
|
||||||
|
password: string,
|
||||||
|
rounds: number = DEFAULT_ITER
|
||||||
|
) => {
|
||||||
|
return new TextDecoder().decode(
|
||||||
|
await decryptArrayBuffer(
|
||||||
|
bufferToArrayBuffer(base64url.parse(text, { loose: true })),
|
||||||
password,
|
password,
|
||||||
rounds
|
rounds
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Vault } from "obsidian";
|
import { Vault } from "obsidian";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
|
|
||||||
import { base32 } from "rfc4648";
|
import { base32, base64url } from "rfc4648";
|
||||||
import XRegExp from "xregexp";
|
import XRegExp from "xregexp";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
50
src/sync.ts
50
src/sync.ts
@ -14,6 +14,9 @@ import {
|
|||||||
decryptBase32ToString,
|
decryptBase32ToString,
|
||||||
encryptStringToBase32,
|
encryptStringToBase32,
|
||||||
MAGIC_ENCRYPTED_PREFIX_BASE32,
|
MAGIC_ENCRYPTED_PREFIX_BASE32,
|
||||||
|
decryptBase64urlToString,
|
||||||
|
encryptStringToBase64url,
|
||||||
|
MAGIC_ENCRYPTED_PREFIX_BASE64URL,
|
||||||
} from "./encrypt";
|
} from "./encrypt";
|
||||||
|
|
||||||
export type SyncStatusType =
|
export type SyncStatusType =
|
||||||
@ -85,7 +88,7 @@ export const isPasswordOk = async (
|
|||||||
}
|
}
|
||||||
const santyCheckKey = remote[0].key;
|
const santyCheckKey = remote[0].key;
|
||||||
if (santyCheckKey.startsWith(MAGIC_ENCRYPTED_PREFIX_BASE32)) {
|
if (santyCheckKey.startsWith(MAGIC_ENCRYPTED_PREFIX_BASE32)) {
|
||||||
// this is encrypted!
|
// this is encrypted using old base32!
|
||||||
// try to decrypt it using the provided password.
|
// try to decrypt it using the provided password.
|
||||||
if (password === "") {
|
if (password === "") {
|
||||||
return {
|
return {
|
||||||
@ -96,6 +99,38 @@ export const isPasswordOk = async (
|
|||||||
try {
|
try {
|
||||||
const res = await decryptBase32ToString(santyCheckKey, password);
|
const res = await decryptBase32ToString(santyCheckKey, password);
|
||||||
|
|
||||||
|
// additional test
|
||||||
|
// because iOS Safari bypasses decryption with wrong password!
|
||||||
|
if (isVaildText(res)) {
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
reason: "password_matched",
|
||||||
|
} as PasswordCheckType;
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
reason: "invalid_text_after_decryption",
|
||||||
|
} as PasswordCheckType;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
reason: "password_not_matched",
|
||||||
|
} as PasswordCheckType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (santyCheckKey.startsWith(MAGIC_ENCRYPTED_PREFIX_BASE64URL)) {
|
||||||
|
// this is encrypted using new base64url!
|
||||||
|
// try to decrypt it using the provided password.
|
||||||
|
if (password === "") {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
reason: "remote_encrypted_local_no_password",
|
||||||
|
} as PasswordCheckType;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await decryptBase64urlToString(santyCheckKey, password);
|
||||||
|
|
||||||
// additional test
|
// additional test
|
||||||
// because iOS Safari bypasses decryption with wrong password!
|
// because iOS Safari bypasses decryption with wrong password!
|
||||||
if (isVaildText(res)) {
|
if (isVaildText(res)) {
|
||||||
@ -145,7 +180,15 @@ const ensembleMixedStates = async (
|
|||||||
const remoteEncryptedKey = entry.key;
|
const remoteEncryptedKey = entry.key;
|
||||||
let key = remoteEncryptedKey;
|
let key = remoteEncryptedKey;
|
||||||
if (password !== "") {
|
if (password !== "") {
|
||||||
|
if (remoteEncryptedKey.startsWith(MAGIC_ENCRYPTED_PREFIX_BASE32)) {
|
||||||
key = await decryptBase32ToString(remoteEncryptedKey, password);
|
key = await decryptBase32ToString(remoteEncryptedKey, password);
|
||||||
|
} else if (
|
||||||
|
remoteEncryptedKey.startsWith(MAGIC_ENCRYPTED_PREFIX_BASE64URL)
|
||||||
|
) {
|
||||||
|
key = await decryptBase64urlToString(remoteEncryptedKey, password);
|
||||||
|
} else {
|
||||||
|
throw Error(`unexpected key=${remoteEncryptedKey}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const backwardMapping = await getSyncMetaMappingByRemoteKey(
|
const backwardMapping = await getSyncMetaMappingByRemoteKey(
|
||||||
remoteType,
|
remoteType,
|
||||||
@ -435,7 +478,10 @@ const dispatchOperationToActual = async (
|
|||||||
if (password !== "") {
|
if (password !== "") {
|
||||||
remoteEncryptedKey = state.remote_encrypted_key;
|
remoteEncryptedKey = state.remote_encrypted_key;
|
||||||
if (remoteEncryptedKey === undefined || remoteEncryptedKey === "") {
|
if (remoteEncryptedKey === undefined || remoteEncryptedKey === "") {
|
||||||
remoteEncryptedKey = await encryptStringToBase32(key, password);
|
// the old version uses base32
|
||||||
|
// remoteEncryptedKey = await encryptStringToBase32(key, password);
|
||||||
|
// the new version users base64url
|
||||||
|
remoteEncryptedKey = await encryptStringToBase64url(key, password);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user