VeraCrypt
aboutsummaryrefslogtreecommitdiff
path: root/src/Common/Volumes.c
AgeCommit message (Collapse)AuthorFilesLines
2016-05-10Remove trailing whitespaceDavid Foerster1-26/+26
2016-05-10Normalize all line terminatorsDavid Foerster1-1270/+1270
2016-04-20Windows: Add option to avoid PIM prompt in pre-boot authentication by ↵Mounir IDRASSI1-2/+3
storing PIM value unencrypted in MBR.
2016-04-17Windows: fix keys parts not shown in system encryption wizard when the ↵Mounir IDRASSI1-1/+1
display keys checkbox is checked. This occurred when the "Display pool content" in the previous wizard page was unchecked before clicking Next.
2016-02-07Windows:Fix various issues and warnings reported by static code analysis ↵Mounir IDRASSI1-4/+4
tool Coverity.
2016-01-20Copyright: update dates to include 2016.Mounir IDRASSI1-1/+1
2016-01-03Cryptography: Set 16-byte alignment for KEY_INFO structure that is used as ↵Mounir IDRASSI1-2/+2
input for Whirlpool hash. This helps improve performance.
2015-12-21Windows: Implement PIM caching, both for system encryption and for normal ↵Mounir IDRASSI1-0/+8
volumes. Add options to activate it in the Preferences and System Settings.
2015-11-26Windows: solve GUI issues caused by using ANSI string instead of UNICODE ↵Mounir IDRASSI1-6/+6
ones. Remove Unused functions.
2015-08-06Update license information to reflect the use of a dual license Apache 2.0 ↵Mounir IDRASSI1-7/+9
and TrueCrypt 3.0.
2015-07-29Windows: Implement Evil-Maid-Attack detection mechanism. Write the correct ↵Mounir IDRASSI1-0/+37
bootloader when changing the system encryption password: this enables to recover if an attack is detected.
2015-07-11Use Pim name for internal variables instead of the old name PinMounir IDRASSI1-11/+11
2015-06-07Windows: Add support for PIN in favorites. Several enhancements to GUI ↵Mounir IDRASSI1-0/+4
handling of Dynamic Mode.
2015-05-26Windows: first implementation of dynamic modeMounir IDRASSI1-9/+12
2015-03-02Windows: if TrueCrypt volume created with a version prior to 6.0, display ↵Mounir IDRASSI1-1/+1
this version in the error message to help users understand why it is not working.
2015-01-04Windows: Add support for TrueCrypt 6.x since its format (v4) is identical to ↵Mounir IDRASSI1-1/+1
7.x apart from the sector size field which we already handle correctly.
2014-12-28Windows: support loading TrueCrypt volumes. Implement converting TrueCrypt ↵Mounir IDRASSI1-9/+36
volumes to VeraCrypt using the change password functionality.
2014-12-27Windows: use the correct window handle for creating message boxes. This ↵Mounir IDRASSI1-6/+6
became important after the introduction of the wait dialog in order to avoid having message boxes behind the wait dialog.
2014-12-16Windows: Enhance performance by implementing the possibility to choose the ↵Mounir IDRASSI1-4/+9
correct hash algorithm of volumes during various operations (mount, change password...). In case of system encryption, slightly speedup Windows startup time by making the driver pickup the correct hash algorithm used for the encryption.
2014-11-08Simplify code handling iterations count: in boot mode, we'll set the correct ↵Mounir IDRASSI1-5/+5
iterations count inside derive_u_sha256 and derive_u_ripemd160 depending in the value of the iterations parameter. On normal mode, we use normal values of iterations count. Removes the special test parameter from RIPEMD160 functions.
2014-11-08Bootloader: in function ReadVolumeHeader, arrays dk and masterKey have the ↵Mounir IDRASSI1-12/+9
same size and they are never needed at the same time. So, we can minimize stack memory usage by using only one array instead of two. At the end of the function, the array is erased securely.
2014-11-08Bootloader: optimize code size in single cipher mode by manually inlining ↵Mounir IDRASSI1-2/+54
EAInit, EAGetFirst and EAGetKeySize, and by removing the loop in ReadVolumeHeader that tests for encryption algorithms.
2014-11-08Add support for SHA-256 in key derivation for bootloader encryption. Create ↵Mounir IDRASSI1-0/+24
separate bootloader images for SHA-256 and RIPEMD-160. Set SHA-256 as the default PRF for boot encryption and SHA-512 as default PRF for all other cases. Depricate RIPEMD-160.
2014-11-08Remove deprecated/legacy cryptographic algorithms and encryption modes that ↵Mounir IDRASSI1-80/+3
are never used by VeraCrypt. This will speed up volumes opening in many cases.
2014-11-08Static Code Analysis : Generalize the use of Safe String functions. Add some ↵Mounir IDRASSI1-4/+10
NULL pointer checks. Avoid false-positive detection in AppendMenu (MF_SEPARATOR) calls by setting the last parameter to "" instead of NULL.
2014-11-08Replace 'TRUE' by 'VERA' in some GUI constants and commentsMounir IDRASSI1-5/+5
2014-11-08Correct issue in handling hidden system partitions that made it impossible ↵Mounir IDRASSI1-2/+2
to verify their password.
2014-11-08Only position legacy flag if the first release of VeraCrypt is detectedMounir IDRASSI1-1/+1
2014-11-08Change the required version in volume header to the one of VeraCrypt (now 1.0)Mounir IDRASSI1-18/+1
2014-11-08Enhance security by rising the iterations used in PBKDF2 : 327670 instead of ↵Mounir IDRASSI1-4/+4
1000 when booting in encrypted system partition, and 2000000 instead of 2000 when using encrypted containers and partitions
2014-11-08Modifications to remove all TrueCrypt references in names. generate new ↵Mounir IDRASSI1-3/+3
GUIDs for VeraCrypt. Replace "TRUE" by "VERA" in volume headers and driver magic word.
2014-11-08Add original TrueCrypt 7.1a sourcesMounir IDRASSI1-0/+1198
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
/** @file
Picture password

Copyright (c) 2016. Disk Cryptography Services for EFI (DCS), Alex Kolotnikov
Copyright (c) 2016. VeraCrypt, Mounir IDRASSI 

This program and the accompanying materials
are licensed and made available under the terms and conditions
of the GNU Lesser General Public License, version 3.0 (LGPL-3.0).

The full text of the license may be found at
https://opensource.org/licenses/LGPL-3.0
**/

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>

#include <Library/CommonLib.h>
#include <Library/GraphLib.h>
#include <Library/PasswordLib.h>

CHAR16*	gPasswordPictureFileName = NULL;

CHAR8*	gPasswordPictureChars = NULL;
CHAR8*	gPasswordPictureCharsDefault = "MN/[aQ-eyPr}GT: |V^UqiI_gbdA9YwZ%f8t6S@D\"7uXl\\30R#+zH*,W4J?=&BLFv]hx~E;$<.o'sp1`(>C)O{!5j2nmkcK";
//CHAR8*	gPicturePasswordCharsDefault = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
UINTN		gPasswordPictureCharsLen = 95;

UINT8		gPasswordVisible = 0;
int		gPasswordShowMark = 1;

int		gPlatformLocked = 0;
int		gTPMLocked = 0;
int		gSCLocked = 0;


//////////////////////////////////////////////////////////////////////////
// Picture password
//////////////////////////////////////////////////////////////////////////
CONST CHAR16* BmpName = L"Test.bmp";
VOID*		Bmp = NULL;
UINTN		BmpSize = 0;
BLT_HEADER*	bltPwd = NULL;
UINTN		   posPictX, posPictY;
BLT_HEADER*	bltScrn = NULL;
UINTN*		cellSelected = NULL;
UINTN			picPwdIdx = 0;
UINTN			sHeight;
UINTN			sWidth;
UINTN			step;

DRAW_CONTEXT ctxCursor;
DRAW_CONTEXT ctxCell;
DRAW_CONTEXT ctxMark;
DRAW_CONTEXT ctxSet;

VOID
CellUpdate(
	IN OUT BLT_HEADER*	blt,
	IN UINTN		x,
	IN UINTN		y,
	IN BOOLEAN  selected) {
	if (selected && gPasswordShowMark) {
		BltCircle(blt, &ctxMark, (INT32)(x * step + step / 2), (INT32)(y * step + step / 2), (INT32)(step / 3), TRUE);
		BltCircle(blt, &ctxCell, (INT32)(x * step + step / 2), (INT32)(y * step + step / 2), (INT32)(step / 9), TRUE);
	}
	else {
		CHAR8	ch[2] = { 0,0 };
		BltCircle(blt, &ctxCell, (INT32)(x * step + step / 2), (INT32)(y * step + step / 2), (INT32)(step / 3), FALSE);
		if (gPasswordVisible) {
			ch[0] = gPasswordPictureChars[(x + blt->Width / step * y) % gPasswordPictureCharsLen];
			BltText(blt, &ctxCell, (INT32)(x * step + step / 2 - 12), (INT32)(y * step + step / 2 - 12), 256, ch);
		}
	}
}

BOOLEAN
CellGetSelected(
	IN OUT BLT_HEADER*	blt,
	IN UINTN		x,
	IN UINTN		y,
	OUT UINTN*	cellX,
	OUT UINTN*	cellY) {
	RECT	reg;
	UINTN	sen;
	if (x > blt->Width || y > blt->Height) return FALSE;
	*cellX = x / step;
	*cellY = y / step;
	sen = step / 5;	// zone sensible
	reg.left = (UINT32)(*cellX * step + step / 2 - sen);
	reg.right = (UINT32)(*cellX * step + step / 2 + sen);
	reg.top = (UINT32)(*cellY * step + step / 2 - sen);
	reg.bottom = (UINT32)(*cellY * step + step / 2 + sen);
	return (x > reg.left && x < reg.right && y > reg.top && y < reg.bottom);
}

VOID
DrawCursor(IN OUT BLT_HEADER*	blt,
	IN UINTN		x,
	IN UINTN		y
	) {
	BltLine(blt, &ctxCursor, (INT32)(x - 10), (INT32)y, (INT32)(x + 10), (INT32)y);
	BltLine(blt, &ctxCursor, (INT32)x, (INT32)(y - 10), (INT32)x, (INT32)(y + 10));
}

typedef struct _TOUCH_ZONE {
	UINTN		Zone;
	CHAR8*	Message;
	UINTN		RetCode;
	INT32		TextScale;
} TOUCH_ZONE, *PTOUCH_ZONE;

enum TouchZoneIds {
	tznLogin = 1,
	tznBeep,
	tznShow,
	tznChange
};

CHAR8*	msgLogin = "LOGIN";
CHAR8*	msgBeepOn = "Beep\non";
CHAR8*	msgBeepOff = "Beep\noff";
CHAR8*	msgChg = "Change\npwd";
CHAR8*	msgShowPwd = "Show\npwd";
CHAR8*	msgHidePwd = "Hide\npwd";
CHAR8*	msgNewPwd = "New\npwd";
CHAR8*	msgConfirmPwd = "Confirm\npwd";
CHAR8*	msgPlatformLocked = "PLT\nlkd";
CHAR8*	msgPlatformUnLocked = "PLT\nunlkd";
CHAR8*	msgTpmLocked = "TPM\nlkd";
CHAR8*	msgTpmUnLocked = "TPM\nunlkd";

TOUCH_ZONE	TZN_Login = { 0, NULL, tznLogin, 128 };
TOUCH_ZONE	TZN_Speaker = { 2, NULL, tznBeep, 64 };
TOUCH_ZONE	TZN_Show = { 3, NULL, tznShow, 64 };
TOUCH_ZONE	TZN_Change = { 5, NULL, tznChange, 32 };
TOUCH_ZONE	TZN_Platform = { 6, NULL, 0, 32 };
TOUCH_ZONE	TZN_Tpm = { 7, NULL, 0, 32 };

VOID
DrawTouchZone(
	IN PTOUCH_ZONE zone
	) {
	BltFill(bltScrn, gColorBlack, (INT32)(sWidth - step), (INT32)(2 + zone->Zone * step), (INT32)(INT32)(sWidth - 2), (INT32)(step + zone->Zone * step));
	BltBox(bltScrn, &ctxCell, (INT32)(sWidth - step), (INT32)(2 + zone->Zone * step), (INT32)(INT32)(sWidth - 2), (INT32)(step + zone->Zone * step));
	BltText(bltScrn, &ctxCell, (INT32)(sWidth - step * 3 / 4), (INT32)(step * 1 / 3 + zone->Zone * step), 128, zone->Message);
}

BOOLEAN
IsTouchZone(
	IN PTOUCH_ZONE zone,
	IN UINTN		   x,
	IN UINTN		   y
	) {
	if (x > sWidth - step &&
		(y > (zone->Zone * step)) &&
		(y < (step + zone->Zone * step))) {
		return TRUE;
	}
	return FALSE;
}

VOID
DrawPwdZone(
	IN CHAR8*	pwd, 
	IN UINT32 pwdMax) 
{
	INT32 pwdGrphMaxLen = (INT32)(sWidth - 2 * step);
	BltFill(bltScrn, gColorBlack, 0, 0, (INT32)(sWidth - 2 * step), (INT32)(posPictY));
	if (gPasswordVisible) {
		BltText(bltScrn, &ctxCell, 0, 0, 256, pwd);
	}
	else {
		INT32 pwdGrphLen = (INT32)(pwdGrphMaxLen * picPwdIdx / pwdMax);
		INT32 pwdGrphHeight = (INT32)(posPictY) / 2;
		INT32 pwdGrphTop = (INT32)(posPictY) / 4;
		BltFill(bltScrn, gColorGreen, 0, pwdGrphTop, pwdGrphLen, pwdGrphHeight + pwdGrphTop);
		BltFill(bltScrn, gColorBlack, pwdGrphLen, pwdGrphTop, pwdGrphMaxLen, pwdGrphHeight + pwdGrphTop);
	}
}

EFI_STATUS
DrawPwdPicture()
{
	EFI_STATUS   res;
	UINTN		    idx;
	UINTN		    cellX, cellY;

	if (bltPwd != NULL) MEM_FREE(bltPwd);

	res = BmpToBlt(Bmp, BmpSize, &bltPwd);
	if (EFI_ERROR(res)) {
		return res;
	}
	cellY = 0;
	do {
		cellX = 0;
		do {
			CellUpdate(bltPwd, cellX, cellY, FALSE);
			cellX++;
		} while ((cellX + 1) * step <= (bltPwd->Width));
		cellY++;
	} while ((cellY + 1)* step <= (bltPwd->Height));

	// Update selected
	for (idx = 0; idx < picPwdIdx; ++idx) {
		if (cellSelected[idx * 2] != MAX_INTN) {
			CellUpdate(bltPwd, cellSelected[idx * 2], cellSelected[idx * 2 + 1], TRUE);
		}
	}
	return EFI_SUCCESS;
}

VOID
CreateDraws() {
	// Set
	ctxSet.Color = gColorGray;
	ctxSet.DashLine = 0xFFFFFFFF;
	ctxSet.Op = DrawOpSet;
	ctxSet.Brush = NULL;

	// Cursor
	ctxCursor.Color = gColorWhite;
	ctxCursor.DashLine = 0xFFFFFFFF;
	ctxCursor.Op = DrawOpXor;
	ctxCursor.Brush = NULL;

	// Cell
	ctxCell.Color = gColorGreen;
	ctxCell.DashLine = 0xFFFFFFFF;
	ctxCell.Op = DrawOpSet;
	ctxCell.Brush = gBrush3;

	// Shade (close to black)
	ctxMark.AlphaColor = gColorWhite;
	ctxMark.Alpha = 128;
	ctxMark.Op = DrawOpAlpha;
	ctxMark.Brush = NULL;
}

enum PictPwdAction {
	PwdActNone = 0,
	PwdActLogin,
	PwdActCancel,
	PwdActChange,
	PwdActShow,
	PwdActBeep,
	PwdActNewChar,
	PwdActUpdateZones,
};

VOID
AskPictPwdInt(
	IN  UINTN	pwdType,
	IN  UINTN	pwdMax,
	OUT CHAR8*	pwd,
	OUT UINT32*	pwdLen,
	OUT INT32*	retCode
	) {
	EFI_STATUS	res;
	UINTN		   cellX, cellY;
	UINTN		   cellPrevX, cellPrevY;
	UINTN		   curX, curY;
	UINTN		   curPrevX, curPrevY;
	EFI_INPUT_KEY key;
	EFI_EVENT      UpdateEvent;
	EFI_EVENT      BeepOffEvent;
	EFI_EVENT      InputEvents[3];
	UINTN          EventIndex = 0;
	UINTN          eventsCount = 2;
	EFI_ABSOLUTE_POINTER_STATE		aps = {0};
	BOOLEAN        showCursor = FALSE;
	BOOLEAN        beepOn = FALSE;
	UINTN          pwdAction = PwdActNone;
	CHAR8          pwdNewChar = 0;

	InitConsoleControl();
	if (gBeepEnabled) {
		InitSpeaker();
	}

	if (Bmp == NULL) {
		if (gPasswordPictureFileName != NULL) {
			res = FileLoad(NULL, (CHAR16*)gPasswordPictureFileName, &Bmp, &BmpSize);
			if (EFI_ERROR(res)) {
				ERR_PRINT(L"File load - %r\n", res);
				return;
			}
		}	else {
			ERR_PRINT(L"Picture file name undefined\n");
			return;
		}
	}
	// Init draws
	CreateDraws();

	// Init screen
	if (bltScrn != NULL) MEM_FREE(bltScrn);
	ScreenSaveBlt(&bltScrn);
	sWidth = bltScrn->Width;
	sHeight = bltScrn->Height;
	step = sWidth >> 4;

	// Init picture password
	picPwdIdx = 0;
	res = DrawPwdPicture();
	if (EFI_ERROR(res)) {
		MEM_FREE(bltScrn);
		ERR_PRINT(L"BmpToBlt - %r", res);
		return;
	}

	// Touch zones
	switch (pwdType) {
	case AskPwdConfirm:
		TZN_Login.Message = msgConfirmPwd;
		break;
	case AskPwdNew:
		TZN_Login.Message = msgNewPwd;
		break;
	case AskPwdLogin:
	default:
		TZN_Login.Message = msgLogin;
	}
	DrawTouchZone(&TZN_Login);

	if (pwdType == AskPwdLogin) {
		TZN_Change.Message = msgChg;
		DrawTouchZone(&TZN_Change);
	}

	if (gBeepControlEnabled) {
		TZN_Speaker.Message = gBeepEnabled ? msgBeepOff : msgBeepOn;
		DrawTouchZone(&TZN_Speaker);
	}

	TZN_Platform.Message = gPlatformLocked? msgPlatformLocked : msgPlatformUnLocked;
	DrawTouchZone(&TZN_Platform);

	TZN_Tpm.Message = gTPMLocked ? msgTpmLocked : msgTpmUnLocked;
	DrawTouchZone(&TZN_Tpm);

	TZN_Show.Message = gPasswordVisible ? msgHidePwd : msgShowPwd;
	DrawTouchZone(&TZN_Show);
	cellSelected = MEM_ALLOC(sizeof(UINTN) * 2 * (pwdMax + 1));

	ScreenUpdateDirty(bltScrn);

	// Prepare cursors
	posPictX = (sWidth - bltPwd->Width) >> 1;
	posPictY = (sHeight - bltPwd->Height) >> 1;
	cellPrevX = MAX_INTN;
	cellPrevY = MAX_INTN;
	curX = sWidth / 2;
	curY = posPictY / 2;
	BltDrawBlt(bltScrn, bltPwd, posPictX, posPictY);

	// Prepare events
	InputEvents[0] = gST->ConIn->WaitForKey;
	gBS->CreateEvent(EVT_TIMER, 0, (EFI_EVENT_NOTIFY)NULL, NULL, &InputEvents[1]);
	gBS->CreateEvent(EVT_TIMER, 0, (EFI_EVENT_NOTIFY)NULL, NULL, &BeepOffEvent);
	gBS->CreateEvent(EVT_TIMER, 0, (EFI_EVENT_NOTIFY)NULL, NULL, &UpdateEvent);
	gBS->SetTimer(UpdateEvent, TimerRelative, 500000);			// 20 times per second to update
	gBS->SetTimer(BeepOffEvent, TimerRelative, gBeepDurationDefault * 10);
	gBS->SetTimer(InputEvents[1], TimerRelative, 5000000);

	if (gTouchPointer != NULL) {
		eventsCount = 3;
		InputEvents[2] = gTouchPointer->WaitForInput;
		while (gBS->CheckEvent(InputEvents[2]) == EFI_SUCCESS) {
			gTouchPointer->GetState(gTouchPointer, &aps);
		}
	}
	while (gBS->CheckEvent(InputEvents[0]) == EFI_SUCCESS) {
		gST->ConIn->ReadKeyStroke(gST->ConIn, &key);
	}
	do
	{
		curPrevX = curX;
		curPrevY = curY;
		ZeroMem(&key, sizeof(key));
		res = gBS->WaitForEvent(eventsCount, InputEvents, &EventIndex);
		if (EventIndex == 0) {
			res = gST->ConIn->ReadKeyStroke(gST->ConIn, &key);
			if (EFI_ERROR(res)) {
				continue;
			}
		}
		// OUT_PRINT(L" \r%05d %05d", (UINT32)curX, (UINT32)curY, );
		// recharge timeout event and stop beep
		if (gBeepControlEnabled && gBeepEnabled && beepOn) {
			if (gBS->CheckEvent(BeepOffEvent) == EFI_SUCCESS) {
				beepOn = FALSE;
				SpeakerBeep((UINT16)gBeepToneDefault, 0, 0, 0);
				gBS->SetTimer(InputEvents[1], TimerRelative, 5000000);
			}
		}
		else {
			gBS->SetTimer(InputEvents[1], TimerRelative, 5000000);
		}

		// hide cursor
		if (showCursor && EventIndex != 1) {
			DrawCursor(bltScrn, curX, curY);
			showCursor = FALSE;
		}

		// Blink cursor
		if (EventIndex == 1) {
			DrawCursor(bltScrn, curX, curY);
			showCursor = !showCursor;
		}

		if (EventIndex == 0) {
//			gST->ConIn->ReadKeyStroke(gST->ConIn, &key);
			// OUT_PRINT(L" %04x, %04x\r", key.ScanCode, key.UnicodeChar);
			// Remove dirty chars 0.1s
			FlushInputDelay(100000);
			switch (key.ScanCode)
			{
			case SCAN_HOME:
				curX -= gTouchSimulateStep;
				curY -= gTouchSimulateStep;
				break;
			case SCAN_LEFT:
				curX -= gTouchSimulateStep;
				break;
			case SCAN_END:
				curX -= gTouchSimulateStep;
				curY += gTouchSimulateStep;
				break;
			case SCAN_DOWN:
				curY += gTouchSimulateStep;
				break;
			case SCAN_PAGE_DOWN:
				curX += gTouchSimulateStep;
				curY += gTouchSimulateStep;
				break;
			case SCAN_RIGHT:
				curX += gTouchSimulateStep;
				break;
			case SCAN_PAGE_UP:
				curX += gTouchSimulateStep;
				curY -= gTouchSimulateStep;
				break;
			case SCAN_UP:
				curY -= gTouchSimulateStep;
				break;
			case SCAN_F11:
				if (gTouchSimulateStep > 1)
					gTouchSimulateStep--;
				break;
			case SCAN_F12:
				gTouchSimulateStep++;
				break;
			case SCAN_ESC:
				pwdAction = PwdActCancel;
				break;
			case SCAN_F2:
				if (pwdType == AskPwdLogin) {
					pwdAction = PwdActChange;
				}
				break;
			case SCAN_F4:
				if (gBeepControlEnabled) {
					pwdAction = PwdActBeep;
				}
				break;
			case SCAN_F5:
				pwdAction = PwdActShow;
				break;
			case SCAN_F7:
				gPlatformLocked = gPlatformLocked ? 0 : 1;
				pwdAction = PwdActUpdateZones;
				break;
			case SCAN_F8:
				gTPMLocked = gTPMLocked ? 0 : 1;
				pwdAction = PwdActUpdateZones;
				break;
			default:
				;
			}

			if (key.UnicodeChar != 0) {
				if (key.UnicodeChar == 0x0d) {
					pwdAction = PwdActLogin;
				}
				else {
					pwdNewChar = (CHAR8)key.UnicodeChar;
					pwdAction = PwdActNewChar;
					cellSelected[picPwdIdx * 2] = MAX_INTN;
				}
			}

		}

		if (EventIndex == 2) {
			res = gTouchPointer->GetState(gTouchPointer, &aps);
			if (!EFI_ERROR(res)) {
				curX = (UINTN)(aps.CurrentX * sWidth / (gTouchPointer->Mode->AbsoluteMaxX - gTouchPointer->Mode->AbsoluteMinX));
				curY = (UINTN)(aps.CurrentY * sHeight / (gTouchPointer->Mode->AbsoluteMaxY - gTouchPointer->Mode->AbsoluteMinY));
			}
		}

		if (curX > sWidth) curX = sWidth;
		if (curY > sHeight) curY = sHeight;

		// Cell check
		if (CellGetSelected(bltPwd, curX - posPictX, curY - posPictY, &cellX, &cellY) && picPwdIdx < pwdMax) {
			if (cellPrevX != cellX || cellPrevY != cellY) {
				cellPrevX = cellX;
				cellPrevY = cellY;
				cellSelected[picPwdIdx * 2] = cellX;
				cellSelected[picPwdIdx * 2 + 1] = cellY;
				CellUpdate(bltPwd, cellX, cellY, TRUE);
				BltDrawBlt(bltScrn, bltPwd, posPictX, posPictY);
				pwdAction = PwdActNewChar;
				pwdNewChar = gPasswordPictureChars[(cellX + bltPwd->Width / step * cellY) % gPasswordPictureCharsLen];
			}
			else if (EventIndex == 2 && aps.ActiveButtons == 0) {
				cellPrevX = MAX_INTN;
				cellPrevY = MAX_INTN;
				curX = posPictX + cellX * step;
				curY = posPictY + cellY * step;
				while (gBS->CheckEvent(InputEvents[2]) == EFI_SUCCESS) {
					gTouchPointer->GetState(gTouchPointer, &aps);
				}
			}
		}

		if (pwdAction == PwdActNone) {
			if (IsTouchZone(&TZN_Login, curX, curY)) {
				pwdAction = PwdActLogin;
			}

			if (pwdType == AskPwdLogin && IsTouchZone(&TZN_Change, curX, curY)) {
				pwdAction = PwdActChange;
			}

			if (curPrevX != curX || curPrevY != curY) {
				if (gSpeakerCount > 0 && IsTouchZone(&TZN_Speaker, curX, curY)) {
					pwdAction = PwdActBeep;
				}
				if (IsTouchZone(&TZN_Show, curX, curY)) {
					pwdAction = PwdActShow;
				}
				if (IsTouchZone(&TZN_Tpm, curX, curY)) {
					gTPMLocked = gTPMLocked ? 0 : 1;
					pwdAction = PwdActUpdateZones;
				}
				if (IsTouchZone(&TZN_Platform, curX, curY)) {
					gPlatformLocked = gPlatformLocked ? 0 : 1;
					pwdAction = PwdActUpdateZones;
				}
			}
		}

		if (PwdActNewChar == pwdAction) {
			BOOLEAN bUpdPwdZone = FALSE;
			if (pwdNewChar == '\b' && picPwdIdx > 0) {
				picPwdIdx--;
				pwd[picPwdIdx] = 0;
				bUpdPwdZone = TRUE;
			} else if ((picPwdIdx < pwdMax - 1) && (pwdNewChar >= 32)) {
				pwd[picPwdIdx++] = pwdNewChar;
				pwd[picPwdIdx] = 0;
				bUpdPwdZone = TRUE;
			}
			if(bUpdPwdZone) {
				*pwdLen = (int)picPwdIdx;
				DrawPwdZone(pwd, (INT32)pwdMax);
				if (gBeepControlEnabled && gBeepEnabled) {
					SpeakerBeep((UINT16)gBeepToneDefault, gBeepNumberDefault, 0, 0);
					gBS->SetTimer(BeepOffEvent, TimerRelative, gBeepDurationDefault * 10);
					gBS->SetTimer(InputEvents[1], TimerRelative, gBeepDurationDefault * 10);
					beepOn = TRUE;
				}
			}
		}
		else if (PwdActBeep == pwdAction && gBeepControlEnabled) {
			if (gBeepEnabled && beepOn) {
				beepOn = FALSE;
				SpeakerBeep((UINT16)gBeepToneDefault, 0, 0, 0);
			}
			gBeepEnabled = gBeepEnabled ? 0 : 1;
			TZN_Speaker.Message = gBeepEnabled ? msgBeepOff : msgBeepOn;
			DrawTouchZone(&TZN_Speaker);
		}
		else if (PwdActShow == pwdAction) {
			gPasswordVisible = gPasswordVisible ? 0 : 1;
			DrawPwdZone(pwd, (INT32)pwdMax);
			DrawPwdPicture();
			BltDrawBlt(bltScrn, bltPwd, posPictX, posPictY);
			TZN_Show.Message = gPasswordVisible ? msgHidePwd : msgShowPwd;
			DrawTouchZone(&TZN_Show);
		}
		else if (PwdActLogin == pwdAction) {
			*retCode = AskPwdRetLogin;
			break;
		}
		else if (pwdType == AskPwdLogin && PwdActChange == pwdAction) {
			*retCode = AskPwdRetChange;
			break;
		}
		else if (PwdActCancel == pwdAction){
			*retCode = AskPwdRetCancel;
			break;
		}
		else if (PwdActUpdateZones == pwdAction) {
			TZN_Platform.Message = gPlatformLocked ? msgPlatformLocked : msgPlatformUnLocked;
			DrawTouchZone(&TZN_Platform);

			TZN_Tpm.Message = gTPMLocked ? msgTpmLocked : msgTpmUnLocked;
			DrawTouchZone(&TZN_Tpm);
		}

		if (curPrevX != curX || curPrevY != curY) {
			DrawCursor(bltScrn, curX, curY);
			showCursor = TRUE;
		}
		// Time to update screen?
		if (gBS->CheckEvent(UpdateEvent) == EFI_SUCCESS) {
			ScreenUpdateDirty(bltScrn);
			gBS->SetTimer(UpdateEvent, TimerRelative, 500000);			// 20 times per second to update
		}

		pwdAction = PwdActNone;
	} while (TRUE);
	MEM_BURN (&key, sizeof (key));
	MEM_BURN (&pwdNewChar, sizeof (pwdNewChar));
	gBS->CloseEvent(InputEvents[1]);
	gBS->CloseEvent(UpdateEvent);
	gBS->CloseEvent(BeepOffEvent);
	ScreenFillRect(&gColorBlack, 0, 0, sWidth, sHeight);
	gBS->Stall(500000);
}