KeePass Help Center KeePass Home | Downloads | Translations | Plugins | Donate 
Help Center Home | Forums | Awards | Links 







Key Provider Development (1.x)

How to develop key provider plugins for KeePass 1.x.

This documentation applies to KeePass 1.x plugins. 2.x plugins are fundamentally different from 1.x plugins. 2.x plugins cannot be loaded by KeePass 1.x.

This article is intended for KeePass plugin developers. If you are looking for ready-to-use key providers, see the Plugins page.


Introduction

Screenshot Starting with KeePass 1.10, KeePass supports a key provider API. Plugins can easily extend the built-in key methods (master password and/or key file) by new ones, like USB device ID, certificates, smart cards, ...

A key provider plugin can offer one or more keys. In the screenshot on the right, the plugin has added the options 'Certificate', 'Smart Card' and 'User Credentials' to the key files combo box. It is possible for your plugin to show additional file selection dialog boxes, etc., however it is recommended that you provide keys directly, so that a user has just to pick it from the combo box (like KeePass offers multiple file locations already).

Each time the master key dialog opens, KeePass first queries information from all plugins using the KPM_KEYPROV_QUERY_INFO_FIRST and KPM_KEYPROV_QUERY_INFO_NEXT messages. The information you supply in response to these messages is shown in the key files combo box. When the user selects one of the options you provide and clicks [OK], KeePass will send a KPM_KEYPROV_QUERY_KEY message to your plugin to actually get the key. For details, see the sections below. When the dialog is closed, you get a KPM_KEYPROV_FINALIZE message, in which you can clean up any cached keys.


Sample Source Code

Here's the source code of a very simple key provider plugin for KeePass. Note that this source code cannot be compiled as is, because a few code blocks have been left out. The complete version is part of the downloadable sample key provider Visual C++ 2005 project.

static std::vector<KP_KEYPROV_INFO> g_vKeyProv;
static DWORD g_dwIterPos = 0;

Initialization
{
	g_vKeyProv.clear();
	_ProvAdd(_T("Certificate"), 10);
	_ProvAdd(_T("Smart Card"), 20);
	_ProvAdd(_T("User Credentials"), 9);

	return TRUE;
}

void _ProvAdd(LPCTSTR lpProvDisplayName, DWORD dwImageIndex)
{
	KP_KEYPROV_INFO c;
	ZeroMemory(&c, sizeof(KP_KEYPROV_INFO));

	c.lpName = lpProvDisplayName;
	c.dwImageIndex = dwImageIndex;

	g_vKeyProv.push_back(c);
}

OnMessage
{
	switch(dwCode)
	{
	case KPM_KEYPROV_QUERY_INFO_FIRST:
		g_dwIterPos = 0;
		*((KP_KEYPROV_INFO*)lParamL) = g_vKeyProv[0];
		break;

	case KPM_KEYPROV_QUERY_INFO_NEXT:
		++g_dwIterPos;
		if(g_dwIterPos == 1)
			*((KP_KEYPROV_INFO*)lParamL) = g_vKeyProv[1];
		else if(g_dwIterPos == 2)
			*((KP_KEYPROV_INFO*)lParamL) = g_vKeyProv[2];
		break;

	case KPM_KEYPROV_QUERY_KEY:
		_QueryProvKey((LPCTSTR)lParamW, (KP_KEYPROV_KEY*)lParamL);
		break;

	case KPM_KEYPROV_FINALIZE:
		// Nothing to clean up in this small sample
		break;

	default:
		break;
	}

	return TRUE;
}

void _QueryProvKey(LPCTSTR lpProvDisplayName, KP_KEYPROV_KEY* lpKey)
{
	// Some sample keys. In a real key provider plugin, the key
	// would be retrieved from smart card, USB device, ...
	static BYTE vKey1[4] = { 1, 2, 3, 4 };
	static BYTE vKey2[8] = { 2, 4, 6, 8, 10, 12, 14, 16 };
	static BYTE vKey3[10] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };

	if(_tcscmp(g_vKeyProv[0].lpName, lpProvDisplayName) == 0)
	{
		lpKey->lpData = &vKey1[0];
		lpKey->dwDataSize = 4;
	}
	else if(_tcscmp(g_vKeyProv[1].lpName, lpProvDisplayName) == 0)
	{
		lpKey->lpData = &vKey2[0];
		lpKey->dwDataSize = 8;
	}
	else if(_tcscmp(g_vKeyProv[2].lpName, lpProvDisplayName) == 0)
	{
		lpKey->lpData = &vKey3[0];
		lpKey->dwDataSize = 10;
	}
}

KPM_KEYPROV_QUERY_INFO_FIRST and KPM_KEYPROV_QUERY_INFO_NEXT Messages

These two messages are used to query information about the keys you provide. The KPM_KEYPROV_QUERY_INFO_FIRST message is sent once, afterwards the KPM_KEYPROV_QUERY_INFO_NEXT message is sent repeatedly until you don't provide a new key any more. The parameters are the same for both messages:

lParamW [in]: NULL. Reserved for future use.

lParamL [out]: Pointer to a KP_KEYPROV_INFO structure.

You need to fill out the KP_KEYPROV_INFO structure in lParamL. If you don't want to return a provider, simply leave this structure untouched.


KPM_KEYPROV_QUERY_KEY Message

This message is sent to your plugin when the user has selected one of the keys you provide and has clicked [OK]. Parameters:

lParamW [in]: Pointer to a string: displayed name in the combo box that has been selected.

lParamL [out]: Pointer to a KP_KEYPROV_KEY structure.

You should fill out the KP_KEYPROV_KEY structure in lParamL in order to return a key.

In the sample source code above, constant keys are returned. In an actual key provider plugin, you'd here build up the actual key based on USB device ID, smart card, ...


KPM_KEYPROV_QUERY_KEY_EX Message

This message is sent to your plugin when the user has selected one of the keys you provide and has clicked [OK]. Parameters:

lParamW [in]: Pointer to a KP_KEYPROV_CONTEXT structure.

lParamL [out]: Pointer to a KP_KEYPROV_KEY structure.

You should fill out the KP_KEYPROV_KEY structure in lParamL in order to return a key.

This message is an extended version of the KPM_KEYPROV_QUERY_KEY message. Its lParamW is a pointer to a KP_KEYPROV_CONTEXT, which contains more information about the requested key and context.

Your plugin should either reply to KPM_KEYPROV_QUERY_KEY or to KPM_KEYPROV_QUERY_KEY_EX.


KP_KEYPROV_INFO Structure

Each key that you provide (certificate, smart card, ...) must be registered by responding to KPM_KEYPROV_QUERY_INFO_FIRST and KPM_KEYPROV_QUERY_INFO_NEXT messages by filling out the structure in lParamL. This structure looks like the following:

  • DWORD dwFlags: Reserved for future use, must be 0.
  • LPCTSTR lpName: Unique display name of the key.
  • DWORD dwImageIndex: Index of the icon shown in the combo box.
  • DWORD dwReserved: Reserved for future use, must be 0.

Make sure that the lpName pointer you return is still valid when KeePass tries to read from it. The message processing function will already be finished when KeePass reads the pointer, therefore you should store the name in some global/static place.

Icon IDs specify the index of the icon in the built-in icons list of KeePass that is used in the combo box. Custom icons are not supported.


Returning a Key In KP_KEYPROV_KEY

In the KPM_KEYPROV_QUERY_KEY message, you need to return the actual key by filling out the KP_KEYPROV_KEY structure in lParamL.

  • DWORD dwType: Reserved for future use, must be 0.
  • DWORD dwFormat: Reserved for future use, must be 0.
  • LPVOID lpData: Key data pointer.
  • DWORD dwDataSize: Size of the key (lpData) in bytes.
  • DWORD dwReserved: Reserved for future use, must be 0.

Make sure that the lpData pointer you return is still valid when KeePass tries to read from it. The message processing function will already be finished when KeePass reads the pointer, therefore you should store the key data in some global/static place.

You can clean up the global/static memory when you get the KPM_KEYPROV_FINALIZE message (it is sent when the dialog is closed and KeePass has successfully copied the key you provided). It is highly recommended that you zeroize out key memory before releasing it.









Get KeePass