/** Writes 4 byte wallet version. This is in its own function because
  * it's used by both newWallet() and changeEncryptionKey().
  * \return See #NonVolatileReturnEnum.
  */
static NonVolatileReturn writeWalletVersion(void)
{
	uint8_t buffer[4];

	if (isEncryptionKeyNonZero())
	{
		writeU32LittleEndian(buffer, VERSION_IS_ENCRYPTED);
	}
	else
	{
		writeU32LittleEndian(buffer, VERSION_UNENCRYPTED);
	}
	return nonVolatileWrite(buffer, OFFSET_VERSION, 4);
}
/** Wrapper around nonVolatileRead() which also decrypts data
  * using xexDecrypt(). Because this uses encryption, it is much slower
  * than nonVolatileRead(). The parameters and return values are identical
  * to that of nonVolatileRead().
  * \param data A pointer to the buffer which will receive the data.
  * \param address Byte offset specifying where in non-volatile storage to
  *                start reading from.
  * \param length The number of bytes to read.
  * \return See #NonVolatileReturnEnum for return values.
  */
NonVolatileReturn encryptedNonVolatileRead(uint8_t *data, uint32_t address, uint8_t length)
{
	uint32_t block_start;
	uint32_t block_end;
	uint8_t block_offset;
	uint8_t ciphertext[16];
	uint8_t plaintext[16];
	uint8_t n[16];
	NonVolatileReturn r;

	block_start = address & 0xfffffff0;
	block_offset = (uint8_t)(address & 0x0000000f);
	block_end = (address + length - 1) & 0xfffffff0;

	memset(n, 0, 16);
	for (; block_start <= block_end; block_start += 16)
	{
		r = nonVolatileRead(ciphertext, block_start, 16);
		if (r != NV_NO_ERROR)
		{
			return r;
		}
		writeU32LittleEndian(n, block_start);
		xexDecrypt(plaintext, ciphertext, n, 1, nv_storage_tweak_key, nv_storage_encrypt_key);
		while (length && block_offset < 16)
		{
			*data++ = plaintext[block_offset++];
			length--;
		}
		block_offset = 0;
	}

	return NV_NO_ERROR;
}
/** Generate a new address using the deterministic private key generator.
  * \param out_address The new address will be written here (if everything
  *                    goes well). This must be a byte array with space for
  *                    20 bytes.
  * \param out_public_key The public key corresponding to the new address will
  *                       be written here (if everything goes well).
  * \return The address handle of the new address on success,
  *         or #BAD_ADDRESS_HANDLE if an error occurred.
  *         Use walletGetLastError() to get more detail about an error.
  */
AddressHandle makeNewAddress(uint8_t *out_address, PointAffine *out_public_key)
{
	uint8_t buffer[4];

	if (!wallet_loaded)
	{
		last_error = WALLET_NOT_THERE;
		return BAD_ADDRESS_HANDLE;
	}
#ifdef TEST_WALLET
	if (num_addresses == MAX_TESTING_ADDRESSES)
#else
	if (num_addresses == MAX_ADDRESSES)
#endif // #ifdef TEST_WALLET
	{
		last_error = WALLET_FULL;
		return BAD_ADDRESS_HANDLE;
	}
	num_addresses++;
	writeU32LittleEndian(buffer, num_addresses);
	if (encryptedNonVolatileWrite(buffer, OFFSET_NUM_ADDRESSES, 4) != NV_NO_ERROR)
	{
		last_error = WALLET_WRITE_ERROR;
		return BAD_ADDRESS_HANDLE;
	}
	last_error = getAddressAndPublicKey(out_address, out_public_key, num_addresses);
	if (last_error != WALLET_NO_ERROR)
	{
		return BAD_ADDRESS_HANDLE;
	}
	else
	{
		return num_addresses;
	}
}
// Get a byte from the serial link, sending an acknowledgement if required.
static uint8_t receiveByte(void)
{
    uint8_t ack_buffer[5];
    uint8_t buffer;

    read(fd_serial, &buffer, 1);
    rx_bytes_to_ack--;
    if (!rx_bytes_to_ack)
    {
        rx_bytes_to_ack = RX_ACKNOWLEDGE_INTERVAL;
        ack_buffer[0] = 0xff;
        writeU32LittleEndian(&(ack_buffer[1]), rx_bytes_to_ack);
        write(fd_serial, ack_buffer, 5);
    }
    return buffer;
}
/** Translates a return value from one of the wallet functions into a response
  * packet to be written to the stream. If the wallet return value indicates
  * success, a payload can be included with the packet. Otherwise, if the
  * wallet return value indicates failure, the payload is a text error message
  * describing how the wallet function failed.
  * \param r The return value from the wallet function.
  * \param length The length of the success payload (use 0 for no payload) in
  *               number of bytes.
  * \param data A byte array holding the data of the success payload.
  *             Use #NULL for no payload.
  */
static void translateWalletError(WalletErrors r, uint8_t length, uint8_t *data)
{
	uint8_t buffer[4];

	if (r == WALLET_NO_ERROR)
	{
		streamPutOneByte(PACKET_TYPE_SUCCESS); // type
		writeU32LittleEndian(buffer, length);
		writeBytesToStream(buffer, 4); // length
		writeBytesToStream(data, length); // value
	}
	else
	{
		writeString(STRINGSET_WALLET, (uint8_t)r, PACKET_TYPE_FAILURE);
	}
}
/** Sends a packet with a string as payload.
  * \param set See getString().
  * \param spec See getString().
  * \param command The type of the packet, as defined in the file PROTOCOL.
  */
static void writeString(StringSet set, uint8_t spec, uint8_t command)
{
	uint8_t buffer[4];
	uint8_t one_char;
	uint16_t length;
	uint16_t i;

	streamPutOneByte(command); // type
	length = getStringLength(set, spec);
	writeU32LittleEndian(buffer, length);
	writeBytesToStream(buffer, 4); // length
	for (i = 0; i < length; i++)
	{
		one_char = (uint8_t)getString(set, spec, i);
		streamPutOneByte(one_char); // value
	}
}
/** Wrapper around nonVolatileWrite() which also encrypts data
  * using xexEncrypt(). Because this uses encryption, it is much slower
  * than nonVolatileWrite(). The parameters and return values are identical
  * to that of nonVolatileWrite().
  * \param data A pointer to the data to be written.
  * \param partition The partition to write to. Must be one of #NVPartitions.
  * \param address Byte offset specifying where in the partition to
  *                start writing to.
  * \param length The number of bytes to write.
  * \return See #NonVolatileReturnEnum for return values.
  * \warning Writes may be buffered; use nonVolatileFlush() to be sure that
  *          data is actually written to non-volatile storage.
  */
NonVolatileReturn encryptedNonVolatileWrite(uint8_t *data, NVPartitions partition, uint32_t address, uint32_t length)
{
	uint32_t block_start;
	uint32_t block_end;
	uint8_t block_offset;
	uint8_t ciphertext[16];
	uint8_t plaintext[16];
	uint8_t n[16];
	NonVolatileReturn r;

	block_start = address & 0xfffffff0;
	block_offset = (uint8_t)(address & 0x0000000f);
	block_end = (address + length - 1) & 0xfffffff0;
	if ((address + length) < address)
	{
		// Overflow occurred.
		return NV_INVALID_ADDRESS;
	}

	memset(n, 0, 16);
	for (; block_start <= block_end; block_start += 16)
	{
		r = nonVolatileRead(ciphertext, partition, block_start, 16);
		if (r != NV_NO_ERROR)
		{
			return r;
		}
		writeU32LittleEndian(n, block_start);
		xexDecrypt(plaintext, ciphertext, n, 1);
		while (length && block_offset < 16)
		{
			plaintext[block_offset++] = *data++;
			length--;
		}
		block_offset = 0;
		xexEncrypt(ciphertext, plaintext, n, 1);
		r = nonVolatileWrite(ciphertext, partition, block_start, 16);
		if (r != NV_NO_ERROR)
		{
			return r;
		}
	}

	return NV_NO_ERROR;
}
/** Send a packet containing a list of wallets.
  */
static NOINLINE void listWallets(void)
{
	uint8_t version[4];
	uint8_t name[NAME_LENGTH];
	uint8_t buffer[4];
	WalletErrors wallet_return;

	if (getWalletInfo(version, name) != WALLET_NO_ERROR)
	{
		wallet_return = walletGetLastError();
		translateWalletError(wallet_return, 0, NULL);
	}
	else
	{
		streamPutOneByte(PACKET_TYPE_SUCCESS); // type
		writeU32LittleEndian(buffer, 4 + NAME_LENGTH); // length
		writeBytesToStream(buffer, 4);
		writeBytesToStream(version, 4);
		writeBytesToStream(name, NAME_LENGTH);
	}
}
/** Create new wallet. A brand new wallet contains no addresses and should
  * have a unique, unpredictable deterministic private key generation seed.
  * \param name Should point to #NAME_LENGTH bytes (padded with spaces if
  *             necessary) containing the desired name of the wallet.
  * \return #WALLET_NO_ERROR on success, or one of #WalletErrorsEnum if an
  *         error occurred. If this returns #WALLET_NO_ERROR, then the
  *         wallet will also be loaded.
  * \warning This will erase the current one.
  */
WalletErrors newWallet(uint8_t *name)
{
	uint8_t buffer[32];
	WalletErrors r;

	// Erase all traces of the existing wallet.
	r = sanitiseNonVolatileStorage(0, WALLET_RECORD_LENGTH);
	if (r != WALLET_NO_ERROR)
	{
		last_error = r;
		return last_error;
	}

	// Write version.
	if (writeWalletVersion() != NV_NO_ERROR)
	{
		last_error = WALLET_WRITE_ERROR;
		return last_error;
	}
	// Write reserved area 1.
	writeU32LittleEndian(buffer, 0);
	if (nonVolatileWrite(buffer, OFFSET_RESERVED1, 4) != NV_NO_ERROR)
	{
		last_error = WALLET_WRITE_ERROR;
		return last_error;
	}
	// Write name of wallet.
	if (nonVolatileWrite(name, OFFSET_NAME, NAME_LENGTH) != NV_NO_ERROR)
	{
		last_error = WALLET_WRITE_ERROR;
		return last_error;
	}
	// Write number of addresses.
	writeU32LittleEndian(buffer, 0);
	if (encryptedNonVolatileWrite(buffer, OFFSET_NUM_ADDRESSES, 4) != NV_NO_ERROR)
	{
		last_error = WALLET_WRITE_ERROR;
		return last_error;
	}
	// Write nonce 1.
	getRandom256(buffer);
	if (encryptedNonVolatileWrite(buffer, OFFSET_NONCE1, 8) != NV_NO_ERROR)
	{
		last_error = WALLET_WRITE_ERROR;
		return last_error;
	}
	// Write reserved area 2.
	writeU32LittleEndian(buffer, 0);
	if (encryptedNonVolatileWrite(buffer, OFFSET_RESERVED2, 4) != NV_NO_ERROR)
	{
		last_error = WALLET_WRITE_ERROR;
		return last_error;
	}
	// Write seed for deterministic address generator.
	getRandom256(buffer);
	if (encryptedNonVolatileWrite(buffer, OFFSET_SEED, 32) != NV_NO_ERROR)
	{
		last_error = WALLET_WRITE_ERROR;
		return last_error;
	}
	getRandom256(buffer);
	if (encryptedNonVolatileWrite(buffer, OFFSET_SEED + 32, 32) != NV_NO_ERROR)
	{
		last_error = WALLET_WRITE_ERROR;
		return last_error;
	}
	nonVolatileFlush();

	// Write checksum.
	r = writeWalletChecksum();
	if (r != WALLET_NO_ERROR)
	{
		last_error = r;
		return last_error;
	}
	nonVolatileFlush();

	last_error = initWallet();
	return last_error;
}
/** Sanitise (clear) a selected area of non-volatile storage. This will clear
  * the area between start (inclusive) and end (exclusive).
  * \param start The first address which will be cleared.
  * \param end One byte past the last address which will be cleared.
  * \return #WALLET_NO_ERROR on success, or one of #WalletErrorsEnum if an
  *         error occurred. This will still return #WALLET_NO_ERROR even if
  *         end is an address beyond the end of the non-volatile storage area.
  *         This is done so that using start = 0 and end = 0xffffffff will
  *         clear the entire non-volatile storage area.
  * \warning start and end must be a multiple of 32 (unless start is 0 and
  *          end is 0xffffffff).
  */
WalletErrors sanitiseNonVolatileStorage(uint32_t start, uint32_t end)
{
	uint8_t buffer[32];
	uint32_t address;
	NonVolatileReturn r;
	uint8_t pass;

	r = NV_NO_ERROR;
	for (pass = 0; pass < 4; pass++)
	{
		address = start;
		r = NV_NO_ERROR;
		while ((r == NV_NO_ERROR) && (address < end))
		{
			if (pass == 0)
			{
				memset(buffer, 0, sizeof(buffer));
			}
			else if (pass == 1)
			{
				memset(buffer, 0xff, sizeof(buffer));
			}
			else
			{
				getRandom256(buffer);
			}
			r = nonVolatileWrite(buffer, address, 32);
			nonVolatileFlush();
			address += 32;
		}

		if ((r != NV_INVALID_ADDRESS) && (r != NV_NO_ERROR))
		{
			// Uh oh, probably an I/O error.
			break;
		}
	} // end for (pass = 0; pass < 4; pass++)

	if ((r == NV_INVALID_ADDRESS) || (r == NV_NO_ERROR))
	{
		// Write VERSION_NOTHING_THERE to all possible locations of the
		// version field. This ensures that a wallet won't accidentally
		// (1 in 2 ^ 31 chance) be recognised as a valid wallet by
		// getWalletInfo().
		writeU32LittleEndian(buffer, VERSION_NOTHING_THERE);
		r = nonVolatileWrite(buffer, OFFSET_VERSION, 4);
		if (r == NV_NO_ERROR)
		{
			last_error = WALLET_NO_ERROR;
		}
		else
		{
			last_error = WALLET_WRITE_ERROR;
		}
	}
	else
	{
		last_error = WALLET_WRITE_ERROR;
	}
	return last_error;
}
/** Get packet from stream and deal with it. This basically implements the
  * protocol described in the file PROTOCOL.
  * 
  * This function will always completely
  * read a packet before sending a response packet. As long as the host
  * does the same thing, deadlocks cannot occur. Thus a productive
  * communication session between the hardware Bitcoin wallet and a host
  * should consist of the wallet and host alternating between sending a
  * packet and receiving a packet.
  */
void processPacket(void)
{
	uint8_t command;
	// Technically, the length of buffer should also be >= 4, since it is used
	// in a couple of places to obtain 32 bit values. This is guaranteed by
	// the reference to WALLET_ENCRYPTION_KEY_LENGTH, since no-one in their
	// right mind would use encryption with smaller than 32 bit keys.
	uint8_t buffer[MAX(NAME_LENGTH, WALLET_ENCRYPTION_KEY_LENGTH)];
	uint32_t num_addresses;
	AddressHandle ah;
	WalletErrors wallet_return;

	command = streamGetOneByte();
	getBytesFromStream(buffer, 4);
	payload_length = readU32LittleEndian(buffer);

	// Checklist for each case:
	// 1. Have you checked or dealt with length?
	// 2. Have you fully read the input stream before writing (to avoid
	//    deadlocks)?
	// 3. Have you asked permission from the user (for potentially dangerous
	//    operations)?
	// 4. Have you checked for errors from wallet functions?
	// 5. Have you used the right check for the wallet functions?

	switch (command)
	{

	case PACKET_TYPE_PING:
		// Ping request.
		// Just throw away the data and then send response.
		readAndIgnoreInput();
		writeString(STRINGSET_MISC, MISCSTR_VERSION, PACKET_TYPE_PING_REPLY);
		break;

	// Commands PACKET_TYPE_PING_REPLY, PACKET_TYPE_SUCCESS and
	// PACKET_TYPE_FAILURE should never be received; they are only sent.

	case PACKET_TYPE_NEW_WALLET:
		// Create new wallet.
		if (!expectLength(WALLET_ENCRYPTION_KEY_LENGTH + NAME_LENGTH))
		{
			getBytesFromStream(buffer, WALLET_ENCRYPTION_KEY_LENGTH);
			setEncryptionKey(buffer);
			getBytesFromStream(buffer, NAME_LENGTH);
			if (askUser(ASKUSER_NUKE_WALLET))
			{
				writeString(STRINGSET_MISC, MISCSTR_PERMISSION_DENIED, PACKET_TYPE_FAILURE);
			}
			else
			{
				wallet_return = newWallet(buffer);
				translateWalletError(wallet_return, 0, NULL);
			}
		}
		break;

	case PACKET_TYPE_NEW_ADDRESS:
		// Create new address in wallet.
		if (!expectLength(0))
		{
			if (askUser(ASKUSER_NEW_ADDRESS))
			{
				writeString(STRINGSET_MISC, MISCSTR_PERMISSION_DENIED, PACKET_TYPE_FAILURE);
			}
			else
			{
				getAndSendAddressAndPublicKey(1);
			}
		}
		break;

	case PACKET_TYPE_GET_NUM_ADDRESSES:
		// Get number of addresses in wallet.
		if (!expectLength(0))
		{
			num_addresses = getNumAddresses();
			writeU32LittleEndian(buffer, num_addresses);
			wallet_return = walletGetLastError();
			translateWalletError(wallet_return, 4, buffer);
		}
		break;

	case PACKET_TYPE_GET_ADDRESS_PUBKEY:
		// Get address and public key corresponding to an address handle.
		if (!expectLength(4))
		{
			getAndSendAddressAndPublicKey(0);
		}
		break;

	case PACKET_TYPE_SIGN_TRANSACTION:
		// Sign a transaction.
		if (payload_length <= 4)
		{
			readAndIgnoreInput();
			writeString(STRINGSET_MISC, MISCSTR_INVALID_PACKET, PACKET_TYPE_FAILURE);
		}
		else
		{
			getBytesFromStream(buffer, 4);
			ah = readU32LittleEndian(buffer);
			// Don't need to subtract 4 off payload_length because
			// getBytesFromStream() has already done so.
			validateAndSignTransaction(ah, payload_length);
			payload_length = 0;
		}
		break;

	case PACKET_TYPE_LOAD_WALLET:
		// Load wallet.
		if (!expectLength(WALLET_ENCRYPTION_KEY_LENGTH))
		{
			getBytesFromStream(buffer, WALLET_ENCRYPTION_KEY_LENGTH);
			setEncryptionKey(buffer);
			wallet_return = initWallet();
			translateWalletError(wallet_return, 0, NULL);
		}
		break;

	case PACKET_TYPE_UNLOAD_WALLET:
		// Unload wallet.
		if (!expectLength(0))
		{
			clearEncryptionKey();
			sanitiseRam();
			memset(buffer, 0xff, sizeof(buffer));
			memset(buffer, 0, sizeof(buffer));
			wallet_return = uninitWallet();
			translateWalletError(wallet_return, 0, NULL);
		}
		break;

	case PACKET_TYPE_FORMAT:
		// Format storage.
		if (!expectLength(0))
		{
			if (askUser(ASKUSER_FORMAT))
			{
				writeString(STRINGSET_MISC, MISCSTR_PERMISSION_DENIED, PACKET_TYPE_FAILURE);
			}
			else
			{
				wallet_return = sanitiseNonVolatileStorage(0, 0xffffffff);
				translateWalletError(wallet_return, 0, NULL);
				uninitWallet(); // force wallet to unload
			}
		}
		break;

	case PACKET_TYPE_CHANGE_KEY:
		// Change wallet encryption key.
		if (!expectLength(WALLET_ENCRYPTION_KEY_LENGTH))
		{
			getBytesFromStream(buffer, WALLET_ENCRYPTION_KEY_LENGTH);
			wallet_return = changeEncryptionKey(buffer);
			translateWalletError(wallet_return, 0, NULL);
		}
		break;

	case PACKET_TYPE_CHANGE_NAME:
		// Change wallet name.
		if (!expectLength(NAME_LENGTH))
		{
			getBytesFromStream(buffer, NAME_LENGTH);
			if (askUser(ASKUSER_CHANGE_NAME))
			{
				writeString(STRINGSET_MISC, MISCSTR_PERMISSION_DENIED, PACKET_TYPE_FAILURE);
			}
			else
			{
				wallet_return = changeWalletName(buffer);
				translateWalletError(wallet_return, 0, NULL);
			}
		}
		break;

	case PACKET_TYPE_LIST_WALLETS:
		// List wallets.
		if (!expectLength(0))
		{
			listWallets();
		}
		break;

	default:
		// Unknown command.
		readAndIgnoreInput();
		writeString(STRINGSET_MISC, MISCSTR_INVALID_PACKET, PACKET_TYPE_FAILURE);
		break;

	}

#ifdef TEST_STREAM_COMM
	assert(payload_length == 0);
#endif

}
/** Send a packet containing an address and its corresponding public key.
  * This can generate new addresses as well as obtain old addresses. Both
  * use cases were combined into one function because they involve similar
  * processes.
  * \param generate_new If this is non-zero, a new address will be generated
  *                     and the address handle of the generated address will
  *                     be prepended to the output packet.
  *                     If this is zero, the address handle will be read from
  *                     the input stream. No address handle will be prepended
  *                     to the output packet.
  */
static NOINLINE void getAndSendAddressAndPublicKey(uint8_t generate_new)
{
	AddressHandle ah;
	PointAffine public_key;
	uint8_t address[20];
	uint8_t buffer[4];
	WalletErrors r;

	if (generate_new)
	{
		// Generate new address handle.
		r = WALLET_NO_ERROR;
		ah = makeNewAddress(address, &public_key);
		if (ah == BAD_ADDRESS_HANDLE)
		{
			r = walletGetLastError();
		}
	}
	else
	{
		// Read address handle from input stream.
		getBytesFromStream(buffer, 4);
		ah = readU32LittleEndian(buffer);
		r = getAddressAndPublicKey(address, &public_key, ah);
	}

	if (r == WALLET_NO_ERROR)
	{
		streamPutOneByte(PACKET_TYPE_SUCCESS); // type
		if (generate_new)
		{
			// 4 (address handle) + 20 (address) + 65 (public key)
			writeU32LittleEndian(buffer, 89);
		}
		else
		{
			// 20 (address) + 65 (public key)
			writeU32LittleEndian(buffer, 85);
		}
		writeBytesToStream(buffer, 4); // length
		if (generate_new)
		{
			writeU32LittleEndian(buffer, ah);
			writeBytesToStream(buffer, 4);
		}
		writeBytesToStream(address, 20);
		// The format of public keys sent is compatible with
		// "SEC 1: Elliptic Curve Cryptography" by Certicom research, obtained
		// 15-August-2011 from: http://www.secg.org/collateral/sec1_final.pdf
		// section 2.3 ("Data Types and Conversions"). The document basically
		// says that integers should be represented big-endian and that a 0x04
		// should be prepended to indicate that the public key is
		// uncompressed.
		streamPutOneByte(0x04);
		swapEndian256(public_key.x);
		swapEndian256(public_key.y);
		writeBytesToStream(public_key.x, 32);
		writeBytesToStream(public_key.y, 32);
	}
	else
	{
		translateWalletError(r, 0, NULL);
	} // end if (r == WALLET_NO_ERROR)
}