Skip to content

Instantly share code, notes, and snippets.

@errordeveloper
Last active December 10, 2015 10:39
Show Gist options
  • Save errordeveloper/4422798 to your computer and use it in GitHub Desktop.
Save errordeveloper/4422798 to your computer and use it in GitHub Desktop.
Cosm-connected device activation code generator (kernel-based)

This Linux kernel module utilises ready-available HMAC-SHA1 functions to generate Cosm device activation code. You can pass it the device's serial number (whatever you want it to be) via load parameter.

This is better then cosm_provisioning.sh, as it doesn't require Bash or OpenSSL installed on a constrained taget.

USAGE

You can load it from an init script like so:

sudo insmod cosm-prov-basic.ko serial=`cat /sys/class/net/eth0/address`

Or pass the serial parameter from uBoot, where the MAC address is also known. Althouth you can use a custom serial number for your devices.

Once loaded, you can fetch you API credentials:

curl -s "https://api.cosm.com/v2/devices/`cat /sys/cosm/device_activation`/activate.csv" > /etc/cosm_device

So it might be a good idea to do something like:

#!/bin/sh
if [ ! -f /etc/cosm_device ]; then
  insmod cosm-prov-basic.ko serial=`cat /sys/class/net/eth0/address`
  curl -s "https://api.cosm.com/v2/devices/`cat /sys/cosm/device_activation`/activate.csv" > /etc/cosm_device
fi
API_KEY=`cat /etc/cosm_device | cut -f1 -d,`
FEED_ID=`cat /etc/cosm_device | cut -f2 -d,`
## The serial number is read back from the module parameters, as it is more reliable
## (in case you are not useing MAC addresses as your product's serials)
echo "Provisioned device with Cosm feed $FEED_ID (S/N: `cat /sys/module/cosm_prov_basic/parameters/serial`)."

NOTE: In the event of factory-reset, /etc/cosm_device should be removed from the filesystem.

TODO

  • OpenEmbedded metadata layer (meta-cosm-sdk)
    • minimalised version of curl
    • minimalised version of openssl (optional) and gnutls
    • trimmed down busybox (no wget, no ftp - there is curl for that)
    • git-core without perl

FYI

  • /usr/bin/curl: 0.12MB
  • /usr/bin/openssl: 0.44MB
  • /bin/busybox: 0.5MB
/*
* Cosm-connected product activation code generator.
*
* Authors:
* Andrei Sambra <andrei.sambra@telecom-sudparis.eu>
* Ilya Dmitrichenko <ilya.dmitrichenko@cosm.com>
* 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 <ilya.dmitrichenko@cosm.com>");
MODULE_DESCRIPTION("Cosm-connected product activation code generator");
module_param(serial, charp, S_IRUGO);
MODULE_PARM_DESC(serial, "A device serial number string");
config COSM_PROV_BASIC
tirstate "Cosm-connected product activation code generator"
default y
select CRYPTO_HMAC && CRYPTO_SHA1
obj-m += cosm-prov-basic.o
MAKE := make -j3 -C /lib/modules/$(shell uname -r)/build M=$(PWD)
all:
$(MAKE) modules
clean:
$(MAKE) clean
#define PRODUCT_SECRET { 0x7c, 0xd9, 0x50, 0xa5, 0x9f, 0x05, 0xc0, 0xc5, 0x2a, 0x6c, 0x49, 0x26, 0x29, 0x8a, 0x77, 0x5f, 0x91, 0xd0, 0x09, 0x1d }
#define DEFAULT_SERIAL "00:0c:29:4d:cf:f1"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment