<commit_before>/// <reference path="../typings/DefinitelyTyped/q/Q.d.ts"/>

import atob = require('atob');
import btoa = require('btoa');
import Q = require('q');
import underscore = require('underscore');

import collectionutil = require('./base/collectionutil');
import crypto = require('./onepass_crypto');
import key_agent = require('./key_agent');
import key_value_store = require('./base/key_value_store');
import local_store = require('./local_store');
import stringutil = require('./base/stringutil');
import testLib = require('./test');

class FakeKeyValueStore implements key_value_store.Store {
	private items: collectionutil.PMap<string,any>;

	constructor() {
		this.items = new collectionutil.PMap<string,any>();
	}

	set<T>(key: string, value: T) {
		this.items.set(key, value);
		return Q<void>(null);
	}

	get<T>(key: string) {
		return Q(<T>this.items.get(key));
	}

	remove(key: string) {
		this.items.delete(key);
		return Q<void>(null);
	}

	list(prefix: string = '') {
		var keys: string[] = [];
		this.items.forEach((value, key) => {
			if (stringutil.startsWith(key, prefix)) {
				keys.push(key);
			}
		});
		keys.sort();
		return Q(keys);
	}
}

function generateKey(password: string, iterations: number) : key_agent.Key {
	var masterKey = crypto.randomBytes(1024);
	var salt = crypto.randomBytes(8);
	var derivedKey = key_agent.keyFromPasswordSync(password, salt, iterations);
	var encryptedKey = key_agent.encryptKey(derivedKey, masterKey);

	var key: key_agent.Key = {
		format: key_agent.KeyFormat.AgileKeychainKey,
		identifier: crypto.newUUID(),
		data: btoa('Salted__' + salt + encryptedKey.key),
		iterations: iterations,
		validation: btoa(encryptedKey.validation)
	};
	return key;
}

testLib.addAsyncTest('save and load keys', (assert) => {
	var keyAgent = new key_agent.SimpleKeyAgent();

	var stores = new collectionutil.PMap<string, key_value_store.Store>();
	var kvStoreFactory = (name: string) => {
		var store = stores.get(name);
		if (!store) {
			store = new FakeKeyValueStore();
			stores.set(name, store);
		}
		return store;
	};

	var masterKey = generateKey('testpass', 100);
	var store = new local_store.Store(kvStoreFactory, keyAgent);
	var params: key_agent.CryptoParams = {
		algo: key_agent.CryptoAlgorithm.AES128_OpenSSLKey
	};

	return store.saveKeys([masterKey]).then(() => {
		return store.listKeys();
	}).then((keys) => {
		assert.equal(keys.length, 1);
		return store.unlock('testpass');
	}).then(() => {
		return keyAgent.encrypt(masterKey.identifier, 'testcontent', params);
	}).then((encrypted) => {
		return keyAgent.decrypt(masterKey.identifier, encrypted, params);
	}).then((plainText) => {
		assert.equal(plainText, 'testcontent');

		// reset the store and try unlocking again
		store = new local_store.Store(kvStoreFactory, keyAgent);
		return keyAgent.forgetKeys();
	}).then(() => {
		return store.unlock('testpass');
	}).then(() => {
		return keyAgent.encrypt(masterKey.identifier, 'testcontent2', params);
	}).then((encrypted) => {
		return keyAgent.decrypt(masterKey.identifier, encrypted, params);
	}).then((plainText) => {
		assert.equal(plainText, 'testcontent2');
	});
})

testLib.start();
<commit_msg>Refactor setup of common objects for local store tests<commit_after>/// <reference path="../typings/DefinitelyTyped/q/Q.d.ts"/>

import atob = require('atob');
import btoa = require('btoa');
import Q = require('q');
import underscore = require('underscore');

import collectionutil = require('./base/collectionutil');
import crypto = require('./onepass_crypto');
import key_agent = require('./key_agent');
import key_value_store = require('./base/key_value_store');
import local_store = require('./local_store');
import stringutil = require('./base/stringutil');
import testLib = require('./test');

class FakeKeyValueStore implements key_value_store.Store {
	private items: collectionutil.PMap<string,any>;

	constructor() {
		this.items = new collectionutil.PMap<string,any>();
	}

	set<T>(key: string, value: T) {
		this.items.set(key, value);
		return Q<void>(null);
	}

	get<T>(key: string) {
		return Q(<T>this.items.get(key));
	}

	remove(key: string) {
		this.items.delete(key);
		return Q<void>(null);
	}

	list(prefix: string = '') {
		var keys: string[] = [];
		this.items.forEach((value, key) => {
			if (stringutil.startsWith(key, prefix)) {
				keys.push(key);
			}
		});
		keys.sort();
		return Q(keys);
	}
}

interface Env {
	masterPass: string;
	masterKey: key_agent.Key;
	keyAgent: key_agent.SimpleKeyAgent;
	stores: collectionutil.PMap<string, key_value_store.Store>;
	storeFactory: (name: string) => key_value_store.Store;
}

// TODO - Move to onepass_crypto
function generateKey(password: string, iterations: number) : key_agent.Key {
	var masterKey = crypto.randomBytes(1024);
	var salt = crypto.randomBytes(8);
	var derivedKey = key_agent.keyFromPasswordSync(password, salt, iterations);
	var encryptedKey = key_agent.encryptKey(derivedKey, masterKey);

	var key: key_agent.Key = {
		format: key_agent.KeyFormat.AgileKeychainKey,
		identifier: crypto.newUUID(),
		data: btoa('Salted__' + salt + encryptedKey.key),
		iterations: iterations,
		validation: btoa(encryptedKey.validation)
	};
	return key;
}

function setupEnv() : Env {
	var keyAgent = new key_agent.SimpleKeyAgent();
	var stores = new collectionutil.PMap<string, key_value_store.Store>();
	var kvStoreFactory = (name: string) => {
		var store = stores.get(name);
		if (!store) {
			store = new FakeKeyValueStore();
			stores.set(name, store);
		}
		return store;
	};

	var masterPass = '<PASSWORD>';
	var masterKey = generateKey(masterPass, 100);

	return {
		masterPass: <PASSWORD>,
		masterKey: masterKey,
		keyAgent: keyAgent,
		stores: stores,
		storeFactory: kvStoreFactory
	};
}

testLib.addAsyncTest('save and load keys', (assert) => {
	var env = setupEnv();

	var store = new local_store.Store(env.storeFactory, env.keyAgent);
	var params: key_agent.CryptoParams = {
		algo: key_agent.CryptoAlgorithm.AES128_OpenSSLKey
	};

	return store.saveKeys([env.masterKey]).then(() => {
		return store.listKeys();
	}).then((keys) => {
		assert.equal(keys.length, 1);
		return store.unlock(env.masterPass);
	}).then(() => {
		return env.keyAgent.encrypt(env.masterKey.identifier, 'testcontent', params);
	}).then((encrypted) => {
		return env.keyAgent.decrypt(env.masterKey.identifier, encrypted, params);
	}).then((plainText) => {
		assert.equal(plainText, 'testcontent');

		// reset the store and try unlocking again
		store = new local_store.Store(env.storeFactory, env.keyAgent);
		return env.keyAgent.forgetKeys();
	}).then(() => {
		return store.unlock(env.masterPass);
	}).then(() => {
		return env.keyAgent.encrypt(env.masterKey.identifier, 'testcontent2', params);
	}).then((encrypted) => {
		return env.keyAgent.decrypt(env.masterKey.identifier, encrypted, params);
	}).then((plainText) => {
		assert.equal(plainText, 'testcontent2');
	});
});

testLib.start();

{'fraction_non_alphanumeric': 0.1635419671185463, 'fraction_numerical': 0.004038073262186328, 'mean_word_length': 4.978448275862069, 'pattern_counts': {'":': 0, '<': 24, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6165198', 'n_tokens_mistral': 2441, 'n_tokens_neox': 2288, 'n_words': 471}