Keyrings gsoc project status update

Vivien Bernet-Rollande vivien.bernet-rollande at etu.utc.fr
Wed Jul 2 14:00:22 EDT 2008


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi everyone.

I've added Trannie Carter to  the recipients as I'm not sure he's
suscribed to the devel list, and he might be interested.

OK, so my exams finished last week, and I started effectively working
on the project friday. I've been spending most of that time reading
documentation and code. Namely, documentation on cryptography and key
storage, glib, gtk, gnome keyring, KWallet, Windows data protection
API, and a couple more things. I've also spent some time visiting a
few old friends from highschool, as well as fixing a few computers in
the neigbourhood. I've spent some time reading libpurple's code too,
to have a better idea of the "pidgin way of doing things". Also, i've
started reading Trannie Carter's code.
There are a couple things in that code that I might change, but i am
most thankful for his async code that gives me a great base to work
on. For now, I'm waiting for him to complete his merge with trunk, so
I can get a clear diff and do the few changes I have in head (mainly
rewrite a few thing using gobjects, slightly extend the API, and a few
changes suggested by Richard).

So, in the mean time, I'm working on plugins themselves.

Since code already exists for KWallet and Gnome-Keyring, the first
thing I'm going to do, starting tonight, is write a "cleartext" plugin
that is compatible with the existing storage system. Once this is
done, I'll extend it to also support a master password mode.
This mode will be using PCKS#5's PBKDF2 key derivation scheme, based
on HMAC-SHA-1 (see PKCS#5 for details), although any pseudo random
generator could be used at that point. Once that key is derived, I'll
use a block cipher (probably blowfish, but anything would go), to
encrypt passwords in chain block mode. Passwords will probably be
encrypted in CBC mode, using a hash to detect errors. I've got the
details on dead tree draft paper, so if someone wants me to type it
out and send, please feel free to ask.

Once i have that, I'll hex-encode it and store the result in
accounts.xml. Since I'll store an error detection hash (probably like
the first 8 bytes of a SHA1 hash) allong with the password, that will
make a string of (8 bytes + one int + password)*2 chars take or leave
a few bytes for the block size.
Now, the best way I can think of for the storage in the xml file is
the following :

    <account>
        <protocol>prpl-whatever</protocol>
        <name>scrouaf at account</name>
        <password encryption=internal
option=masterpassword>12afb097ead9897</password>
    </account>

The idea is that I can just use encryption=off or remove the field to
store cleartext. Or I could even do something like :

    <account>
        <protocol>prpl-whatever</protocol>
        <name>scrouaf at account</name>
        <password encryption=internal
option=cleartext>mys3cr3Tpassword</password>
    </account>

It only requires a few minor fixes accounts.xml, and keeps cleartext
compatibility with the existing code. The only problem would be if a
recent file, using encryption, was opened by an older version of
pidgin. If someone thinks this is indeed an issue, let me know, I'll
have to change the name of the field when the password is encrypted.
I'd get something like this :

    <account>
        <protocol>prpl-whatever</protocol>
        <name>scrouaf at account</name>
        <encyptedpassword encryption=internal
option=masterpassword>12afb097ead9897</password>
    </account>

And, on the old version, the user would be prompted for the password.
I find the first one more elegant, but if we're looking for total
compatibility, then I guess I'll have to go for the second one.

Now, the cool thing is, once this is done, writing the Windows DPAPI
plugin will be a piece of cake, since it will pretty much re-use
existing code. Some this code could eventually be put in libpurple for
other plugin developpers to use.

Once I'm done with this, I hope Trannie will be done with his merge as
well, and I'll work on a couple changes to libpurple and pidgin. (And
probably finch as well, although i'll first need to learn how to use
finch).

Now, a few words on the API. I hope this is the final version ;). It
takes into account Trannie's code, and what has been said about the
previous version of that API both on the mailing list and IRC.

The general idea is pretty close to Trannie Carter's code except I've
added the possibility for return values (int result) and error
management and forwarding, and options. Older versions of this API
would pass a master password but it makes sense to remove the
masterpassword argument. In that case, the plugin would have to call a
slightly modified version of purple_account_request_password() (this
functions takes an account as argument, we would want it to take a
string. So I'll write a function that takes a string, and then get
purple_account_request_password() to use that function to avoid
duplicate code). However, the plugin would still need to know if it is
operating with a master password or not. The same thing goes with the
master password change : plugins will call a modified version of
purple_account_request_change_password(), and do everything themselves.

What needs to be done :
    Store a password (master password is taken care of by the plugin)
    Retrieve a password (master password is taken care of by the plugin)
    Forget/delete a password
    Know if a master password can/must be used
    Update a password
    change master password
    close the keyring (open is of course automatic)
    free/clean memory allocated for a password

Several solutions exist, but the one most likely to be used is the
following :
 - A NULL password is passed to remove the account from the keyring.
This allows us
 - The first idea for the fift problem is for libpurple to update the
master password, libpurple reads the password, erases it, and stores
it back. But considering some keyrings might have a keyring-wide
master password, i might be better to have one function to do it all.
So this will have it's own function
 - to update a password, a new password is simply written
 - close is a simple function call
 - now, for the delicate part. We need to know what the capabilities
for our plugin are. There are several ways for that too, but the one I
prefer is the following : a bitmask is stored allong with the
structure that contains all the info on the plugin. Using a function,
or even worse, return error codes for such a simple task would be
pretty ugly imho. So unless someone points out a reason that changes
my mind, it will be a bitmask and a couple macros.

So, now we know what libpurple needs to know about each plugin :
 - read a password
 - store a password
 - change master password
 - capabilities bitmask
 - close the safe
 - free a password's memory    ( no need to make that async )

So, now we know what we need to register a plugin, let's write some
defs (copy/pasted from my current keyring.h).

typedef void (*PurpleKeyringReadCallback)(const PurpleAccount *
account, int result, gchar * password, GError * error, gpointer data);
typedef void (*PurpleKeyringSaveCallback)(const PurpleAccount *
account, int result, GError * error, gpointer data);
typedef void (*PurpleKeyringCloseCallback)(int result, Gerror * error,
gpointer data);    /* async just so we can check for errors */
typedef void (*PurpleKeyringChangeMasterCallback)(int result, Gerror *
error, gpointer data);

typedef void (*PurpleKeyringRead)(const PurpleAccount * account,
GError ** error, PurpleKeyringReadCallback cb, gpointer data);
typedef void (*PurpleKeyringSave)(const PurpleAccount * account, gchar
* password, GError ** error, PurpleKeyringSaveCallback cb, gpointer data);
typedef void (*PurpleKeyringClose)(GError ** error, gpointer data);
typedef void (*PurpleKeyringChangeMaster)(GError ** error,
PurpleKeyringChangeMasterCallback cb, gpointer data);
typedef void (*PurpleKeyringFree)(gchar * password, GError ** error);

typedef void (*PurpleKeyringSetOps)(int ops);

typedef struct _PurpleKeyringPluginInfo {
    PurpleKeyringRead read_password;
    PurpleKeyringSave save_password;
    PurpleKeyringClose close_keyring;
    PurpleKeyringFree free_password;
    PurpleKeyringChangeMaster change_master;
    PurpleKeyringUpdateOps update_options;
    int capabilities;
    int options;
    gpointer r1;    /* RESERVED */
    gpointer r2;    /* RESERVED */
    gpointer r3;    /* RESERVED */
} PurpleKeyringPluginInfo;

void purple_plugin_keyring_register(PurpleKeyringPluginInfo * info) /*
do we need a callback ? probably not */

For now, the only options passed to a plugin I can think of are wether
to use master password or not, and wether the plugin is active or not.
Others might come later though, so if they are not all binary, I'll
pass a structure pointer instead of just a bitmask. Comments on this
are very welcome. I'm thinking about allowing plugins to store their
own config, but it seems like it's already possible in an easyer way.

Now, we want to keep track of what plugin is currently being used, and
how.
Trannie Carter's code allows us to have a different keyring for each
account, but I'd rather have only one plugin doing all accounts. This
means in our case we'll have a linked list of all registered plugins,
and a single global pointer to the currently active plugin.

In the preferences, I'll add information on which plugin is being
used, as well as wether it's currently using a master password. This
will most likely be done through the same way other preferences are
accessed and stored. I haven't look into that part in details yet, but
from what I remember, libpurple offers an easy way to do that. I might
also add an "advanced" configuration tab so that plugins could offer
their own config interface, but for now I believe it's not really needed.

Also, I have a few questions :
 - In a plugin, there seem to be several ways to execute stuff at
startup : init_plugin, plugin_load, and plugin_actions. I understand
that plugin_actions is there to register plugin actions. But what are
the differences between init_plugin and plugin_load ? If what I
understand is right, the keyring plugin should register itself in
plugin_actions, but I might very well be wrong.
 - The windows PDAPI plugin will use MS librairies, and therefore
headers from the SDK. Should I take these headers and include them as
is in the pidgin tree ? Should I copy/paste only the useful parts ?
Should I assume that the build system will have them installed ? Or
should I, because of licensing considerations, re-write entirely the
parts that I need ?
 - I believe we are not using OpenSSL for licensing reasons. Are we
using another crypto librairy ? Or should I grab code from GPL/public
domain files ? In that case, is mentionning the origin of that
specific code in the source file enough, or should that be mentionned
elsewhere ?

Anyone having comments on the API or on anything, as well as answers
to my questions is, as usual, most welcome.

Oh, also, I'm currently at my grandparents. I'm working all day, but
the DSL modem has a hard time synching, so I'm not on IRC all the time
as I usually am.

- -- Vivien
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFIa8I230k75O5OLaURApYrAJ9fEDdJiRhW/9SsOABae2XdFVxx4gCcCZyB
RKt+T+RWuXNskionhpk9qy4=
=Xle3
-----END PGP SIGNATURE-----




More information about the Devel mailing list