aboutsummaryrefslogtreecommitdiffstats
path: root/hasplib.c
diff options
context:
space:
mode:
Diffstat (limited to 'hasplib.c')
-rw-r--r--hasplib.c266
1 files changed, 266 insertions, 0 deletions
diff --git a/hasplib.c b/hasplib.c
new file mode 100644
index 0000000..70170da
--- /dev/null
+++ b/hasplib.c
@@ -0,0 +1,266 @@
+/*
+ * hasplib.c: The guts of the Hasp HL USB library implementation.
+ *
+ * Copyright (C) 2014 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hasplib.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libusb.h>
+
+#pragma pack(1)
+struct hasp_response {
+ uint8_t status;
+ uint8_t status_checksum;
+ uint8_t data[16];
+};
+#pragma pack()
+
+struct _hasp_dongle {
+ libusb_device_handle *handle;
+ bool initialized;
+
+ uint16_t cipher_key1;
+ uint16_t cipher_key2;
+
+ uint16_t password1;
+ uint16_t password2;
+};
+
+enum HASP_FUNCTION_LIST {
+ HASP_FUNCTION_SET_KEYS = 0x80,
+ HASP_FUNCTION_CHECK_PASS = 0x81,
+ HASP_FUNCTION_READ_3WORDS = 0x82,
+ HASP_FUNCTION_WRITE_WORD = 0x83,
+ HASP_FUNCTION_READ_ST = 0x84,
+ HASP_FUNCTION_READ_NETMEMORY_3WORDS = 0x8B,
+ HASP_FUNCTION_HASH_DWORD = 0x98
+};
+
+enum HASP_STATUS {
+ HASP_STATUS_OK = 0,
+ HASP_STATUS_ERROR = 1,
+ HASP_STATUS_INVALID_MEMORY_ADDRESS = 4,
+ HASP_STATUS_LAST = 0x1F,
+ HASP_USB_ERROR = 0x6D
+};
+
+static void cipher(hasp_dongle *dongle, uint8_t *buffer, size_t len)
+{
+ uint16_t key1 = dongle->cipher_key1, key2 = dongle->cipher_key2;
+ uint8_t xor_byte, multiplier;
+
+ while (len--) {
+ xor_byte = 0;
+ for (int i = 0; i < 4; ++i) {
+ multiplier = xor_byte << 1;
+ if (key1 & 1) {
+ multiplier |= 1;
+ key1 = ((key1 ^ key2) >> 1) | 0x8000;
+ } else
+ key1 >>= 1;
+ xor_byte = multiplier << 1;
+ if (key1 & 0x80)
+ xor_byte |= 1;
+ }
+ *(buffer++) ^= xor_byte;
+ }
+ dongle->cipher_key1 = key1;
+}
+
+static void usb_error(int error)
+{
+ fprintf(stderr, "USB error: %s\n", libusb_strerror(error));
+}
+
+static void make_request(hasp_dongle *dongle, uint8_t function, uint16_t param1, uint16_t param2, size_t expected_data_len, int cipher_params, bool retry, struct hasp_response *response)
+{
+ int ret;
+ response->status = HASP_STATUS_ERROR;
+ memset(response->data, 0, sizeof(response->data));
+
+ uint16_t params[2] = { param1, param2 };
+ if (cipher_params)
+ cipher(dongle, (uint8_t *)params, cipher_params * sizeof(uint16_t));
+
+ ret = libusb_control_transfer(dongle->handle, 0xC0, function, params[0], params[1], (unsigned char *)response, expected_data_len + 2, 3000);
+ if (ret < 0) {
+ usb_error(ret);
+ response->status = HASP_USB_ERROR;
+ return;
+ }
+
+ cipher(dongle, (uint8_t *)response, expected_data_len + 2);
+
+ if (response->status == HASP_STATUS_OK)
+ dongle->cipher_key2 = (dongle->cipher_key2 & 0xFF) | (response->status_checksum << 8);
+ else if (retry && dongle->password1 && dongle->password2) {
+ if (hasp_init(dongle) && hasp_login(dongle, dongle->password1, dongle->password2, NULL))
+ make_request(dongle, function, param1, param2, expected_data_len, cipher_params, false, response);
+ }
+}
+
+size_t hasp_find_dongles(hasp_dongle ***dongles)
+{
+ size_t dongle_count;
+ ssize_t size;
+ libusb_device **all_devs, **all_pos, *dev;
+ hasp_dongle **pos, *dongle;
+ int ret;
+
+ if ((ret = libusb_init(NULL)) < 0) {
+ usb_error(ret);
+ return 0;
+ }
+
+ if ((size = libusb_get_device_list(NULL, &all_devs)) < 0) {
+ usb_error((int)size);
+ return 0;
+ }
+ if (!size)
+ return 0;
+ all_pos = all_devs;
+
+ pos = calloc(size + 1, sizeof(hasp_dongle *));
+ if (!pos)
+ return 0;
+ *dongles = pos;
+
+ dongle_count = 0;
+ while ((dev = *(all_pos++)) != NULL) {
+ struct libusb_device_descriptor desc;
+ ret = libusb_get_device_descriptor(dev, &desc);
+ if (ret < 0) {
+ usb_error(ret);
+ continue;
+ }
+ if (desc.idVendor == 0x0529 && desc.idProduct == 0x0001) {
+ libusb_device_handle *handle;
+ if ((ret = libusb_open(dev, &handle)) < 0) {
+ usb_error(ret);
+ continue;
+ }
+
+ dongle = calloc(1, sizeof(hasp_dongle));
+ if (!dongle)
+ return 0;
+ dongle->handle = handle;
+
+ ++dongle_count;
+ *(pos++) = dongle;
+ }
+ }
+ libusb_free_device_list(all_devs, 1);
+ return dongle_count;
+}
+hasp_dongle *hasp_find_login_first_dongle(uint16_t password1, uint16_t password2)
+{
+ hasp_dongle *found = NULL;
+ hasp_dongle **all = NULL;
+ size_t num_dongles;
+ if (!(num_dongles = hasp_find_dongles(&all))) {
+ free(all);
+ return NULL;
+ }
+ for (size_t i = 0; i < num_dongles; ++i) {
+ if (!found && hasp_login(all[i], password1, password2, NULL))
+ found = all[i];
+ else
+ hasp_free_dongle(all[i]);
+ }
+ free(all);
+ return found;
+}
+
+void hasp_free_dongles(hasp_dongle **dongles)
+{
+ for (hasp_dongle **i = dongles; *i; ++i)
+ hasp_free_dongle(*i);
+ free(dongles);
+}
+
+void hasp_free_dongle(hasp_dongle *dongle)
+{
+ if (dongle->handle)
+ libusb_close(dongle->handle);
+ free(dongle);
+}
+
+bool hasp_init(hasp_dongle *dongle)
+{
+ dongle->cipher_key1 = 0xFABE;
+ dongle->cipher_key2 = 0xA0CB;
+
+ struct hasp_response response;
+ make_request(dongle, HASP_FUNCTION_SET_KEYS, dongle->cipher_key1, 0 /* unused */, 5, false, false, &response);
+
+ if (response.status == HASP_STATUS_OK && response.data[0] == 0x2 && response.data[1] == 0xEA /* 0x0A? */ && response.data[2] == 0x00) {
+ dongle->initialized = true;
+ return true;
+ }
+ return false;
+}
+
+bool hasp_login(hasp_dongle *dongle, uint16_t password1, uint16_t password2, uint16_t *memory_size)
+{
+ dongle->password1 = password1;
+ dongle->password2 = password2;
+
+ if (!dongle->initialized && !hasp_init(dongle))
+ return false;
+
+ struct hasp_response response;
+ make_request(dongle, HASP_FUNCTION_CHECK_PASS, dongle->password1, dongle->password2, 3, 2, false, &response);
+ if (response.status != HASP_STATUS_OK && response.status != HASP_USB_ERROR) {
+ hasp_init(dongle);
+ make_request(dongle, HASP_FUNCTION_CHECK_PASS, dongle->password1, dongle->password2, 3, 2, false, &response);
+ }
+
+ if (response.status != HASP_STATUS_OK && response.data[2] != 0x10)
+ return false;
+ if (memory_size)
+ *memory_size = response.data[0] | (response.data[1] << 8);
+ return true;
+}
+
+bool hasp_id(hasp_dongle *dongle, uint32_t *id)
+{
+ struct hasp_response response;
+ make_request(dongle, HASP_FUNCTION_READ_NETMEMORY_3WORDS, 0 /* first location has id */, 0 /* unused */, 6, 1, true, &response);
+ if (response.status != HASP_STATUS_OK)
+ return false;
+ *id = response.data[0] | (response.data[1] << 8) | (response.data[2] << 16) | (response.data[3] << 24);
+ return true;
+}
+
+bool hasp_read(hasp_dongle *dongle, uint16_t location, uint16_t *value)
+{
+ struct hasp_response response;
+ make_request(dongle, HASP_FUNCTION_READ_3WORDS, location, 0 /* unused */, 6, 1, true, &response);
+ if (response.status != HASP_STATUS_OK)
+ return false;
+ *value = response.data[0] | (response.data[1] << 8);
+ return true;
+}
+
+bool hasp_write(hasp_dongle *dongle, uint16_t location, uint16_t value)
+{
+ struct hasp_response response;
+ make_request(dongle, HASP_FUNCTION_WRITE_WORD, location, value, 0, 2, true, &response);
+ return response.status == HASP_STATUS_OK;
+}