Comparing version 0.0.2 to 0.0.3
{ | ||
"name": "autho", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"main": "index.js", | ||
"type": "module", | ||
"bin":{ | ||
"autho": "./src/cli/bin.js"}, | ||
"bin": { | ||
"autho": "src/cli/bin.js" | ||
}, | ||
"scripts": { | ||
@@ -9,0 +10,0 @@ "test": "echo \"Error: no test specified\" && exit 1" |
@@ -16,5 +16,10 @@ #!/usr/bin/env node | ||
const existingSecrets = config.get("secrets", []); | ||
if (existingSecrets.length === 0) { | ||
throw new Error("No secrets found"); | ||
} | ||
const choices = existingSecrets.map((secret) => ({ | ||
value: secret.id, | ||
name: secret.name, | ||
name: `${secret.name} (${secret.typeOptions.username || secret.id})`, | ||
})); | ||
@@ -29,3 +34,2 @@ | ||
}); | ||
const secrets = new Secrets(config); | ||
@@ -51,16 +55,10 @@ const secret = await secrets.get(secretId); | ||
: await ask({ | ||
name: "masterPassword", | ||
message: "Password:", | ||
type: "password", | ||
required: true, | ||
}); | ||
name: "masterPassword", | ||
message: "Password:", | ||
type: "password", | ||
required: true, | ||
}); | ||
const masterPasswordHash = Cipher.hash(masterPassword); | ||
const config = new Config({ encryptionKey: masterPasswordHash }); | ||
let salt = config.get(); | ||
if (!salt) { | ||
salt = Cipher.random(); | ||
config.set("salt", salt); | ||
} | ||
let choices = [ | ||
@@ -85,19 +83,29 @@ { value: "create", name: "Create new secret" }, | ||
const readSecret = await getSecret(config); | ||
switch (readSecret.type) { | ||
case "otp": | ||
const otp = new OTP(config, readSecret, masterPasswordHash); | ||
console.log(otp.generate()); | ||
setTimeout(() => { | ||
console.log("Expired"); | ||
process.exit(0); | ||
}, 30000); | ||
break; | ||
} | ||
readSecret.value = Cipher.decrypt(readSecret.value, readSecret.publicKey, masterPasswordHash) | ||
switch (readSecret.type) { | ||
case "password": | ||
console.log("Username:", readSecret.typeOptions.username); | ||
console.log("Password:", readSecret.value); | ||
break; | ||
case "note": | ||
console.log("Note:", readSecret.value); | ||
break; | ||
case "otp": | ||
const otp = new OTP(readSecret); | ||
console.log("OTP code:", otp.generate()); | ||
setTimeout(() => { | ||
console.log("Expired"); | ||
process.exit(0); | ||
}, 30000); | ||
break; | ||
} | ||
break; | ||
case "delete": | ||
const deleteSecret = getSecret(config); | ||
const secrets = new Secrets(config); | ||
secrets.remove(deleteSecret.id) | ||
const deleteSecret = await getSecret(config); | ||
const secrets = new Secrets(config); | ||
await secrets.remove(deleteSecret.id) | ||
console.log("Removed"); | ||
process.exit(0); | ||
break; | ||
@@ -104,0 +112,0 @@ } |
@@ -15,5 +15,5 @@ import { prompt } from "../utils.js"; | ||
message: "type:", | ||
type: "choice", | ||
default: "otp", | ||
choices: ["otp"], | ||
type: "list", | ||
default: "password", | ||
choices: ["password", "otp", "note"], | ||
required: true, | ||
@@ -26,25 +26,67 @@ } | ||
switch (info.type) { | ||
case "password": | ||
const password = await prompt([ | ||
{ | ||
name: "username", | ||
message: "username:", | ||
type: "input", | ||
required: true, | ||
}, | ||
{ | ||
name: "value", | ||
message: "password:", | ||
type: "password", | ||
required: true, | ||
}, | ||
]); | ||
newSecret = { | ||
name: info.name, | ||
type: info.type, | ||
value: password.value, | ||
typeOptions: { | ||
username: password.username, | ||
}, | ||
}; | ||
break; | ||
case "note": | ||
const note = await prompt([ | ||
{ | ||
name: "value", | ||
message: "note:", | ||
type: "password", | ||
required: true, | ||
}, | ||
]); | ||
newSecret = { | ||
name: info.name, | ||
type: info.type, | ||
value: note.value, | ||
typeOptions: { | ||
}, | ||
}; | ||
break; | ||
case "otp": | ||
const secret = await prompt([ | ||
{ | ||
name: "username", | ||
message: "username:", | ||
type: "input", | ||
required: true, | ||
}, | ||
{ | ||
name: "value", | ||
message: "value:", | ||
type: "password", | ||
required: true, | ||
}, | ||
]); | ||
newSecret = { | ||
name: info.name, | ||
type: info.type, | ||
value: secret.value, | ||
typeOptions: { | ||
username: secret.username, | ||
}, | ||
}; | ||
const otp = await prompt([ | ||
{ | ||
name: "username", | ||
message: "username:", | ||
type: "input", | ||
required: true, | ||
}, | ||
{ | ||
name: "value", | ||
message: "value:", | ||
type: "password", | ||
required: true, | ||
}, | ||
]); | ||
newSecret = { | ||
name: info.name, | ||
type: info.type, | ||
value: otp.value, | ||
typeOptions: { | ||
username: otp.username, | ||
}, | ||
}; | ||
break; | ||
@@ -51,0 +93,0 @@ } |
@@ -5,6 +5,6 @@ import Joi from 'joi'; | ||
export const createSecretSchema = Joi.object({ | ||
id: Joi.string().default(Cipher.random()), | ||
id: Joi.string().default(Cipher.randomString()), | ||
protected: Joi.boolean().default(false), // ask for password | ||
name: Joi.string().required(), | ||
type: Joi.string().required().valid('otp'), | ||
type: Joi.string().required().valid('otp', 'password', 'note'), | ||
value: Joi.string().required(), | ||
@@ -11,0 +11,0 @@ typeOptions: Joi.object().default({}), |
import crypto from "crypto"; | ||
const algorithm = "aes-256-ctr"; | ||
const IV_LENGTH = 16; | ||
export default class Cipher { | ||
constructor(config) { | ||
this.config = config; | ||
this.salt = config.get("salt", ""); | ||
} | ||
constructor() { } | ||
@@ -16,4 +16,4 @@ static hash(text) { | ||
static random() { | ||
const rnd = crypto.randomBytes(16).toString("hex"); | ||
static random(size = IV_LENGTH) { | ||
const rnd = crypto.randomBytes(size); | ||
@@ -23,16 +23,18 @@ return rnd; | ||
static createKey(salt="") { | ||
const rnd = Cipher.random(salt) | ||
static randomString() { | ||
const rnd = Cipher.random().toString("hex"); | ||
return Cipher.hash(salt+rnd); | ||
return rnd; | ||
} | ||
encrypt(text, password) { | ||
const publicKey = Cipher.createKey(this.salt); | ||
const cipher = crypto.createCipher( | ||
"aes-256-cbc", | ||
publicKey + password | ||
static encrypt(text, password) { | ||
const publicKey = Cipher.randomString(); | ||
let cipher = crypto.createCipheriv( | ||
algorithm, | ||
Buffer.from(password, "hex"), | ||
Buffer.from(publicKey, "hex"), | ||
); | ||
let encrypted = cipher.update(text, "utf8", "hex"); | ||
encrypted += cipher.final("hex"); | ||
let encrypted = cipher.update(text); | ||
encrypted = Buffer.concat([encrypted, cipher.final()]); | ||
encrypted = encrypted.toString("hex"); | ||
@@ -42,12 +44,14 @@ return { publicKey, encrypted }; | ||
decrypt(encryptedText, publicKey, password) { | ||
const decipher = crypto.createDecipher( | ||
"aes-256-cbc", | ||
publicKey + password | ||
static decrypt(encryptedText, publicKey, password) { | ||
encryptedText = Buffer.from(encryptedText, "hex"); | ||
let decipher = crypto.createDecipheriv( | ||
algorithm, | ||
Buffer.from(password, "hex"), | ||
Buffer.from(publicKey, "hex") | ||
); | ||
let decrypted = decipher.update(encryptedText, "hex", "utf8"); | ||
decrypted += decipher.final("utf8"); | ||
let decrypted = decipher.update(encryptedText); | ||
decrypted = Buffer.concat([decrypted, decipher.final()]); | ||
return decrypted; | ||
return decrypted.toString(); | ||
} | ||
} |
import Conf from "conf"; | ||
const { AUTHO_ENCRYPTION_KEY = "", AUTHO_NAME = 'default' } = process.env; | ||
export default class Config { | ||
constructor({ | ||
encryptionKey, | ||
configName='default' | ||
encryptionKey = AUTHO_ENCRYPTION_KEY, | ||
configName = AUTHO_NAME | ||
}) { | ||
@@ -8,0 +10,0 @@ this.config = new Conf({ projectName: "autho", encryptionKey, configName }); |
import * as OTPAuth from "otpauth"; | ||
import Cipher from "./cipher.js"; | ||
export default class OTP { | ||
constructor(config, secret, password) { | ||
const cipher = new Cipher(config); | ||
constructor(secret) { | ||
const options = { | ||
issuer: "ACME", | ||
issuer: "Autho", | ||
label: secret.name, | ||
@@ -13,3 +12,3 @@ algorithm: "SHA1", | ||
period: 30, | ||
secret: cipher.decrypt(secret.value, secret.publicKey, password), | ||
secret: secret.value, | ||
}; | ||
@@ -16,0 +15,0 @@ |
@@ -7,3 +7,2 @@ import { createSecretSchema } from "../models/Secret.js"; | ||
this.config = config; | ||
this.cipher = new Cipher(config); | ||
} | ||
@@ -29,3 +28,3 @@ | ||
const { publicKey, encrypted } = this.cipher.encrypt(value.value, password); | ||
const { publicKey, encrypted } = Cipher.encrypt(value.value, password); | ||
value.value = encrypted; | ||
@@ -40,2 +39,6 @@ value.publicKey = publicKey; | ||
} | ||
async clear() { | ||
this.secrets = []; | ||
} | ||
} |
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 1 instance in 1 package
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
13351
327
2