This commit is contained in:
aet
2021-07-11 19:26:33 -04:00
parent 69fd89ad6b
commit 99fa963fc0
4 changed files with 98 additions and 26 deletions

38
repl.ts
View File

@ -1,4 +1,5 @@
#!/usr/bin/env ts-node-transpile-only
import fs from "fs"
import chalk from "chalk"
import prompts from "prompts"
import { OnePassword } from "./src/index"
@ -7,24 +8,35 @@ async function main(args: string[]) {
const instance = new OnePassword({ path: args[0] })
const profiles = await instance.getProfileNames()
const { profile } = await prompts({
type: "select",
name: "profile",
choices: profiles.map(t => ({ title: t, value: t })),
message: "Which vault?",
})
// const { profile } = await prompts({
// type: "select",
// name: "profile",
// choices: profiles.map(t => ({ title: t, value: t })),
// message: "Which vault?",
// })
console.log(chalk`You have chosen {green ${profile}}.`)
// console.log(chalk`You have chosen {green ${profile}}.`)
const profile = "default"
const vault = await instance.getProfile(profile)
const { password } = await prompts({
type: "invisible",
name: "password",
message: "Master Password?",
})
// const { password } = await prompts({
// type: "invisible",
// name: "password",
// message: "Master Password?",
// })
const password = "freddy"
vault.unlock(password)
console.log(vault.overviews.values())
const d = vault.decryptAttachment(
fs.readFileSync(
"./freddy-2013-12-04.opvault/default/1C7D72EFA19A4EE98DB7A9661D2F5732_3B94A1F475014E27BFB00C99A42214DF.attachment"
)
)
fs.writeFileSync("./test", d)
// console.log(vault.overviews.values())
}
main(process.argv.slice(2))

27
src/crypto.ts Normal file
View File

@ -0,0 +1,27 @@
import { webcrypto } from "crypto"
declare module "crypto" {
export const webcrypto: Crypto
}
async function pbkdf2(password: string, salt: string, iterations = 1000, length = 256) {
const encoder = new TextEncoder()
const key = await webcrypto.subtle.importKey(
"raw",
encoder.encode(password),
"PBKDF2",
false,
["deriveBits"]
)
const bits = await webcrypto.subtle.deriveBits(
{
name: "PBKDF2",
hash: "SHA-512",
salt: encoder.encode(salt),
iterations,
},
key,
length
)
return bits
}

View File

@ -1,32 +1,46 @@
import type { FieldType } from "./models"
type integer = number
export interface Profile {
lastUpdatedBy: "Dropbox"
/** Unix seconds */
updatedAt: number
updatedAt: integer
profileName: string
salt: string // base64
masterKey: string // base64
iterations: number // 50000
iterations: integer // 50000
uuid: string // 32 chars
overviewKey: string // "b3B...IMO52D"
createdAt: number // Unix seconds
createdAt: integer // Unix seconds
}
export interface EncryptedItem {
category: string // "001"
/** Unix seconds */
created: number
created: integer
d: string // "b3BkYXRhMbt"
folder: string // 32 chars
hmac: string // base64
k: string // base64
o: string // base64
tx: number // Unix seconds
updated: number // Unix seconds
tx: integer // Unix seconds
updated: integer // Unix seconds
uuid: string // 32 chars
}
export interface AttachmentMetadata {
itemUUID: string
contentSize: integer
external: boolean
updatedAt: integer
txTimestamp: integer
/** Base64 encoded OPData */
overview: string
createdAt: integer
uuid: string
}
export type TextField = {
type: FieldType.Text
value: string

View File

@ -4,7 +4,14 @@ import type { IFileSystem } from "./fs"
import { OnePasswordFileManager } from "./fs"
import { i18n } from "./i18n"
import type { Profile, Band, Overview, EncryptedItem, Item } from "./types"
import type {
Profile,
Band,
Overview,
EncryptedItem,
Item,
AttachmentMetadata,
} from "./types"
type FoldersMap = { [uuid: string]: Band }
@ -89,11 +96,11 @@ export class Vault {
unlock(masterPassword: string) {
const profile = this.#profile
const derivedKey = crypto.pbkdf2Sync(
masterPassword,
toBuffer(profile.salt),
profile.iterations,
/* password */ masterPassword,
/* salt */ toBuffer(profile.salt),
/* iterations */ profile.iterations,
/* keylen */ 64,
"sha512"
/* digest */ "sha512"
)
const cipher = splitPlainText(derivedKey)
@ -113,6 +120,10 @@ export class Vault {
return this
}
decryptAttachment(buffer: Buffer) {
return decryptAttachment(buffer, this.#master!)
}
/**
* Remove derived keys stored within the class instance.
*/
@ -175,7 +186,10 @@ function decryptItem(item: EncryptedItem, master: Cipher): Item {
}
function decryptAttachment(item: Buffer, master: Cipher) {
invariant(item.slice(0, 7).toString("utf-8") === "OPCLDA")
invariant(
item.slice(0, 6).toString("utf-8") === "OPCLDA",
"Attachment must start with OPCLDA"
)
invariant(
item.readIntLE(7, 1) === 1,
"The version for this attachment file format is not supported."
@ -184,8 +198,13 @@ function decryptAttachment(item: Buffer, master: Cipher) {
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 metadata: AttachmentMetadata = JSON.parse(
item.slice(16, 16 + metadataSize).toString("utf-8")
)
const icondata = item.slice(16 + metadataSize, 16 + metadataSize + iconSize)
console.log(icondata.slice(0, 8).toString())
const iconData = decryptOPData(icondata, master)
return iconData
}
/** Encryption and MAC */