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
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.
|