2006-08-21 15:08:13 +04:00
/*
* Cryptographic API for algorithms ( i . e . , low - level API ) .
*
* Copyright ( c ) 2006 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
* Software Foundation ; either version 2 of the License , or ( at your option )
* any later version .
*
*/
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/kernel.h>
2006-08-06 15:16:34 +04:00
# include <linux/list.h>
2006-08-21 15:08:13 +04:00
# include <linux/module.h>
# include <linux/string.h>
# include "internal.h"
2006-08-06 15:16:34 +04:00
static LIST_HEAD ( crypto_template_list ) ;
2006-08-21 15:08:13 +04:00
static inline int crypto_set_driver_name ( struct crypto_alg * alg )
{
static const char suffix [ ] = " -generic " ;
char * driver_name = 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 ;
}
2006-08-06 15:16:34 +04:00
static int crypto_check_alg ( struct crypto_alg * alg )
2006-08-21 15:08:13 +04:00
{
if ( alg - > cra_alignmask & ( alg - > cra_alignmask + 1 ) )
return - EINVAL ;
if ( alg - > cra_alignmask & alg - > cra_blocksize )
return - EINVAL ;
if ( alg - > cra_blocksize > PAGE_SIZE / 8 )
return - EINVAL ;
if ( alg - > cra_priority < 0 )
return - EINVAL ;
2006-08-06 15:16:34 +04:00
return crypto_set_driver_name ( alg ) ;
}
static int __crypto_register_alg ( struct crypto_alg * alg )
{
struct crypto_alg * q ;
int ret = - EEXIST ;
2006-08-21 15:08:13 +04:00
list_for_each_entry ( q , & crypto_alg_list , cra_list ) {
2006-08-06 15:16:34 +04:00
if ( q = = alg )
2006-08-21 15:08:13 +04:00
goto out ;
}
list_add ( & alg - > cra_list , & crypto_alg_list ) ;
atomic_set ( & alg - > cra_refcnt , 1 ) ;
2006-08-06 15:16:34 +04:00
ret = 0 ;
2006-08-21 15:08:13 +04:00
out :
return ret ;
}
2006-08-06 15:16:34 +04:00
int crypto_register_alg ( struct crypto_alg * alg )
{
int err ;
err = crypto_check_alg ( alg ) ;
if ( err )
return err ;
down_write ( & crypto_alg_sem ) ;
err = __crypto_register_alg ( alg ) ;
up_write ( & crypto_alg_sem ) ;
return err ;
}
2006-08-21 15:08:13 +04:00
EXPORT_SYMBOL_GPL ( crypto_register_alg ) ;
int crypto_unregister_alg ( struct crypto_alg * alg )
{
int ret = - ENOENT ;
down_write ( & crypto_alg_sem ) ;
2006-08-06 15:16:34 +04:00
if ( likely ( ! list_empty ( & alg - > cra_list ) ) ) {
list_del_init ( & alg - > cra_list ) ;
ret = 0 ;
2006-08-21 15:08:13 +04:00
}
up_write ( & crypto_alg_sem ) ;
if ( ret )
return ret ;
BUG_ON ( atomic_read ( & alg - > cra_refcnt ) ! = 1 ) ;
if ( alg - > cra_destroy )
alg - > cra_destroy ( alg ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( crypto_unregister_alg ) ;
2006-08-06 15:16:34 +04:00
int crypto_register_template ( struct crypto_template * tmpl )
{
struct crypto_template * q ;
int err = - EEXIST ;
down_write ( & crypto_alg_sem ) ;
list_for_each_entry ( q , & crypto_template_list , list ) {
if ( q = = tmpl )
goto out ;
}
list_add ( & tmpl - > list , & crypto_template_list ) ;
err = 0 ;
out :
up_write ( & crypto_alg_sem ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( crypto_register_template ) ;
void crypto_unregister_template ( struct crypto_template * tmpl )
{
struct crypto_instance * inst ;
struct hlist_node * p , * n ;
struct hlist_head * list ;
down_write ( & crypto_alg_sem ) ;
BUG_ON ( list_empty ( & tmpl - > list ) ) ;
list_del_init ( & tmpl - > list ) ;
list = & tmpl - > instances ;
hlist_for_each_entry ( inst , p , list , list ) {
BUG_ON ( list_empty ( & inst - > alg . cra_list ) ) ;
list_del_init ( & inst - > alg . cra_list ) ;
}
up_write ( & crypto_alg_sem ) ;
hlist_for_each_entry_safe ( inst , p , n , list , list ) {
BUG_ON ( atomic_read ( & inst - > alg . cra_refcnt ) ! = 1 ) ;
tmpl - > free ( inst ) ;
}
}
EXPORT_SYMBOL_GPL ( crypto_unregister_template ) ;
static struct crypto_template * __crypto_lookup_template ( const char * name )
{
struct crypto_template * q , * tmpl = NULL ;
down_read ( & crypto_alg_sem ) ;
list_for_each_entry ( q , & crypto_template_list , list ) {
if ( strcmp ( q - > name , name ) )
continue ;
if ( unlikely ( ! crypto_tmpl_get ( q ) ) )
continue ;
tmpl = q ;
break ;
}
up_read ( & crypto_alg_sem ) ;
return tmpl ;
}
struct crypto_template * crypto_lookup_template ( const char * name )
{
return try_then_request_module ( __crypto_lookup_template ( name ) , name ) ;
}
EXPORT_SYMBOL_GPL ( crypto_lookup_template ) ;
int crypto_register_instance ( struct crypto_template * tmpl ,
struct crypto_instance * inst )
{
int err = - EINVAL ;
if ( inst - > alg . cra_destroy )
goto err ;
err = crypto_check_alg ( & inst - > alg ) ;
if ( err )
goto err ;
inst - > alg . cra_module = tmpl - > module ;
down_write ( & crypto_alg_sem ) ;
err = __crypto_register_alg ( & inst - > alg ) ;
if ( err )
goto unlock ;
hlist_add_head ( & inst - > list , & tmpl - > instances ) ;
inst - > tmpl = tmpl ;
unlock :
up_write ( & crypto_alg_sem ) ;
err :
return err ;
}
EXPORT_SYMBOL_GPL ( crypto_register_instance ) ;
2006-08-21 15:08:13 +04:00
static int __init crypto_algapi_init ( void )
{
crypto_init_proc ( ) ;
return 0 ;
}
static void __exit crypto_algapi_exit ( void )
{
crypto_exit_proc ( ) ;
}
module_init ( crypto_algapi_init ) ;
module_exit ( crypto_algapi_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Cryptographic algorithms API " ) ;