export default class Crypto {
	constructor(key) {//{{{
		if(key === null)
			throw new Error("No key provided")

		if(typeof key === 'string')
			this.key = sjcl.codec.base64.toBits(base64_key)
		else
			this.key = key

		this.aes = new sjcl.cipher.aes(this.key)
	}//}}}

	static generate_key() {//{{{
		return sjcl.random.randomWords(8)
	}//}}}
	static pass_to_key(pass, salt = null) {//{{{
		if(salt === null)
			salt = sjcl.random.randomWords(4) // 128 bits (16 bytes)
		let key = sjcl.misc.pbkdf2(pass, salt, 10000)

		return {
			salt,
			key,
		}
	}//}}}

	encrypt(plaintext_data_in_bits, counter, return_encoded = true) {//{{{
		// 8 bytes of random data, (1 word = 4 bytes) * 2
		// with 8 bytes of byte encoded counter is used as
		// IV to guarantee a non-repeated IV (which is a catastrophe).
		// Assumes counter value is kept unique. Counter is taken from
		// Postgres sequence.
		let random_bits = sjcl.random.randomWords(2)
		let iv_bytes = sjcl.codec.bytes.fromBits(random_bits)
		for (let i = 0; i < 8; ++i) {
			let mask = 0xffn << BigInt(i*8)
			let counter_i_byte = (counter & mask) >> BigInt(i*8)
			iv_bytes[15-i] = Number(counter_i_byte)
		}
		let iv = sjcl.codec.bytes.toBits(iv_bytes)

		let encrypted = sjcl.mode['ccm'].encrypt(
			this.aes,
			plaintext_data_in_bits,
			iv,
		)

		// Returning 16 bytes (4 words) IV + encrypted data.
		if(return_encoded)
			return sjcl.codec.base64.fromBits(
				iv.concat(encrypted)
			)
		else
			return iv.concat(encrypted)
	}//}}}
	decrypt(encrypted_base64_data) {//{{{
		try {
			let encoded = sjcl.codec.base64.toBits(encrypted_base64_data)
			let iv = encoded.slice(0, 4) // in words (4 bytes), not bytes
			let encrypted_data = encoded.slice(4)
			return sjcl.mode['ccm'].decrypt(this.aes, encrypted_data, iv)
		} catch(err) {
			if(err.message == `ccm: tag doesn't match`)
				throw('Decryption failed')
			else
				throw(err)
		}
	}//}}}
}

// vim: foldmethod=marker
