Sunday, July 26, 2009

Win32CryptoKeyring and CryptedFileKeyring

We've finished the keyring for Windows: Win32CryptoKeyring. It is based on the Windows's CryptAPI. CryptAPI is a set of APIs which can encrypt/decrpty the data using the login user's info. Thus, the user dose not need to type in the password when he/she is going to unlock the data.

We've also created a CryptedFileKeyring in the similar way. The difference between Win32CryptoKeyring and CryptedFileKeyring is that CryptedFileKeyring uses the AES algorithm provided by PyCrypto to encrypt/decrypt users' passwords. This results that CryptedFileKeyring need the user input their password in encryption/decryption. This may be annoying, so this keyring is not encouraged for daily use.

Both keyrings extend the BasicFileKeyring in the lib. BasicFileKeyring is the abstract base class for general file keyring which supports encryption/decryption. You can created a keyring with your encrypt/decrypt algorithms by easily extending BasicFileKeyring.

For example, here is the source code for the UncryptedFileKeyring of the lib.
class UncrpytedFileKeyring(BasicFileKeyring):
    """A simple filekeyring which dose not encrypt the password.
    """
    def filename(self):
        """Return the filename of the password file. It should be
        "keyring_password.cfg" for Windows, ".keyring_password" for other
        platforms.
        """
        import sys
        if sys.platform in ['win32']:
            return "keyring_password.cfg"
        return ".keyring_password"

    def encrypt(self, password):
        """Directly return the password itself.
        """
        return password
    def decrypt(self, password_encrypted):
        """Directly return encrypted password.
        """
        return password_encrypted

    def supported(self):
        """Applicable for all platforms, but do not recommend.
        """
        return 0
Since UncryptFileKeyring dose not encrypt the password, its implementation is simple. The BasicFileKeyring handle all file parsing/stroring affairs. Here we just need due with the encryption/decryption.

Notice that there is a supported() method for the keyring. It is a new abstract method added for the KeyringBackend. Every keyring needs implement this method to tell if it is applicable for current environment.

We've also polish the code according to PEP 008. So some method names have been changed. Here is the new definition for the KeyringBackend.
class KeyringBackend():
    """The abstract base class of the keyring, every backend must implement
    this interface.
    """
    __metaclass__ = ABCMeta

    @abstractmethod
    def supported(self):
        """Return if this keyring supports current enviroment.
        -1: not applicable
         0: suitable
         1: recommended
        """
        return -1

    @abstractmethod
    def get_password(self, service, username): 
        """Get password of the username for the service
        """
        pass

    @abstractmethod
    def set_password(self, service, username, password): 
        """Set password for the username of the service
        """
        return -1

For more information, please visit our repository.

Sunday, July 12, 2009

KWallet Porting and the Keyring Backend Selection

Last week I've finished the KDE KWallet porting of the keyring lib. And now I'm working on the keyring backend selection feature of the lib. We are going to enable the user choosing the keyring both in runtime and by the config file.

There's already a pototype demo in our demo folder(demo/keyring_demo.py). Here's some snippets from the demo.
KEYRINGRC = ".keyringrc"

def load_keyring_by_config():
    """
    This function shows how to enable a keyring using config file
    """

    # create the config file
    f = open(KEYRINGRC,'w')
    f.writelines(["[backend]\n",
                  # the path for the user created keyring
                  "keyring-path= %s\n" % str(os.path.abspath(__file__))[:-16],
                  # the name of the keyring class 
                  "default-keyring=simplekeyring.SimpleKeyring\n" ])
    f.close()

    # import the keyring lib, the lib will automaticlly load the 
    # config file and load the user defined module
    import keyring

    # invoke the keyring to store and fetch the password
    if keyring.setpass("demo-service","tarek","passexample") == 0:
        print "password stored sucessful"
    print "password", keyring.getpass("demo-service","tarek")

    os.remove(KEYRINGRC)

def set_keyring_in_runtime():
    """
    This function shows how to create a keyring manully and use it in runtime
    """

    # define a new keyring class which extends the KeyringBackend
    import keyring.backend
    class TestKeyring(keyring.backend.KeyringBackend):
        def setpass(self,servicename,username,password): return 0 
        def getpass(self,servicename,username): return "password from TestKeyring"
    
    # set the keyring for keyring lib
    import keyring
    keyring.set_keyring(TestKeyring())

    # invoke the keyring lib
    if keyring.setpass("demo-service","tarek","passexample") == 0:
        print "password stored successful"
    print "password", keyring.getpass("demo-service","tarek")

That two funtions illustrate the process of enabling a user created keyring by the config file and set the keyring in the runtime. The keyring class that load from disk is stored in demo/simplekeyring.py. Here's the definition of the keyring class.
class SimpleKeyring(KeyringBackend):
    """Simple Keyring is a keyring which can store only one
    password in memory.
    """
    def __init__(self):
        self.password = ''
    def getpass(self,servicename,username):
        return self.password
    def setpass(self,servicename,username,password):
        print "calling SimpleKeyring.setpass()"
        self.password = password
        return 0 
For more details, please refer to our repository