Start working on attachment decryption

This commit is contained in:
aet 2021-07-11 18:01:34 -04:00
parent 845c9865de
commit 69fd89ad6b
4 changed files with 53 additions and 9 deletions

View File

@ -3,7 +3,7 @@
## Testing
```sh
wget -qO- https://cache.agilebits.com/security-kb/freddy-2013-12-04.tar.gz | tar xvz -
wget -qO- https://cache.agilebits.com/security-kb/freddy-2013-12-04.tar.gz | tar xvz
mv onepassword_data freddy-2013-12-04.opvault
pnpm run test
```

11
src/errors.ts Normal file
View File

@ -0,0 +1,11 @@
export abstract class OPVaultError extends Error {}
export class AssertionError extends OPVaultError {}
export class HMACAssertionError extends AssertionError {}
export function invariant(condition: any, message?: string): asserts condition {
if (!condition) {
throw new AssertionError(message)
}
}

View File

@ -1,4 +1,4 @@
import invariant from "tiny-invariant"
import { invariant } from "./errors"
export enum Category {
Login = 1,

View File

@ -1,4 +1,5 @@
import * as crypto from "crypto"
import { HMACAssertionError, invariant } from "./errors"
import type { IFileSystem } from "./fs"
import { OnePasswordFileManager } from "./fs"
import { i18n } from "./i18n"
@ -44,7 +45,9 @@ export class Vault {
*/
static async of(path: string, profileName = "default", fs: IFileSystem) {
const files = new OnePasswordFileManager(fs, path, profileName)
const profile = JSON.parse(stripText(await files.getProfile(), "var profile=", ";"))
const profile = JSON.parse(
stripText(await files.getProfile(), /^var profile\s*=/, ";")
)
const folders = JSON.parse(stripText(await files.getFolders(), "loadFolders(", ");"))
const bands = new Map<string, Band>()
@ -111,7 +114,7 @@ export class Vault {
}
/**
* Remove derived keys within the class instance.
* Remove derived keys stored within the class instance.
*/
lock() {
this.#master = null!
@ -171,6 +174,20 @@ function decryptItem(item: EncryptedItem, master: Cipher): Item {
return JSON.parse(detail.toString("utf-8"))
}
function decryptAttachment(item: Buffer, master: Cipher) {
invariant(item.slice(0, 7).toString("utf-8") === "OPCLDA")
invariant(
item.readIntLE(7, 1) === 1,
"The version for this attachment file format is not supported."
)
const metadataSize = item.readIntLE(8, 2)
const iconSize = item.readIntLE(12, 3)
const metadata = JSON.parse(item.slice(16, 16 + metadataSize).toString("utf-8"))
const icondata = item.slice(16 + metadataSize, 16 + metadataSize + iconSize)
}
/** Encryption and MAC */
interface Cipher {
/** Encryption key */
@ -179,9 +196,27 @@ interface Cipher {
hmac: Buffer
}
function stripText(text: string, prefix: string, suffix: string) {
if (text.startsWith(prefix)) text = text.slice(prefix.length)
if (text.endsWith(suffix)) text = text.slice(0, -suffix.length)
function stripText(text: string, prefix: string | RegExp, suffix: string | RegExp) {
if (typeof prefix === "string") {
if (text.startsWith(prefix)) {
text = text.slice(prefix.length)
}
} else {
const prefixMatch = text.match(prefix)
if (prefixMatch) {
text = text.slice(prefixMatch[0].length)
}
}
if (typeof suffix === "string") {
if (text.endsWith(suffix)) {
text = text.slice(0, -suffix.length)
}
} else {
const suffixMatch = text.match(suffix)
if (suffixMatch) {
text = text.slice(0, -suffixMatch[0].length)
}
}
return text
}
@ -190,8 +225,6 @@ const splitPlainText = (derivedKey: Buffer): Cipher => ({
hmac: derivedKey.slice(32, 64),
})
class HMACAssertionError extends Error {}
function decryptOPData(cipherText: Buffer, cipher: Cipher) {
const key = cipherText.slice(0, -32)
assertHMac(key, cipher.hmac, cipherText.slice(-32))