Comparing version 0.0.3 to 0.0.4
{ | ||
"name": "autho", | ||
"version": "0.0.3", | ||
"version": "0.0.4", | ||
"main": "index.js", | ||
@@ -5,0 +5,0 @@ "type": "module", |
@@ -6,5 +6,7 @@ #!/usr/bin/env node | ||
import { prompt, ask } from "./utils.js"; | ||
import Config from "../sdk/config.js"; | ||
import DB from "../sdk/db.js"; | ||
import Cipher from "../sdk/cipher.js"; | ||
import createSecret from "./wizards/createSecret.js"; | ||
import getEncryptionKey from "./wizards/getEncryptionKey.js"; | ||
import getSecret from "./wizards/getSecret.js"; | ||
import Secrets from "../sdk/secrets.js"; | ||
@@ -15,31 +17,25 @@ import OTP from "../sdk/otp.js"; | ||
const getSecret = async (config) => { | ||
const existingSecrets = config.get("secrets", []); | ||
//type AppOptions = { | ||
// masterPasswordHash?: string; | ||
// masterPassword?: string; | ||
// appFolder?: string; | ||
// }; | ||
if (existingSecrets.length === 0) { | ||
throw new Error("No secrets found"); | ||
class App { | ||
constructor(options = {}) { | ||
let masterPasswordHash = | ||
options.masterPasswordHash || process.env.AUTHO_MASTER_PASSWORD_HASH; | ||
const masterPassword = options.masterPassword || process.env.AUTHO_MASTER_PASSWORD; | ||
if (!masterPasswordHash && !masterPassword) { | ||
throw new Error("Master password or master password hash is required") | ||
} | ||
masterPasswordHash = | ||
masterPasswordHash || | ||
Cipher.hash(masterPassword); | ||
this.db = new DB({ encryptionKey: masterPasswordHash }); | ||
this.secrets = new Secrets(this); | ||
} | ||
const choices = existingSecrets.map((secret) => ({ | ||
value: secret.id, | ||
name: `${secret.name} (${secret.typeOptions.username || secret.id})`, | ||
})); | ||
} | ||
const { id: secretId } = await prompt({ | ||
name: "id", | ||
message: "Secrets:", | ||
type: "list", | ||
choices, | ||
required: true, | ||
}); | ||
const secrets = new Secrets(config); | ||
const secret = await secrets.get(secretId); | ||
if (!secret) { | ||
throw new Error("Secret not found"); | ||
} | ||
return secret; | ||
}; | ||
program | ||
@@ -60,5 +56,5 @@ .name("autho") | ||
}); | ||
const masterPasswordHash = Cipher.hash(masterPassword); | ||
const config = new Config({ encryptionKey: masterPasswordHash }); | ||
const app = new App({ masterPassword }); | ||
let choices = [ | ||
@@ -76,11 +72,21 @@ { value: "create", name: "Create new secret" }, | ||
}); | ||
switch (action) { | ||
case "create": | ||
await createSecret(config, masterPasswordHash); | ||
await createSecret(app); | ||
break; | ||
case "read": | ||
const readSecret = await getSecret(config); | ||
readSecret.value = Cipher.decrypt(readSecret.value, readSecret.publicKey, masterPasswordHash) | ||
const readSecret = await getSecret(app); | ||
let encryptionKey = app.db.encryptionKey | ||
if (readSecret.protected) { | ||
encryptionKey = await getEncryptionKey() | ||
} | ||
readSecret.value = Cipher.decrypt( | ||
readSecret.value, | ||
readSecret.publicKey, | ||
encryptionKey, | ||
readSecret.signature | ||
); | ||
switch (readSecret.type) { | ||
@@ -106,8 +112,9 @@ case "password": | ||
case "delete": | ||
const deleteSecret = await getSecret(config); | ||
const secrets = new Secrets(config); | ||
await secrets.remove(deleteSecret.id) | ||
const deleteSecret = await getSecret(app); | ||
await app.secrets.remove(deleteSecret.id); | ||
console.log("Removed"); | ||
process.exit(0); | ||
break; | ||
default: | ||
console.log("Unknown action:", action); | ||
process.exit(1); | ||
} | ||
@@ -114,0 +121,0 @@ } catch (error) { |
import { prompt } from "../utils.js"; | ||
import Secrets from "../../sdk/secrets.js"; | ||
import getEncryptionKey from "./getEncryptionKey.js"; | ||
const wizard = async (config, masterPasswordHash) => { | ||
const wizard = async (app) => { | ||
const secrets = app.secrets; | ||
const info = await prompt([ | ||
@@ -19,2 +20,9 @@ { | ||
required: true, | ||
}, | ||
{ | ||
name: "protected", | ||
message: "protected:", | ||
type: "confirm", | ||
default: false, | ||
required: true, | ||
} | ||
@@ -24,3 +32,8 @@ ]); | ||
let newSecret = {}; | ||
let encryptionKey = app.db.encryptionKey | ||
if (info.protected) { | ||
encryptionKey = await getEncryptionKey(true) | ||
} | ||
switch (info.type) { | ||
@@ -43,4 +56,3 @@ case "password": | ||
newSecret = { | ||
name: info.name, | ||
type: info.type, | ||
...info, | ||
value: password.value, | ||
@@ -62,4 +74,3 @@ typeOptions: { | ||
newSecret = { | ||
name: info.name, | ||
type: info.type, | ||
...info, | ||
value: note.value, | ||
@@ -87,4 +98,3 @@ typeOptions: { | ||
newSecret = { | ||
name: info.name, | ||
type: info.type, | ||
...info, | ||
value: otp.value, | ||
@@ -98,6 +108,5 @@ typeOptions: { | ||
const secrets = new Secrets(config); | ||
await secrets.add(newSecret, masterPasswordHash); | ||
await secrets.add(newSecret, encryptionKey); | ||
}; | ||
export default wizard; |
import crypto from "crypto"; | ||
const algorithm = "aes-256-ctr"; | ||
const IV_LENGTH = 16; | ||
export default class Cipher { | ||
constructor() { } | ||
static hash(text) { | ||
const hash = crypto.createHash("sha256"); | ||
static hash(text, algorithm = "sha256") { | ||
const hash = crypto.createHash(algorithm); | ||
hash.update(text); | ||
@@ -16,3 +12,3 @@ | ||
static random(size = IV_LENGTH) { | ||
static random(size = 16) { | ||
const rnd = crypto.randomBytes(size); | ||
@@ -29,7 +25,20 @@ | ||
static encrypt(text, password) { | ||
static sign(text) { | ||
const hash = Cipher.hash(text) | ||
const signature = `${hash.substring(0, 10)}:${hash.substring(hash.length - 10)}`; | ||
return signature; | ||
} | ||
static verify(text, signature) { | ||
const expectedSignature = Cipher.sign(text) | ||
return expectedSignature === signature; | ||
} | ||
static encrypt(text, encryptionKey, algorithm = "aes-256-ctr") { | ||
const publicKey = Cipher.randomString(); | ||
let cipher = crypto.createCipheriv( | ||
algorithm, | ||
Buffer.from(password, "hex"), | ||
Buffer.from(encryptionKey, "hex"), | ||
Buffer.from(publicKey, "hex"), | ||
@@ -41,17 +50,24 @@ ); | ||
return { publicKey, encrypted }; | ||
return { publicKey, encrypted, algorithm, signature: Cipher.sign(text) }; | ||
} | ||
static decrypt(encryptedText, publicKey, password) { | ||
static decrypt(encryptedText, publicKey, encryptionKey, signature, algorithm = "aes-256-ctr") { | ||
encryptedText = Buffer.from(encryptedText, "hex"); | ||
let decipher = crypto.createDecipheriv( | ||
algorithm, | ||
Buffer.from(password, "hex"), | ||
Buffer.from(encryptionKey, "hex"), | ||
Buffer.from(publicKey, "hex") | ||
); | ||
let decrypted = decipher.update(encryptedText); | ||
decrypted = Buffer.concat([decrypted, decipher.final()]); | ||
decrypted = decrypted.toString(); | ||
return decrypted.toString(); | ||
if (!Cipher.verify(decrypted, signature)) { | ||
throw new Error("Invalid signature") | ||
} | ||
return decrypted; | ||
} | ||
} |
import { createSecretSchema } from "../models/Secret.js"; | ||
import Cipher from "./cipher.js"; | ||
import Cipher from "../sdk/cipher.js"; | ||
export default class Secrets { | ||
constructor(config) { | ||
this.config = config; | ||
constructor(app) { | ||
this.db = app.db; | ||
} | ||
get secrets() { | ||
return this.config.get("secrets", []); | ||
return this.db.get("secrets", []); | ||
} | ||
set secrets(value) { | ||
this.config.set("secrets", value); | ||
this.db.set("secrets", value); | ||
} | ||
@@ -21,3 +21,3 @@ | ||
async add(secret, password) { | ||
async add(secret, encryptionKey) { | ||
const { value, error } = createSecretSchema.validate(secret); | ||
@@ -28,7 +28,5 @@ if (error) { | ||
const { publicKey, encrypted } = Cipher.encrypt(value.value, password); | ||
value.value = encrypted; | ||
value.publicKey = publicKey; | ||
const { encrypted, ...encryption } = Cipher.encrypt(value.value, encryptionKey); | ||
this.secrets = [...this.secrets, value]; | ||
this.secrets = [...this.secrets, { ...value, ...encryption, value: encrypted }]; | ||
} | ||
@@ -35,0 +33,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Possible typosquat attack
Supply chain riskThere is a package with a similar name that is downloaded much more often.
Did you mean |
---|
auth0 |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
15691
12
400
0
4