[CRYPTO] Allow multiple implementations of the same algorithm
authorHerbert Xu <herbert@gondor.apana.org.au>
Sat, 5 Nov 2005 05:58:14 +0000 (16:58 +1100)
committerDavid S. Miller <davem@sunset.davemloft.net>
Mon, 9 Jan 2006 22:15:37 +0000 (14:15 -0800)
This is the first step on the road towards asynchronous support in
the Crypto API.  It adds support for having multiple crypto_alg objects
for the same algorithm registered in the system.

For example, each device driver would register a crypto_alg object
for each algorithm that it supports.  While at the same time the
user may load software implementations of those same algorithms.

Users of the Crypto API may then select a specific implementation
by name, or choose any implementation for a given algorithm with
the highest priority.

The priority field is a 32-bit signed integer.  In future it will be
possible to modify it from user-space.

This also provides a solution to the problem of selecting amongst
various AES implementations, that is, aes vs. aes-i586 vs. aes-padlock.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
crypto/api.c
crypto/internal.h
crypto/proc.c
include/linux/crypto.h

index 40ae42e9b6a62acd1047f8be7ecb09600f794e97..2715afdf678c19c7b908d01c76c3fbce1160a9df 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
  * Copyright (c) 2002 David S. Miller (davem@redhat.com)
+ * Copyright (c) 2005 Herbert Xu <herbert@gondor.apana.org.au>
  *
  * Portions derived from Cryptoapi, by Alexander Kjeldaas <astor@fast.no>
  * and Nettle, by Niels Möller.
 #include <linux/init.h>
 #include <linux/crypto.h>
 #include <linux/errno.h>
+#include <linux/kernel.h>
 #include <linux/kmod.h>
 #include <linux/rwsem.h>
 #include <linux/slab.h>
+#include <linux/string.h>
 #include "internal.h"
 
 LIST_HEAD(crypto_alg_list);
@@ -39,6 +42,7 @@ static inline void crypto_alg_put(struct crypto_alg *alg)
 static struct crypto_alg *crypto_alg_lookup(const char *name)
 {
        struct crypto_alg *q, *alg = NULL;
+       int best = -1;
 
        if (!name)
                return NULL;
@@ -46,11 +50,23 @@ static struct crypto_alg *crypto_alg_lookup(const char *name)
        down_read(&crypto_alg_sem);
        
        list_for_each_entry(q, &crypto_alg_list, cra_list) {
-               if (!(strcmp(q->cra_name, name))) {
-                       if (crypto_alg_get(q))
-                               alg = q;
+               int exact, fuzzy;
+
+               exact = !strcmp(q->cra_driver_name, name);
+               fuzzy = !strcmp(q->cra_name, name);
+               if (!exact && !(fuzzy && q->cra_priority > best))
+                       continue;
+
+               if (unlikely(!crypto_alg_get(q)))
+                       continue;
+
+               best = q->cra_priority;
+               if (alg)
+                       crypto_alg_put(alg);
+               alg = q;
+
+               if (exact)
                        break;
-               }
        }
        
        up_read(&crypto_alg_sem);
@@ -207,9 +223,26 @@ void crypto_free_tfm(struct crypto_tfm *tfm)
        kfree(tfm);
 }
 
+static inline int crypto_set_driver_name(struct crypto_alg *alg)
+{
+       static const char suffix[] = "-generic";
+       char *driver_name = (char *)alg->cra_driver_name;
+       int len;
+
+       if (*driver_name)
+               return 0;
+
+       len = strlcpy(driver_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
+       if (len + sizeof(suffix) > CRYPTO_MAX_ALG_NAME)
+               return -ENAMETOOLONG;
+
+       memcpy(driver_name + len, suffix, sizeof(suffix));
+       return 0;
+}
+
 int crypto_register_alg(struct crypto_alg *alg)
 {
-       int ret = 0;
+       int ret;
        struct crypto_alg *q;
 
        if (alg->cra_alignmask & (alg->cra_alignmask + 1))
@@ -220,11 +253,18 @@ int crypto_register_alg(struct crypto_alg *alg)
 
        if (alg->cra_blocksize > PAGE_SIZE)
                return -EINVAL;
+
+       if (alg->cra_priority < 0)
+               return -EINVAL;
        
+       ret = crypto_set_driver_name(alg);
+       if (unlikely(ret))
+               return ret;
+
        down_write(&crypto_alg_sem);
        
        list_for_each_entry(q, &crypto_alg_list, cra_list) {
-               if (!(strcmp(q->cra_name, alg->cra_name))) {
+               if (!strcmp(q->cra_driver_name, alg->cra_driver_name)) {
                        ret = -EEXIST;
                        goto out;
                }
index 37aa652ce5ce6f89e4738ac07dd770b8df4a6ac9..959e602909a60e1042e2d07d4d19471d9b0cd23c 100644 (file)
@@ -2,6 +2,7 @@
  * Cryptographic API.
  *
  * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
+ * Copyright (c) 2005 Herbert Xu <herbert@gondor.apana.org.au>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free
 #include <linux/highmem.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
+#include <linux/list.h>
 #include <linux/kernel.h>
+#include <linux/rwsem.h>
 #include <linux/slab.h>
 #include <asm/kmap_types.h>
 
+extern struct list_head crypto_alg_list;
+extern struct rw_semaphore crypto_alg_sem;
+
 extern enum km_type crypto_km_types[];
 
 static inline enum km_type crypto_kmap_type(int out)
index 630ba91c08f139fa02a84ef970867eaf1887edee..c0a5dd7ce2ccbacc98b419b8667853bcdf5c2d73 100644 (file)
@@ -4,6 +4,7 @@
  * Procfs information.
  *
  * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
+ * Copyright (c) 2005 Herbert Xu <herbert@gondor.apana.org.au>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free
@@ -18,9 +19,6 @@
 #include <linux/seq_file.h>
 #include "internal.h"
 
-extern struct list_head crypto_alg_list;
-extern struct rw_semaphore crypto_alg_sem;
-
 static void *c_start(struct seq_file *m, loff_t *pos)
 {
        struct list_head *v;
@@ -53,7 +51,9 @@ static int c_show(struct seq_file *m, void *p)
        struct crypto_alg *alg = (struct crypto_alg *)p;
        
        seq_printf(m, "name         : %s\n", alg->cra_name);
+       seq_printf(m, "driver       : %s\n", alg->cra_driver_name);
        seq_printf(m, "module       : %s\n", module_name(alg->cra_module));
+       seq_printf(m, "priority     : %d\n", alg->cra_priority);
        
        switch (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) {
        case CRYPTO_ALG_TYPE_CIPHER:
index 3c89df6e7768451acf625236790753f2ab7f5139..d88bf8aa8b4776c736ab5500679b71683f973c03 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
  * Copyright (c) 2002 David S. Miller (davem@redhat.com)
+ * Copyright (c) 2005 Herbert Xu <herbert@gondor.apana.org.au>
  *
  * Portions derived from Cryptoapi, by Alexander Kjeldaas <astor@fast.no>
  * and Nettle, by Niels Möller.
@@ -126,7 +127,11 @@ struct crypto_alg {
        unsigned int cra_blocksize;
        unsigned int cra_ctxsize;
        unsigned int cra_alignmask;
+
+       int cra_priority;
+
        const char cra_name[CRYPTO_MAX_ALG_NAME];
+       const char cra_driver_name[CRYPTO_MAX_ALG_NAME];
 
        union {
                struct cipher_alg cipher;