
import * as crypto from 'crypto'

export interface packetBuffer {
	command: number;
	uuid: string;
	buffer: Buffer;
	serial: number
}

export interface pairConnect {
	serverListen: string;
	clientListen: string;
}

export const encrypt = ( text: string, masterkey ) => {

		// random initialization vector
		var iv = crypto.randomBytes ( 12 );

		// random salt
		var salt = crypto.randomBytes ( 64 );

		// derive key: 32 byte key length - in assumption the masterkey is a cryptographic and NOT a password there is no need for
		// a large number of iterations. It may can replaced by HKDF
		var key = crypto.pbkdf2Sync ( masterkey, salt, 2145, 32, 'sha512' );

		// AES 256 GCM Mode
		var cipher = crypto.createCipheriv ( 'aes-256-gcm', key, iv );

		// encrypt the given text
		var encrypted = Buffer.concat ([ cipher.update ( Buffer.from ( text, 'utf8' )), cipher.final ()]);

		// extract the auth tag
		var tag = cipher.getAuthTag ();

		// generate output
		return Buffer.concat ([ salt, iv, tag, encrypted ])

}
/**
 * Decrypts text by given key
 * @param String base64 encoded input data
 * @param Buffer masterkey
 * @returns String decrypted (original) text
 */
export const decrypt =  ( data: Buffer, masterkey, CallBack ) => {
	if ( !data || !data.length )
		return CallBack ( new Error('null'))
	try {
		// base64 decoding

		// convert data to buffers

		var salt = data.slice ( 0, 64 );
		var iv = data.slice ( 64, 76 );
		var tag = data.slice ( 76, 92 );
		var text = data.slice ( 92 );

		// derive key using; 32 byte key length
		var key = crypto.pbkdf2Sync ( masterkey, salt , 2145, 32, 'sha512' );

		// AES 256 GCM Mode
		var decipher = crypto.createDecipheriv ( 'aes-256-gcm', key, iv );
		decipher.setAuthTag ( tag );

		// encrypt the given text
		var decrypted = decipher.update ( text ) + decipher.final ( 'utf8' );

		return CallBack ( null, decrypted )

	} catch ( e ) {
		return CallBack ( e )
	}

}

export const packetBuffer = ( bit0: number, _serial: number, id: string, buffer: Buffer ) => {
	
	const _buffer = new Buffer ( 6 )
	_buffer.fill ( 0 )
	_buffer.writeUInt8 ( bit0, 0 )
	_buffer.writeUInt32BE ( _serial, 1 )

	const uuid = new Buffer ( id, 'utf8' )
	_buffer.writeUInt8 ( id.length, 5 )
	if ( buffer && buffer.length )
		return Buffer.concat ([ _buffer, uuid, buffer ])
	return Buffer.concat ([ _buffer, uuid ])
}

export const openPacket = ( buffer: Buffer ) => {
	const idLength = buffer.readUInt8 ( 5 )
	return  {
		command: buffer.readUInt8 ( 0 ),
		serial: buffer.readUInt32BE ( 1 ),
		uuid: buffer.toString ( 'utf8', 6, 6 + idLength ),
		buffer: buffer.slice ( 6 + idLength )
	}
}
