mirror of
https://github.com/pumpitupdev/pumptools.git
synced 2026-10-01 19:27:57 +03:00
Version 1.08 with public source code release.
This commit is contained in:
@@ -0,0 +1,518 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#include "asn1.h"
|
||||
|
||||
static uint32_t sec_hasp_asn1_decode_int(const uint8_t* buffer, size_t* offset, uint32_t blen);
|
||||
static uint8_t* sec_hasp_asn1_decode_data(const uint8_t* buffer, size_t* offset, uint32_t blen);
|
||||
static char* sec_hasp_asn1_decode_str(const uint8_t* buffer, size_t* offset, uint32_t blen);
|
||||
static uint32_t sec_hasp_asn1_decode_length(const uint8_t* buffer, size_t* offset);
|
||||
static uint16_t sec_hasp_asn1_decode_tag(const uint8_t* buffer, size_t* offset);
|
||||
static void sec_hasp_asn1_encode_int(uint32_t val, uint8_t* buffer, size_t* offset);
|
||||
static void sec_hasp_asn1_encode_data(const uint8_t* data, size_t len, uint8_t* buffer, size_t* offset);
|
||||
static void sec_hasp_asn1_encode_str(const char* str, uint8_t* buffer, size_t* offset);
|
||||
static void sec_hasp_asn1_encode_length(uint32_t len, uint8_t* buffer, size_t* offset);
|
||||
static void sec_hasp_asn1_encode_tag(uint16_t tag, uint8_t* buffer, size_t* offset);
|
||||
|
||||
struct sec_hasp_asn1_store* sec_hasp_asn1_object_store_alloc(uint16_t oid, uint32_t size)
|
||||
{
|
||||
struct sec_hasp_asn1_store* tmp = malloc(sizeof(struct sec_hasp_asn1_store));
|
||||
|
||||
tmp->objects = malloc(sizeof(struct sec_hasp_asn1_object) * size);
|
||||
tmp->capacity = size;
|
||||
tmp->oid = oid;
|
||||
tmp->pos = 0;
|
||||
|
||||
memset(tmp->objects, 0, sizeof(struct sec_hasp_asn1_object) * size);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void sec_hasp_asn1_store_free(struct sec_hasp_asn1_store* store)
|
||||
{
|
||||
for (size_t i = 0; i < store->pos; i++) {
|
||||
if (store->objects[i].type == SEC_HASP_ASN1_VT_STR) {
|
||||
free(store->objects[i].str);
|
||||
} else if (store->objects[i].type == SEC_HASP_ASN1_VT_DATA) {
|
||||
free(store->objects[i].data);
|
||||
}
|
||||
}
|
||||
|
||||
free(store);
|
||||
}
|
||||
|
||||
void sec_hasp_asn1_object_add_int(struct sec_hasp_asn1_store* store, uint16_t tag, uint32_t value)
|
||||
{
|
||||
if (store->pos == store->capacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
store->objects[store->pos].tag = tag;
|
||||
store->objects[store->pos].type = SEC_HASP_ASN1_VT_INT;
|
||||
|
||||
/* Determine length, considers padding for encoding later as well */
|
||||
// TODO not sure if this is correct, ask mario
|
||||
if (value <= 0xFF) {
|
||||
store->objects[store->pos].blen = 1;
|
||||
} else if (value <= 0xFFFF) {
|
||||
store->objects[store->pos].blen = 2;
|
||||
} else if (value <= 0xFFFFFF) {
|
||||
store->objects[store->pos].blen = 3;
|
||||
} else if (value <= 0x7FFFFFFF) {
|
||||
store->objects[store->pos].blen = 4;
|
||||
} else {
|
||||
store->objects[store->pos].blen = 5;
|
||||
}
|
||||
|
||||
store->objects[store->pos].int_val = value;
|
||||
store->pos++;
|
||||
}
|
||||
|
||||
void sec_hasp_asn1_object_add_str(struct sec_hasp_asn1_store* store, uint16_t tag, const char* str)
|
||||
{
|
||||
if (store->pos == store->capacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
store->objects[store->pos].tag = tag;
|
||||
store->objects[store->pos].type = SEC_HASP_ASN1_VT_STR;
|
||||
|
||||
/* Store len + null terminator */
|
||||
store->objects[store->pos].blen = strlen(str) + 1;
|
||||
store->objects[store->pos].str = util_str_dup(str);
|
||||
store->pos++;
|
||||
}
|
||||
|
||||
void sec_hasp_asn1_object_add_time(struct sec_hasp_asn1_store* store, uint16_t tag, uint32_t time)
|
||||
{
|
||||
if (store->pos == store->capacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
store->objects[store->pos].tag = tag;
|
||||
store->objects[store->pos].type = SEC_HASP_ASN1_VT_TIME;
|
||||
|
||||
store->objects[store->pos].blen = 5;
|
||||
store->objects[store->pos].time = time;
|
||||
store->pos++;
|
||||
}
|
||||
|
||||
void sec_hasp_asn1_object_add_data(struct sec_hasp_asn1_store* store, uint16_t tag, const uint8_t* data,
|
||||
size_t len)
|
||||
{
|
||||
if (store->pos == store->capacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
store->objects[store->pos].tag = tag;
|
||||
store->objects[store->pos].type = SEC_HASP_ASN1_VT_DATA;
|
||||
|
||||
/* Store len + null terminator */
|
||||
store->objects[store->pos].blen = len;
|
||||
store->objects[store->pos].data = util_xmalloc(len);
|
||||
memcpy(store->objects[store->pos].data, data, len);
|
||||
|
||||
store->pos++;
|
||||
}
|
||||
|
||||
void sec_hasp_asn1_object_set_int(struct sec_hasp_asn1_store* store, uint16_t tag)
|
||||
{
|
||||
store->objects[store->pos].tag = tag;
|
||||
store->objects[store->pos].type = SEC_HASP_ASN1_VT_INT;
|
||||
store->pos++;
|
||||
}
|
||||
|
||||
void sec_hasp_asn1_object_set_str(struct sec_hasp_asn1_store* store, uint16_t tag)
|
||||
{
|
||||
store->objects[store->pos].tag = tag;
|
||||
store->objects[store->pos].type = SEC_HASP_ASN1_VT_STR;
|
||||
store->pos++;
|
||||
}
|
||||
|
||||
void sec_hasp_asn1_object_set_time(struct sec_hasp_asn1_store* store, uint16_t tag)
|
||||
{
|
||||
store->objects[store->pos].tag = tag;
|
||||
store->objects[store->pos].type = SEC_HASP_ASN1_VT_TIME;
|
||||
store->pos++;
|
||||
}
|
||||
|
||||
void sec_hasp_asn1_object_set_data(struct sec_hasp_asn1_store* store, uint16_t tag)
|
||||
{
|
||||
store->objects[store->pos].tag = tag;
|
||||
store->objects[store->pos].type = SEC_HASP_ASN1_VT_DATA;
|
||||
store->pos++;
|
||||
}
|
||||
|
||||
bool sec_hasp_asn1_object_get_int(struct sec_hasp_asn1_store* store, uint16_t tag, uint32_t* ret_value)
|
||||
{
|
||||
for (size_t i = 0; i < store->pos; i++) {
|
||||
if (store->objects[i].tag == tag) {
|
||||
if (store->objects[i].type != SEC_HASP_ASN1_VT_INT) {
|
||||
log_error("Type of tag %X is not int", tag);
|
||||
return false;
|
||||
} else {
|
||||
*ret_value = store->objects[i].int_val;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sec_hasp_asn1_object_get_str(struct sec_hasp_asn1_store* store, uint16_t tag, char** ret_value)
|
||||
{
|
||||
for (size_t i = 0; i < store->pos; i++) {
|
||||
if (store->objects[i].tag == tag) {
|
||||
if (store->objects[i].type != SEC_HASP_ASN1_VT_STR) {
|
||||
log_error("Type of tag %X is not str", tag);
|
||||
return false;
|
||||
} else {
|
||||
*ret_value = util_str_dup(store->objects[i].str);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sec_hasp_asn1_object_get_time(struct sec_hasp_asn1_store* store, uint16_t tag, uint32_t* ret_value)
|
||||
{
|
||||
for (size_t i = 0; i < store->pos; i++) {
|
||||
if (store->objects[i].tag == tag) {
|
||||
if (store->objects[i].type != SEC_HASP_ASN1_VT_TIME) {
|
||||
log_error("Type of tag %X is not time", tag);
|
||||
return false;
|
||||
} else {
|
||||
*ret_value = store->objects[i].int_val;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sec_hasp_asn1_object_get_data(struct sec_hasp_asn1_store* store, uint16_t tag, uint8_t** ret_data,
|
||||
size_t* ret_len)
|
||||
{
|
||||
for (size_t i = 0; i < store->pos; i++) {
|
||||
if (store->objects[i].tag == tag) {
|
||||
if (store->objects[i].type != SEC_HASP_ASN1_VT_DATA) {
|
||||
log_error("Type of tag %X is not str", tag);
|
||||
return false;
|
||||
} else {
|
||||
*ret_len = store->objects[i].blen;
|
||||
*ret_data = util_xmalloc(*ret_len);
|
||||
memcpy(*ret_data, store->objects[i].data, *ret_len);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
size_t sec_hasp_asn1_encode(struct sec_hasp_asn1_store* store, uint8_t* buffer, size_t len)
|
||||
{
|
||||
uint8_t buffer_tmp[32768];
|
||||
size_t offset = 0;
|
||||
|
||||
/* we need to know the size of the payload to encode the first length field, encode all objects first */
|
||||
for (size_t i = 0; i < store->pos; i++) {
|
||||
sec_hasp_asn1_encode_tag(store->objects[i].tag, buffer_tmp, &offset);
|
||||
sec_hasp_asn1_encode_length(store->objects[i].blen, buffer_tmp, &offset);
|
||||
|
||||
switch(store->objects[i].type) {
|
||||
case SEC_HASP_ASN1_VT_INVALID:
|
||||
log_error("Invalid object type");
|
||||
break;
|
||||
|
||||
case SEC_HASP_ASN1_VT_TIME:
|
||||
buffer_tmp[offset++] = 0x00;
|
||||
sec_hasp_asn1_encode_int(store->objects[i].time, buffer_tmp, &offset);
|
||||
break;
|
||||
|
||||
case SEC_HASP_ASN1_VT_INT:
|
||||
sec_hasp_asn1_encode_int(store->objects[i].time, buffer_tmp, &offset);
|
||||
break;
|
||||
|
||||
case SEC_HASP_ASN1_VT_STR:
|
||||
sec_hasp_asn1_encode_str(store->objects[i].str, buffer_tmp, &offset);
|
||||
break;
|
||||
|
||||
case SEC_HASP_ASN1_VT_DATA:
|
||||
sec_hasp_asn1_encode_data(store->objects[i].data, store->objects[i].blen, buffer_tmp, &offset);
|
||||
break;
|
||||
|
||||
default:
|
||||
log_error("Unknown object type %d", store->objects[i].type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t offset_out = 0;
|
||||
|
||||
sec_hasp_asn1_encode_tag(store->oid, buffer, &offset_out);
|
||||
sec_hasp_asn1_encode_length(offset, buffer, &offset_out);
|
||||
|
||||
if (offset > len) {
|
||||
log_error("Target buffer too small: %d < %d", len, offset + offset_out);
|
||||
} else {
|
||||
memcpy(&buffer[offset_out], buffer_tmp, offset);
|
||||
}
|
||||
|
||||
return offset + offset_out;
|
||||
}
|
||||
|
||||
void sec_hasp_asn1_decode(struct sec_hasp_asn1_store* store, const uint8_t* buffer, size_t len)
|
||||
{
|
||||
size_t offset = 0;
|
||||
|
||||
store->oid = sec_hasp_asn1_decode_tag(buffer, &offset);
|
||||
sec_hasp_asn1_decode_length(buffer, &offset);
|
||||
|
||||
while (offset < len) {
|
||||
uint16_t tag = sec_hasp_asn1_decode_tag(buffer, &offset);
|
||||
uint32_t obj_len = sec_hasp_asn1_decode_length(buffer, &offset);
|
||||
|
||||
if (tag <= 0x7F || tag >= 0x9F) {
|
||||
log_error("Found invalid object tag: %X", tag);
|
||||
return;
|
||||
}
|
||||
|
||||
struct sec_hasp_asn1_object* obj = NULL;
|
||||
|
||||
/* find object that matches the tag and type */
|
||||
for (size_t i = 0; i < store->pos; i++) {
|
||||
if (store->objects[i].tag == tag) {
|
||||
obj = &store->objects[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!obj) {
|
||||
log_error("No object in store that matches tag %X", tag);
|
||||
return;
|
||||
}
|
||||
|
||||
obj->blen = obj_len;
|
||||
|
||||
switch (obj->type) {
|
||||
case SEC_HASP_ASN1_VT_INVALID:
|
||||
log_error("Invalid object type specified for tag %X", obj->tag);
|
||||
break;
|
||||
|
||||
case SEC_HASP_ASN1_VT_TIME:
|
||||
case SEC_HASP_ASN1_VT_INT:
|
||||
obj->int_val = sec_hasp_asn1_decode_int(buffer, &offset, obj->blen);
|
||||
break;
|
||||
|
||||
case SEC_HASP_ASN1_VT_STR:
|
||||
obj->str = sec_hasp_asn1_decode_str(buffer, &offset, obj->blen);
|
||||
break;
|
||||
|
||||
case SEC_HASP_ASN1_VT_DATA:
|
||||
obj->data = sec_hasp_asn1_decode_data(buffer, &offset, obj->blen);
|
||||
break;
|
||||
|
||||
default:
|
||||
log_error("Unknown object type for tag %X", obj->tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t sec_hasp_asn1_decode_int(const uint8_t* buffer, size_t* offset, uint32_t blen)
|
||||
{
|
||||
uint32_t data = 0;
|
||||
|
||||
switch (blen) {
|
||||
case 1:
|
||||
data = buffer[(*offset)++];
|
||||
break;
|
||||
|
||||
case 2:
|
||||
data |= buffer[(*offset)++] << 8;
|
||||
data |= buffer[(*offset)++];
|
||||
break;
|
||||
|
||||
case 3:
|
||||
data |= buffer[(*offset)++] << 16;
|
||||
data |= buffer[(*offset)++] << 8;
|
||||
data |= buffer[(*offset)++];
|
||||
break;
|
||||
|
||||
case 4:
|
||||
data |= buffer[(*offset)++] << 24;
|
||||
data |= buffer[(*offset)++] << 16;
|
||||
data |= buffer[(*offset)++] << 8;
|
||||
data |= buffer[(*offset)++];
|
||||
break;
|
||||
|
||||
case 5:
|
||||
/* skip first byte? */
|
||||
(*offset)++;
|
||||
data |= buffer[(*offset)++] << 24;
|
||||
data |= buffer[(*offset)++] << 16;
|
||||
data |= buffer[(*offset)++] << 8;
|
||||
data |= buffer[(*offset)++];
|
||||
break;
|
||||
|
||||
default:
|
||||
// TODO invalid len?
|
||||
break;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static uint8_t* sec_hasp_asn1_decode_data(const uint8_t* buffer, size_t* offset, uint32_t blen)
|
||||
{
|
||||
uint8_t* data = util_xmalloc(sizeof(uint8_t) * blen);
|
||||
|
||||
memcpy(data, buffer + *offset, blen);
|
||||
(*offset) += blen;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static char* sec_hasp_asn1_decode_str(const uint8_t* buffer, size_t* offset, uint32_t blen)
|
||||
{
|
||||
/* len already includes null terminator */
|
||||
char* str = util_xmalloc(sizeof(char) * blen);
|
||||
|
||||
memcpy(str, buffer + *offset, blen);
|
||||
(*offset) += blen;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static uint32_t sec_hasp_asn1_decode_length(const uint8_t* buffer, size_t* offset)
|
||||
{
|
||||
uint32_t len;
|
||||
|
||||
len = buffer[(*offset)++];
|
||||
|
||||
if (len == 0x81) {
|
||||
len = buffer[(*offset)++];
|
||||
} else if (len == 0x82) {
|
||||
/* Big endian */
|
||||
len = buffer[(*offset)++] << 8;
|
||||
len |= buffer[(*offset)++];
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static uint16_t sec_hasp_asn1_decode_tag(const uint8_t* buffer, size_t* offset)
|
||||
{
|
||||
uint16_t tag;
|
||||
|
||||
tag = buffer[(*offset)++];
|
||||
|
||||
if (tag == 0x7F) {
|
||||
/* Big endian */
|
||||
tag = tag << 8;
|
||||
tag |= buffer[(*offset)++];
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
static void sec_hasp_asn1_encode_int(uint32_t val, uint8_t* buffer, size_t* offset)
|
||||
{
|
||||
uint8_t tmp[8];
|
||||
size_t tmp_pos = 0;
|
||||
|
||||
if (val <= 0xFF) {
|
||||
tmp[tmp_pos++] = (uint8_t) val;
|
||||
} else if (val <= 0xFFFF) {
|
||||
/* Big endian */
|
||||
tmp[tmp_pos++] = (uint8_t) (val >> 8);
|
||||
tmp[tmp_pos++] = (uint8_t) val;
|
||||
} else if (val <= 0xFFFFFF) {
|
||||
/* Big endian */
|
||||
tmp[tmp_pos++] = (uint8_t) (val >> 16);
|
||||
tmp[tmp_pos++] = (uint8_t) (val >> 8);
|
||||
tmp[tmp_pos++] = (uint8_t) val;
|
||||
} else if (val <= 0xFFFFFFFF) {
|
||||
/* Big endian */
|
||||
tmp[tmp_pos++] = (uint8_t) (val >> 24);
|
||||
tmp[tmp_pos++] = (uint8_t) (val >> 16);
|
||||
tmp[tmp_pos++] = (uint8_t) (val >> 8);
|
||||
tmp[tmp_pos++] = (uint8_t) val;
|
||||
} else if (val <= 0xFFFFFFFFFFFFFFFF) {
|
||||
// TODO quad words not supported in decode?
|
||||
}
|
||||
|
||||
/* Deal with padding */
|
||||
if (tmp[0] > 0x7F) {
|
||||
buffer[(*offset)++] = 0x00;
|
||||
}
|
||||
|
||||
memcpy(buffer + *offset, tmp, tmp_pos);
|
||||
(*offset) += tmp_pos;
|
||||
}
|
||||
|
||||
static void sec_hasp_asn1_encode_data(const uint8_t* data, size_t len, uint8_t* buffer, size_t* offset)
|
||||
{
|
||||
memcpy(buffer + *offset, data, len);
|
||||
(*offset) += len;
|
||||
}
|
||||
|
||||
static void sec_hasp_asn1_encode_str(const char* str, uint8_t* buffer, size_t* offset)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
|
||||
memcpy(buffer + *offset, str, len);
|
||||
(*offset) += len;
|
||||
|
||||
/* Add null terminator */
|
||||
buffer[(*offset)++] = 0x00;
|
||||
}
|
||||
|
||||
static void sec_hasp_asn1_encode_length(uint32_t len, uint8_t* buffer, size_t* offset)
|
||||
{
|
||||
if (len <= 0xF7) {
|
||||
buffer[(*offset)++] = (uint8_t) len;
|
||||
} else if (len <= 0xFF) {
|
||||
buffer[(*offset)++] = 0x81;
|
||||
buffer[(*offset)++] = (uint8_t) len;
|
||||
} else if (len <= 0xFFFF) {
|
||||
buffer[(*offset)++] = 0x82;
|
||||
/* Big endian */
|
||||
buffer[(*offset)++] = (uint8_t) (len >> 8);
|
||||
buffer[(*offset)++] = (uint8_t) len;
|
||||
} else if (len <= 0xFFFF) {
|
||||
buffer[(*offset)++] = 0x82;
|
||||
/* Big endian */
|
||||
buffer[(*offset)++] = (uint8_t) (len >> 16);
|
||||
buffer[(*offset)++] = (uint8_t) (len >> 8);
|
||||
buffer[(*offset)++] = (uint8_t) len;
|
||||
} else if (len <= 0xFFFFFFFF) {
|
||||
buffer[(*offset)++] = 0x84;
|
||||
/* Big endian */
|
||||
buffer[(*offset)++] = (uint8_t) (len >> 24);
|
||||
buffer[(*offset)++] = (uint8_t) (len >> 16);
|
||||
buffer[(*offset)++] = (uint8_t) (len >> 8);
|
||||
buffer[(*offset)++] = (uint8_t) len;
|
||||
}
|
||||
}
|
||||
|
||||
static void sec_hasp_asn1_encode_tag(uint16_t tag, uint8_t* buffer, size_t* offset)
|
||||
{
|
||||
if (tag > 0xFF) {
|
||||
/* Big endian */
|
||||
buffer[(*offset)++] = (uint8_t) (tag >> 8);
|
||||
buffer[(*offset)++] = (uint8_t) (tag);
|
||||
} else {
|
||||
buffer[(*offset)++] = (uint8_t) (tag);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef SEC_HASP_ASN1_H
|
||||
#define SEC_HASP_ASN1_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "util/list.h"
|
||||
|
||||
enum sec_hasp_asn1_value_type {
|
||||
SEC_HASP_ASN1_VT_INVALID = 0,
|
||||
SEC_HASP_ASN1_VT_INT = 1,
|
||||
SEC_HASP_ASN1_VT_STR = 2,
|
||||
SEC_HASP_ASN1_VT_TIME = 3,
|
||||
SEC_HASP_ASN1_VT_DATA = 4,
|
||||
};
|
||||
|
||||
struct sec_hasp_asn1_object {
|
||||
uint16_t tag;
|
||||
uint32_t blen;
|
||||
enum sec_hasp_asn1_value_type type;
|
||||
|
||||
union {
|
||||
uint32_t time;
|
||||
uint32_t int_val;
|
||||
uint8_t* data;
|
||||
char* str;
|
||||
};
|
||||
};
|
||||
|
||||
struct sec_hasp_asn1_store {
|
||||
uint16_t oid;
|
||||
size_t capacity;
|
||||
size_t pos;
|
||||
struct sec_hasp_asn1_object* objects;
|
||||
};
|
||||
|
||||
struct sec_hasp_asn1_store* sec_hasp_asn1_object_store_alloc(uint16_t oid, uint32_t size);
|
||||
|
||||
void sec_hasp_asn1_store_free(struct sec_hasp_asn1_store* store);
|
||||
|
||||
void sec_hasp_asn1_object_add_int(struct sec_hasp_asn1_store* store, uint16_t tag, uint32_t value);
|
||||
|
||||
void sec_hasp_asn1_object_add_str(struct sec_hasp_asn1_store* store, uint16_t tag, const char* str);
|
||||
|
||||
void sec_hasp_asn1_object_add_time(struct sec_hasp_asn1_store* store, uint16_t tag, uint32_t time);
|
||||
|
||||
void sec_hasp_asn1_object_add_data(struct sec_hasp_asn1_store* store, uint16_t tag, const uint8_t* data,
|
||||
size_t len);
|
||||
|
||||
void sec_hasp_asn1_object_set_int(struct sec_hasp_asn1_store* store, uint16_t tag);
|
||||
|
||||
void sec_hasp_asn1_object_set_str(struct sec_hasp_asn1_store* store, uint16_t tag);
|
||||
|
||||
void sec_hasp_asn1_object_set_time(struct sec_hasp_asn1_store* store, uint16_t tag);
|
||||
|
||||
void sec_hasp_asn1_object_set_data(struct sec_hasp_asn1_store* store, uint16_t tag);
|
||||
|
||||
bool sec_hasp_asn1_object_get_int(struct sec_hasp_asn1_store* store, uint16_t tag, uint32_t* ret_value);
|
||||
|
||||
bool sec_hasp_asn1_object_get_str(struct sec_hasp_asn1_store* store, uint16_t tag, char** ret_value);
|
||||
|
||||
bool sec_hasp_asn1_object_get_time(struct sec_hasp_asn1_store* store, uint16_t tag, uint32_t* ret_value);
|
||||
|
||||
bool sec_hasp_asn1_object_get_data(struct sec_hasp_asn1_store* store, uint16_t tag, uint8_t** ret_data,
|
||||
size_t* ret_len);
|
||||
|
||||
size_t sec_hasp_asn1_encode(struct sec_hasp_asn1_store* store, uint8_t* buffer, size_t len);
|
||||
|
||||
void sec_hasp_asn1_decode(struct sec_hasp_asn1_store* store, const uint8_t* buffer, size_t len);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
#ifndef SEC_HASP_CONST_H
|
||||
#define SEC_HASP_CONST_H
|
||||
|
||||
#define SEC_HAS_CONST_HEADER_SZ 24
|
||||
|
||||
#define SEC_HAS_CONST_FILEID_RW = 0xfff4
|
||||
#define SEC_HAS_CONST_FILEID_RO = 0xfff5
|
||||
|
||||
#define SEC_HAS_CONST_SCOPE_LM "<?xml version=\"1.0\" encoding=\"UTF-8\"?><haspscope><hasp type=\"HASP-HL\"><license_manager ip=\"127.0.0.1\" /></hasp></haspscope>"
|
||||
#define SEC_HAS_CONST_SCOPE_HANDLE "<haspscope><session handle=\"%d\"/></haspscope>"
|
||||
|
||||
#define SEC_HAS_CONST_FORMAT_GETID "<?xml version=\"1.0\" encoding=\"UTF-8\"?><haspformat><hasp><attribute name=\"id\" /></hasp></haspformat>"
|
||||
#define SEC_HAS_CONST_FORMAT_GETSESSION "<haspformat root=\"hasp_info\"><si_feature /></haspformat>"
|
||||
#define SEC_HAS_CONST_FORMAT_GETKEYINFO "<haspformat root=\"hasp_info\"><si_container /></haspformat>"
|
||||
|
||||
#define SEC_HAS_CONST_SPEC_FEATURE_ID "<haspspec><feature id=\"%d\" /></haspspec>"
|
||||
|
||||
enum sec_hasp_const_status {
|
||||
SEC_HASP_CONST_STATUS_OK = 0,
|
||||
SEC_HASP_CONST_STATUS_MEM_RANGE = 1,
|
||||
SEC_HASP_CONST_STATUS_INV_FILEID = 10,
|
||||
SEC_HASP_CONST_STATUS_NO_TIME = 12,
|
||||
SEC_HASP_CONST_STATUS_INV_VCODE = 22,
|
||||
SEC_HASP_CONST_STATUS_INV_HND = 9,
|
||||
SEC_HASP_CONST_STATUS_TOO_SHORT = 8,
|
||||
SEC_HASP_CONST_STATUS_DEVICE_ERR = 43,
|
||||
SEC_HASP_CONST_STATUS_SCHAN_ERR = 46,
|
||||
SEC_HASP_CONST_STATUS_BROKEN_SESSION = 0x7F000027
|
||||
};
|
||||
|
||||
enum sec_hasp_const_operation_id {
|
||||
/* Not Used - Admin or Get LMS Version? */
|
||||
SEC_HASP_CONST_OP_ID_ADMIN = 0,
|
||||
/* Not Used */
|
||||
SEC_HASP_CONST_OP_ID_ECHO = 1,
|
||||
SEC_HASP_CONST_OP_ID_LOGIN = 0x2711,
|
||||
SEC_HASP_CONST_OP_ID_LOGOUT = 0x2712,
|
||||
SEC_HASP_CONST_OP_ID_LOGINSCOPE = 0x2713,
|
||||
SEC_HASP_CONST_OP_ID_GETINFO = 0x2714,
|
||||
SEC_HASP_CONST_OP_ID_PARSE_SCOPE = 0x2715,
|
||||
SEC_HASP_CONST_OP_ID_SETUPSCHANNEL = 0x2716,
|
||||
SEC_HASP_CONST_OP_ID_ENCRYPT = 0x2724,
|
||||
SEC_HASP_CONST_OP_ID_DECRYPT = 0x2725,
|
||||
SEC_HASP_CONST_OP_ID_READ = 0x271A,
|
||||
SEC_HASP_CONST_OP_ID_WRITE = 0x271B,
|
||||
SEC_HASP_CONST_OP_ID_GETSIZE = 0x271C,
|
||||
SEC_HASP_CONST_OP_ID_GETRTC = 0x271D,
|
||||
SEC_HASP_CONST_OP_ID_GETAPIUID = 0x2774,
|
||||
/* Not Used */
|
||||
SEC_HASP_CONST_OP_ID_UPDATE = 0x2775,
|
||||
/* Not Used */
|
||||
SEC_HASP_CONST_OP_ID_DETACH = 0x2779,
|
||||
};
|
||||
|
||||
enum sec_hasp_const_operational_object_id {
|
||||
SEC_HASP_CONST_OID_CLIENTID_REQ = 0x7F34,
|
||||
SEC_HASP_CONST_OID_CLIENTID_RESP =0x7F35,
|
||||
SEC_HASP_CONST_OID_LOGIN_REQ = 0x61,
|
||||
SEC_HASP_CONST_OID_LOGIN_REP = 0x62,
|
||||
SEC_HASP_CONST_OID_LOGINSCOPE_REQ = 0x63,
|
||||
SEC_HASP_CONST_OID_LOGINSCOPE_REP = 0x64,
|
||||
SEC_HASP_CONST_OID_LOGOUT_REQ = 0x65,
|
||||
SEC_HASP_CONST_OID_LOGOUT_REP = 0x66,
|
||||
SEC_HASP_CONST_OID_INFO_REQ = 0x67,
|
||||
SEC_HASP_CONST_OID_INFO_REP = 0x68,
|
||||
SEC_HASP_CONST_OID_READ_REQ = 0x6A,
|
||||
SEC_HASP_CONST_OID_READ_REP = 0x6B,
|
||||
SEC_HASP_CONST_OID_WRITE_REQ = 0x6C,
|
||||
SEC_HASP_CONST_OID_WRITE_REP = 0x6D,
|
||||
SEC_HASP_CONST_OID_GETSIZE_REQ = 0x6E,
|
||||
SEC_HASP_CONST_OID_GETSIZE_REP = 0x6F,
|
||||
SEC_HASP_CONST_OID_GETRTC_REQ = 0x70,
|
||||
SEC_HASP_CONST_OID_GETRTC_REP = 0x71,
|
||||
SEC_HASP_CONST_OID_SCHANNEL_REQ = 0x72,
|
||||
SEC_HASP_CONST_OID_SCHANNEL_REP = 0x73,
|
||||
SEC_HASP_CONST_OID_CRYPT_REQ = 0x77,
|
||||
SEC_HASP_CONST_OID_CRYPT_REP = 0x78,
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,118 @@
|
||||
#define LOG_MODULE "sec-hasp-handler"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "handler.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
enum sec_hasp_handler_packet_type {
|
||||
SEC_HASP_HANDLER_PT_APIUID = 0x2774,
|
||||
SEC_HASP_HANDLER_PT_LOGIN = 0x2711,
|
||||
SEC_HASP_HANDLER_PT_LOGOUT = 0x2712,
|
||||
SEC_HASP_HANDLER_PT_GETINFO = 0x2714,
|
||||
SEC_HASP_HANDLER_PT_SCHANNEL = 0x2716,
|
||||
SEC_HASP_HANDLER_PT_ENCRYPT = 0x2724,
|
||||
SEC_HASP_HANDLER_PT_DECRYPT = 0x2725,
|
||||
SEC_HASP_HANDLER_PT_READ = 0x271A,
|
||||
SEC_HASP_HANDLER_PT_WRITE = 0x271B,
|
||||
};
|
||||
|
||||
void sec_hasp_handler_init(const uint8_t* key_data, size_t len)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
struct sec_hasp_handler_transaction* sec_hasp_handler_alloc_transaction(size_t max_payload_size)
|
||||
{
|
||||
struct sec_hasp_handler_transaction* trans;
|
||||
|
||||
trans = malloc(sizeof(struct sec_hasp_handler_transaction));
|
||||
trans->req_resp_max_size = sizeof(struct sec_hasp_handler_header) + max_payload_size;
|
||||
trans->req = malloc(trans->req_resp_max_size);
|
||||
trans->resp = malloc(trans->req_resp_max_size);
|
||||
|
||||
return trans;
|
||||
}
|
||||
|
||||
void sec_hasp_handler_free_transaction(struct sec_hasp_handler_transaction* transaction)
|
||||
{
|
||||
free(transaction->req);
|
||||
free(transaction->resp);
|
||||
free(transaction);
|
||||
}
|
||||
|
||||
void sec_hasp_handler_do_transaction(struct sec_hasp_handler_transaction* transaction)
|
||||
{
|
||||
log_debug("Req: packet_size %d, head_value 0x%X, transaction_num %d, session_id 0x%X, packet_type 0x%X, "
|
||||
"unknown 0x%X, tail_value 0x%X", transaction->req->header.packet_size, transaction->req->header.head_value,
|
||||
transaction->req->header.transaction_num, transaction->req->header.session_id,
|
||||
transaction->req->header.packet_type, transaction->req->header.tail_value);
|
||||
|
||||
switch (transaction->req->header.packet_type) {
|
||||
case SEC_HASP_HANDLER_PT_APIUID:
|
||||
log_debug("SEC_HASP_HANDLER_PT_APIUID");
|
||||
break;
|
||||
|
||||
case SEC_HASP_HANDLER_PT_LOGIN:
|
||||
log_debug("SEC_HASP_HANDLER_PT_LOGIN");
|
||||
break;
|
||||
|
||||
case SEC_HASP_HANDLER_PT_LOGOUT:
|
||||
log_debug("SEC_HASP_HANDLER_PT_LOGOUT");
|
||||
break;
|
||||
|
||||
case SEC_HASP_HANDLER_PT_GETINFO:
|
||||
log_debug("SEC_HASP_HANDLER_PT_GETINFO");
|
||||
break;
|
||||
|
||||
case SEC_HASP_HANDLER_PT_SCHANNEL:
|
||||
log_debug("SEC_HASP_HANDLER_PT_SCHANNEL");
|
||||
break;
|
||||
|
||||
case SEC_HASP_HANDLER_PT_ENCRYPT:
|
||||
log_debug("SEC_HASP_HANDLER_PT_ENCRYPT");
|
||||
break;
|
||||
|
||||
case SEC_HASP_HANDLER_PT_DECRYPT:
|
||||
log_debug("SEC_HASP_HANDLER_PT_DECRYPT");
|
||||
break;
|
||||
|
||||
case SEC_HASP_HANDLER_PT_READ:
|
||||
log_debug("SEC_HASP_HANDLER_PT_READ");
|
||||
break;
|
||||
|
||||
case SEC_HASP_HANDLER_PT_WRITE:
|
||||
log_debug("SEC_HASP_HANDLER_PT_WRITE");
|
||||
break;
|
||||
|
||||
default:
|
||||
log_error("Invalid packet type: %X", transaction->req->header.packet_type);
|
||||
memset(&transaction->req->header, 0, sizeof(struct sec_hasp_handler_header));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//static void sec_hasp_handler_get_client_id(struct sec_hasp_handler_transaction* transaction)
|
||||
//{
|
||||
// transaction->resp->header.packet_size
|
||||
// transaction->resp->header.head_value
|
||||
// transaction->resp->header.transaction_num
|
||||
// transaction->resp->header.session_id
|
||||
// transaction->resp->header.packet_type
|
||||
// transaction->resp->header.tail_value
|
||||
//
|
||||
// rp = request_packet.payload_object
|
||||
//
|
||||
// client_id = HaspUtils.make_fake_handle_value()
|
||||
// client_id_response = HaspObject.HO_Client_ID_Response()
|
||||
// client_id_response.populate(HaspConst.HASP_STATUS_OK,client_id)
|
||||
//
|
||||
// self.client_db[client_id] = {
|
||||
// "hasp_serial":0,
|
||||
// "sessions":{}
|
||||
// }
|
||||
//
|
||||
// return client_id_response
|
||||
//}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef SEC_HASP_HANDLER_H
|
||||
#define SEC_HASP_HANDLER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct sec_hasp_handler_header {
|
||||
uint32_t packet_size;
|
||||
uint16_t head_value;
|
||||
uint32_t transaction_num;
|
||||
uint32_t session_id;
|
||||
uint16_t unknown;
|
||||
uint16_t packet_type;
|
||||
uint32_t tail_value;
|
||||
} __attribute((__packed__));
|
||||
|
||||
struct sec_hasp_handler_req {
|
||||
struct sec_hasp_handler_header header;
|
||||
uint8_t payload[];
|
||||
};
|
||||
|
||||
struct sec_hasp_handler_resp {
|
||||
struct sec_hasp_handler_header header;
|
||||
uint8_t payload[];
|
||||
};
|
||||
|
||||
struct sec_hasp_handler_transaction {
|
||||
size_t req_resp_max_size;
|
||||
struct sec_hasp_handler_req* req;
|
||||
struct sec_hasp_handler_resp* resp;
|
||||
};
|
||||
|
||||
void sec_hasp_handler_init(const uint8_t* key_data, size_t len);
|
||||
|
||||
struct sec_hasp_handler_transaction* sec_hasp_handler_alloc_transaction(size_t max_payload_size);
|
||||
|
||||
void sec_hasp_handler_free_transaction(struct sec_hasp_handler_transaction* transaction);
|
||||
|
||||
void sec_hasp_handler_do_transaction(struct sec_hasp_handler_transaction* transaction);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,4 @@
|
||||
//
|
||||
// Created by on 6/2/18.
|
||||
//
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef SEC_HASP_OBJECT_H
|
||||
#define SEC_HASP_OBJECT_H
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
#include <time.h>
|
||||
|
||||
#include "sec/hasp/lib/asn1.h"
|
||||
#include "sec/hasp/lib/const.h"
|
||||
|
||||
#include "util/mem.h"
|
||||
|
||||
#include "client-id-req.h"
|
||||
|
||||
struct sec_hasp_object_client_id_req* sec_hasp_object_client_id_req_alloc()
|
||||
{
|
||||
struct sec_hasp_object_client_id_req* obj = util_xmalloc(sizeof(struct sec_hasp_object_client_id_req));
|
||||
|
||||
obj->oid = SEC_HASP_CONST_OID_CLIENTID_REQ;
|
||||
obj->val_80 = 0;
|
||||
obj->api_version_major = 0;
|
||||
obj->api_version_minor = 0;
|
||||
obj->timestamp = 0;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void sec_hasp_object_client_id_req_free(struct sec_hasp_object_client_id_req* obj)
|
||||
{
|
||||
free(obj);
|
||||
}
|
||||
|
||||
size_t sec_hasp_object_client_id_req_encode(struct sec_hasp_object_client_id_req* obj, uint8_t* buffer,
|
||||
size_t len)
|
||||
{
|
||||
struct sec_hasp_asn1_store* store = sec_hasp_asn1_object_store_alloc(obj->oid, 4);
|
||||
|
||||
/* populate right before encoding */
|
||||
obj->timestamp = (uint32_t) time(NULL);
|
||||
|
||||
sec_hasp_asn1_object_add_int(store, 0x80, obj->val_80);
|
||||
sec_hasp_asn1_object_add_int(store, 0x81, obj->api_version_major);
|
||||
sec_hasp_asn1_object_add_int(store, 0x82, obj->api_version_minor);
|
||||
sec_hasp_asn1_object_add_time(store, 0x84, obj->timestamp);
|
||||
|
||||
return sec_hasp_asn1_encode(store, buffer, len);
|
||||
}
|
||||
|
||||
void sec_hasp_object_client_id_req_decode(uint8_t* buffer, size_t len, struct sec_hasp_object_client_id_req* obj)
|
||||
{
|
||||
struct sec_hasp_asn1_store* store = sec_hasp_asn1_object_store_alloc(obj->oid, 4);
|
||||
|
||||
sec_hasp_asn1_object_set_int(store, 0x80);
|
||||
sec_hasp_asn1_object_set_int(store, 0x81);
|
||||
sec_hasp_asn1_object_set_int(store, 0x82);
|
||||
sec_hasp_asn1_object_set_time(store, 0x84);
|
||||
|
||||
sec_hasp_asn1_decode(store, buffer, len);
|
||||
|
||||
obj->oid = store->oid;
|
||||
|
||||
sec_hasp_asn1_object_get_int(store, 0x80, &obj->val_80);
|
||||
sec_hasp_asn1_object_get_int(store, 0x81, &obj->api_version_major);
|
||||
sec_hasp_asn1_object_get_int(store, 0x82, &obj->api_version_minor);
|
||||
sec_hasp_asn1_object_get_time(store, 0x84, &obj->timestamp);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef SEC_HASP_OBJECT_CLIENT_ID_REQ_H
|
||||
#define SEC_HASP_OBJECT_CLIENT_ID_REQ_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct sec_hasp_object_client_id_req {
|
||||
uint16_t oid;
|
||||
uint32_t val_80;
|
||||
uint32_t api_version_major;
|
||||
uint32_t api_version_minor;
|
||||
uint32_t timestamp;
|
||||
};
|
||||
|
||||
struct sec_hasp_object_client_id_req* sec_hasp_object_client_id_req_alloc();
|
||||
|
||||
void sec_hasp_object_client_id_req_free(struct sec_hasp_object_client_id_req* obj);
|
||||
|
||||
size_t sec_hasp_object_client_id_req_encode(struct sec_hasp_object_client_id_req* obj, uint8_t* buffer,
|
||||
size_t len);
|
||||
|
||||
void sec_hasp_object_client_id_req_decode(uint8_t* buffer, size_t len, struct sec_hasp_object_client_id_req* obj);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "sec/hasp/lib/asn1.h"
|
||||
#include "sec/hasp/lib/const.h"
|
||||
|
||||
#include "util/mem.h"
|
||||
|
||||
#include "client-id-resp.h"
|
||||
|
||||
struct sec_hasp_object_client_id_resp* sec_hasp_object_client_id_resp_alloc()
|
||||
{
|
||||
struct sec_hasp_object_client_id_resp* obj = util_xmalloc(sizeof(struct sec_hasp_object_client_id_resp));
|
||||
|
||||
obj->oid = SEC_HASP_CONST_OID_CLIENTID_RESP;
|
||||
obj->status = 0;
|
||||
obj->client_id = 0;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void sec_hasp_object_client_id_resp_free(struct sec_hasp_object_client_id_resp* obj)
|
||||
{
|
||||
free(obj);
|
||||
}
|
||||
|
||||
size_t sec_hasp_object_client_id_resp_encode(struct sec_hasp_object_client_id_resp* obj, uint8_t* buffer,
|
||||
size_t len)
|
||||
{
|
||||
struct sec_hasp_asn1_store* store = sec_hasp_asn1_object_store_alloc(obj->oid, 4);
|
||||
|
||||
sec_hasp_asn1_object_add_int(store, 0x80, obj->status);
|
||||
sec_hasp_asn1_object_add_int(store, 0x81, obj->client_id);
|
||||
|
||||
return sec_hasp_asn1_encode(store, buffer, len);
|
||||
}
|
||||
|
||||
void sec_hasp_object_client_id_resp_decode(uint8_t* buffer, size_t len, struct sec_hasp_object_client_id_resp* obj)
|
||||
{
|
||||
struct sec_hasp_asn1_store* store = sec_hasp_asn1_object_store_alloc(obj->oid, 2);
|
||||
|
||||
sec_hasp_asn1_object_set_int(store, 0x80);
|
||||
sec_hasp_asn1_object_set_int(store, 0x81);
|
||||
|
||||
sec_hasp_asn1_decode(store, buffer, len);
|
||||
|
||||
obj->oid = store->oid;
|
||||
|
||||
sec_hasp_asn1_object_get_int(store, 0x80, &obj->status);
|
||||
sec_hasp_asn1_object_get_int(store, 0x81, &obj->client_id);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef SEC_HASP_OBJECT_CLIENT_ID_RESP_H
|
||||
#define SEC_HASP_OBJECT_CLIENT_ID_RESP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct sec_hasp_object_client_id_resp {
|
||||
uint16_t oid;
|
||||
uint32_t status;
|
||||
uint32_t client_id;
|
||||
};
|
||||
|
||||
struct sec_hasp_object_client_id_resp* sec_hasp_object_client_id_resp_alloc();
|
||||
|
||||
void sec_hasp_object_client_id_resp_free(struct sec_hasp_object_client_id_resp* obj);
|
||||
|
||||
size_t sec_hasp_object_client_id_resp_encode(struct sec_hasp_object_client_id_resp* obj, uint8_t* buffer,
|
||||
size_t len);
|
||||
|
||||
void sec_hasp_object_client_id_resp_decode(uint8_t* buffer, size_t len, struct sec_hasp_object_client_id_resp* obj);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,139 @@
|
||||
#include <time.h>
|
||||
|
||||
#include "sec/hasp/main.h"
|
||||
#include "sec/hasp/const.h"
|
||||
|
||||
#include "util/mem.h"
|
||||
|
||||
#include "login-req.h"
|
||||
|
||||
struct sec_hasp_object_login_req* sec_hasp_object_login_req_init()
|
||||
{
|
||||
struct sec_hasp_object_login_req* obj = util_xmalloc(sizeof(struct sec_hasp_object_login_req));
|
||||
|
||||
obj->oid = SEC_HASP_CONST_OID_LOGIN_REQ;
|
||||
obj->pid = 0;
|
||||
obj->tid = 0;
|
||||
obj->hasp_uid = 0;
|
||||
obj->vendor_id = 0;
|
||||
obj->feature_id = 0;
|
||||
obj->username = NULL;
|
||||
obj->machine_name = NULL;
|
||||
obj->login_type = NULL;
|
||||
obj->api_version_major = 0;
|
||||
obj->api_version_minor = 0;
|
||||
obj->timestamp = 0;
|
||||
obj->val_8b = 0;
|
||||
obj->val_8c = 0;
|
||||
obj->val_8d = 0;
|
||||
obj->val_8e = 0;
|
||||
obj->volume_serial = 0;
|
||||
obj->val_90 = 0;
|
||||
obj->hasp_handle = 0;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void sec_hasp_object_login_req_free(struct sec_hasp_object_login_req* obj)
|
||||
{
|
||||
if (obj->username) {
|
||||
free(obj->username);
|
||||
}
|
||||
|
||||
if (obj->machine_name) {
|
||||
free(obj->machine_name);
|
||||
}
|
||||
|
||||
if (obj->login_type) {
|
||||
free(obj->login_type);
|
||||
}
|
||||
}
|
||||
|
||||
size_t sec_hasp_object_login_req_encode(struct sec_hasp_object_login_req* obj, uint8_t* buffer,
|
||||
size_t len)
|
||||
{
|
||||
struct sec_hasp_asn1_store* store = sec_hasp_asn1_object_store_alloc(obj->oid, 18);
|
||||
|
||||
/* populate right before encoding */
|
||||
obj->timestamp = (uint32_t) time(NULL);
|
||||
|
||||
sec_hasp_asn1_object_add_int(store, 0x80, obj->pid);
|
||||
sec_hasp_asn1_object_add_int(store, 0x81, obj->tid);
|
||||
sec_hasp_asn1_object_add_int(store, 0x82, obj->hasp_uid);
|
||||
sec_hasp_asn1_object_add_int(store, 0x83, obj->vendor_id);
|
||||
sec_hasp_asn1_object_add_int(store, 0x84, obj->feature_id);
|
||||
|
||||
sec_hasp_asn1_object_add_str(store, 0x85, obj->username);
|
||||
sec_hasp_asn1_object_add_str(store, 0x86, obj->machine_name);
|
||||
sec_hasp_asn1_object_add_str(store, 0x87, obj->login_type);
|
||||
|
||||
sec_hasp_asn1_object_add_int(store, 0x88, obj->api_version_major);
|
||||
sec_hasp_asn1_object_add_int(store, 0x89, obj->api_version_minor);
|
||||
|
||||
sec_hasp_asn1_object_add_time(store, 0x8A, obj->timestamp);
|
||||
|
||||
sec_hasp_asn1_object_add_int(store, 0x8B, obj->val_8b);
|
||||
sec_hasp_asn1_object_add_int(store, 0x8C, obj->val_8c);
|
||||
sec_hasp_asn1_object_add_int(store, 0x8D, obj->val_8d);
|
||||
sec_hasp_asn1_object_add_int(store, 0x8E, obj->val_8e);
|
||||
sec_hasp_asn1_object_add_int(store, 0x8F, obj->volume_serial);
|
||||
sec_hasp_asn1_object_add_int(store, 0x90, obj->val_90);
|
||||
sec_hasp_asn1_object_add_int(store, 0x91, obj->hasp_handle);
|
||||
|
||||
return sec_hasp_asn1_encode(store, buffer, len);
|
||||
}
|
||||
|
||||
void sec_hasp_object_login_req_decode(uint8_t* buffer, size_t len, struct sec_hasp_object_login_req* obj)
|
||||
{
|
||||
struct sec_hasp_asn1_store* store = sec_hasp_asn1_object_store_alloc(obj->oid, 18);
|
||||
|
||||
sec_hasp_asn1_object_set_int(store, 0x80);
|
||||
sec_hasp_asn1_object_set_int(store, 0x81);
|
||||
sec_hasp_asn1_object_set_int(store, 0x82);
|
||||
sec_hasp_asn1_object_set_int(store, 0x83);
|
||||
sec_hasp_asn1_object_set_int(store, 0x84);
|
||||
|
||||
sec_hasp_asn1_object_set_str(store, 0x85);
|
||||
sec_hasp_asn1_object_set_str(store, 0x86);
|
||||
sec_hasp_asn1_object_set_str(store, 0x87);
|
||||
|
||||
sec_hasp_asn1_object_set_int(store, 0x88);
|
||||
sec_hasp_asn1_object_set_int(store, 0x89);
|
||||
|
||||
sec_hasp_asn1_object_set_time(store, 0x8A);
|
||||
|
||||
sec_hasp_asn1_object_set_int(store, 0x8B);
|
||||
sec_hasp_asn1_object_set_int(store, 0x8C);
|
||||
sec_hasp_asn1_object_set_int(store, 0x8D);
|
||||
sec_hasp_asn1_object_set_int(store, 0x8E);
|
||||
sec_hasp_asn1_object_set_int(store, 0x8F);
|
||||
sec_hasp_asn1_object_set_int(store, 0x90);
|
||||
sec_hasp_asn1_object_set_int(store, 0x91);
|
||||
|
||||
sec_hasp_asn1_decode(store, buffer, len);
|
||||
|
||||
obj->oid = store->oid;
|
||||
|
||||
sec_hasp_asn1_object_get_int(store, 0x80, &obj->pid);
|
||||
sec_hasp_asn1_object_get_int(store, 0x81, &obj->tid);
|
||||
sec_hasp_asn1_object_get_int(store, 0x82, &obj->hasp_uid);
|
||||
sec_hasp_asn1_object_get_int(store, 0x83, &obj->vendor_id);
|
||||
sec_hasp_asn1_object_get_int(store, 0x84, &obj->feature_id);
|
||||
|
||||
sec_hasp_asn1_object_get_str(store, 0x85, &obj->username);
|
||||
sec_hasp_asn1_object_get_str(store, 0x86, &obj->machine_name);
|
||||
sec_hasp_asn1_object_get_str(store, 0x87, &obj->login_type);
|
||||
|
||||
sec_hasp_asn1_object_get_int(store, 0x88, &obj->api_version_major);
|
||||
sec_hasp_asn1_object_get_int(store, 0x89, &obj->api_version_minor);
|
||||
|
||||
sec_hasp_asn1_object_get_time(store, 0x8A, &obj->timestamp);
|
||||
|
||||
sec_hasp_asn1_object_get_int(store, 0x8B, &obj->val_8b);
|
||||
sec_hasp_asn1_object_get_int(store, 0x8C, &obj->val_8c);
|
||||
sec_hasp_asn1_object_get_int(store, 0x8D, &obj->val_8d);
|
||||
sec_hasp_asn1_object_get_int(store, 0x8E, &obj->val_8e);
|
||||
sec_hasp_asn1_object_get_int(store, 0x8F, &obj->volume_serial);
|
||||
sec_hasp_asn1_object_get_int(store, 0x90, &obj->val_90);
|
||||
sec_hasp_asn1_object_get_int(store, 0x91, &obj->hasp_handle);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef SEC_HASP_OBJECT_LOGIN_REQ_H
|
||||
#define SEC_HASP_OBJECT_LOGIN_REQ_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct sec_hasp_object_login_req {
|
||||
uint16_t oid;
|
||||
uint32_t pid;
|
||||
uint32_t tid;
|
||||
uint32_t hasp_uid;
|
||||
uint32_t vendor_id;
|
||||
uint32_t feature_id;
|
||||
char* username;
|
||||
char* machine_name;
|
||||
char* login_type;
|
||||
uint32_t api_version_major;
|
||||
uint32_t api_version_minor;
|
||||
uint32_t timestamp;
|
||||
uint32_t val_8b;
|
||||
uint32_t val_8c;
|
||||
uint32_t val_8d;
|
||||
uint32_t val_8e;
|
||||
uint32_t volume_serial;
|
||||
uint32_t val_90;
|
||||
uint32_t hasp_handle;
|
||||
};
|
||||
|
||||
struct sec_hasp_object_login_req* sec_hasp_object_login_req_init();
|
||||
|
||||
void sec_hasp_object_login_req_free(struct sec_hasp_object_login_req* obj);
|
||||
|
||||
size_t sec_hasp_object_login_req_encode(struct sec_hasp_object_login_req* obj, uint8_t* buffer,
|
||||
size_t len);
|
||||
|
||||
void sec_hasp_object_login_req_decode(uint8_t* buffer, size_t len, struct sec_hasp_object_login_req* obj);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,162 @@
|
||||
#define LOG_MODULE "sec-hasp-server"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sec/hasp/lib/handler.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
#include "server.h"
|
||||
|
||||
#define HASP_PORT 1947
|
||||
#define HASP_MAX_BUFFER 16384
|
||||
|
||||
static bool sec_hasp_server_run_s;
|
||||
static bool sec_hasp_server_is_running_s;
|
||||
|
||||
void sec_hasp_server_init(const uint8_t* key_data, size_t len)
|
||||
{
|
||||
sec_hasp_handler_init(key_data, len);
|
||||
sec_hasp_server_run_s = true;
|
||||
sec_hasp_server_is_running_s = false;
|
||||
}
|
||||
|
||||
void sec_hasp_server_run()
|
||||
{
|
||||
int fd = -1;
|
||||
struct sockaddr_in addr;
|
||||
struct sockaddr_in addr_remote;
|
||||
socklen_t addr_len_remote;
|
||||
int fd_remote = -1;
|
||||
int retval;
|
||||
struct sec_hasp_handler_transaction* transaction;
|
||||
ssize_t recv_len;
|
||||
|
||||
log_info("Setting up socket for server");
|
||||
|
||||
sec_hasp_server_is_running_s = true;
|
||||
|
||||
transaction = sec_hasp_handler_alloc_transaction(HASP_MAX_BUFFER);
|
||||
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (fd == -1) {
|
||||
log_error("Opening socket failed: %s", strerror(errno));
|
||||
goto return_cleanup;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(struct sockaddr_in));
|
||||
addr.sin_family = AF_INET;
|
||||
/* bind to localhost which limits accepting connections to localhost, only */
|
||||
addr.sin_addr.s_addr = htonl(0x7F000001);
|
||||
addr.sin_port = htons(HASP_PORT);
|
||||
|
||||
retval = bind(fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in));
|
||||
|
||||
if (retval == -1) {
|
||||
log_error("Binding socket failed: %s", strerror(errno));
|
||||
goto return_cleanup;
|
||||
}
|
||||
|
||||
log_info("Running server...");
|
||||
|
||||
while (sec_hasp_server_run_s) {
|
||||
/* a single connection is fine here */
|
||||
retval = listen(fd, 1);
|
||||
|
||||
if (retval == -1) {
|
||||
log_error("Listening for incoming connections failed: %s", strerror(errno));
|
||||
goto return_cleanup;
|
||||
}
|
||||
|
||||
/* got a connection */
|
||||
addr_len_remote = sizeof(addr_remote);
|
||||
fd_remote = accept(fd, &addr_remote, &addr_len_remote);
|
||||
|
||||
if (fd_remote == -1) {
|
||||
log_error("Accepting incoming connection failed: %s", strerror(errno));
|
||||
goto return_cleanup;
|
||||
}
|
||||
|
||||
log_debug("Remote %X connected", addr_remote.sin_addr.s_addr);
|
||||
|
||||
/* put the socket to the remote into non blocking mode */
|
||||
if (fcntl(fd_remote, F_SETFL, fcntl(fd_remote, F_GETFL) | O_NONBLOCK) < 0) {
|
||||
log_error("Setting socket to non blocking failed: %s", strerror(errno));
|
||||
goto return_cleanup;
|
||||
}
|
||||
|
||||
while (sec_hasp_server_run_s) {
|
||||
recv_len = recv(fd_remote, transaction->req, transaction->req_resp_max_size, 0);
|
||||
|
||||
if (recv_len == -1) {
|
||||
/* no data available, sleep to reduce cpu load and retry */
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
/* 10 ms */
|
||||
usleep(1000 * 10);
|
||||
continue;
|
||||
} else if (errno == ECONNRESET) {
|
||||
log_debug("Remote %X disconnected", addr_remote.sin_addr.s_addr);
|
||||
close(fd_remote);
|
||||
break;
|
||||
} else {
|
||||
log_error("Reading from connection failed: %s", strerror(errno));
|
||||
goto return_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (recv_len == 0) {
|
||||
/* remote disconnected */
|
||||
log_debug("Remote %X disconnected", addr_remote.sin_addr.s_addr);
|
||||
close(fd_remote);
|
||||
break;
|
||||
}
|
||||
|
||||
log_debug("Received data: %d bytes", recv_len);
|
||||
|
||||
sec_hasp_handler_do_transaction(transaction);
|
||||
|
||||
/* send reply */
|
||||
recv_len = send(fd_remote, transaction->resp, transaction->resp->header.packet_size, 0);
|
||||
|
||||
if (recv_len == -1) {
|
||||
if (errno == ECONNRESET) {
|
||||
log_debug("Remote %X disconnected", addr_remote.sin_addr.s_addr);
|
||||
close(fd_remote);
|
||||
break;
|
||||
} else {
|
||||
log_error("Writing to connection failed: %s", strerror(errno));
|
||||
goto return_cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return_cleanup:
|
||||
if (fd_remote != -1) {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
sec_hasp_handler_free_transaction(transaction);
|
||||
|
||||
sec_hasp_server_is_running_s = false;
|
||||
}
|
||||
|
||||
bool sec_hasp_server_is_running(void)
|
||||
{
|
||||
return sec_hasp_server_is_running_s;
|
||||
}
|
||||
|
||||
void sec_hasp_server_shutdown(void)
|
||||
{
|
||||
sec_hasp_server_run_s = false;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef SEC_HASP_SERVER_H
|
||||
#define SEC_HASP_SERVER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* Initialize the hasp server module. This just initializes the hasp
|
||||
* dongle emulator module
|
||||
*
|
||||
* @param key_data Pointer to loaded key data (attributes, key table)
|
||||
* @param len Length of the buffer
|
||||
*/
|
||||
void sec_hasp_server_init(const uint8_t* key_data, size_t len);
|
||||
|
||||
/**
|
||||
* Run the server. This call blocks until the server is shut down.
|
||||
* Recommended to run this in a separate thread.
|
||||
*/
|
||||
void sec_hasp_server_run(void);
|
||||
|
||||
/**
|
||||
* Check if the server is running
|
||||
*/
|
||||
bool sec_hasp_server_is_running(void);
|
||||
|
||||
/**
|
||||
* Signal a shut down to the server
|
||||
*/
|
||||
void sec_hasp_server_shutdown(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <time.h>
|
||||
|
||||
#include "util/rand.h"
|
||||
|
||||
#include "util.h"
|
||||
|
||||
uint32_t sec_hasp_util_make_fake_handle_value()
|
||||
{
|
||||
return util_rand_gen_32();
|
||||
}
|
||||
|
||||
uint32_t sec_hasp_util_get_timestamp()
|
||||
{
|
||||
return (uint32_t) time(NULL);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef SEC_HASP_UTIL_H
|
||||
#define SEC_HASP_UTIL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t sec_hasp_util_make_fake_handle_value();
|
||||
|
||||
uint32_t sec_hasp_util_get_timestamp();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
#define LOG_MODULE "sec-hasp"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "sec/hasp/old/hasp.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
struct sec_hasp_key_table {
|
||||
uint32_t nkeys;
|
||||
struct sec_hasp_key* keys;
|
||||
};
|
||||
|
||||
/* index 0 (type): 1 = dev, 2 = retail
|
||||
index 1 (language): 3001 = english */
|
||||
/* static const uint32_t sec_hasp_features[] = {2, 3001}; */
|
||||
static const uint32_t sec_hasp_key_id = 0xDEADBEEF;
|
||||
static struct sec_hasp_key_table sec_hasp_keys;
|
||||
|
||||
void sec_hasp_init(const uint8_t* key_data, size_t len)
|
||||
{
|
||||
sec_hasp_keys.nkeys = len / sizeof(struct sec_hasp_key);
|
||||
sec_hasp_keys.keys =
|
||||
util_xmalloc(sizeof(struct sec_hasp_key) * sec_hasp_keys.nkeys);
|
||||
|
||||
memcpy(sec_hasp_keys.keys, key_data,
|
||||
sec_hasp_keys.nkeys * sizeof(struct sec_hasp_key));
|
||||
|
||||
log_info("Loaded %d keys", sec_hasp_keys.nkeys);
|
||||
}
|
||||
|
||||
int sec_hasp_api_login(int feature, int vendor_code, int* handle)
|
||||
{
|
||||
*handle = (int) &sec_hasp_key_id;
|
||||
|
||||
log_debug("login, feature %d, vendor_code %d, ret handle 0x%X", feature,
|
||||
vendor_code, *handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sec_hasp_api_logout(int handle)
|
||||
{
|
||||
if (handle != (int) &sec_hasp_key_id) {
|
||||
log_error("Logout of unknown handle 0x%X", handle);
|
||||
} else {
|
||||
log_debug("Logout: 0x%X", handle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int sec_hasp_api_getid(void)
|
||||
{
|
||||
return sec_hasp_key_id;
|
||||
}
|
||||
|
||||
int sec_hasp_api_decrypt(int handle, void* buffer, size_t length)
|
||||
{
|
||||
char* buf;
|
||||
|
||||
if (handle != (int) &sec_hasp_key_id) {
|
||||
log_error("Unknown handle 0x%X sending decrypt request, ignored");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < sec_hasp_keys.nkeys; i++) {
|
||||
if (!memcmp(buffer, sec_hasp_keys.keys[i].req,
|
||||
sizeof(sec_hasp_keys.keys[i].req))) {
|
||||
log_debug("Decrypt %u -> %u",
|
||||
*((uint64_t*) sec_hasp_keys.keys[i].req),
|
||||
*((uint64_t*) sec_hasp_keys.keys[i].resp));
|
||||
memcpy(buffer, sec_hasp_keys.keys[i].resp,
|
||||
sizeof(sec_hasp_keys.keys[i].resp));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
buf = util_str_buffer(buffer, length);
|
||||
|
||||
log_warn("Missing key for request %s", buf);
|
||||
free(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Hasp emulator (Fiesta 2 and up)
|
||||
*/
|
||||
#ifndef SEC_HASP_H
|
||||
#define SEC_HASP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* A single entry in the key table
|
||||
*/
|
||||
struct sec_hasp_key {
|
||||
uint8_t req[64];
|
||||
uint8_t resp[64];
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* Initialize the hasp emulation module. The dongle for this
|
||||
* was used on Prime and up
|
||||
*
|
||||
* @param key_data Pointer to loaded key data (key table)
|
||||
* @param len Length of the buffer
|
||||
*/
|
||||
void sec_hasp_init(const uint8_t* key_data, size_t len);
|
||||
|
||||
int sec_hasp_api_login(int feature, int vendor_code, int* handle);
|
||||
|
||||
int sec_hasp_api_logout(int handle);
|
||||
|
||||
unsigned int sec_hasp_api_getid(void);
|
||||
|
||||
int sec_hasp_api_decrypt(int handle, void* buffer, size_t length);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "sec/hasp/lib/server.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
sec_hasp_server_init(NULL, 0);
|
||||
sec_hasp_server_run();
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef SEC_LOCKCHIP_DEFS_H
|
||||
#define SEC_LOCKCHIP_DEFS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* List of games using the old type mk3 lockchip board */
|
||||
enum sec_lockchip_defs_games {
|
||||
SEC_LOCK_CHIP_DEFS_GAME_3RD_SE = 0,
|
||||
SEC_LOCK_CHIP_DEFS_GAME_COLLECTION = 1,
|
||||
SEC_LOCK_CHIP_DEFS_GAME_PERFECT_COLLECTION = 2,
|
||||
SEC_LOCK_CHIP_DEFS_GAME_EXTRA_PROTO = 3,
|
||||
SEC_LOCK_CHIP_DEFS_GAME_PREMIERE = 4,
|
||||
SEC_LOCK_CHIP_DEFS_GAME_PREX = 5,
|
||||
SEC_LOCK_CHIP_DEFS_GAME_REBIRTH = 6,
|
||||
SEC_LOCK_CHIP_DEFS_GAME_PREMIERE_2 = 7,
|
||||
/* Extra Plus */
|
||||
SEC_LOCK_CHIP_DEFS_GAME_PREX_2_BETA = 8,
|
||||
SEC_LOCK_CHIP_DEFS_GAME_PREX_2 = 9,
|
||||
SEC_LOCK_CHIP_DEFS_GAME_PREMIERE_3 = 10,
|
||||
SEC_LOCK_CHIP_DEFS_GAME_PREX_3 = 11,
|
||||
SEC_LOCK_CHIP_DEFS_GAME_EXTRA = 12,
|
||||
/* NXA for mk3, funbox... */
|
||||
SEC_LOCK_CHIP_DEFS_GAME_BOOTLEGS = 13,
|
||||
SEC_LOCK_CHIP_DEFS_GAME_EXCEED = 14,
|
||||
SEC_LOCK_CHIP_DEFS_GAME_COUNT = 15,
|
||||
};
|
||||
|
||||
/** List of lockchip transforms of all pump games that used it.
|
||||
*/
|
||||
static const uint8_t sec_lockchip_defs_transforms
|
||||
[SEC_LOCK_CHIP_DEFS_GAME_COUNT][8] = {
|
||||
/* 3rd SE */
|
||||
{0xF0, 0x1C, 0xFE, 0x03, 0x81, 0x40, 0x38, 0xF8},
|
||||
/* Collection */
|
||||
{0xF0, 0x1C, 0xFE, 0x03, 0x81, 0x40, 0x38, 0xF8},
|
||||
/* Perfect Collection */
|
||||
{0xF0, 0x1C, 0xFE, 0x03, 0x81, 0x40, 0x38, 0xF8},
|
||||
/* Extra Proto */
|
||||
{0xF8, 0x82, 0x70, 0x0C, 0xFD, 0xC1, 0x20, 0xE0},
|
||||
/* Premiere */
|
||||
{0xE0, 0x78, 0x7C, 0xFD, 0xF1, 0xF2, 0xC2, 0xC0},
|
||||
/* Prex */
|
||||
{0xE0, 0x78, 0x7C, 0xFD, 0xF1, 0xF2, 0xC2, 0xC0},
|
||||
/* Rebirth */
|
||||
{0xFC, 0xF9, 0x08, 0x10, 0xE2, 0xC2, 0x40, 0x80},
|
||||
/* Premiere 2 */
|
||||
{0xFC, 0x03, 0xE1, 0x20, 0x38, 0x78, 0x70, 0xF0},
|
||||
/* Prex 2 Beta */
|
||||
{0xE0, 0x38, 0x78, 0x81, 0x03, 0xFE, 0x0C, 0xF0},
|
||||
/* Prex 2 */
|
||||
{0xE0, 0x38, 0x78, 0x81, 0x03, 0xFE, 0x0C, 0xF0},
|
||||
/* Premiere 3 */
|
||||
{0x02, 0xC1, 0xF9, 0x78, 0x7C, 0x1C, 0x10, 0xF0},
|
||||
/* Prex 3 */
|
||||
{0xF0, 0xE1, 0xF9, 0x78, 0x7C, 0xFE, 0xC2, 0xC0},
|
||||
/* Extra */
|
||||
{0xF8, 0x82, 0x70, 0x0C, 0xFD, 0xC1, 0x20, 0xE0},
|
||||
/* Bootlegs */
|
||||
{0x02, 0xC1, 0xF9, 0x78, 0x7C, 0x1C, 0x10, 0xF0},
|
||||
/* Exceed */
|
||||
{0xF0, 0x78, 0xF9, 0xFD, 0x1C, 0x20, 0xC2, 0x02},
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
#define LOG_MODULE "sec-lockchip"
|
||||
|
||||
#include "sec/lockchip/lockchip.h"
|
||||
|
||||
static const uint8_t sec_lockchip_initial_sbox[8] = {
|
||||
0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x7f
|
||||
};
|
||||
|
||||
static const uint8_t* sec_lockchip_transform;
|
||||
static uint8_t sec_lockchip_state;
|
||||
|
||||
static void sec_lockchip_apply_initial_sbox(void)
|
||||
{
|
||||
uint8_t r = 0;
|
||||
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
|
||||
if (sec_lockchip_state & (1 << i)) {
|
||||
r ^= sec_lockchip_initial_sbox[i];
|
||||
}
|
||||
}
|
||||
|
||||
sec_lockchip_state = r;
|
||||
}
|
||||
|
||||
static uint8_t sec_lockchip_comp_sbox_ceof(uint8_t sel, uint8_t bit)
|
||||
{
|
||||
uint8_t r ;
|
||||
|
||||
if (!sel) {
|
||||
return sec_lockchip_transform[bit];
|
||||
}
|
||||
|
||||
r = sec_lockchip_comp_sbox_ceof((sel - 1) & 7, (bit - 1) & 7);
|
||||
r = (r << 1) | (((r >> 7) ^ (r >> 6)) & 1);
|
||||
|
||||
if (bit != 7) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return r ^ sec_lockchip_comp_sbox_ceof(sel, 0);
|
||||
}
|
||||
|
||||
static void sec_lockchip_apply_bit_sbox(uint8_t bit)
|
||||
{
|
||||
uint8_t r = 0;
|
||||
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
|
||||
if (sec_lockchip_state & (1 << i)) {
|
||||
r ^= sec_lockchip_comp_sbox_ceof(bit, i);
|
||||
}
|
||||
}
|
||||
|
||||
sec_lockchip_state = r;
|
||||
}
|
||||
|
||||
void sec_lockchip_init(const uint8_t* transform)
|
||||
{
|
||||
sec_lockchip_transform = transform;
|
||||
sec_lockchip_state = 0;
|
||||
}
|
||||
|
||||
void sec_lockchip_start(void)
|
||||
{
|
||||
sec_lockchip_state = 0xFC;
|
||||
}
|
||||
|
||||
uint8_t sec_lockchip_step(uint8_t data)
|
||||
{
|
||||
uint8_t result = 0;
|
||||
|
||||
for (uint8_t bit = 0; bit < 8; bit++) {
|
||||
|
||||
if (bit == 0) {
|
||||
sec_lockchip_apply_initial_sbox();
|
||||
}
|
||||
|
||||
result ^= (((sec_lockchip_state >> bit) & 1) << bit);
|
||||
|
||||
if (((data >> bit) & 1) == 0) {
|
||||
sec_lockchip_apply_bit_sbox(bit);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Lockchip (MK3 games) emulator
|
||||
*
|
||||
* Credits to TheSchaf for the emulation code and transform data
|
||||
*/
|
||||
#ifndef SEC_LOCKCHIP_H
|
||||
#define SEC_LOCKCHIP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Initialize (or reset) the lockchip emulation module
|
||||
*
|
||||
* @param transform Pointer to a transform to use for the step calls
|
||||
*/
|
||||
void sec_lockchip_init(const uint8_t* transform);
|
||||
|
||||
/**
|
||||
* Start lockchip emulation and set the initial state before calling step
|
||||
*/
|
||||
void sec_lockchip_start(void);
|
||||
|
||||
/**
|
||||
* Execute a step on some data
|
||||
*
|
||||
* @param data Input data to transform
|
||||
* @return Transformed output
|
||||
*/
|
||||
uint8_t sec_lockchip_step(uint8_t data);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,399 @@
|
||||
#define LOG_MODULE "sec-microdog34"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#define IOCTL_REQUEST_MD_XACT 0x6B00
|
||||
#define GOLD_SALT 0x646C6F47
|
||||
#define MAGIC 0x484D
|
||||
|
||||
enum sec_microdog34_req_opcode {
|
||||
SEC_MICRODOG34_OP_DOG_CHECK = 0x01,
|
||||
SEC_MICRODOG34_OP_READ_DOG = 0x02,
|
||||
SEC_MICRODOG34_OP_WRITE_DOG = 0x03,
|
||||
SEC_MICRODOG34_OP_CONVERT = 0x04,
|
||||
SEC_MICRODOG34_OP_SET_PASS = 0x07,
|
||||
SEC_MICRODOG34_OP_SET_SHARE = 0x08,
|
||||
SEC_MICRODOG34_OP_GET_LOCK_NO = 0x0B,
|
||||
SEC_MICRODOG34_OP_LOGIN = 0x14,
|
||||
SEC_MICRODOG34_OP_DOG_CASCADE = 0x15,
|
||||
SEC_MICRODOG34_OP_RAINBOW_TABLE_ADD = 0x539,
|
||||
SEC_MICRODOG34_OP_DOG_SERIAL = 0x53A,
|
||||
SEC_MICRODOG34_OP_SET_MFG_SERIAL = 0x53B,
|
||||
SEC_MICRODOG34_OP_SET_VID = 0x53C,
|
||||
SEC_MICRODOG34_OP_RELOAD_RAINBOW_TABLE = 0x53D
|
||||
};
|
||||
|
||||
struct sec_microdog34_req {
|
||||
uint16_t magic;
|
||||
uint16_t opcode;
|
||||
uint32_t dog_serial;
|
||||
uint32_t mask_key;
|
||||
uint16_t dog_addr;
|
||||
uint16_t dog_bytes;
|
||||
uint8_t dog_data[256];
|
||||
uint32_t dog_password;
|
||||
uint8_t host_id;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
enum sec_microdog34_resp_code {
|
||||
SEC_MICRODOG34_RESP_NO_ERR = 0,
|
||||
SEC_MICRODOG34_RESP_ERR_PASS = 0x2745
|
||||
};
|
||||
|
||||
struct sec_microdog34_resp {
|
||||
uint32_t dog_serial;
|
||||
uint32_t return_code;
|
||||
uint8_t dog_data[200];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct sec_microdog34_header {
|
||||
uint32_t serial;
|
||||
uint32_t password;
|
||||
uint8_t vendor_id[8];
|
||||
uint32_t mfg_serial;
|
||||
uint8_t flash_memory[200];
|
||||
uint32_t num_keys;
|
||||
};
|
||||
|
||||
struct sec_microdog34_key {
|
||||
uint32_t response;
|
||||
uint32_t algorithm;
|
||||
uint32_t req_len;
|
||||
uint8_t request[64];
|
||||
};
|
||||
|
||||
static void sec_microdog34_dispatch(struct sec_microdog34_req* req,
|
||||
struct sec_microdog34_resp* resp);
|
||||
static void sec_microdog34_crypt_req(struct sec_microdog34_req* req);
|
||||
static void sec_microdog34_crypt_resp(struct sec_microdog34_req* req,
|
||||
struct sec_microdog34_resp* resp);
|
||||
static void sec_microdog34_dump_req(struct sec_microdog34_req* req);
|
||||
static void sec_microdog34_dump_resq(struct sec_microdog34_resp* resp);
|
||||
static uint32_t sec_microdog34_convert_req(const uint8_t* req_data,
|
||||
uint16_t req_len);
|
||||
|
||||
static struct sec_microdog34_header sec_microdog34_header;
|
||||
static struct sec_microdog34_key* sec_microdog34_key_table;
|
||||
|
||||
static uint8_t sec_microdog34_share;
|
||||
|
||||
void sec_microdog34_init(const uint8_t* key_data, size_t len)
|
||||
{
|
||||
memcpy(&sec_microdog34_header, key_data,
|
||||
sizeof(struct sec_microdog34_header));
|
||||
|
||||
if (len - sizeof(struct sec_microdog34_header) <
|
||||
sec_microdog34_header.num_keys *
|
||||
sizeof(struct sec_microdog34_key)) {
|
||||
log_error("Loading key table failed, incomplete key table "
|
||||
"(num_keys entry: %d, num_keys size: %d",
|
||||
sec_microdog34_header.num_keys,
|
||||
(len - sizeof(struct sec_microdog34_header)) /
|
||||
sizeof(struct sec_microdog34_key));
|
||||
return;
|
||||
}
|
||||
|
||||
sec_microdog34_key_table = util_xmalloc(sec_microdog34_header.num_keys *
|
||||
sizeof(struct sec_microdog34_key));
|
||||
|
||||
memcpy(sec_microdog34_key_table,
|
||||
key_data + sizeof(struct sec_microdog34_header),
|
||||
sec_microdog34_header.num_keys * sizeof(struct sec_microdog34_key));
|
||||
|
||||
log_info("Loaded %d keys, serial 0x%X, password 0x%X",
|
||||
sec_microdog34_header.num_keys, sec_microdog34_header.serial,
|
||||
sec_microdog34_header.password);
|
||||
|
||||
sec_microdog34_share = 0;
|
||||
}
|
||||
|
||||
int sec_microdog34_process(int request, void* data)
|
||||
{
|
||||
if (request == IOCTL_REQUEST_MD_XACT) {
|
||||
struct sec_microdog34_req req;
|
||||
struct sec_microdog34_resp resp;
|
||||
|
||||
memset(&resp, 0, sizeof(struct sec_microdog34_resp));
|
||||
|
||||
// where to get the data? weird stuff, but that's how it's done
|
||||
// it really has to be like that, otherwise we get trash from data
|
||||
uint32_t req_data_offset = *(unsigned int*) data;
|
||||
memcpy(&req, (void*) req_data_offset,
|
||||
sizeof(struct sec_microdog34_req));
|
||||
|
||||
sec_microdog34_crypt_req(&req);
|
||||
sec_microdog34_dispatch(&req, &resp);
|
||||
sec_microdog34_crypt_resp(&req, &resp);
|
||||
|
||||
// again, same as req data
|
||||
// it really has to be like that, otherwise we get trash from data
|
||||
uint32_t resp_data_offset = *(unsigned int*) data - 272;
|
||||
memcpy((void*) resp_data_offset, &resp,
|
||||
sizeof(struct sec_microdog34_resp));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void sec_microdog34_dispatch(struct sec_microdog34_req* req,
|
||||
struct sec_microdog34_resp* resp)
|
||||
{
|
||||
struct sec_microdog34_key* key;
|
||||
|
||||
resp->dog_serial = req->dog_serial;
|
||||
|
||||
if (req->magic != MAGIC) {
|
||||
log_error("Bad magic 0x%X for request", req->magic);
|
||||
}
|
||||
|
||||
sec_microdog34_dump_req(req);
|
||||
|
||||
switch (req->opcode) {
|
||||
case SEC_MICRODOG34_OP_DOG_CHECK:
|
||||
log_debug("SEC_MICRODOG34_OP_DOG_CHECK");
|
||||
break;
|
||||
|
||||
case SEC_MICRODOG34_OP_READ_DOG:
|
||||
log_debug("SEC_MICRODOG34_OP_READ_DOG");
|
||||
|
||||
if (req->dog_password == sec_microdog34_header.password) {
|
||||
memcpy(resp->dog_data,
|
||||
sec_microdog34_header.flash_memory + req->dog_addr,
|
||||
req->dog_bytes);
|
||||
} else {
|
||||
resp->return_code = SEC_MICRODOG34_RESP_ERR_PASS;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SEC_MICRODOG34_OP_WRITE_DOG:
|
||||
log_debug("SEC_MICRODOG34_OP_WRITE_DOG");
|
||||
|
||||
if (req->dog_password == sec_microdog34_header.password) {
|
||||
memcpy(sec_microdog34_header.flash_memory + req->dog_addr,
|
||||
req->dog_data, req->dog_bytes);
|
||||
} else {
|
||||
resp->return_code = SEC_MICRODOG34_RESP_ERR_PASS;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SEC_MICRODOG34_OP_CONVERT:
|
||||
log_debug("SEC_MICRODOG34_OP_CONVERT");
|
||||
|
||||
uint32_t resp_data = sec_microdog34_convert_req(req->dog_data,
|
||||
req->dog_bytes);
|
||||
memcpy(resp->dog_data, &resp_data, sizeof(uint32_t));
|
||||
|
||||
break;
|
||||
|
||||
case SEC_MICRODOG34_OP_SET_PASS:
|
||||
log_debug("SEC_MICRODOG34_OP_SET_PASS: 0x%08X",
|
||||
*((uint32_t*) req->dog_data));
|
||||
|
||||
if (req->dog_password == sec_microdog34_header.password) {
|
||||
sec_microdog34_header.password = *((uint32_t*) req->dog_data);
|
||||
}
|
||||
else
|
||||
resp->return_code = SEC_MICRODOG34_RESP_ERR_PASS;
|
||||
|
||||
break;
|
||||
|
||||
case SEC_MICRODOG34_OP_SET_SHARE:
|
||||
log_debug("SEC_MICRODOG34_OP_SET_SHARE");
|
||||
|
||||
sec_microdog34_share = req->dog_data[0];
|
||||
resp->dog_data[0] = sec_microdog34_share;
|
||||
|
||||
break;
|
||||
|
||||
case SEC_MICRODOG34_OP_GET_LOCK_NO:
|
||||
log_debug("SEC_MICRODOG34_OP_GET_LOCK_NO: 0x%08X",
|
||||
sec_microdog34_header.mfg_serial);
|
||||
|
||||
memcpy(resp->dog_data, &sec_microdog34_header.mfg_serial, 4);
|
||||
|
||||
break;
|
||||
|
||||
case SEC_MICRODOG34_OP_LOGIN:
|
||||
log_debug("SEC_MICRODOG34_OP_LOGIN");
|
||||
|
||||
memcpy(resp->dog_data, sec_microdog34_header.vendor_id,
|
||||
sizeof(sec_microdog34_header.vendor_id));
|
||||
|
||||
break;
|
||||
|
||||
case SEC_MICRODOG34_OP_DOG_CASCADE:
|
||||
log_debug("SEC_MICRODOG34_OP_DOG_CASCADE");
|
||||
|
||||
resp->dog_data[0] = req->dog_data[0];
|
||||
|
||||
break;
|
||||
|
||||
case SEC_MICRODOG34_OP_RAINBOW_TABLE_ADD:
|
||||
log_debug("SEC_MICRODOG34_OP_RAINBOW_TABLE_ADD");
|
||||
|
||||
/* expand table */
|
||||
sec_microdog34_key_table = util_xrealloc(sec_microdog34_key_table,
|
||||
(sec_microdog34_header.num_keys + 1) *
|
||||
sizeof(struct sec_microdog34_key));
|
||||
|
||||
key = &sec_microdog34_key_table[sec_microdog34_header.num_keys];
|
||||
key->response = req->dog_password;
|
||||
/* algorithm(last 4 bytes of flash memory) */
|
||||
key->algorithm =
|
||||
*((uint32_t*) (sec_microdog34_header.flash_memory + 196));
|
||||
key->req_len = req->dog_bytes;
|
||||
memcpy(key->request, req->dog_data, sizeof(key->request));
|
||||
|
||||
sec_microdog34_header.num_keys++;
|
||||
|
||||
break;
|
||||
|
||||
case SEC_MICRODOG34_OP_DOG_SERIAL:
|
||||
log_debug("SEC_MICRODOG34_OP_DOG_SERIAL: 0x%08X",
|
||||
*((uint32_t*) req->dog_data));
|
||||
|
||||
sec_microdog34_header.serial = *((uint32_t*) req->dog_data);
|
||||
|
||||
break;
|
||||
|
||||
case SEC_MICRODOG34_OP_SET_MFG_SERIAL:
|
||||
log_debug("SEC_MICRODOG34_OP_SET_MFG_SERIAL: 0x%08X ",
|
||||
*((uint32_t*) req->dog_data));
|
||||
|
||||
sec_microdog34_header.mfg_serial = *((uint32_t*) req->dog_data);
|
||||
|
||||
break;
|
||||
|
||||
case SEC_MICRODOG34_OP_SET_VID:
|
||||
log_debug("SEC_MICRODOG34_OP_SET_VID");
|
||||
|
||||
memcpy(sec_microdog34_header.vendor_id, req->dog_data,
|
||||
sizeof(sec_microdog34_header.vendor_id));
|
||||
|
||||
break;
|
||||
|
||||
case SEC_MICRODOG34_OP_RELOAD_RAINBOW_TABLE:
|
||||
log_debug("SEC_MICRODOG34_OP_RELOAD_RAINBOW_TABLE");
|
||||
|
||||
/* nothing to load here */
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warn("Unsupported opcode: %04X", req->opcode);
|
||||
break;
|
||||
}
|
||||
|
||||
sec_microdog34_dump_resq(resp);
|
||||
}
|
||||
|
||||
static void sec_microdog34_crypt_req(struct sec_microdog34_req* req)
|
||||
{
|
||||
uint32_t tmp_mask = (req->mask_key + GOLD_SALT) & 0xFFFFFFFF;
|
||||
uint8_t tmb_mask[4] = {0, 0, 0, 0};
|
||||
memcpy(tmb_mask, &tmp_mask, 4);
|
||||
|
||||
req->dog_addr ^= (tmp_mask & 0xFFFF);
|
||||
req->dog_bytes ^= (tmp_mask & 0xFFFF);
|
||||
req->dog_password ^= tmp_mask;
|
||||
req->host_id ^= (tmp_mask & 0xFF);
|
||||
|
||||
for (int i = 0 ; i < sizeof(req->dog_data); i++) {
|
||||
req->dog_data[i] ^= tmb_mask[i % 4];
|
||||
}
|
||||
}
|
||||
|
||||
static void sec_microdog34_crypt_resp(struct sec_microdog34_req* req,
|
||||
struct sec_microdog34_resp* resp)
|
||||
{
|
||||
uint32_t tmp_mask = (req->mask_key + GOLD_SALT) & 0xFFFFFFFF;
|
||||
uint8_t tmb_mask[4] = {0, 0, 0, 0};
|
||||
memcpy(tmb_mask, &tmp_mask, 4);
|
||||
|
||||
for (int i = 0 ; i < sizeof(resp->dog_data); i++) {
|
||||
resp->dog_data[i] ^= tmb_mask[i % 4];
|
||||
}
|
||||
}
|
||||
|
||||
static void sec_microdog34_dump_req(struct sec_microdog34_req* req)
|
||||
{
|
||||
char* data_tmp = util_str_buffer(req->dog_data, sizeof(req->dog_data));
|
||||
|
||||
log_debug(
|
||||
"Dog request:\n"
|
||||
"magic: 0x%X\n"
|
||||
"opcode: 0x%X\n"
|
||||
"dog_serial: 0x%X\n"
|
||||
"mask_key: 0x%X\n"
|
||||
"dog_addr: 0x%X\n"
|
||||
"dog_bytes: 0x%X\n"
|
||||
"dog_data: %s\n"
|
||||
"dog_pass: 0x%X\n"
|
||||
"host_id: 0x%X",
|
||||
req->magic,
|
||||
req->opcode,
|
||||
req->dog_serial,
|
||||
req->mask_key,
|
||||
req->dog_addr,
|
||||
req->dog_bytes,
|
||||
data_tmp,
|
||||
req->dog_password,
|
||||
req->host_id);
|
||||
|
||||
free(data_tmp);
|
||||
}
|
||||
|
||||
static void sec_microdog34_dump_resq(struct sec_microdog34_resp* resp)
|
||||
{
|
||||
char* data_tmp = util_str_buffer(resp->dog_data, sizeof(resp->dog_data));
|
||||
|
||||
log_debug(
|
||||
"Dog response:\n"
|
||||
"dog_serial: 0x%X\n"
|
||||
"return_code: 0x%X\n"
|
||||
"dog_data: %s\n",
|
||||
resp->dog_serial,
|
||||
resp->return_code,
|
||||
data_tmp);
|
||||
|
||||
free(data_tmp);
|
||||
}
|
||||
|
||||
static uint32_t sec_microdog34_convert_req(const uint8_t* req_data,
|
||||
uint16_t req_len)
|
||||
{
|
||||
uint32_t cur_algo;
|
||||
|
||||
cur_algo = 0;
|
||||
memcpy(&cur_algo, sec_microdog34_header.flash_memory + 196, 4);
|
||||
|
||||
for (uint32_t i = 0; i < sec_microdog34_header.num_keys; i++) {
|
||||
struct sec_microdog34_key* key = &sec_microdog34_key_table[i];
|
||||
|
||||
if (key->algorithm == cur_algo) {
|
||||
|
||||
if (key->req_len == req_len) {
|
||||
|
||||
if (!memcmp(key->request, req_data, req_len)) {
|
||||
char* tmp = util_str_buffer(req_data, req_len);
|
||||
log_debug("Converting: %s ==> %X", tmp, key->response);
|
||||
free(tmp);
|
||||
return key->response;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char* tmp = util_str_buffer(req_data, req_len);
|
||||
log_warn("Converting request %s failed, no matching response found", tmp);
|
||||
free(tmp);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Microdog 3.4 emulator
|
||||
*
|
||||
* Credits to batteryshark for the emulation code and key files
|
||||
*/
|
||||
#ifndef SEC_MICRODOG34_H
|
||||
#define SEC_MICRODOG34_H
|
||||
|
||||
/**
|
||||
* Header of a key file containing attributes of the dongle. This is followed
|
||||
* by an arbitrary number of keys
|
||||
*/
|
||||
struct sec_microdog34_header {
|
||||
uint32_t serial;
|
||||
uint32_t password;
|
||||
uint8_t vendor_id[8];
|
||||
uint32_t mfg_serial;
|
||||
uint8_t flash_memory[200];
|
||||
uint32_t num_keys;
|
||||
};
|
||||
|
||||
/**
|
||||
* A single entry in the key table (comes after the header)
|
||||
*/
|
||||
struct sec_microdog34_key {
|
||||
uint32_t response;
|
||||
uint32_t algorithm;
|
||||
uint32_t req_len;
|
||||
uint8_t request[64];
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the microdog (3.4) emulation module. The dongle for this
|
||||
* was used on Exceed2, Zero and NX
|
||||
*
|
||||
* @param key_data Pointer to loaded key data (dog attributes, key table)
|
||||
* @param len Length of the buffer
|
||||
*/
|
||||
void sec_microdog34_init(const uint8_t* key_data, size_t len);
|
||||
|
||||
/**
|
||||
* Process an incoming request to the dongle
|
||||
*
|
||||
* @param request IOCTL request for the dongle
|
||||
* @param data Pointer to a buffer with request data. Response is returned
|
||||
* to the same buffer (request overwritten)
|
||||
* @return 0 and success, -1 on failure
|
||||
*/
|
||||
int sec_microdog34_process(int request, void* data);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,427 @@
|
||||
#define LOG_MODULE "sec-microdog40"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "crypt/aes.h"
|
||||
#include "crypt/md5.h"
|
||||
|
||||
#include "sec/microdog40/microdog40.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#define REQHEAD_SIZE 0x120
|
||||
#define REQTAIL_SIZE 0x10
|
||||
#define PROTOCOL_VERSION 0x11
|
||||
|
||||
enum sec_microdog40_req_opcode {
|
||||
SEC_MICRODOG40_REQ_OPCODE_SET_SHARE = 0x08,
|
||||
SEC_MICRODOG40_REQ_OPCODE_GET_LOCK_NO = 0x0B,
|
||||
SEC_MICRODOG40_REQ_OPCODE_GET_ID = 0x14,
|
||||
SEC_MICRODOG40_REQ_OPCODE_CONVERT = 0x40,
|
||||
SEC_MICRODOG40_REQ_OPCODE_AUTH_STEP_1 = 0x65,
|
||||
SEC_MICRODOG40_REQ_OPCODE_AUTH_STEP_2 = 0x67,
|
||||
SEC_MICRODOG40_REQ_OPCODE_AUTH_STEP_3 = 0x66
|
||||
};
|
||||
|
||||
struct sec_microdog40_month_key_hash_input {
|
||||
uint8_t ssk_prefix[25];
|
||||
uint16_t year;
|
||||
uint8_t const45;
|
||||
uint16_t month;
|
||||
} __attribute((__packed__));
|
||||
|
||||
static const uint8_t sec_microdog40_shared_secret[30] = {
|
||||
0x2A, 0x2F, 0xED, 0x5E, 0x49,
|
||||
0x26, 0x40, 0x19, 0x40, 0x40,
|
||||
0xE2, 0x51, 0xAA, 0xFA, 0xDB,
|
||||
0xCB, 0x67, 0x21, 0x4C, 0xA4,
|
||||
0x10, 0x7E, 0x51, 0x22, 0x25,
|
||||
0x11, 0x2B, 0x3C, 0x46, 0x5E
|
||||
};
|
||||
|
||||
static const uint16_t sec_microdog40_magic = 0x484D;
|
||||
|
||||
static struct sec_microdog40_header sec_microdog40_header;
|
||||
static struct sec_microdog40_key* sec_microdog40_key_table;
|
||||
|
||||
static uint8_t sec_microdog40_share;
|
||||
|
||||
static void sec_microdog40_log_req(const struct sec_microdog40_req* req)
|
||||
{
|
||||
char* data_tmp = util_str_buffer(req->payload, sizeof(req->payload));
|
||||
|
||||
log_debug(
|
||||
"Processing request:\n"
|
||||
"magic: 0x%X\n"
|
||||
"req_type: 0x%X\n"
|
||||
"mask_key1: 0x%X\n"
|
||||
"dog_cascade: 0x%X\n"
|
||||
"mfg_serial: 0x%X\n"
|
||||
"mask_key2: 0x%X\n"
|
||||
"dog_addr: 0x%X\n"
|
||||
"payload_size: %d\n"
|
||||
"mask_key3: 0x%X\n"
|
||||
"payload: %s\n"
|
||||
"dog_password: 0x%X\n"
|
||||
"host_id: 0x%X\n"
|
||||
"mask_key4: 0x%X\n"
|
||||
"timestamp.year: %d\n"
|
||||
"timestamp.month: %d\n"
|
||||
"timestamp.day: %d\n"
|
||||
"timestamp.hour: %d\n"
|
||||
"timestamp.minute: %d\n"
|
||||
"timestamp.second: %d",
|
||||
req->magic,
|
||||
req->req_type,
|
||||
req->mask_key1,
|
||||
req->dog_cascade,
|
||||
req->mfg_serial,
|
||||
req->mask_key2,
|
||||
req->dog_addr,
|
||||
req->payload_size,
|
||||
req->mask_key3,
|
||||
data_tmp,
|
||||
req->dog_password,
|
||||
req->host_id,
|
||||
req->mask_key4,
|
||||
req->timestamp.year,
|
||||
req->timestamp.month,
|
||||
req->timestamp.day,
|
||||
req->timestamp.hour,
|
||||
req->timestamp.minute,
|
||||
req->timestamp.second);
|
||||
|
||||
free(data_tmp);
|
||||
}
|
||||
|
||||
static void sec_microdog40_get_month_key(uint8_t* month_key,
|
||||
const struct sec_microdog40_timestamp* timestamp)
|
||||
{
|
||||
struct sec_microdog40_month_key_hash_input hash_in;
|
||||
|
||||
memcpy(hash_in.ssk_prefix, sec_microdog40_shared_secret,
|
||||
sizeof(hash_in.ssk_prefix));
|
||||
hash_in.year = timestamp->year;
|
||||
hash_in.month = timestamp->month;
|
||||
hash_in.const45 = 45;
|
||||
|
||||
crypt_md5_hash(month_key, (const uint8_t*) &hash_in,
|
||||
sizeof(struct sec_microdog40_month_key_hash_input));
|
||||
}
|
||||
|
||||
static void sec_microdog40_dec_req(const struct sec_microdog40_req* enc_req,
|
||||
struct sec_microdog40_req* dec_req)
|
||||
{
|
||||
uint8_t key[16];
|
||||
|
||||
crypt_md5_hash(key, sec_microdog40_shared_secret,
|
||||
sizeof(sec_microdog40_shared_secret));
|
||||
crypt_aes_dec(key, CRYPT_AES_KEY_LENGTH_16_BYTES, ((uint8_t *) dec_req) +
|
||||
REQHEAD_SIZE, ((uint8_t*) enc_req) + REQHEAD_SIZE, REQTAIL_SIZE);
|
||||
|
||||
sec_microdog40_get_month_key(key, &dec_req->timestamp);
|
||||
crypt_aes_dec(key, CRYPT_AES_KEY_LENGTH_16_BYTES, ((uint8_t *) dec_req),
|
||||
((uint8_t*) enc_req), REQHEAD_SIZE);
|
||||
|
||||
dec_req->magic ^= dec_req->mask_key4;
|
||||
dec_req->req_type ^= dec_req->mask_key4;
|
||||
dec_req->mask_key1 ^= dec_req->mask_key4;
|
||||
dec_req->dog_cascade ^= dec_req->mask_key4;
|
||||
dec_req->mfg_serial ^= dec_req->mask_key4;
|
||||
dec_req->mask_key2 ^= dec_req->mask_key4;
|
||||
dec_req->dog_addr ^= dec_req->mask_key4;
|
||||
dec_req->payload_size ^= dec_req->mask_key4;
|
||||
dec_req->mask_key3 ^= dec_req->mask_key4;
|
||||
dec_req->dog_password ^= dec_req->mask_key4;
|
||||
dec_req->host_id ^= dec_req->mask_key4;
|
||||
|
||||
for (int i = 0 ; i < 64 ; i++) {
|
||||
((uint32_t *) dec_req->payload)[i] ^= dec_req->mask_key4;
|
||||
}
|
||||
|
||||
dec_req->magic ^= dec_req->mask_key3;
|
||||
dec_req->req_type ^= dec_req->mask_key3;
|
||||
dec_req->mask_key1 ^= dec_req->mask_key3;
|
||||
dec_req->dog_cascade ^= dec_req->mask_key3;
|
||||
dec_req->mfg_serial ^= dec_req->mask_key3;
|
||||
dec_req->mask_key2 ^= dec_req->mask_key3;
|
||||
dec_req->dog_addr ^= dec_req->mask_key3;
|
||||
dec_req->payload_size ^= dec_req->mask_key3;
|
||||
|
||||
dec_req->magic ^= dec_req->mask_key2;
|
||||
dec_req->req_type ^= dec_req->mask_key2;
|
||||
dec_req->mask_key1 ^= dec_req->mask_key2;
|
||||
dec_req->dog_cascade ^= dec_req->mask_key2;
|
||||
dec_req->mfg_serial ^= dec_req->mask_key2;
|
||||
|
||||
dec_req->magic ^= dec_req->mask_key1;
|
||||
dec_req->req_type ^= dec_req->mask_key1;
|
||||
}
|
||||
|
||||
/* unused */
|
||||
/*
|
||||
static void sec_microdog40_dec_resp(const struct sec_microdog40_resp* enc_resp,
|
||||
struct sec_microdog40_resp* dec_resp,
|
||||
const struct sec_microdog40_timestamp* timestamp)
|
||||
{
|
||||
uint8_t key[16];
|
||||
|
||||
sec_microdog40_get_month_key(key, timestamp);
|
||||
crypt_aes_dec(key, CRYPT_AES_KEY_LENGTH_16_BYTES, (uint8_t*) dec_resp,
|
||||
(uint8_t*) enc_resp, sizeof(struct sec_microdog40_resp));
|
||||
|
||||
for (int i = 0 ; i < 64 ; i++) {
|
||||
((uint32_t *) dec_resp->payload)[i] ^= dec_resp->mask_key4;
|
||||
}
|
||||
|
||||
dec_resp->mask_key3 ^= dec_resp->mask_key4;
|
||||
dec_resp->krnl_retval ^= dec_resp->mask_key4;
|
||||
dec_resp->mask_key2 ^= dec_resp->mask_key4;
|
||||
dec_resp->req_type ^= dec_resp->mask_key4;
|
||||
dec_resp->mask_key1 ^= dec_resp->mask_key4;
|
||||
dec_resp->magic ^= dec_resp->mask_key4;
|
||||
|
||||
dec_resp->krnl_retval ^= dec_resp->mask_key3;
|
||||
dec_resp->mask_key2 ^= dec_resp->mask_key3;
|
||||
dec_resp->req_type ^= dec_resp->mask_key3;
|
||||
dec_resp->mask_key1 ^= dec_resp->mask_key3;
|
||||
dec_resp->magic ^= dec_resp->mask_key3;
|
||||
|
||||
dec_resp->req_type ^= dec_resp->mask_key2;
|
||||
dec_resp->mask_key1 ^= dec_resp->mask_key2;
|
||||
dec_resp->magic ^= dec_resp->mask_key2;
|
||||
|
||||
dec_resp->magic ^= dec_resp->mask_key1;
|
||||
}
|
||||
*/
|
||||
|
||||
/* unused */
|
||||
/*
|
||||
static void sec_microdog40_enc_req(const struct sec_microdog40_req* dec_req,
|
||||
struct sec_microdog40_req* enc_req)
|
||||
{
|
||||
uint8_t key[16];
|
||||
|
||||
// Mask keying not necessary
|
||||
|
||||
sec_microdog40_get_month_key(key, &dec_req->timestamp);
|
||||
crypt_aes_enc(key, CRYPT_AES_KEY_LENGTH_16_BYTES, (uint8_t*) enc_req,
|
||||
(uint8_t*) dec_req, sizeof(struct sec_microdog40_req));
|
||||
|
||||
crypt_md5_hash(key, sec_microdog40_shared_secret,
|
||||
sizeof(sec_microdog40_shared_secret));
|
||||
crypt_aes_enc(key, CRYPT_AES_KEY_LENGTH_16_BYTES, ((uint8_t*) enc_req) +
|
||||
REQHEAD_SIZE, ((uint8_t*) &dec_req) + REQHEAD_SIZE, REQTAIL_SIZE);
|
||||
}
|
||||
*/
|
||||
|
||||
static void sec_microdog40_encrypt_resp(
|
||||
const struct sec_microdog40_resp* dec_resp,
|
||||
struct sec_microdog40_resp* enc_resp,
|
||||
const struct sec_microdog40_timestamp* timestamp)
|
||||
{
|
||||
uint8_t key[16];
|
||||
|
||||
sec_microdog40_get_month_key(key, timestamp);
|
||||
crypt_aes_enc(key, CRYPT_AES_KEY_LENGTH_16_BYTES, (uint8_t*) enc_resp,
|
||||
(uint8_t*) dec_resp, sizeof(struct sec_microdog40_resp));
|
||||
}
|
||||
|
||||
static uint32_t sec_microdog40_convert_req(const uint8_t* req_data,
|
||||
uint16_t req_len)
|
||||
{
|
||||
uint32_t cur_algo;
|
||||
|
||||
cur_algo = 0;
|
||||
memcpy(&cur_algo, sec_microdog40_header.flash_memory + 196, 4);
|
||||
|
||||
for (uint32_t i = 0; i < sec_microdog40_header.num_keys; i++) {
|
||||
struct sec_microdog40_key* key = &sec_microdog40_key_table[i];
|
||||
|
||||
if (key->algorithm == cur_algo) {
|
||||
|
||||
if (key->req_len == req_len) {
|
||||
|
||||
if (!memcmp(key->request, req_data, req_len)) {
|
||||
char* tmp = util_str_buffer(req_data, req_len);
|
||||
log_debug("Converting: %s ==> %X", tmp, key->response);
|
||||
free(tmp);
|
||||
return key->response;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char* tmp = util_str_buffer(req_data, req_len);
|
||||
log_warn("Converting request %s failed, no matching response found", tmp);
|
||||
free(tmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sec_microdog40_dispatch(const struct sec_microdog40_req* req,
|
||||
struct sec_microdog40_resp* resp)
|
||||
{
|
||||
// prepare response
|
||||
resp->magic = sec_microdog40_magic;
|
||||
resp->req_type = req->req_type;
|
||||
|
||||
if (req->magic != sec_microdog40_magic) {
|
||||
log_warn("Bad magic for request: %X", req->magic);
|
||||
}
|
||||
|
||||
sec_microdog40_log_req(req);
|
||||
|
||||
switch (req->req_type)
|
||||
{
|
||||
case SEC_MICRODOG40_REQ_OPCODE_SET_SHARE:
|
||||
{
|
||||
log_debug("SEC_MICRODOG40_REQ_OPCODE_SET_SHARE");
|
||||
|
||||
sec_microdog40_share = req->payload[0];
|
||||
break;
|
||||
}
|
||||
|
||||
case SEC_MICRODOG40_REQ_OPCODE_GET_LOCK_NO:
|
||||
{
|
||||
log_debug("SEC_MICRODOG40_REQ_OPCODE_GET_LOCK_NO 0x%08X",
|
||||
sec_microdog40_header.mfg_serial);
|
||||
|
||||
memcpy(resp->payload, &sec_microdog40_header.mfg_serial, 4);
|
||||
break;
|
||||
}
|
||||
|
||||
case SEC_MICRODOG40_REQ_OPCODE_GET_ID:
|
||||
{
|
||||
log_debug("SEC_MICRODOG40_REQ_OPCODE_GET_ID 0x%02X 0x%02X 0x%02X "
|
||||
"0x%02X 0x%02X 0x%02X 0x%02X 0x%02X",
|
||||
sec_microdog40_header.vendor_id[0],
|
||||
sec_microdog40_header.vendor_id[1],
|
||||
sec_microdog40_header.vendor_id[2],
|
||||
sec_microdog40_header.vendor_id[3],
|
||||
sec_microdog40_header.vendor_id[4],
|
||||
sec_microdog40_header.vendor_id[5],
|
||||
sec_microdog40_header.vendor_id[6],
|
||||
sec_microdog40_header.vendor_id[7]);
|
||||
|
||||
memcpy(resp->payload, sec_microdog40_header.vendor_id,
|
||||
sizeof(sec_microdog40_header.vendor_id));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SEC_MICRODOG40_REQ_OPCODE_CONVERT:
|
||||
{
|
||||
uint32_t resp_data;
|
||||
|
||||
log_debug("SEC_MICRODOG40_REQ_OPCODE_CONVERT");
|
||||
|
||||
resp_data = sec_microdog40_convert_req(req->payload,
|
||||
req->payload_size);
|
||||
memcpy(resp->payload, &resp_data, sizeof(uint32_t));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SEC_MICRODOG40_REQ_OPCODE_AUTH_STEP_1:
|
||||
{
|
||||
log_debug("SEC_MICRODOG40_REQ_OPCODE_AUTH_STEP_1");
|
||||
/* no-op */
|
||||
break;
|
||||
}
|
||||
|
||||
case SEC_MICRODOG40_REQ_OPCODE_AUTH_STEP_2:
|
||||
{
|
||||
log_debug("SEC_MICRODOG40_REQ_OPCODE_AUTH_STEP_2");
|
||||
/* no-op */
|
||||
break;
|
||||
}
|
||||
|
||||
case SEC_MICRODOG40_REQ_OPCODE_AUTH_STEP_3:
|
||||
{
|
||||
uint8_t month_key[16];
|
||||
|
||||
log_debug("SEC_MICRODOG40_REQ_OPCODE_AUTH_STEP_3");
|
||||
|
||||
sec_microdog40_get_month_key(month_key, &req->timestamp);
|
||||
|
||||
crypt_aes_enc(month_key, CRYPT_AES_KEY_LENGTH_16_BYTES,
|
||||
resp->payload, req->payload, 16);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
log_warn("Unsupported Opcode: %04X", req->req_type);
|
||||
resp->magic = 0; // mark to indicate error
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sec_microdog40_init(const uint8_t* key_data, size_t len)
|
||||
{
|
||||
if (len < sizeof(struct sec_microdog40_header)) {
|
||||
log_error("Loading key table failed, incomplete header");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&sec_microdog40_header, key_data,
|
||||
sizeof(struct sec_microdog40_header));
|
||||
|
||||
if (len - sizeof(struct sec_microdog40_header) <
|
||||
sec_microdog40_header.num_keys *
|
||||
sizeof(struct sec_microdog40_key)) {
|
||||
log_error("Loading key table failed, incomplete key table");
|
||||
return;
|
||||
}
|
||||
|
||||
sec_microdog40_key_table = util_xmalloc(sec_microdog40_header.num_keys *
|
||||
sizeof(struct sec_microdog40_key));
|
||||
|
||||
memcpy(sec_microdog40_key_table,
|
||||
key_data + sizeof(struct sec_microdog40_header),
|
||||
sec_microdog40_header.num_keys * sizeof(struct sec_microdog40_key));
|
||||
|
||||
log_info("Loaded %d keys, serial 0x%X, password 0x%X",
|
||||
sec_microdog40_header.num_keys, sec_microdog40_header.serial,
|
||||
sec_microdog40_header.password);
|
||||
|
||||
sec_microdog40_share = 0;
|
||||
}
|
||||
|
||||
bool sec_microdog40_do_transaction(
|
||||
struct sec_microdog40_transaction* transaction)
|
||||
{
|
||||
struct sec_microdog40_req dec_req;
|
||||
struct sec_microdog40_resp dec_resp;
|
||||
|
||||
if (transaction->header != PROTOCOL_VERSION) {
|
||||
log_error("Wrong protocol version of transaction %X.",
|
||||
transaction->header);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_debug("Decrypting request...");
|
||||
sec_microdog40_dec_req(&transaction->request, &dec_req);
|
||||
|
||||
sec_microdog40_dispatch(&dec_req, &dec_resp);
|
||||
|
||||
/* no error */
|
||||
if (dec_resp.magic != 0) {
|
||||
transaction->header = 0;
|
||||
|
||||
log_debug("Encrypting response...");
|
||||
sec_microdog40_encrypt_resp(&dec_resp, &transaction->response,
|
||||
&dec_req.timestamp);
|
||||
} else {
|
||||
transaction->header = 20023;
|
||||
log_error("Transaction failed.");
|
||||
}
|
||||
|
||||
log_debug("Transaction successful");
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Microdog 4.0 emulator
|
||||
*
|
||||
* Credits to batteryshark for the emulation code and key files
|
||||
*/
|
||||
#ifndef SEC_MICRODOG40_H
|
||||
#define SEC_MICRODOG40_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Header of a key file containing attributes of the dongle. This is followed
|
||||
* by an arbitrary number of keys
|
||||
*/
|
||||
struct sec_microdog40_header {
|
||||
uint32_t serial;
|
||||
uint32_t password;
|
||||
uint8_t vendor_id[8];
|
||||
uint32_t mfg_serial;
|
||||
uint8_t flash_memory[200];
|
||||
uint32_t num_keys;
|
||||
};
|
||||
|
||||
/**
|
||||
* A single entry in the key table (comes after the header)
|
||||
*/
|
||||
struct sec_microdog40_key {
|
||||
uint32_t response;
|
||||
uint32_t algorithm;
|
||||
uint32_t req_len;
|
||||
uint8_t request[64];
|
||||
};
|
||||
|
||||
/**
|
||||
* Timestamp for a dongle request
|
||||
*/
|
||||
struct sec_microdog40_timestamp {
|
||||
uint16_t year;
|
||||
uint16_t month;
|
||||
uint16_t day;
|
||||
uint16_t hour;
|
||||
uint16_t minute;
|
||||
uint16_t second;
|
||||
uint8_t padding[3];
|
||||
} __attribute((__packed__));
|
||||
|
||||
/**
|
||||
* Request to the dongle as part of a transaction
|
||||
*/
|
||||
struct sec_microdog40_req {
|
||||
uint16_t magic;
|
||||
uint8_t req_type;
|
||||
uint32_t mask_key1;
|
||||
uint8_t dog_cascade;
|
||||
uint32_t mfg_serial;
|
||||
uint32_t mask_key2;
|
||||
uint16_t dog_addr;
|
||||
uint16_t payload_size;
|
||||
uint32_t mask_key3;
|
||||
uint8_t payload[256];
|
||||
uint32_t dog_password;
|
||||
uint8_t host_id;
|
||||
uint32_t mask_key4;
|
||||
struct sec_microdog40_timestamp timestamp;
|
||||
} __attribute((__packed__));
|
||||
|
||||
/**
|
||||
* Dongle response as part of a transaction
|
||||
*/
|
||||
struct sec_microdog40_resp {
|
||||
uint16_t magic;
|
||||
uint32_t mask_key1;
|
||||
uint8_t req_type;
|
||||
uint32_t mask_key2;
|
||||
uint32_t krnl_retval;
|
||||
uint32_t mask_key3;
|
||||
uint8_t payload[256];
|
||||
uint32_t mask_key4;
|
||||
uint8_t padding[9];
|
||||
} __attribute((__packed__));
|
||||
|
||||
/**
|
||||
* Dongle emulator transaction data
|
||||
*/
|
||||
struct sec_microdog40_transaction {
|
||||
uint32_t header;
|
||||
struct sec_microdog40_req request;
|
||||
struct sec_microdog40_resp response;
|
||||
} __attribute((__packed__));
|
||||
|
||||
/**
|
||||
* Initialize the microdog (4.0) emulation module. The dongle for this
|
||||
* was used on NX2, NXA, Fiesta and FiestaEX
|
||||
*
|
||||
* @param key_data Pointer to loaded key data (dog attributes, key table)
|
||||
* @param len Length of the buffer
|
||||
*/
|
||||
void sec_microdog40_init(const uint8_t* key_data, size_t len);
|
||||
|
||||
/**
|
||||
* Execute a transaction on the dongle
|
||||
*
|
||||
* @param transaction Transaction containing the request. The response is
|
||||
* written to it after successfully processing the request
|
||||
* @return True if the request was successfully processed and a response was
|
||||
* written, false on failure
|
||||
*/
|
||||
bool sec_microdog40_do_transaction(struct sec_microdog40_transaction* transaction);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,109 @@
|
||||
#define LOG_MODULE "sec-microdog40d"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sec/microdog40/microdog40.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static const char* sec_microdog40d_sock_path = "/var/run/microdog/u.daemon";
|
||||
|
||||
static bool sec_microdog40d_run_d;
|
||||
static bool sec_microdog40d_is_running_d;
|
||||
|
||||
void sec_microdog40d_init(const uint8_t* key_data, size_t len)
|
||||
{
|
||||
sec_microdog40_init(key_data, len);
|
||||
sec_microdog40d_run_d = true;
|
||||
sec_microdog40d_is_running_d = false;
|
||||
}
|
||||
|
||||
void sec_microdog40d_run(void)
|
||||
{
|
||||
int fd;
|
||||
struct sec_microdog40_transaction transaction;
|
||||
struct sockaddr_un peer_addr;
|
||||
socklen_t peer_addr_len;
|
||||
int retval;
|
||||
|
||||
sec_microdog40d_is_running_d = true;
|
||||
|
||||
fd = -1;
|
||||
peer_addr_len = sizeof(peer_addr);
|
||||
retval = 0;
|
||||
|
||||
unlink(sec_microdog40d_sock_path);
|
||||
|
||||
/* make sure /var/run/microdog folder exists */
|
||||
mkdir("/var/run/microdog", 0700);
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (fd == -1) {
|
||||
log_error("Opening socket failed: %s", strerror(errno));
|
||||
sec_microdog40d_is_running_d = false;
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&peer_addr, 0, sizeof(struct sockaddr_un));
|
||||
peer_addr.sun_family = AF_UNIX;
|
||||
strcpy(peer_addr.sun_path, sec_microdog40d_sock_path);
|
||||
|
||||
retval = bind(fd, (struct sockaddr *) &peer_addr,
|
||||
sizeof(struct sockaddr_un));
|
||||
|
||||
if (retval == -1) {
|
||||
log_error("Binding socket failed: %s", strerror(errno));
|
||||
sec_microdog40d_is_running_d = false;
|
||||
return;
|
||||
}
|
||||
|
||||
while (sec_microdog40d_run_d) {
|
||||
peer_addr_len = sizeof(peer_addr);
|
||||
retval = recvfrom(fd, &transaction,
|
||||
sizeof(struct sec_microdog40_transaction), 0,
|
||||
(struct sockaddr *) &peer_addr, &peer_addr_len);
|
||||
|
||||
if (retval == -1) {
|
||||
|
||||
if (errno == EBADF) {
|
||||
break;
|
||||
} else {
|
||||
log_error("Receiving data failed: %s", strerror(errno));
|
||||
sec_microdog40d_is_running_d = false;
|
||||
return;
|
||||
}
|
||||
} else if (retval != sizeof(struct sec_microdog40_transaction)) {
|
||||
log_warn("Package too short, expected %d bytes, got %d bytes",
|
||||
sizeof(struct sec_microdog40_transaction), retval);
|
||||
} else {
|
||||
if (sec_microdog40_do_transaction(&transaction)) {
|
||||
sendto(fd, &transaction,
|
||||
sizeof(struct sec_microdog40_transaction), 0,
|
||||
(struct sockaddr *) &peer_addr, peer_addr_len);
|
||||
} else {
|
||||
log_error("Transaction failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup
|
||||
close(fd);
|
||||
unlink(sec_microdog40d_sock_path);
|
||||
|
||||
sec_microdog40d_is_running_d = false;
|
||||
}
|
||||
|
||||
bool sec_microdog40d_is_running(void)
|
||||
{
|
||||
return sec_microdog40d_is_running_d;
|
||||
}
|
||||
|
||||
void sec_microdog40d_shutdown(void)
|
||||
{
|
||||
sec_microdog40d_run_d = false;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Microdog 4.0 daemon module
|
||||
*/
|
||||
#ifndef SEC_MICRODOG40D_H
|
||||
#define SEC_MICRODOG40D_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Initialize the microdog (4.0) daemon module. This just initializes the
|
||||
* dongle emulator module
|
||||
*
|
||||
* @param key_data Pointer to loaded key data (dog attributes, key table)
|
||||
* @param len Length of the buffer
|
||||
*/
|
||||
void sec_microdog40d_init(const uint8_t* key_data, size_t len);
|
||||
|
||||
/**
|
||||
* Run the daemon. This call blocks until the daemon is shut down.
|
||||
* Recommended to run this in a separate thread.
|
||||
*/
|
||||
void sec_microdog40d_run(void);
|
||||
|
||||
/**
|
||||
* Check if the daemon is running
|
||||
*/
|
||||
bool sec_microdog40d_is_running(void);
|
||||
|
||||
/**
|
||||
* Signal a shut down to the daemon
|
||||
*/
|
||||
void sec_microdog40d_shutdown(void);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user