Update
This commit is contained in:
38
repl.ts
38
repl.ts
@ -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
27
src/crypto.ts
Normal 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
|
||||
}
|
26
src/types.ts
26
src/types.ts
@ -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
|
||||
|
33
src/vault.ts
33
src/vault.ts
@ -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 */
|
||||
|
Reference in New Issue
Block a user