Old solution below
I found a better solution, it was painful to learn how to do this, but I did eventually get it working. I'm using the node C++ implementation of argon instead and it no longer echos the minified script into console
This is setup as argon/argon2node.ts, and requires the argon2 node library. I think now that I got this working if I wanted to switch to the rust version or something like that I could probably work that out. It's mostly about figuring out exactly where the parameters need to go, since sometimes the names are a little different and you have to convert various parameters around.
import { Argon2Type, Argon2Version } from "kdbxweb/dist/types/crypto/crypto-engine";
import argon from 'argon2';
export default async function argon2(
password: ArrayBuffer,
salt: ArrayBuffer,
memory: number,
iterations: number,
length: number,
parallelism: number,
type: Argon2Type,
version: Argon2Version
): Promise<ArrayBuffer> {
try {
//https://github.com/keeweb/kdbxweb/blob/master/test/test-support/argon2.ts - reviewed this and eventually figured out how to switch to the C++ implementation below after much pain
const hashArr = new Uint8Array(await argon.hash(
Buffer.from(new Uint8Array(password)), {
timeCost: iterations,
memoryCost: memory,
parallelism: parallelism,
version: version,
type: type,
salt: Buffer.from(new Uint8Array(salt)),
raw: true
}
));
return Promise.resolve(hashArr);
} catch (e) {
return Promise.reject(e);
}
}
And below is my odbc credentials lookup based on it
import * as fs from 'fs';
import * as kdbx from 'kdbxweb';
import argon2 from './argon/argon2node';
import * as byteUtils from 'kdbxweb/lib/utils/byte-utils';
export default async(title : string) => {
try {
kdbx.CryptoEngine.setArgon2Impl(argon2);
const readBuffer = byteUtils.arrayToBuffer(fs.readFileSync('./SQL/credentials/credentials.kdbx'));
const database = await kdbx.Kdbx.load(
readBuffer,
new kdbx.Credentials(kdbx.ProtectedValue.fromString('CredentialsStorage1!'))
);
let result;
database.getDefaultGroup().entries.forEach((e) => {
if(e.fields.get('Title') === title) {
const password = (<kdbx.ProtectedValue>e.fields.get('Password')).getText();
const user = <string>e.fields.get('UserName');
result = `UID=${user};PWD=${password}`;
return;
}
});
return result;
} catch(e : any) {
throw e;
}
}
To resolve this, I had to do a bit of reading to understand buffers and arraybuffers and such from the documentation a bit, which wasn't easy but I eventually figured it out and created below testing reading and writing entries and such. I still have a bit to learn but this is close enough I thought it worth sharing for anyone else who may try to use this
I also had to get a copy of argon2-asm.min.js and argon2.ts which I pulled from the github for keeweb which is built with this library.
import * as fs from 'fs';
import * as kdbx from 'kdbxweb';
import { argon2 } from './argon/argon2';
function toArrayBuffer(buffer : Buffer) {
return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
}
function toBuffer(byteArray : ArrayBuffer) {
return Buffer.from(byteArray);
}
(async() => {
try {
kdbx.CryptoEngine.setArgon2Impl(argon2);
fs.unlinkSync('./SQL/credentials/credentials.kdbx');
const database = kdbx.Kdbx.create(new kdbx.Credentials(kdbx.ProtectedValue.fromString('test')),'credentials');
const entry = database.createEntry(database.getDefaultGroup());
entry.fields.set('Title','odbc');
entry.fields.set('Password',kdbx.ProtectedValue.fromString('test'));
const data = await database.save();
fs.writeFileSync('./SQL/credentials/credentials.kdbx',new DataView(data));
const readData = toArrayBuffer(fs.readFileSync('./SQL/credentials/credentials.kdbx'));
console.log('hithere');
const read = await kdbx.Kdbx.load(
readData,
new kdbx.Credentials(kdbx.ProtectedValue.fromString('test'))
);
console.log('bye');
console.log(read.getDefaultGroup().entries[0].fields.get('Title'));
const protectedPass = <kdbx.ProtectedValue>read.getDefaultGroup().entries[0].fields.get('Password');
console.log(
new kdbx.ProtectedValue(
protectedPass.value,
protectedPass.salt
).getText()
);
} catch (e :any) {
console.error(e);
throw e;
}
})();
Things that I don't grasp I'd like to understand better include why the argon implementation isn't built-in. He says " Due to complex calculations, you have to implement it manually " but this just seems odd. Perhaps not appropriate for this forum, but would be nice to know about alternatives if this is slow or something.