|
/* |
|
* Cosm-connected product activation code generator. |
|
* |
|
* Authors: |
|
* Andrei Sambra <[email protected]> |
|
* Ilya Dmitrichenko <[email protected]> |
|
* GPLv3 License applies to this code. |
|
* |
|
* */ |
|
#include <linux/module.h> |
|
#include <linux/kernel.h> |
|
#include <linux/init.h> |
|
#include <linux/crypto.h> |
|
#include <linux/err.h> |
|
#include <linux/scatterlist.h> |
|
#include <linux/gfp.h> |
|
#include <crypto/hash.h> |
|
#include <linux/device.h> |
|
|
|
#define COSM_API_VERSION 2 |
|
|
|
#include "product.h" |
|
static uint8_t secret[] = PRODUCT_SECRET; |
|
static char *serial = DEFAULT_SERIAL; |
|
|
|
static char hmac[40]; |
|
|
|
struct hmac_sha1_result { |
|
struct completion completion; |
|
int err; |
|
}; |
|
|
|
|
|
static ssize_t device_activation_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) |
|
{ |
|
return scnprintf(buf, PAGE_SIZE, "%s\n", hmac); |
|
} |
|
|
|
static ssize_t api_version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { |
|
return scnprintf(buf, PAGE_SIZE, "v%d\n", COSM_API_VERSION); |
|
} |
|
|
|
struct kobject *cosm_kobj; |
|
static struct kobj_attribute device_activation_attribute = __ATTR_RO(device_activation); |
|
static struct kobj_attribute api_version_attribute = __ATTR_RO(api_version); |
|
|
|
/* |
|
* Create groups of attributes so that we can create and destroy them all |
|
* at once. |
|
*/ |
|
static struct attribute *main_attrs[] = { |
|
&device_activation_attribute.attr, |
|
&api_version_attribute.attr, |
|
NULL, |
|
}; |
|
|
|
/* |
|
* An unnamed attribute group will put all of the attributes directly in |
|
* the kobject directory. If we specify a name, a subdirectory will be |
|
* created for the attributes with the directory being the name of the |
|
* attribute group. |
|
*/ |
|
static struct attribute_group main_attr_group = { |
|
.attrs = main_attrs, |
|
}; |
|
|
|
|
|
static void hmac_sha1_complete(struct crypto_async_request *req, int err) { |
|
struct hmac_sha1_result *r=req->data; |
|
if(err==-EINPROGRESS) |
|
return; |
|
r->err=err; |
|
complete(&r->completion); |
|
} |
|
|
|
static int do_hmac_sha1(uint8_t *key, size_t klen, // key and key length |
|
char *data_in, size_t dlen, // data in and length |
|
char *hash_out, size_t outlen) { // hash buffer and length |
|
int rc=0; |
|
struct crypto_ahash *tfm; |
|
struct scatterlist sg; |
|
struct ahash_request *req; |
|
struct hmac_sha1_result tresult; |
|
void *hash_buf; |
|
|
|
int len = 20; |
|
char hash_tmp[20]; |
|
char *hash_res = hash_tmp; |
|
|
|
/* Set hash output to 0 initially */ |
|
memset(hash_out, 0, outlen); |
|
|
|
init_completion(&tresult.completion); |
|
tfm=crypto_alloc_ahash("hmac(sha1)",0,0); |
|
if(IS_ERR(tfm)) { |
|
printk(KERN_ERR "do_hmac_sha1: crypto_alloc_ahash failed\n"); |
|
rc=PTR_ERR(tfm); |
|
goto err_tfm; |
|
} |
|
if(!(req=ahash_request_alloc(tfm,GFP_KERNEL))) { |
|
printk(KERN_ERR "do_hmac_sha1: failed to allocate request for hmac(sha1)\n"); |
|
rc=-ENOMEM; |
|
goto err_req; |
|
} |
|
if(crypto_ahash_digestsize(tfm)>len) { |
|
printk(KERN_ERR "do_hmac_sha1: tfm size > result buffer\n"); |
|
rc=-EINVAL; |
|
goto err_req; |
|
} |
|
ahash_request_set_callback(req,CRYPTO_TFM_REQ_MAY_BACKLOG, |
|
hmac_sha1_complete,&tresult); |
|
|
|
if(!(hash_buf=kzalloc(dlen,GFP_KERNEL))) { |
|
printk(KERN_ERR "do_hmac_sha1: failed to kzalloc hash_buf\n"); |
|
rc=-ENOMEM; |
|
goto err_hash_buf; |
|
} |
|
memcpy(hash_buf,data_in,dlen); |
|
sg_init_one(&sg,hash_buf,dlen); |
|
|
|
crypto_ahash_clear_flags(tfm,-0); |
|
if((rc=crypto_ahash_setkey(tfm,key,klen))){ |
|
printk(KERN_ERR "do_hmac_sha1: crypto_ahash_setkey failed\n"); |
|
goto err_setkey; |
|
} |
|
ahash_request_set_crypt(req,&sg,hash_res,dlen); |
|
rc=crypto_ahash_digest(req); |
|
switch(rc) { |
|
case 0: |
|
while (len--) { |
|
snprintf(hash_out, outlen, "%02x", (*hash_res++ & 0x0FF)); |
|
hash_out += 2; |
|
} |
|
|
|
break; |
|
case -EINPROGRESS: |
|
case -EBUSY: |
|
rc=wait_for_completion_interruptible(&tresult.completion); |
|
if(!rc && !(rc=tresult.err)) { |
|
INIT_COMPLETION(tresult.completion); |
|
break; |
|
} else { |
|
printk(KERN_ERR "do_hmac_sha1: wait_for_completion_interruptible failed\n"); |
|
goto out; |
|
} |
|
default: |
|
goto out; |
|
} |
|
|
|
out: |
|
err_setkey: |
|
kfree(hash_buf); |
|
err_hash_buf: |
|
ahash_request_free(req); |
|
err_req: |
|
crypto_free_ahash(tfm); |
|
err_tfm: |
|
return rc; |
|
} |
|
|
|
static int __init init_main(void) |
|
{ |
|
int retval; |
|
cosm_kobj = kobject_create_and_add("cosm", NULL); |
|
//if(!cosm_kobj) { |
|
// retval = -ENOMEM; |
|
// goto out; |
|
//} |
|
do_hmac_sha1(secret, strlen(secret), serial, strlen(serial), hmac, sizeof(hmac)); |
|
retval = sysfs_create_group(cosm_kobj, &main_attr_group); |
|
//if(!retval) { |
|
// goto err_sysfs; |
|
//} else { |
|
// printk(KERN_DEBUG "HERE!"); |
|
// goto out; |
|
//} |
|
//err_sysfs: |
|
// sysfs_remove_group(cosm_kobj, &main_attr_group); |
|
//out: |
|
printk(KERN_DEBUG "init_main -> %d\n", retval); |
|
return retval; |
|
} |
|
|
|
static void __exit cleanup_main(void) |
|
{ |
|
sysfs_remove_group(cosm_kobj, &main_attr_group); |
|
kobject_put(cosm_kobj); |
|
} |
|
|
|
module_init(init_main); |
|
module_exit(cleanup_main); |
|
|
|
MODULE_LICENSE("GPL"); |
|
MODULE_AUTHOR("Ilya Dmitrichenko <[email protected]>"); |
|
MODULE_DESCRIPTION("Cosm-connected product activation code generator"); |
|
|
|
module_param(serial, charp, S_IRUGO); |
|
MODULE_PARM_DESC(serial, "A device serial number string"); |