diff --git a/Slon/Api/V1/Accounts.HC b/Slon/Api/V1/Accounts.HC
new file mode 100644
index 0000000..a744a1b
--- /dev/null
+++ b/Slon/Api/V1/Accounts.HC
@@ -0,0 +1,274 @@
+U0 @slon_api_v1_accounts_get(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn scratch_buffer;
+
+ U8* path = @slon_strnew(session, @slon_http_request_path(session));
+ I64 path_segments_count = 0;
+ U8** path_segments = String.Split(path, '/', &path_segments_count);
+
+ JsonObject* acct = NULL;
+
+ if (!StrICmp("verify_credentials", path_segments[3])) {
+ if (@slon_api_authorized(session)) {
+ SLON_AUTH_ACCOUNT_ID
+ acct = @slon_api_account_by_id(account_id);
+ if (acct) {
+ @slon_http_send_json(session, acct);
+ } else {
+ @slon_http_set_status_code(session, 404);
+ }
+ } else {
+ @slon_http_set_status_code(session, 401);
+ }
+ } else {
+ // Work with account :id
+ U8* some_account_id = path_segments[3];
+ acct = @slon_api_account_by_id(some_account_id);
+ if (!acct) {
+ @slon_http_set_status_code(session, 404);
+ goto slon_api_v1_accounts_get_return;
+ }
+ if (path_segments_count > 5) {
+ U8* method = path_segments[4];
+ if (!StrICmp("following", method)) {
+ // FIXME: Implement this
+ @slon_http_send_json(session, SLON_EMPTY_JSON_ARRAY);
+ goto slon_api_v1_accounts_get_return;
+ }
+ if (!StrICmp("statuses", method)) {
+ // Return the Account's Statuses
+ JsonArray* status_array = db->o("statuses")->a(some_account_id);
+
+ I64 count = 0;
+
+ // FILTERS
+ I64 limit = 20; // default
+ U64 max_id = 0;
+ U64 min_id = 0;
+ Bool only_media = request_json->@("only_media");
+ Bool exclude_replies = request_json->@("exclude_replies");
+ Bool exclude_reblogs = request_json->@("exclude_reblogs");
+ no_warn exclude_reblogs;
+ Bool pinned = request_json->@("pinned");
+ // FIXME: Implement "only_media", "exclude_reblogs", "tagged"
+
+ Bool exclude_status = FALSE;
+ U64 status_id = 0;
+
+ if (StrLen(request_json->@("limit")) > 0) {
+ // 40 = maximum per https://docs.joinmastodon.org/methods/accounts/#statuses
+ limit = MinI64(40, Str2I64(request_json->@("limit")));
+ }
+ if (StrLen(request_json->@("max_id")) > 0) {
+ max_id = Str2I64(request_json->@("max_id"));
+ }
+ if (StrLen(request_json->@("min_id")) > 0) {
+ min_id = Str2I64(request_json->@("min_id"));
+ }
+
+ JsonArray* statuses = Json.CreateArray();
+ JsonObject* status = NULL;
+
+ if (status_array && status_array->length) {
+ I64 i;
+ for (i = 0; i < status_array->length; i++) {
+ status = status_array->o(i);
+ status_id = Str2I64(status->@("id"));
+ exclude_status = FALSE;
+ if (status->@("deleted")) {
+ exclude_status = TRUE;
+ }
+ if (max_id > 0 && status_id >= max_id) {
+ exclude_status = TRUE;
+ }
+ if (min_id > 0 && status_id <= min_id) {
+ exclude_status = TRUE;
+ }
+ if (only_media && !Json.Get(status, "media_attachments")(JsonArray*)->length) {
+ exclude_status = TRUE;
+ }
+ if (exclude_replies && StrLen(status->@("in_reply_to_account_id")) > 0 && StrICmp(account_id, status->@("in_reply_to_account_id"))) {
+ exclude_status = TRUE;
+ }
+ if (pinned && !status->@("pinned")) {
+ exclude_status = TRUE;
+ }
+ if (!exclude_status) {
+ statuses->append(Json.CreateItem(status, JSON_OBJECT));
+ count++;
+ }
+ if (limit > 0 && count >= limit) {
+ break;
+ }
+ }
+ }
+
+ @slon_http_send_json(session, statuses);
+
+ Json.Delete(statuses);
+ goto slon_api_v1_accounts_get_return;
+ }
+ @slon_http_set_status_code(session, 404);
+ } else {
+ // Return the Account profile
+ JsonObject* profile_object = Json.Clone(acct);
+ profile_object->unset("source");
+ @slon_http_send_json(session, profile_object);
+ Json.Delete(profile_object);
+ }
+ }
+slon_api_v1_accounts_get_return:
+ @slon_free(session, path);
+}
+
+Bool @slon_api_v1_accounts_key_is_boolean(U8* name)
+{
+ return (!StrICmp(name, "locked") || !StrICmp(name, "bot") || !StrICmp(name, "discoverable") || !StrICmp(name, "hide_collections") || !StrICmp(name, "indexable"));
+}
+
+U0 @slon_api_v1_accounts_patch(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ U8* path = @slon_strnew(session, @slon_http_request_path(session));
+ I64 path_segments_count = 0;
+ U8** path_segments = String.Split(path, '/', &path_segments_count);
+
+ JsonObject* acct = NULL;
+
+ if (!StrICmp("update_credentials", path_segments[3])) {
+ if (@slon_api_authorized(session)) {
+ SLON_AUTH_ACCOUNT_ID
+
+ if (!request_json || !request_json->keys) {
+ @slon_http_set_status_code(session, 400);
+ goto slon_api_v1_accounts_patch_return;
+ }
+
+ // FIXME: Support avatars/banners
+ acct = @slon_api_account_by_id(account_id);
+ if (!acct) {
+ @slon_http_set_status_code(session, 404);
+ goto slon_api_v1_accounts_patch_return;
+ }
+ JsonObject* source = acct->@("source");
+
+ I64 fields_attributes_indexes[16];
+ I64 fields_attributes_count = 0;
+ U8* field_name;
+ U8* field_value;
+ JsonKey* update_field_index;
+ JsonObject* field_object;
+ Bool update_fields_from_form_data = FALSE;
+ Bool integer_is_in_index = FALSE;
+
+ I64 i;
+ I64 index;
+ MemSet(fields_attributes_indexes, NULL, sizeof(I64) * 16);
+ JsonArray* fields_array = Json.CreateArray();
+
+ JsonKey* key = request_json->keys;
+ while (key) {
+ if (!String.BeginsWith("fields_attributes", key->name) && !String.BeginsWith("source", key->name)) {
+ if (@slon_api_v1_accounts_key_is_boolean(key->name)) {
+ switch (key->type) {
+ case JSON_STRING:
+ acct->set(key->name, @slon_api_boolean_from_string(key->value), JSON_BOOLEAN);
+ break;
+ default:
+ acct->set(key->name, key->value > 0, JSON_BOOLEAN);
+ break;
+ }
+ } else {
+ acct->set(key->name, key->value, key->type);
+ }
+ } else if (String.BeginsWith("source", key->name)) {
+ if (!StrICmp("source[language]", key->name)) {
+ source->set("language", key->value);
+ }
+ if (!StrICmp("source[privacy]", key->name)) {
+ source->set("privacy", key->value);
+ }
+ } else if (String.BeginsWith("fields_attributes[", key->name)) {
+ // Get fields indexes from form data
+ update_fields_from_form_data = TRUE;
+ index = Str2I64(key->name + StrLen("fields_attributes["));
+ if (!fields_attributes_count) {
+ fields_attributes_indexes[fields_attributes_count] = index;
+ ++fields_attributes_count;
+ } else {
+ integer_is_in_index = FALSE;
+ i = 0;
+ while (i < fields_attributes_count) {
+ if (index == fields_attributes_indexes[i])
+ integer_is_in_index = TRUE;
+ ++i;
+ }
+ if (!integer_is_in_index) {
+ fields_attributes_indexes[fields_attributes_count] = index;
+ ++fields_attributes_count;
+ }
+ }
+ } else if (!StrICmp("fields_attributes", key->name)) {
+ // Get fields data from JSON object
+ AdamLog("let's get fields data from JSON object!!\n");
+ update_field_index = key->value(JsonObject*)->keys;
+ while (update_field_index) {
+ field_object = update_field_index->value;
+ field_object->set("verified_at", NULL, JSON_NULL);
+ AdamLog("before stringify\n");
+ AdamLog("%s\n", Json.Stringify(field_object));
+ AdamLog("after stringify\n");
+ fields_array->append(Json.CreateItem(field_object, JSON_OBJECT));
+ update_field_index = update_field_index->next;
+ }
+ }
+ key = key->next;
+ }
+
+ if (update_fields_from_form_data) {
+ for (i = 0; i < fields_attributes_count; i++) {
+ index = fields_attributes_indexes[i];
+ field_name = NULL;
+ field_value = NULL;
+ key = request_json->keys;
+ while (key) {
+ StrPrint(scratch_buffer, "fields_attributes[%d][name]", index);
+ if (String.BeginsWith(scratch_buffer, key->name)) {
+ field_name = key->value;
+ }
+ StrPrint(scratch_buffer, "fields_attributes[%d][value]", index);
+ if (String.BeginsWith(scratch_buffer, key->name)) {
+ field_value = key->value;
+ }
+ if (field_name && field_value) {
+ // create new field_object, and append to acct->fields
+ field_object = Json.CreateObject();
+ field_object->set("name", field_name, JSON_STRING);
+ field_object->set("value", field_value, JSON_STRING);
+ field_object->set("verified_at", NULL, JSON_NULL);
+ fields_array->append(Json.CreateItem(field_object, JSON_OBJECT));
+ field_name = NULL;
+ field_value = NULL;
+ }
+ key = key->next;
+ }
+ }
+ }
+
+ acct->set("fields", fields_array, JSON_ARRAY);
+ source->set("fields", acct->@("fields"), JSON_ARRAY);
+
+ @slon_db_save_accounts_to_disk;
+ @slon_db_actors_update_user(acct);
+ @slon_http_send_json(session, acct);
+ } else {
+ @slon_http_set_status_code(session, 401);
+ }
+ } else {
+ @slon_http_set_status_code(session, 404);
+ }
+slon_api_v1_accounts_patch_return:
+ @slon_free(session, path);
+}
diff --git a/Slon/Api/V1/Apps.HC b/Slon/Api/V1/Apps.HC
new file mode 100644
index 0000000..da37044
--- /dev/null
+++ b/Slon/Api/V1/Apps.HC
@@ -0,0 +1,73 @@
+U8* @slon_api_v1_apps_generate_app_id(SlonHttpSession* session)
+{
+ U8* app_id = @slon_calloc(session, 16);
+ I64 i;
+ for (i = 0; i < 6; i++) {
+ String.Append(app_id, "%d", RandU64 % 10);
+ }
+ return app_id;
+}
+
+U8* @slon_api_v1_apps_generate_client_id(SlonHttpSession* session)
+{
+ U8* client_id = NULL;
+ Bool client_id_exists = TRUE;
+ while (client_id_exists) {
+ if (client_id)
+ @slon_free(session, client_id);
+ client_id = @slon_api_generate_random_hex_string(session, 16);
+ client_id_exists = db->o("apps")->@(client_id) > 0;
+ }
+ return client_id;
+}
+
+U8* @slon_api_v1_apps_generate_client_secret(SlonHttpSession* session)
+{
+ return @slon_api_generate_random_hex_string(session, 32);
+}
+
+U0 @slon_api_v1_apps_post(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn scratch_buffer;
+
+ U8* id = @slon_api_v1_apps_generate_app_id(session);
+ U8* client_id = @slon_api_v1_apps_generate_client_id(session);
+ U8* client_secret = @slon_api_v1_apps_generate_client_secret(session);
+
+ I64 request_scopes_count = 0;
+ U8** request_scopes = NULL;
+ if (StrFind("+", request_json->@("scopes")) > 0) {
+ request_scopes = String.Split(request_json->@("scopes"), '+', &request_scopes_count);
+ } else {
+ request_scopes = String.Split(request_json->@("scopes"), ' ', &request_scopes_count);
+ }
+
+ JsonArray* scopes = Json.CreateArray();
+ I64 i;
+ for (i = 0; i < request_scopes_count; i++) {
+ scopes->append(Json.CreateItem(request_scopes[i], JSON_STRING));
+ }
+
+ JsonArray* redirect_uris = Json.CreateArray();
+ redirect_uris->append(Json.CreateItem(request_json->@("redirect_uris"), JSON_STRING));
+
+ JsonObject* credential_app = Json.CreateObject();
+ credential_app->set("id", id, JSON_STRING);
+ credential_app->set("name", request_json->@("client_name"), JSON_STRING);
+ credential_app->set("website", request_json->@("website"), JSON_STRING);
+ credential_app->set("scopes", scopes, JSON_ARRAY);
+ credential_app->set("redirect_uris", redirect_uris, JSON_ARRAY);
+ credential_app->set("redirect_uri", request_json->@("redirect_uris"), JSON_STRING);
+ credential_app->set("client_id", client_id, JSON_STRING);
+ credential_app->set("client_secret", client_secret, JSON_STRING);
+ credential_app->set("client_secret_expires_at", "0", JSON_STRING);
+ db->o("apps")->set(client_id, credential_app, JSON_OBJECT);
+ @slon_db_save_apps_to_disk;
+
+ @slon_http_send_json(session, credential_app);
+
+ @slon_free(session, id);
+ @slon_free(session, client_id);
+ @slon_free(session, client_secret);
+}
diff --git a/Slon/Api/V1/Blocks.HC b/Slon/Api/V1/Blocks.HC
new file mode 100644
index 0000000..b64be90
--- /dev/null
+++ b/Slon/Api/V1/Blocks.HC
@@ -0,0 +1,12 @@
+U0 @slon_api_v1_blocks_get(SlonHttpSession* session)
+{
+ // SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ if (@slon_api_authorized(session)) {
+ // SLON_AUTH_ACCOUNT_ID
+ // FIXME: Implement this
+ @slon_http_send_json(session, SLON_EMPTY_JSON_ARRAY);
+ } else {
+ @slon_http_set_status_code(session, 401);
+ }
+}
diff --git a/Slon/Api/V1/Bookmarks.HC b/Slon/Api/V1/Bookmarks.HC
new file mode 100644
index 0000000..9a6e6d5
--- /dev/null
+++ b/Slon/Api/V1/Bookmarks.HC
@@ -0,0 +1,12 @@
+U0 @slon_api_v1_bookmarks_get(SlonHttpSession* session)
+{
+ // SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ if (@slon_api_authorized(session)) {
+ // SLON_AUTH_ACCOUNT_ID
+ // FIXME: Implement this
+ @slon_http_send_json(session, SLON_EMPTY_JSON_ARRAY);
+ } else {
+ @slon_http_set_status_code(session, 401);
+ }
+}
diff --git a/Slon/Api/V1/Conversations.HC b/Slon/Api/V1/Conversations.HC
new file mode 100644
index 0000000..539cacf
--- /dev/null
+++ b/Slon/Api/V1/Conversations.HC
@@ -0,0 +1,12 @@
+U0 @slon_api_v1_conversations_get(SlonHttpSession* session)
+{
+ // SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ if (@slon_api_authorized(session)) {
+ // SLON_AUTH_ACCOUNT_ID
+ // FIXME: Implement this
+ @slon_http_send_json(session, SLON_EMPTY_JSON_ARRAY);
+ } else {
+ @slon_http_set_status_code(session, 401);
+ }
+}
diff --git a/Slon/Api/V1/CustomEmojis.HC b/Slon/Api/V1/CustomEmojis.HC
new file mode 100644
index 0000000..f0ff20a
--- /dev/null
+++ b/Slon/Api/V1/CustomEmojis.HC
@@ -0,0 +1,12 @@
+U0 @slon_api_v1_custom_emojis_get(SlonHttpSession* session)
+{
+ // SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ if (@slon_api_authorized(session)) {
+ // SLON_AUTH_ACCOUNT_ID
+ // FIXME: Implement this
+ @slon_http_send_json(session, SLON_EMPTY_JSON_ARRAY);
+ } else {
+ @slon_http_set_status_code(session, 401);
+ }
+}
diff --git a/Slon/Api/V1/Favourites.HC b/Slon/Api/V1/Favourites.HC
new file mode 100644
index 0000000..f6932d1
--- /dev/null
+++ b/Slon/Api/V1/Favourites.HC
@@ -0,0 +1,12 @@
+U0 @slon_api_v1_favourites_get(SlonHttpSession* session)
+{
+ // SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ if (@slon_api_authorized(session)) {
+ // SLON_AUTH_ACCOUNT_ID
+ // FIXME: Implement this
+ @slon_http_send_json(session, SLON_EMPTY_JSON_ARRAY);
+ } else {
+ @slon_http_set_status_code(session, 401);
+ }
+}
diff --git a/Slon/Api/V1/Filters.HC b/Slon/Api/V1/Filters.HC
new file mode 100644
index 0000000..e644a25
--- /dev/null
+++ b/Slon/Api/V1/Filters.HC
@@ -0,0 +1,12 @@
+U0 @slon_api_v1_filters_get(SlonHttpSession* session)
+{
+ // SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ if (@slon_api_authorized(session)) {
+ // SLON_AUTH_ACCOUNT_ID
+ // FIXME: Implement this
+ @slon_http_send_json(session, SLON_EMPTY_JSON_ARRAY);
+ } else {
+ @slon_http_set_status_code(session, 401);
+ }
+}
diff --git a/Slon/Api/V1/FollowRequests.HC b/Slon/Api/V1/FollowRequests.HC
new file mode 100644
index 0000000..177c825
--- /dev/null
+++ b/Slon/Api/V1/FollowRequests.HC
@@ -0,0 +1,12 @@
+U0 @slon_api_v1_follow_requests_get(SlonHttpSession* session)
+{
+ // SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ if (@slon_api_authorized(session)) {
+ // SLON_AUTH_ACCOUNT_ID
+ // FIXME: Implement this
+ @slon_http_send_json(session, SLON_EMPTY_JSON_ARRAY);
+ } else {
+ @slon_http_set_status_code(session, 401);
+ }
+}
diff --git a/Slon/Api/V1/FollowedTags.HC b/Slon/Api/V1/FollowedTags.HC
new file mode 100644
index 0000000..811c61e
--- /dev/null
+++ b/Slon/Api/V1/FollowedTags.HC
@@ -0,0 +1,12 @@
+U0 @slon_api_v1_followed_tags_get(SlonHttpSession* session)
+{
+ // SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ if (@slon_api_authorized(session)) {
+ // SLON_AUTH_ACCOUNT_ID
+ // FIXME: Implement this
+ @slon_http_send_json(session, SLON_EMPTY_JSON_ARRAY);
+ } else {
+ @slon_http_set_status_code(session, 401);
+ }
+}
diff --git a/Slon/Api/V1/Notifications.HC b/Slon/Api/V1/Notifications.HC
new file mode 100644
index 0000000..8904264
--- /dev/null
+++ b/Slon/Api/V1/Notifications.HC
@@ -0,0 +1,16 @@
+U0 @slon_api_v1_notifications_get(SlonHttpSession* session)
+{
+ // SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ if (@slon_api_authorized(session)) {
+ // SLON_AUTH_ACCOUNT_ID
+ // FIXME: Implement this
+ if (String.EndsWith("policy", @slon_http_request_path(session))) {
+ @slon_http_send_json(session, SLON_EMPTY_JSON_OBJECT);
+ } else {
+ @slon_http_send_json(session, SLON_EMPTY_JSON_ARRAY);
+ }
+ } else {
+ @slon_http_set_status_code(session, 401);
+ }
+}
diff --git a/Slon/Api/V1/Statuses.HC b/Slon/Api/V1/Statuses.HC
new file mode 100644
index 0000000..6d7b214
--- /dev/null
+++ b/Slon/Api/V1/Statuses.HC
@@ -0,0 +1,144 @@
+U0 (*@slon_api_status_create_fedi)(JsonObject* status) = NULL;
+U0 (*@slon_api_status_delete_fedi)(JsonObject* status) = NULL;
+
+U0 @slon_api_v1_statuses_delete(SlonHttpSession* session)
+{
+ if (@slon_api_authorized(session)) {
+ SLON_AUTH_ACCOUNT_ID
+
+ JsonArray* statuses = db->o("statuses")->a(account_id);
+ if (!statuses || !statuses->length) {
+ @slon_http_send_json(session, SLON_EMPTY_JSON_OBJECT);
+ return;
+ }
+
+ U8* path = @slon_strnew(session, @slon_http_request_path(session));
+ I64 path_segments_count = 0;
+ U8** path_segments = String.Split(path, '/', &path_segments_count);
+
+ if (path_segments_count < 4) {
+ goto slon_api_v1_statuses_delete_return;
+ }
+
+ U8* id = path_segments[3];
+ JsonObject* status;
+
+ I64 i;
+ for (i = 0; i < statuses->length; i++) {
+ status = statuses->@(i);
+ if (!StrICmp(status->@("id"), id)) {
+ status->set("deleted", TRUE, JSON_BOOLEAN);
+ @slon_db_save_statuses_to_disk;
+ @slon_db_instance_decrement_status_count;
+ @slon_db_save_instance_to_disk;
+ if (@slon_api_status_delete_fedi) {
+ @slon_api_status_delete_fedi(Json.Clone(status));
+ }
+ goto slon_api_v1_statuses_delete_return;
+ }
+ }
+
+ slon_api_v1_statuses_delete_return:
+ Free(path_segments);
+ @slon_free(session, path);
+ @slon_http_send_json(session, SLON_EMPTY_JSON_OBJECT);
+ } else {
+ @slon_http_set_status_code(session, 401);
+ }
+}
+
+U0 @slon_api_v1_statuses_post(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ if (@slon_api_authorized(session)) {
+ SLON_AUTH_ACCOUNT_ID
+
+ Bool idempotency_key_already_seen = FALSE;
+ U8* idempotency_key = @slon_http_request_header(session, "idempotency-key");
+ if (StrLen(idempotency_key) > 0 && db->o("idempotency_keys")->@(idempotency_key)) {
+ idempotency_key_already_seen = TRUE;
+ }
+ if (!idempotency_key_already_seen) {
+ Json.Set(db->o("idempotency_keys"), idempotency_key, Now, JSON_NUMBER);
+ }
+
+ U8* id = @slon_api_generate_unique_id(session);
+ U8* created_at = @slon_api_timestamp_from_cdate(session, Now);
+
+ JsonObject* app_object = db->o("apps")->@(Json.Get(session->auth, "client_id"));
+
+ JsonObject* status_app = Json.CreateObject();
+ status_app->set("name", app_object->@("name"), JSON_STRING);
+ status_app->set("website", app_object->@("website"), JSON_STRING);
+
+ JsonObject* account_object = Json.Clone(@slon_api_account_by_id(account_id));
+ account_object->unset("source");
+
+ // U8* language = request_json->@("language");
+ U8* username = account_object->@("username");
+
+ Bool sensitive = request_json->@("sensitive") > 0;
+ U8* in_reply_to_id = request_json->@("in_reply_to_id");
+ U8* visibility = request_json->@("visibility");
+
+ if (!StrLen(visibility)) {
+ visibility = "public";
+ }
+
+ StrPrint(scratch_buffer, "https://%s/users/%s/statuses/%s", db->o("instance")->@("uri"), username, id);
+ U8* uri = @slon_strnew(session, scratch_buffer);
+ StrPrint(scratch_buffer, "https://%s/@%s/%s", db->o("instance")->@("uri"), username, id);
+ U8* url = @slon_strnew(session, scratch_buffer);
+
+ // Mona lets us post with: id, created_at, content, visibility, uri, url, account, application
+ // Mastodon iOS app lets us post with +: reblogs_count, favourites_count, emojis, tags, mentions
+ // IceCubesApp lets us post with +: media_attachments, replies_count, spoiler_text, sensitive
+
+ JsonObject* status = Json.CreateObject();
+ status->set("id", id, JSON_STRING);
+ status->set("created_at", created_at, JSON_STRING);
+ status->set("content", request_json->@("status"), JSON_STRING);
+ status->set("visibility", visibility, JSON_STRING);
+ status->set("uri", uri, JSON_STRING);
+ status->set("url", url, JSON_STRING);
+ status->set("account", account_object, JSON_OBJECT);
+ status->set("application", status_app, JSON_OBJECT);
+ status->set("reblogs_count", 0, JSON_NUMBER);
+ status->set("favourites_count", 0, JSON_NUMBER);
+ status->set("emojis", SLON_EMPTY_JSON_ARRAY, JSON_ARRAY);
+ status->set("tags", SLON_EMPTY_JSON_ARRAY, JSON_ARRAY);
+ status->set("mentions", SLON_EMPTY_JSON_ARRAY, JSON_ARRAY);
+ status->set("media_attachments", SLON_EMPTY_JSON_ARRAY, JSON_ARRAY);
+ status->set("replies_count", 0, JSON_NUMBER);
+ status->set("spoiler_text", "", JSON_STRING);
+ status->set("sensitive", sensitive, JSON_BOOLEAN);
+
+ if (StrLen(in_reply_to_id) > 0) {
+ status->set("in_reply_to_id", in_reply_to_id, JSON_STRING);
+ }
+
+ if (!idempotency_key_already_seen) {
+ db->o("statuses")->a(account_id)->append(Json.CreateItem(status, JSON_OBJECT));
+ @slon_db_save_statuses_to_disk;
+ @slon_db_instance_increment_status_count;
+ @slon_db_save_instance_to_disk;
+ if (@slon_api_status_create_fedi) {
+ @slon_api_status_create_fedi(Json.Clone(status));
+ }
+ }
+
+ @slon_http_send_json(session, status);
+
+ Json.Delete(status_app);
+ Json.Delete(account_object);
+ Json.Delete(app_object);
+
+ @slon_free(session, uri);
+ @slon_free(session, url);
+ @slon_free(session, id);
+ @slon_free(session, created_at);
+ } else {
+ @slon_http_set_status_code(session, 401);
+ }
+}
diff --git a/Slon/Api/V1/Timelines.HC b/Slon/Api/V1/Timelines.HC
new file mode 100644
index 0000000..6415444
--- /dev/null
+++ b/Slon/Api/V1/Timelines.HC
@@ -0,0 +1,12 @@
+U0 @slon_api_v1_timelines_get(SlonHttpSession* session)
+{
+ // SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ if (@slon_api_authorized(session)) {
+ // SLON_AUTH_ACCOUNT_ID
+ // FIXME: Implement this
+ @slon_http_send_json(session, SLON_EMPTY_JSON_ARRAY);
+ } else {
+ @slon_http_set_status_code(session, 401);
+ }
+}
diff --git a/Slon/Api/V2/Filters.HC b/Slon/Api/V2/Filters.HC
new file mode 100644
index 0000000..f185abe
--- /dev/null
+++ b/Slon/Api/V2/Filters.HC
@@ -0,0 +1,12 @@
+U0 @slon_api_v2_filters_get(SlonHttpSession* session)
+{
+ // SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ if (@slon_api_authorized(session)) {
+ // SLON_AUTH_ACCOUNT_ID
+ // FIXME: Implement this
+ @slon_http_send_json(session, SLON_EMPTY_JSON_ARRAY);
+ } else {
+ @slon_http_set_status_code(session, 401);
+ }
+}
diff --git a/Slon/Api/V2/Instance.HC b/Slon/Api/V2/Instance.HC
new file mode 100644
index 0000000..0ec92ed
--- /dev/null
+++ b/Slon/Api/V2/Instance.HC
@@ -0,0 +1,9 @@
+U0 @slon_api_v2_instance_get(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn request_json;
+
+ StrPrint(scratch_buffer, "{\"domain\":\"%s\"}", db->o("instance")->@("uri"));
+ @slon_http_set_content_type(session, "application/json; charset=utf-8");
+ @slon_http_send_string(session, scratch_buffer);
+}
diff --git a/Slon/Api/V2/Suggestions.HC b/Slon/Api/V2/Suggestions.HC
new file mode 100644
index 0000000..7e7961e
--- /dev/null
+++ b/Slon/Api/V2/Suggestions.HC
@@ -0,0 +1,12 @@
+U0 @slon_api_v2_suggestions_get(SlonHttpSession* session)
+{
+ // SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ if (@slon_api_authorized(session)) {
+ // SLON_AUTH_ACCOUNT_ID
+ // FIXME: Implement this
+ @slon_http_send_json(session, SLON_EMPTY_JSON_ARRAY);
+ } else {
+ @slon_http_set_status_code(session, 401);
+ }
+}
diff --git a/Slon/Endpoints/Delete/Statuses.HC b/Slon/Endpoints/Delete/Statuses.HC
new file mode 100644
index 0000000..51f7cd5
--- /dev/null
+++ b/Slon/Endpoints/Delete/Statuses.HC
@@ -0,0 +1,4 @@
+if (String.BeginsWith("/api/v1/statuses", @slon_http_request_path(session))) {
+ @slon_api_v1_statuses_delete(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/Accounts.HC b/Slon/Endpoints/Get/Accounts.HC
new file mode 100644
index 0000000..e46f9ed
--- /dev/null
+++ b/Slon/Endpoints/Get/Accounts.HC
@@ -0,0 +1,4 @@
+if (String.BeginsWith("/api/v1/accounts", @slon_http_request_path(session))) {
+ @slon_api_v1_accounts_get(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/ActivityPub.HC b/Slon/Endpoints/Get/ActivityPub.HC
new file mode 100644
index 0000000..974f2a2
--- /dev/null
+++ b/Slon/Endpoints/Get/ActivityPub.HC
@@ -0,0 +1,4 @@
+if (String.BeginsWith("/users/", @slon_http_request_path(session)) && String.EndsWith("json", @slon_http_request_header(session, "accept"))) {
+ @slon_activitypub_users_get(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/Blocks.HC b/Slon/Endpoints/Get/Blocks.HC
new file mode 100644
index 0000000..3474c2e
--- /dev/null
+++ b/Slon/Endpoints/Get/Blocks.HC
@@ -0,0 +1,4 @@
+if (!StrICmp("/api/v1/blocks", @slon_http_request_path(session))) {
+ @slon_api_v1_blocks_get(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/Bookmarks.HC b/Slon/Endpoints/Get/Bookmarks.HC
new file mode 100644
index 0000000..0fdd9b5
--- /dev/null
+++ b/Slon/Endpoints/Get/Bookmarks.HC
@@ -0,0 +1,4 @@
+if (!StrICmp("/api/v1/bookmarks", @slon_http_request_path(session))) {
+ @slon_api_v1_bookmarks_get(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/Conversations.HC b/Slon/Endpoints/Get/Conversations.HC
new file mode 100644
index 0000000..1333d27
--- /dev/null
+++ b/Slon/Endpoints/Get/Conversations.HC
@@ -0,0 +1,4 @@
+if (!StrICmp("/api/v1/conversations", @slon_http_request_path(session))) {
+ @slon_api_v1_conversations_get(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/CustomEmojis.HC b/Slon/Endpoints/Get/CustomEmojis.HC
new file mode 100644
index 0000000..08d2285
--- /dev/null
+++ b/Slon/Endpoints/Get/CustomEmojis.HC
@@ -0,0 +1,4 @@
+if (!StrICmp("/api/v1/custom_emojis", @slon_http_request_path(session))) {
+ @slon_api_v1_custom_emojis_get(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/Favourites.HC b/Slon/Endpoints/Get/Favourites.HC
new file mode 100644
index 0000000..722221e
--- /dev/null
+++ b/Slon/Endpoints/Get/Favourites.HC
@@ -0,0 +1,4 @@
+if (!StrICmp("/api/v1/favourites", @slon_http_request_path(session))) {
+ @slon_api_v1_favourites_get(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/Filters.HC b/Slon/Endpoints/Get/Filters.HC
new file mode 100644
index 0000000..2418420
--- /dev/null
+++ b/Slon/Endpoints/Get/Filters.HC
@@ -0,0 +1,9 @@
+if (!StrICmp("/api/v1/filters", @slon_http_request_path(session))) {
+ @slon_api_v1_filters_get(session);
+ return;
+}
+
+if (!StrICmp("/api/v2/filters", @slon_http_request_path(session))) {
+ @slon_api_v2_filters_get(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/FollowRequests.HC b/Slon/Endpoints/Get/FollowRequests.HC
new file mode 100644
index 0000000..79de6eb
--- /dev/null
+++ b/Slon/Endpoints/Get/FollowRequests.HC
@@ -0,0 +1,4 @@
+if (!StrICmp("/api/v1/follow_requests", @slon_http_request_path(session))) {
+ @slon_api_v1_follow_requests_get(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/FollowedTags.HC b/Slon/Endpoints/Get/FollowedTags.HC
new file mode 100644
index 0000000..28b7cad
--- /dev/null
+++ b/Slon/Endpoints/Get/FollowedTags.HC
@@ -0,0 +1,4 @@
+if (!StrICmp("/api/v1/followed_tags", @slon_http_request_path(session))) {
+ @slon_api_v1_followed_tags_get(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/Instance.HC b/Slon/Endpoints/Get/Instance.HC
new file mode 100644
index 0000000..487288b
--- /dev/null
+++ b/Slon/Endpoints/Get/Instance.HC
@@ -0,0 +1,9 @@
+if (!StrICmp("/api/v1/instance", @slon_http_request_path(session))) {
+ @slon_http_send_json(session, db->o("instance"));
+ return;
+}
+
+if (!StrICmp("/api/v2/instance", @slon_http_request_path(session))) {
+ @slon_api_v2_instance_get(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/Notifications.HC b/Slon/Endpoints/Get/Notifications.HC
new file mode 100644
index 0000000..687e193
--- /dev/null
+++ b/Slon/Endpoints/Get/Notifications.HC
@@ -0,0 +1,4 @@
+if (String.BeginsWith("/api/v1/notifications", @slon_http_request_path(session))) {
+ @slon_api_v1_notifications_get(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/OAuth.HC b/Slon/Endpoints/Get/OAuth.HC
new file mode 100644
index 0000000..46b65f1
--- /dev/null
+++ b/Slon/Endpoints/Get/OAuth.HC
@@ -0,0 +1,9 @@
+if (!StrICmp("/oauth/authorize", @slon_http_request_path(session))) {
+ @slon_http_send_html_file(session, "M:/Slon/Static/oauth/authorize.html");
+ return;
+}
+
+if (!StrICmp("/oauth/verify_access", @slon_http_request_path(session))) {
+ @slon_oauth_verify_access_get(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/Suggestions.HC b/Slon/Endpoints/Get/Suggestions.HC
new file mode 100644
index 0000000..44d465e
--- /dev/null
+++ b/Slon/Endpoints/Get/Suggestions.HC
@@ -0,0 +1,4 @@
+if (!StrICmp("/api/v2/suggestions", @slon_http_request_path(session))) {
+ @slon_api_v2_suggestions_get(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/Timelines.HC b/Slon/Endpoints/Get/Timelines.HC
new file mode 100644
index 0000000..5fc6f85
--- /dev/null
+++ b/Slon/Endpoints/Get/Timelines.HC
@@ -0,0 +1,4 @@
+if (String.BeginsWith("/api/v1/timelines", @slon_http_request_path(session))) {
+ @slon_api_v1_timelines_get(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/Web.HC b/Slon/Endpoints/Get/Web.HC
new file mode 100644
index 0000000..d2231a4
--- /dev/null
+++ b/Slon/Endpoints/Get/Web.HC
@@ -0,0 +1,28 @@
+if (String.EndsWith(".css", @slon_http_request_path(session))) {
+ @slon_http_set_content_type(session, "text/css");
+ @slon_http_send_file(session, "M:/Slon/Static/css/main.css");
+ return;
+}
+
+if (!StrICmp("/js/header.js", @slon_http_request_path(session))) {
+ @slon_http_set_content_type(session, "text/javascript");
+ @slon_http_send_file(session, "M:/Slon/Static/js/header.js");
+ return;
+}
+
+if (String.EndsWith(".js", @slon_http_request_path(session))) {
+ @slon_http_set_content_type(session, "text/javascript");
+ @slon_http_send_file(session, "M:/Slon/Static/js/statuses.js");
+ return;
+}
+
+if (!StrICmp("/alec.png", @slon_http_request_path(session))) {
+ @slon_http_set_content_type(session, "image/png");
+ @slon_http_send_file(session, "A:/avatar-circle-4bpp.png");
+ return;
+}
+
+if (String.BeginsWith("/@", @slon_http_request_path(session))) {
+ @slon_web_user_get(session);
+ return;
+}
diff --git a/Slon/Endpoints/Get/WellKnown.HC b/Slon/Endpoints/Get/WellKnown.HC
new file mode 100644
index 0000000..0f14f2c
--- /dev/null
+++ b/Slon/Endpoints/Get/WellKnown.HC
@@ -0,0 +1,14 @@
+if (!StrICmp("/.well-known/host-meta", @slon_http_request_path(session))) {
+ @slon_host_meta(session);
+ return;
+}
+
+if (!StrICmp("/.well-known/oauth-authorization-server", @slon_http_request_path(session))) {
+ @slon_oauth_well_known(session);
+ return;
+}
+
+if (!StrICmp("/.well-known/webfinger", @slon_http_request_path(session))) {
+ @slon_webfinger(session);
+ return;
+}
diff --git a/Slon/Endpoints/Patch/Accounts.HC b/Slon/Endpoints/Patch/Accounts.HC
new file mode 100644
index 0000000..d98880b
--- /dev/null
+++ b/Slon/Endpoints/Patch/Accounts.HC
@@ -0,0 +1,4 @@
+if (String.BeginsWith("/api/v1/accounts", @slon_http_request_path(session))) {
+ @slon_api_v1_accounts_patch(session);
+ return;
+}
diff --git a/Slon/Endpoints/Post/ActivityPub.HC b/Slon/Endpoints/Post/ActivityPub.HC
new file mode 100644
index 0000000..a9a4b82
--- /dev/null
+++ b/Slon/Endpoints/Post/ActivityPub.HC
@@ -0,0 +1,4 @@
+if (String.BeginsWith("/users/", @slon_http_request_path(session))) {
+ @slon_activitypub_users_post(session);
+ return;
+}
diff --git a/Slon/Endpoints/Post/Apps.HC b/Slon/Endpoints/Post/Apps.HC
new file mode 100644
index 0000000..d26a96e
--- /dev/null
+++ b/Slon/Endpoints/Post/Apps.HC
@@ -0,0 +1,4 @@
+if (!StrICmp("/api/v1/apps", @slon_http_request_path(session))) {
+ @slon_api_v1_apps_post(session);
+ return;
+}
diff --git a/Slon/Endpoints/Post/OAuth.HC b/Slon/Endpoints/Post/OAuth.HC
new file mode 100644
index 0000000..aa1960c
--- /dev/null
+++ b/Slon/Endpoints/Post/OAuth.HC
@@ -0,0 +1,4 @@
+if (!StrICmp("/oauth/token", @slon_http_request_path(session))) {
+ @slon_oauth_token_post(session);
+ return;
+}
diff --git a/Slon/Endpoints/Post/Statuses.HC b/Slon/Endpoints/Post/Statuses.HC
new file mode 100644
index 0000000..0c3ebfe
--- /dev/null
+++ b/Slon/Endpoints/Post/Statuses.HC
@@ -0,0 +1,4 @@
+if (!StrICmp("/api/v1/statuses", @slon_http_request_path(session))) {
+ @slon_api_v1_statuses_post(session);
+ return;
+}
diff --git a/Slon/Http/AdminServer.HC b/Slon/Http/AdminServer.HC
new file mode 100644
index 0000000..235a970
--- /dev/null
+++ b/Slon/Http/AdminServer.HC
@@ -0,0 +1,394 @@
+U0 @slon_admin_html_form_from_json_object(SlonHttpSession* session, U8* buf, JsonObject* o)
+{
+ if (!session || !buf || !o)
+ return;
+
+ JsonKey* key = o->keys;
+ String.Append(buf, "
");
+ while (key) {
+ switch (key->type) {
+ case JSON_BOOLEAN:
+ case JSON_STRING:
+ case JSON_NUMBER:
+ String.Append(buf, "%s ", key->name);
+ break;
+ default:
+ break;
+ }
+ switch (key->type) {
+ case JSON_BOOLEAN:
+ String.Append(buf, " ", key->name, @t(key->value, "checked", ""));
+ break;
+ case JSON_STRING:
+ String.Append(buf, " ", key->name, key->value);
+ break;
+ case JSON_NUMBER:
+ String.Append(buf, " ", key->name, ToI64(key->value));
+ break;
+ default:
+ break;
+ }
+ String.Append(buf, " ");
+ key = key->next;
+ }
+ String.Append(buf, "
");
+}
+
+U0 @slon_admin_create_ap_actor(SlonHttpSession* session, JsonObject* acct)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ JsonObject* actors = db->o("actors");
+ U8* domain = db->o("instance")->@("uri");
+ U8* username = acct->@("username");
+
+ JsonObject* actor = Json.Clone(SLON_DEFAULT_ACTOR_OBJECT);
+
+ StrPrint(scratch_buffer, "https://%s/users/%s", domain, username);
+ actor->set("id", scratch_buffer, JSON_STRING);
+ StrPrint(scratch_buffer, "https://%s/users/%s/following", domain, username);
+ actor->set("following", scratch_buffer, JSON_STRING);
+ StrPrint(scratch_buffer, "https://%s/users/%s/followers", domain, username);
+ actor->set("followers", scratch_buffer, JSON_STRING);
+ StrPrint(scratch_buffer, "https://%s/users/%s/inbox", domain, username);
+ actor->set("inbox", scratch_buffer, JSON_STRING);
+ StrPrint(scratch_buffer, "https://%s/users/%s/outbox", domain, username);
+ actor->set("outbox", scratch_buffer, JSON_STRING);
+ StrPrint(scratch_buffer, "https://%s/users/%s/collections/featured", domain, username);
+ actor->set("featured", scratch_buffer, JSON_STRING);
+ StrPrint(scratch_buffer, "https://%s/users/%s/collections/tags", domain, username);
+ actor->set("featuredTags", scratch_buffer, JSON_STRING);
+ actor->set("preferredUsername", username, JSON_STRING);
+ actor->set("name", acct->@("display_name"), JSON_STRING);
+ actor->set("summary", acct->@("note"), JSON_STRING);
+
+ JsonObject* icon = Json.Parse("{\"type\":\"Image\"}");
+ icon->set("url", acct->@("avatar"), JSON_STRING);
+ actor->set("icon", icon, JSON_OBJECT);
+
+ StrPrint(scratch_buffer, "https://%s/@%s", domain, username);
+ actor->set("url", scratch_buffer, JSON_STRING);
+ actor->set("published", acct->@("created_at"), JSON_STRING);
+ actor->set("attachment", acct->@("fields"), JSON_ARRAY);
+ actor->set("accountId", acct->@("id"), JSON_STRING);
+
+ db->o("private_keys")->set(username, request_json->@("privatekey"), JSON_STRING);
+
+ JsonObject* publickey = Json.CreateObject();
+ StrPrint(scratch_buffer, "https://%s/users/%s#main-key", domain, username);
+ publickey->set("id", scratch_buffer, JSON_STRING);
+ StrPrint(scratch_buffer, "https://%s/users/%s", domain, username);
+ publickey->set("owner", scratch_buffer, JSON_STRING);
+ I64 x;
+ publickey->set("publicKeyPem", @base64_decode(request_json->@("publickey"), &x), JSON_STRING);
+ actor->set("publicKey", publickey, JSON_OBJECT);
+
+ actors->set(acct->@("username"), actor, JSON_OBJECT);
+ @slon_db_save_to_disk;
+}
+
+U0 @slon_admin_create_account(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ U8* id = @slon_api_generate_unique_id(session);
+ U8* created_at = @slon_api_timestamp_from_cdate(session, Now);
+
+ JsonObject* acct = Json.CreateObject();
+ JsonObject* source = Json.CreateObject();
+
+ acct->set("id", id, JSON_STRING);
+ acct->set("created_at", created_at, JSON_STRING);
+ acct->set("username", request_json->@("username"), JSON_STRING);
+ acct->set("acct", request_json->@("username"), JSON_STRING);
+ acct->set("display_name", request_json->@("display_name"), JSON_STRING);
+ acct->set("email", request_json->@("email"), JSON_STRING);
+ acct->set("note", request_json->@("bio"), JSON_STRING);
+ acct->set("avatar", request_json->@("avatar"), JSON_STRING);
+ acct->set("header", request_json->@("header"), JSON_STRING);
+ acct->set("avatar_static", acct->@("avatar"), JSON_STRING);
+ acct->set("header_static", acct->@("header"), JSON_STRING);
+ acct->set("last_status_at", "0", JSON_STRING);
+
+ acct->set("followers_count", 0, JSON_NUMBER);
+ acct->set("following_count", 0, JSON_NUMBER);
+ acct->set("statuses_count", 0, JSON_NUMBER);
+
+ acct->set("locked", FALSE, JSON_BOOLEAN);
+ acct->set("bot", FALSE, JSON_BOOLEAN);
+ acct->set("discoverable", FALSE, JSON_BOOLEAN);
+ acct->set("indexable", FALSE, JSON_BOOLEAN);
+ acct->set("hide_collections", FALSE, JSON_BOOLEAN);
+
+ acct->set("emojis", SLON_EMPTY_JSON_ARRAY, JSON_ARRAY);
+ acct->set("fields", SLON_EMPTY_JSON_ARRAY, JSON_ARRAY);
+
+ source->set("privacy", "public", JSON_STRING);
+ source->set("sensitive", FALSE, JSON_BOOLEAN);
+ source->set("language", "", JSON_STRING);
+ source->set("note", acct->@("note"), JSON_STRING);
+ source->set("fields", acct->@("fields"), JSON_ARRAY);
+ source->set("follow_requests_count", 0, JSON_NUMBER);
+
+ acct->set("source", source, JSON_OBJECT);
+
+ StrPrint(scratch_buffer, "https://%s/@%s", db->o("instance")->@("uri"), acct->@("username"));
+ acct->set("url", scratch_buffer, JSON_STRING);
+
+ db->a("accounts")->append(Json.CreateItem(acct, JSON_OBJECT));
+ db->o("statuses")->set(acct->@("id"), Json.CreateArray(), JSON_ARRAY);
+ @slon_admin_create_ap_actor(session, acct);
+
+ @slon_db_instance_update_user_count;
+ @slon_db_save_to_disk;
+
+ @slon_free(session, created_at);
+ @slon_free(session, id);
+}
+
+U0 @slon_admin_settings_accounts_new_get(SlonHttpSession* session, U8* buf)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn scratch_buffer, request_json;
+ String.Append(buf, "settings/accounts/new");
+ String.Append(buf, " ");
+}
+
+U0 @slon_admin_settings_accounts_get(SlonHttpSession* session, U8* buf)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn scratch_buffer, request_json;
+ String.Append(buf, "settings/accounts");
+ String.Append(buf, "id username ");
+ JsonArray* arr = db->a("accounts");
+ JsonObject* acct;
+ I64 i;
+ for (i = 0; i < arr->length; i++) {
+ acct = arr->@(i);
+ if (acct) {
+ String.Append(buf, "%s %s ", acct->@("id"), acct->@("username"));
+ }
+ }
+ String.Append(buf, "
New ");
+}
+
+U0 @slon_admin_settings_apps_get(SlonHttpSession* session, U8* buf)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn scratch_buffer, request_json;
+ String.Append(buf, "settings/apps");
+ String.Append(buf, " ");
+ U8* tmp = Json.Stringify(db->o("apps"));
+ String.Append(buf, tmp);
+ Free(tmp);
+}
+
+U0 @slon_admin_settings_oauth_get(SlonHttpSession* session, U8* buf)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn scratch_buffer, request_json;
+ String.Append(buf, "settings/oauth");
+ String.Append(buf, " ");
+ U8* tmp = Json.Stringify(db->o("oauth"));
+ String.Append(buf, tmp);
+ Free(tmp);
+}
+
+U0 @slon_admin_settings_instance_save_get(SlonHttpSession* session, U8* buf)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn scratch_buffer;
+
+ JsonObject* instance = db->o("instance");
+ instance->set("uri", request_json->@("uri"));
+ instance->set("title", request_json->@("title"));
+ instance->set("short_description", request_json->@("short_description"));
+ instance->set("description", request_json->@("description"));
+ instance->set("email", request_json->@("email"));
+ instance->set("version", request_json->@("version"));
+ if (!request_json->@("registrations")) {
+ instance->set("registrations", FALSE);
+ } else {
+ instance->set("registrations", !StrICmp("on", request_json->@("registrations")));
+ }
+ String.Append(buf, "");
+}
+
+U0 @slon_admin_settings_instance_get(SlonHttpSession* session, U8* buf)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn scratch_buffer, request_json;
+
+ String.Append(buf, "settings/instance");
+ String.Append(buf, " ");
+}
+
+U0 @slon_admin_new_account(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ if (db->o("actors")->@(request_json->@("username"))) {
+ StrPrint(scratch_buffer, "{\"error\":\"account already exists\"}");
+ @slon_http_set_content_type(session, "application/json");
+ @slon_http_send(session, scratch_buffer, StrLen(scratch_buffer));
+ } else {
+ @slon_admin_create_account(session);
+ @slon_http_send_json(session, SLON_EMPTY_JSON_OBJECT);
+ }
+}
+
+U0 @slon_admin_manage_accounts(SlonHttpSession* session)
+{
+ @slon_http_send_json(session, db->a("accounts"));
+}
+
+U0 @slon_admin_info_stats(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn request_json;
+
+ StrPrint(scratch_buffer, "{");
+ String.Append(scratch_buffer, "\"uptime\":\"%d\"", cnts.jiffies);
+ String.Append(scratch_buffer, "}");
+
+ @slon_http_set_content_type(session, "application/json");
+ @slon_http_send(session, scratch_buffer, StrLen(scratch_buffer));
+}
+
+U0 @slon_admin_server_get(SlonHttpSession* session)
+{
+ if (!db->@("setup")) {
+ if (StrICmp("/", @slon_http_request_path(session))) {
+ @slon_http_set_status_code(session, 302);
+ @slon_http_set_header(session, "Location", "/");
+ } else {
+ @slon_http_send_html_file(session, "M:/Slon/Static/html/admin/setup_instance.html");
+ }
+ return;
+ }
+
+ if (!StrICmp("/info/stats", @slon_http_request_path(session))) {
+ @slon_admin_info_stats(session);
+ return;
+ }
+
+ if (!StrICmp("/manage/accounts", @slon_http_request_path(session))) {
+ @slon_admin_manage_accounts(session);
+ return;
+ }
+
+ if (!StrICmp("/", @slon_http_request_path(session))) {
+ @slon_http_send_html_file(session, "M:/Slon/Static/html/admin/main.html");
+ return;
+ }
+
+ @slon_http_set_status_code(session, 404);
+}
+
+U0 @slon_admin_setup_instance(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn scratch_buffer;
+
+ JsonObject* instance = db->o("instance");
+ instance->set("uri", request_json->@("uri"));
+ instance->set("title", request_json->@("title"));
+ instance->set("short_description", request_json->@("description"));
+ instance->set("description", request_json->@("description"));
+ instance->set("email", request_json->@("email"));
+ instance->set("registrations", request_json->@("registrations"));
+ @slon_db_save_to_disk;
+ db->set("setup", TRUE);
+
+ @slon_http_send_json(session, SLON_EMPTY_JSON_OBJECT);
+}
+
+U0 @slon_admin_server_post(SlonHttpSession* session)
+{
+ if (StrFind("json", @slon_http_request_header(session, "content-type")) > 0) {
+ @slon_http_parse_request_as_json(session);
+ }
+
+ if (!StrICmp("/setup/instance", @slon_http_request_path(session))) {
+ @slon_admin_setup_instance(session);
+ return;
+ }
+
+ if (!StrICmp("/new/account", @slon_http_request_path(session))) {
+ @slon_admin_new_account(session);
+ return;
+ }
+
+ @slon_http_set_status_code(session, 404);
+}
+
+U0 @slon_admin_http_handle_get_request(SlonHttpSession* session)
+{
+ if (@slon_http_request_has_query_string(session)) {
+ @slon_http_parse_query_string(session);
+ }
+ @slon_admin_server_get(session);
+}
+
+U0 @slon_admin_http_handle_post_request(SlonHttpSession* session)
+{
+ @slon_admin_server_post(session);
+}
+
+U0 @slon_admin_http_handle_request(SlonHttpSession* session)
+{
+ switch (@slon_http_request_verb(session)) {
+ case SLON_HTTP_VERB_GET:
+ @slon_admin_http_handle_get_request(session);
+ break;
+ case SLON_HTTP_VERB_POST:
+ @slon_admin_http_handle_post_request(session);
+ break;
+ default:
+ @slon_http_set_status_code(session, 405);
+ }
+}
+
+U0 @slon_admin_http_task(TcpSocket* s)
+{
+ // Bail if we can't acquire socket for some reason
+ if (!@tcp_socket_accept(s))
+ return;
+
+ // Init session
+ SlonHttpSession* session = @slon_http_init_session(s);
+
+ // Parse headers if they are available
+ while (!@slon_http_request_headers_have_been_parsed(session)) {
+ @slon_http_receive(session);
+
+ // Handle malformed requests (anything less than "GET / HTTP/1.0\r\n\r\n" is probably a bad request)
+ if (session->request->buffer->size < 18) {
+ @slon_http_set_status_code(session, 400);
+ goto slon_admin_http_task_send_response;
+ }
+
+ @slon_http_try_parse_request_headers(session);
+ }
+
+ // If we have a content-length header, consume until we receive all the data, then set request->data pointer and size
+ if (StrLen(@slon_http_request_header(session, "content-length"))) {
+ I64 content_length = Str2I64(@slon_http_request_header(session, "content-length"));
+ while (session->request->buffer->data + session->request->buffer->size - session->request->data < content_length)
+ @slon_http_receive(session);
+ }
+
+ @slon_admin_http_handle_request(session);
+
+slon_admin_http_task_send_response:
+ @slon_http_send_response(session);
+ @slon_http_free_session(session);
+ s->close();
+}
+
+Adam("U0 @spawn_slon_admin_http_task(TcpSocket *s){Spawn(%d, s, \"SlonAdminHttpTask\");};\n", &@slon_admin_http_task);
+@tcp_socket_bind(9000, "@spawn_slon_admin_http_task");
diff --git a/Slon/Http/LocalServer.HC b/Slon/Http/LocalServer.HC
new file mode 100644
index 0000000..fdaee70
--- /dev/null
+++ b/Slon/Http/LocalServer.HC
@@ -0,0 +1,232 @@
+U0 @slon_local_server_set_mime_type(SlonHttpSession* session, U8* filepath)
+{
+ // FIXME: Do this programmatically like the Jakt version, this is awful
+ if (String.EndsWith(".html", filepath)) {
+ @slon_http_set_content_type(session, "text/html");
+ return;
+ }
+ if (String.EndsWith(".txt", filepath)) {
+ @slon_http_set_content_type(session, "text/plain");
+ return;
+ }
+ if (String.EndsWith(".css", filepath)) {
+ @slon_http_set_content_type(session, "text/css");
+ return;
+ }
+ if (String.EndsWith(".js", filepath)) {
+ @slon_http_set_content_type(session, "text/javascript");
+ return;
+ }
+ if (String.EndsWith(".json", filepath)) {
+ @slon_http_set_content_type(session, "application/json");
+ return;
+ }
+ if (String.EndsWith(".gif", filepath)) {
+ @slon_http_set_content_type(session, "image/gif");
+ return;
+ }
+ if (String.EndsWith(".png", filepath)) {
+ @slon_http_set_content_type(session, "image/png");
+ return;
+ }
+ if (String.EndsWith(".jpeg", filepath) || String.EndsWith(".jpg", filepath)) {
+ @slon_http_set_content_type(session, "image/jpeg");
+ return;
+ }
+ @slon_http_set_content_type(session, "application/octet-stream");
+}
+
+U0 @slon_local_server_send_file(SlonHttpSession* session, U8* filepath)
+{
+ @slon_local_server_set_mime_type(session, filepath);
+ @slon_http_send_file(session, filepath);
+}
+
+U0 @slon_local_server_directory_listing(SlonHttpSession* session, U8* path)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn request_json;
+
+ U8* html = @slon_calloc(session, 1048576);
+
+ String.Append(html, "Index of ");
+ String.Append(html, path);
+ String.Append(html, " Index of ");
+ String.Append(html, path);
+ String.Append(html, " ");
+ String.Append(html, "Name Last modified Size ");
+ String.Append(html, " ");
+ StrPrint(scratch_buffer, "A:%s*", path);
+ CDirEntry* files = FilesFind(scratch_buffer);
+ CDirEntry* de = files->next;
+ CDateStruct ds;
+ while (de) {
+ String.Append(html, "name);
+ String.Append(html, "\">");
+ if (!StrICmp("..", de->name)) {
+ String.Append(html, " ");
+ } else {
+ if (de->attr & RS_ATTR_DIR) {
+ String.Append(html, " ");
+ } else {
+ String.Append(html, " ");
+ }
+ }
+ String.Append(html, " ");
+ String.Append(html, "name);
+ if (de->attr & RS_ATTR_DIR) {
+ String.Append(html, "/");
+ }
+ String.Append(html, "\">");
+ if (!StrICmp("..", de->name)) {
+ String.Append(html, "Parent Directory");
+ } else {
+ String.Append(html, de->name);
+ }
+ String.Append(html, " ");
+ Date2Struct(&ds, de->datetime);
+ String.Append(html, "%02d-%03tZ-%04d %02d:%02d", ds.day_of_mon, ds.mon - 1, "ST_MONTHS", ds.year, ds.hour, ds.min);
+ String.Append(html, " ");
+ String.Append(html, " ");
+ if (de->attr & RS_ATTR_DIR) {
+ String.Append(html, " - ");
+ } else {
+ String.Append(html, "%d", de->size);
+ }
+
+ String.Append(html, " ");
+ de = de->next;
+ }
+ DirTreeDel(files);
+
+ String.Append(html, " ");
+ String.Append(html, "
");
+ String.Append(html, "Slon static file webserver for (TempleOS) Server ");
+ String.Append(html, "");
+
+ @slon_http_set_content_type(session, "text/html");
+ @slon_http_send(session, html, StrLen(html));
+ @slon_free(session, html);
+}
+
+U0 @slon_local_server_not_found(SlonHttpSession* session)
+{
+ @slon_http_set_status_code(session, 404);
+ @slon_http_set_content_type(session, "text/html");
+ @slon_http_send(session, "404 Not Found ", 22);
+}
+
+U0 @slon_local_server_get(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn request_json;
+
+ U8* path = @slon_http_request_path(session);
+
+ if (!path || !StrLen(path) || StrFind(":", path) > 0) {
+ @slon_local_server_not_found(session);
+ return;
+ }
+
+ if (path[0] == '/' && StrLen(path) == 1) {
+ // Handle root path
+ if (FileFind("A:/index.html")) {
+ @slon_local_server_send_file(session, "A:/index.html");
+ } else {
+ @slon_local_server_directory_listing(session, "/");
+ }
+ return;
+ }
+
+ if (String.EndsWith("/", path)) {
+ StrPrint(scratch_buffer, "A:%sindex.html", path);
+ if (FileFind(scratch_buffer)) {
+ @slon_local_server_send_file(session, scratch_buffer);
+ } else {
+ StrPrint(scratch_buffer, "A:%s", path);
+ scratch_buffer[StrLen(scratch_buffer) - 1] = NULL;
+ if (IsDir(scratch_buffer)) {
+ @slon_local_server_directory_listing(session, path);
+ } else {
+ @slon_local_server_not_found(session);
+ }
+ }
+ return;
+ }
+
+ StrPrint(scratch_buffer, "A:%s", path);
+ if (!FileFind(scratch_buffer)) {
+ @slon_local_server_not_found(session);
+ return;
+ } else {
+ if (IsDir(scratch_buffer)) {
+ @slon_http_set_status_code(session, 301);
+ StrPrint(scratch_buffer, "%s/", path);
+ @slon_http_set_header(session, "Location", scratch_buffer);
+ } else {
+ @slon_local_server_send_file(session, scratch_buffer);
+ }
+ return;
+ }
+
+ // shouldn't get here :^)
+ @slon_http_set_status_code(session, 400);
+}
+
+U0 @slon_local_http_handle_get_request(SlonHttpSession* session)
+{
+ @slon_local_server_get(session);
+}
+
+U0 @slon_local_http_handle_request(SlonHttpSession* session)
+{
+ switch (@slon_http_request_verb(session)) {
+ case SLON_HTTP_VERB_GET:
+ @slon_local_http_handle_get_request(session);
+ break;
+ default:
+ @slon_http_set_status_code(session, 405);
+ }
+}
+
+U0 @slon_local_http_task(TcpSocket* s)
+{
+ // Bail if we can't acquire socket for some reason
+ if (!@tcp_socket_accept(s))
+ return;
+
+ // Init session
+ SlonHttpSession* session = @slon_http_init_session(s);
+
+ // Parse headers if they are available
+ while (!@slon_http_request_headers_have_been_parsed(session)) {
+ @slon_http_receive(session);
+
+ // Handle malformed requests (anything less than "GET / HTTP/1.0\r\n\r\n" is probably a bad request)
+ if (session->request->buffer->size < 18) {
+ @slon_http_set_status_code(session, 400);
+ goto slon_local_http_task_send_response;
+ }
+
+ @slon_http_try_parse_request_headers(session);
+ }
+
+ // If we have a content-length header, consume until we receive all the data, then set request->data pointer and size
+ if (StrLen(@slon_http_request_header(session, "content-length"))) {
+ I64 content_length = Str2I64(@slon_http_request_header(session, "content-length"));
+ while (session->request->buffer->data + session->request->buffer->size - session->request->data < content_length)
+ @slon_http_receive(session);
+ }
+
+ @slon_local_http_handle_request(session);
+
+slon_local_http_task_send_response:
+ @slon_http_send_response(session);
+ @slon_http_free_session(session);
+ s->close();
+}
+
+Adam("U0 @spawn_slon_local_http_task(TcpSocket *s){Spawn(%d, s, \"SlonLocalHttpTask\");};\n", &@slon_local_http_task);
+@tcp_socket_bind(8000, "@spawn_slon_local_http_task");
diff --git a/Slon/Http/Server.HC b/Slon/Http/Server.HC
new file mode 100644
index 0000000..7a326b4
--- /dev/null
+++ b/Slon/Http/Server.HC
@@ -0,0 +1,542 @@
+SlonHttpBuffer* @slon_http_init_buffer(SlonHttpSession* session)
+{
+ SlonHttpBuffer* buffer = @slon_calloc(session, sizeof(SlonHttpBuffer));
+ buffer->data = @slon_calloc(session, SLON_HTTP_BUFFER_SIZE);
+ buffer->capacity = SLON_HTTP_BUFFER_SIZE;
+ return buffer;
+}
+
+U0 @slon_http_free_response(SlonHttpSession* session, SlonHttpResponse* response)
+{
+ // FIXME: Free headers JsonObject
+ if (response) {
+ if (response->buffer && response->buffer->data) {
+ @slon_free(session, response->buffer->data);
+ @slon_free(session, response->buffer);
+ }
+ @slon_free(session, response);
+ }
+}
+
+U0 @slon_http_free_request(SlonHttpSession* session, SlonHttpRequest* request)
+{
+ // FIXME: Free headers JsonObject
+ if (request) {
+ if (request->buffer && request->buffer->data) {
+ @slon_free(session, request->buffer->data);
+ @slon_free(session, request->buffer);
+ }
+ if (request->verb)
+ @slon_free(session, session->request->verb);
+ if (request->raw_path)
+ @slon_free(session, session->request->raw_path);
+ if (request->path)
+ @slon_free(session, session->request->path);
+ @slon_free(session, request);
+ }
+}
+
+U0 @slon_http_free_session(SlonHttpSession* session)
+{
+ if (!session)
+ return;
+ @slon_http_free_response(session, session->response);
+ @slon_http_free_request(session, session->request);
+ I64 bytes_used = session->bytes_used - MSize2(session);
+ Free(session);
+ if (bytes_used) {
+ AdamLog("*** Session leaked %d bytes of memory ***\n", bytes_used);
+ }
+}
+
+SlonHttpRequest* @slon_http_init_request(SlonHttpSession* session)
+{
+ SlonHttpRequest* request = @slon_calloc(session, sizeof(SlonHttpRequest));
+ request->buffer = @slon_http_init_buffer(session);
+ request->headers = Json.CreateObject();
+ return request;
+}
+
+SlonHttpResponse* @slon_http_init_response(SlonHttpSession* session)
+{
+ SlonHttpResponse* response = @slon_calloc(session, sizeof(SlonHttpResponse));
+ response->buffer = @slon_http_init_buffer(session);
+ response->headers = Json.CreateObject();
+ return response;
+}
+
+SlonHttpSession* @slon_http_init_session(TcpSocket* s)
+{
+ SlonHttpSession* session = CAlloc(sizeof(SlonHttpSession), adam_task);
+ session->bytes_used = MSize2(session);
+ session->s = s;
+ session->request = @slon_http_init_request(session);
+ session->response = @slon_http_init_response(session);
+ return session;
+}
+
+U0 @slon_http_receive(SlonHttpSession* session)
+{
+ // FIXME: grow the buffer
+ SlonHttpBuffer* buffer = session->request->buffer;
+ I64 chunk_size = @tcp_socket_receive(session->s, buffer->data + buffer->size, 65536);
+ buffer->size += chunk_size;
+}
+
+Bool @slon_http_request_headers_have_been_parsed(SlonHttpSession* session)
+{
+ return session->request->headers_have_been_parsed;
+}
+
+U0 @slon_http_buffer_append(SlonHttpBuffer* buffer, U8* src, I64 size)
+{
+ if (!buffer || !src || !size)
+ return;
+ MemCpy(buffer->data + buffer->size, src, size);
+ buffer->size += size;
+}
+
+U0 @slon_http_buffer_append_string(SlonHttpBuffer* buffer, U8* str)
+{
+ @slon_http_buffer_append(buffer, str, StrLen(str));
+}
+
+U0 @slon_http_send_response(SlonHttpSession* session)
+{
+ SlonHttpBuffer* buffer = session->response->buffer;
+ U8 scratch_buffer[256][4];
+
+ StrPrint(scratch_buffer[0], "%d", session->response->status_code);
+ StrPrint(scratch_buffer[1], "HTTP/1.0 %d %s\r\n", session->response->status_code, Json.Get(SLON_HTTP_STATUS_CODES, scratch_buffer[0]));
+ @slon_http_buffer_append_string(buffer, scratch_buffer[1]);
+
+ JsonKey* key = session->response->headers->keys;
+ while (key) {
+ StrPrint(scratch_buffer[0], "%s: %s\r\n", key->name, key->value);
+ @slon_http_buffer_append_string(buffer, scratch_buffer[0]);
+ key = key->next;
+ }
+
+ StrPrint(scratch_buffer[0], "content-length: %d\r\n", session->response->size);
+ @slon_http_buffer_append_string(buffer, scratch_buffer[0]);
+
+ StrCpy(scratch_buffer[0], "pragma: no-cache\r\n\r\n");
+ @slon_http_buffer_append_string(buffer, scratch_buffer[0]);
+
+ if (session->response->data && session->response->size) {
+ @slon_http_buffer_append(buffer, session->response->data, session->response->size);
+ @slon_free(session, session->response->data);
+ }
+
+ @tcp_socket_send(session->s, buffer->data, buffer->size);
+}
+
+U0 @slon_http_rstrip_char_from_string(U8* str, I64 ch)
+{
+ while (str[StrLen(str) - 1] == ch)
+ str[StrLen(str) - 1] = NULL;
+}
+
+U0 @slon_http_try_parse_request_headers(SlonHttpSession* session)
+{
+ SlonHttpBuffer* buffer = session->request->buffer;
+ I64 i = 0;
+ // Do we have headers yet? let's find out
+ while (i < buffer->size) {
+ if (!MemCmp(buffer->data + i, "\r\n\r\n", 4)) {
+ i += 4;
+ goto slon_http_parse_request_headers;
+ }
+ ++i;
+ }
+ return;
+
+slon_http_parse_request_headers:
+ // Set pointer for request content
+ session->request->data = buffer->data + i;
+
+ // We have headers, let's parse them
+ U8* raw_headers = @slon_calloc(session, i);
+ MemCpy(raw_headers, buffer->data, i - 4);
+
+ I64 raw_header_lines_count = 0;
+ U8** raw_header_lines = String.Split(raw_headers, '\n', &raw_header_lines_count);
+
+ if (!raw_header_lines_count) {
+ // FIXME: Handle this
+ }
+
+ I64 request_first_line_segments_count = 0;
+ U8** request_first_line_segments = String.Split(raw_header_lines[0], ' ', &request_first_line_segments_count);
+
+ if (request_first_line_segments_count < 2) {
+ // FIXME: Handle this
+ }
+
+ session->request->verb = @slon_strnew(session, request_first_line_segments[0]);
+ session->request->raw_path = @slon_strnew(session, request_first_line_segments[1]);
+ if (StrFind("?", session->request->raw_path)) {
+ session->request->path = @slon_strnew(session, session->request->raw_path);
+ *(StrFind("?", session->request->path)) = NULL;
+ } else {
+ session->request->path = @slon_strnew(session, session->request->raw_path);
+ }
+
+ U8* key;
+ U8* value;
+
+ for (i = 1; i < raw_header_lines_count; i++) {
+ key = NULL;
+ value = NULL;
+ if (StrFind(": ", raw_header_lines[i])) {
+ value = StrFind(": ", raw_header_lines[i]) + 2;
+ @slon_http_rstrip_char_from_string(value, '\r');
+ *(StrFind(": ", raw_header_lines[i])) = NULL;
+ key = raw_header_lines[i];
+ Json.Set(session->request->headers, key, value, JSON_STRING);
+ }
+ }
+
+ @slon_free(session, raw_headers);
+ session->request->headers_have_been_parsed = TRUE;
+}
+
+U0 @slon_http_authorize(SlonHttpSession* session)
+{
+ if (StrLen(@slon_http_request_header(session, "authorization"))) {
+ U8* access_token = StrFind(" ", @slon_http_request_header(session, "authorization")) + 1;
+ session->auth = db->o("oauth")->o("tokens")->@(access_token);
+ }
+}
+
+U0 @slon_http_debug_print_request(SlonHttpSession* session, Bool show_headers = FALSE)
+{
+ AdamLog("[httpd] %d => request: %s %s\n", session->s, session->request->verb, session->request->raw_path);
+ if (show_headers) {
+ U8* headers_stringified = Json.Stringify(session->request->headers);
+ AdamLog("[httpd] %d => headers: %s\n", session->s, headers_stringified);
+ Free(headers_stringified);
+ //@slon_free(session, headers_stringified);
+ }
+}
+
+U0 @slon_http_debug_print_response(SlonHttpSession* session, Bool show_headers = FALSE)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn request_json;
+
+ StrPrint(scratch_buffer, "%d", session->response->status_code);
+ AdamLog("[httpd] %d <= response: %d %s\n", session->s, session->response->status_code, Json.Get(SLON_HTTP_STATUS_CODES, scratch_buffer));
+ if (show_headers) {
+ U8* headers_stringified = Json.Stringify(session->response->headers);
+ AdamLog("[httpd] %d <= headers: %s\n", session->s, headers_stringified);
+ Free(headers_stringified);
+ //@slon_free(session, headers_stringified);
+ }
+ if (session->response->data) {
+ AdamLog("data: %s\n", session->response->data);
+ }
+}
+
+U8* @slon_http_json_string_from_form_urlencoded_string(SlonHttpSession* session, U8* form_urlencoded_string)
+{
+ // FIXME: Implement arrays, objects per https://jsonapi.org/format/#fetching
+ U8* json_string = @slon_calloc(session, StrLen(form_urlencoded_string) * 2);
+ String.Append(json_string, "{");
+ U8* form_urlencoded_string_copy = @slon_strnew(session, form_urlencoded_string);
+ I64 raw_values_count = 0;
+ U8** raw_values = String.Split(form_urlencoded_string_copy, '&', &raw_values_count);
+ I64 i = 0;
+ U8* key;
+ U8* value;
+ for (i = 0; i < raw_values_count; i++) {
+ value = StrFind("=", raw_values[i]) + 1;
+ *(StrFind("=", raw_values[i])) = NULL;
+ key = raw_values[i];
+ U8* decoded_value = @slon_http_decode_urlencoded_string(session, value);
+ String.Append(json_string, "\"%s\":\"%s\"", key, decoded_value);
+ @slon_free(session, decoded_value);
+ if (i < raw_values_count - 1) {
+ String.Append(json_string, ",");
+ }
+ }
+ String.Append(json_string, "}");
+ @slon_free(session, form_urlencoded_string_copy);
+ return json_string;
+}
+
+U8* @slon_http_json_string_from_multipart_form_data(SlonHttpSession* session, U8* multipart_form_data)
+{
+ U8* json_string = @slon_calloc(session, StrLen(multipart_form_data) * 2);
+ String.Append(json_string, "{");
+ U8* multipart_form_data_copy = @slon_strnew(session, multipart_form_data);
+
+ U8* boundary = StrFind("boundary=", @slon_http_request_header(session, "content-type")) + 9;
+ // Strip begin double-quotes and ending CRLF, double-quotes
+ while (boundary[0] == '"')
+ boundary++;
+
+ while (boundary[StrLen(boundary) - 1] == '\"' || boundary[StrLen(boundary) - 1] == ' ' || boundary[StrLen(boundary) - 1] == '\r' || boundary[StrLen(boundary) - 1] == '\n')
+ boundary[StrLen(boundary) - 1] = NULL;
+
+ I64 state = SLON_MULTIPART_PARSER_CONSUME_BOUNDARY;
+ I64 lines_count = 0;
+ U8** lines = String.Split(multipart_form_data_copy, '\n', &lines_count);
+
+ U8* line;
+ U8* name;
+ U8* replace_line;
+
+ I64 i = 0;
+ while (i < lines_count) {
+ line = lines[i];
+ // Strip any ending CRLF
+ while (line[StrLen(line) - 1] == '\r' || line[StrLen(line) - 1] == '\n') {
+ line[StrLen(line) - 1] = NULL;
+ }
+ switch (state) {
+ case SLON_MULTIPART_PARSER_CONSUME_BOUNDARY:
+ if (StrFind(boundary, line)) {
+ state = SLON_MULTIPART_PARSER_CONSUME_CONTENT_DISPOSITION;
+ }
+ break;
+ case SLON_MULTIPART_PARSER_CONSUME_CONTENT_DISPOSITION:
+ if (StrFind("ontent-", line) && StrFind("isposition:", line) && StrFind("name=", line)) {
+ name = StrFind("name=", line) + 5;
+ // Strip begin/end double-quotes
+ while (name[0] == '"')
+ name++;
+ while (name[StrLen(name) - 1] == '\"')
+ name[StrLen(name) - 1] = NULL;
+ String.Append(json_string, "\"%s\":\"", name);
+ state = SLON_MULTIPART_PARSER_CONSUME_CONTENT;
+ }
+ break;
+ case SLON_MULTIPART_PARSER_CONSUME_CONTENT:
+ if (StrFind(boundary, line)) {
+ String.Append(json_string, "\"");
+ if (!String.EndsWith("--", line)) {
+ String.Append(json_string, ",");
+ state = SLON_MULTIPART_PARSER_CONSUME_CONTENT_DISPOSITION;
+ } else {
+ state = SLON_MULTIPART_PARSER_DONE;
+ }
+ } else {
+ replace_line = String.Replace(line, "\"", "\\\"");
+ String.Append(json_string, replace_line);
+ Free(replace_line);
+ }
+ break;
+ default:
+ break;
+ }
+ ++i;
+ }
+ String.Append(json_string, "}");
+ @slon_free(session, multipart_form_data_copy);
+ return json_string;
+}
+
+U0 @slon_http_parse_query_string(SlonHttpSession* session)
+{
+ U8* raw_path_copy = @slon_strnew(session, session->request->raw_path);
+ I64 raw_path_split_count = 0;
+ U8** raw_path_split = String.Split(raw_path_copy, '?', &raw_path_split_count);
+ if (raw_path_split_count > 1) {
+ U8* json_string = @slon_http_json_string_from_form_urlencoded_string(session, raw_path_split[1]);
+ session->request->json = Json.Parse(json_string);
+ @slon_free(session, json_string);
+ }
+ @slon_free(session, raw_path_copy);
+}
+
+U0 @slon_http_parse_request_as_form_urlencoded(SlonHttpSession* session)
+{
+ U8* json_string = @slon_http_json_string_from_form_urlencoded_string(session, session->request->data);
+ session->request->json = Json.Parse(json_string);
+ @slon_free(session, json_string);
+}
+
+U0 @slon_http_parse_request_as_multipart_form_data(SlonHttpSession* session)
+{
+ U8* json_string = @slon_http_json_string_from_multipart_form_data(session, session->request->data);
+ session->request->json = Json.Parse(json_string);
+ @slon_free(session, json_string);
+}
+
+U0 @slon_http_parse_request_as_json(SlonHttpSession* session)
+{
+ session->request->json = Json.Parse(session->request->data);
+}
+
+U0 @slon_http_handle_delete_request(SlonHttpSession* session)
+{
+
+ /* clang-format off */
+
+ #include "Endpoints/Delete/Statuses";
+
+ /* clang-format on */
+
+ // FIXME: Implement this
+ @slon_http_send_json(session, SLON_EMPTY_JSON_OBJECT);
+}
+
+U0 @slon_http_handle_get_request(SlonHttpSession* session)
+{
+ if (@slon_http_request_has_query_string(session)) {
+ @slon_http_parse_query_string(session);
+ }
+
+ SLON_DEBUG_PRINT_REQUEST_JSON
+
+ /* clang-format off */
+
+ #include "Endpoints/Get/Accounts";
+ #include "Endpoints/Get/ActivityPub";
+ #include "Endpoints/Get/Blocks";
+ #include "Endpoints/Get/Bookmarks";
+ #include "Endpoints/Get/Conversations";
+ #include "Endpoints/Get/CustomEmojis";
+ #include "Endpoints/Get/Favourites";
+ #include "Endpoints/Get/Filters";
+ #include "Endpoints/Get/FollowRequests";
+ #include "Endpoints/Get/FollowedTags";
+ #include "Endpoints/Get/Instance";
+ #include "Endpoints/Get/Notifications";
+ #include "Endpoints/Get/OAuth";
+ #include "Endpoints/Get/Suggestions";
+ #include "Endpoints/Get/Timelines";
+ #include "Endpoints/Get/Web";
+ #include "Endpoints/Get/WellKnown";
+
+ /* clang-format on */
+
+ @slon_http_set_status_code(session, 404);
+}
+
+U0 @slon_http_handle_patch_request(SlonHttpSession* session)
+{
+ if (StrFind("json", @slon_http_request_header(session, "content-type")) > 0) {
+ @slon_http_parse_request_as_json(session);
+ }
+ if (String.BeginsWith("application/x-www-form-urlencoded", @slon_http_request_header(session, "content-type"))) {
+ @slon_http_parse_request_as_form_urlencoded(session);
+ }
+ if (String.BeginsWith("multipart/form-data", @slon_http_request_header(session, "content-type"))) {
+ @slon_http_parse_request_as_multipart_form_data(session);
+ }
+
+ SLON_DEBUG_PRINT_REQUEST_JSON
+
+ /* clang-format off */
+
+ #include "Endpoints/Patch/Accounts";
+
+ /* clang-format on */
+
+ @slon_http_set_status_code(session, 404);
+}
+
+U0 @slon_http_handle_post_request(SlonHttpSession* session)
+{
+ if (StrFind("json", @slon_http_request_header(session, "content-type")) > 0) {
+ @slon_http_parse_request_as_json(session);
+ }
+ if (String.BeginsWith("application/x-www-form-urlencoded", @slon_http_request_header(session, "content-type"))) {
+ @slon_http_parse_request_as_form_urlencoded(session);
+ }
+ if (String.BeginsWith("multipart/form-data", @slon_http_request_header(session, "content-type"))) {
+ @slon_http_parse_request_as_multipart_form_data(session);
+ }
+ // Workaround for IceCubesApp: https://github.com/Dimillian/IceCubesApp/issues/2235
+ if (!StrLen(@slon_http_request_header(session, "content-type")) && @slon_http_request_has_query_string(session)) {
+ @slon_http_parse_query_string(session);
+ }
+
+ SLON_DEBUG_PRINT_REQUEST_JSON
+
+ /* clang-format off */
+
+ #include "Endpoints/Post/ActivityPub";
+ #include "Endpoints/Post/Apps";
+ #include "Endpoints/Post/OAuth";
+ #include "Endpoints/Post/Statuses";
+
+ /* clang-format on */
+
+ @slon_http_set_status_code(session, 404);
+}
+
+U0 @slon_http_handle_request(SlonHttpSession* session)
+{
+
+ // .purge_expired_idempotency_keys()
+ @slon_http_authorize(session);
+ switch (@slon_http_request_verb(session)) {
+ case SLON_HTTP_VERB_DELETE:
+ @slon_http_handle_delete_request(session);
+ break;
+ case SLON_HTTP_VERB_GET:
+ @slon_http_handle_get_request(session);
+ break;
+ case SLON_HTTP_VERB_OPTIONS:
+ @slon_http_set_status_code(session, 200);
+ break;
+ case SLON_HTTP_VERB_PATCH:
+ @slon_http_handle_patch_request(session);
+ break;
+ case SLON_HTTP_VERB_POST:
+ @slon_http_handle_post_request(session);
+ break;
+ default:
+ @slon_http_set_status_code(session, 405);
+ }
+}
+
+U0 @slon_http_task(TcpSocket* s)
+{
+ // Bail if we can't acquire socket for some reason
+ if (!@tcp_socket_accept(s))
+ return;
+
+ // Init session
+ SlonHttpSession* session = @slon_http_init_session(s);
+
+ // Parse headers if they are available
+ while (!@slon_http_request_headers_have_been_parsed(session)) {
+ @slon_http_receive(session);
+
+ // Handle malformed requests (anything less than "GET / HTTP/1.0\r\n\r\n" is probably a bad request)
+ if (session->request->buffer->size < 18) {
+ @slon_http_set_status_code(session, 400);
+ goto slon_http_task_send_response;
+ }
+
+ @slon_http_try_parse_request_headers(session);
+ }
+
+ //@slon_http_debug_print_request(session, FALSE);
+
+ // If we have a content-length header, consume until we receive all the data, then set request->data pointer and size
+ if (StrLen(@slon_http_request_header(session, "content-length"))) {
+ I64 content_length = Str2I64(@slon_http_request_header(session, "content-length"));
+ while (session->request->buffer->data + session->request->buffer->size - session->request->data < content_length)
+ @slon_http_receive(session);
+ }
+
+ @slon_http_handle_request(session);
+
+slon_http_task_send_response:
+ //@slon_http_debug_print_response(session, FALSE);
+
+ @slon_http_send_response(session);
+
+ @slon_http_free_session(session);
+
+ AdamLog("\n");
+ s->close();
+}
+
+Adam("U0 @spawn_slon_http_task(TcpSocket *s){Spawn(%d, s, \"SlonHttpTask\");};\n", &@slon_http_task);
+@tcp_socket_bind(80, "@spawn_slon_http_task");
diff --git a/Slon/MakeSlon.HC b/Slon/MakeSlon.HC
new file mode 100644
index 0000000..b100ab4
--- /dev/null
+++ b/Slon/MakeSlon.HC
@@ -0,0 +1,42 @@
+/* clang-format off */
+
+DocMax(Fs);
+WinMax(Fs);
+
+#include "Modules/Log";
+#include "Modules/Db";
+#include "Modules/Http";
+#include "Modules/Api";
+
+#include "Api/V1/Accounts";
+#include "Api/V1/Apps";
+#include "Api/V1/Blocks";
+#include "Api/V1/Bookmarks";
+#include "Api/V1/Conversations";
+#include "Api/V1/CustomEmojis";
+#include "Api/V1/Favourites";
+#include "Api/V1/Filters";
+#include "Api/V1/FollowRequests";
+#include "Api/V1/FollowedTags";
+#include "Api/V1/Notifications";
+#include "Api/V1/Statuses";
+#include "Api/V1/Timelines";
+
+#include "Api/V2/Filters";
+#include "Api/V2/Instance";
+#include "Api/V2/Suggestions";
+
+#include "Modules/ActivityPub";
+#include "Modules/Meta";
+#include "Modules/OAuth";
+#include "Modules/Web";
+#include "Modules/Webfinger";
+
+#include "Http/Server";
+#include "Http/LocalServer";
+#include "Http/AdminServer";
+
+@slon_log(0, "slon is up and running");
+@slon_log(0, "instance on port 80, fs on port 8000, admin on port 9000");
+
+WinToTop(adam_task);
diff --git a/Slon/Modules/ActivityPub.HC b/Slon/Modules/ActivityPub.HC
new file mode 100644
index 0000000..0a00f88
--- /dev/null
+++ b/Slon/Modules/ActivityPub.HC
@@ -0,0 +1,795 @@
+U8* @slon_activitypub_strip_double_quotes(U8* str)
+{
+ while (str[0] == '"')
+ str++;
+ while (str[StrLen(str) - 1] == '"')
+ str[StrLen(str) - 1] = NULL;
+ return str;
+}
+
+Bool @slon_activitypub_http_signature_is_valid(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn scratch_buffer;
+
+ // 1. Check that we have a signature and digest
+ if (!StrLen(@slon_http_request_header(session, "signature")) || !StrLen(@slon_http_request_header(session, "digest"))) {
+ AdamLog("[verify_signature] no signature or digest header present\n");
+ return FALSE;
+ }
+
+ // 2. Check that digest 1) is SHA-256 and 2) matches content
+ U8* request_digest = @slon_http_request_header(session, "digest");
+ if (!(String.BeginsWith("SHA-256", request_digest) || String.BeginsWith("sha-256", request_digest))) {
+ AdamLog("[verify_signature] digest is not SHA-256\n");
+ return FALSE;
+ }
+ request_digest = StrFind("=", request_digest) + 1;
+ I64 content_length = Str2I64(@slon_http_request_header(session, "content-length"));
+ if (!content_length) {
+ AdamLog("[verify_signature] content-length is 0\n");
+ return FALSE;
+ }
+
+ U8 content_hash[512];
+ calc_sha_256(content_hash, session->request->data, content_length);
+ U8* computed_digest = @base64_encode(content_hash, 32);
+
+ if (StrICmp(computed_digest, request_digest)) {
+ AdamLog("[verify_signature] digest header and computed digest do not match\n");
+ Free(computed_digest);
+ return FALSE;
+ } else {
+ Free(computed_digest);
+ }
+
+ // Parse values from Signature header
+ U8* signature_header = @slon_http_request_header(session, "signature");
+ I64 signature_fragment_count = 0;
+ U8** signature_fragments = String.Split(signature_header, ',', &signature_fragment_count);
+
+ U8* keyId = NULL;
+ U8* algorithm = NULL;
+ U8* headers = NULL;
+ U8* signature = NULL;
+
+ I64 i;
+ for (i = 0; i < signature_fragment_count; i++) {
+ if (String.BeginsWith("keyId=", signature_fragments[i])) {
+ keyId = signature_fragments[i] + 6;
+ keyId = @slon_activitypub_strip_double_quotes(keyId);
+ }
+ if (String.BeginsWith("algorithm=", signature_fragments[i])) {
+ algorithm = signature_fragments[i] + 10;
+ algorithm = @slon_activitypub_strip_double_quotes(algorithm);
+ }
+ if (String.BeginsWith("headers=", signature_fragments[i])) {
+ headers = signature_fragments[i] + 8;
+ headers = @slon_activitypub_strip_double_quotes(headers);
+ }
+ if (String.BeginsWith("signature=", signature_fragments[i])) {
+ signature = signature_fragments[i] + 10;
+ signature = @slon_activitypub_strip_double_quotes(signature);
+ }
+ }
+
+ // 3. Confirm actor matches keyId
+ if (!request_json->@("actor")) {
+ AdamLog("[verify_signature] actor is not present in request\n");
+ return FALSE;
+ }
+ if (!String.BeginsWith(request_json->@("actor"), keyId)) {
+ AdamLog("[verify_signature] actor does not match keyId\n");
+ return FALSE;
+ }
+
+ // Check if public key is cached for keyId, if not, fetch it
+ if (!db->o("public_keys")->@(keyId)) {
+
+ @slon_log(LOG_HTTPD, "Actor's public key is not cached, attempting to fetch");
+
+ HttpUrl* url = @http_parse_url(request_json->@("actor"));
+ if (!url) {
+ @slon_log(LOG_HTTPD, "Could not fetch actor's public key, malformed url or unspecified error");
+ return FALSE;
+ }
+
+ U8* fetch_buffer = CAlloc(HTTP_FETCH_BUFFER_SIZE, adam_task);
+ JsonObject* http_headers = Json.CreateObject();
+ http_headers->set("accept", "application/json", JSON_STRING);
+ @http_response* resp = Http.Get(url, fetch_buffer, NULL, http_headers);
+
+ if (!resp) {
+ @slon_log(LOG_HTTPD, "Could not fetch actor's public key, invalid response from remote server");
+ Free(fetch_buffer);
+ return FALSE;
+ }
+
+ while (resp->state != HTTP_STATE_DONE) {
+ Sleep(1);
+ }
+
+ if (!resp->body.length) {
+ @slon_log(LOG_HTTPD, "Could not fetch actor's public key, empty response from remote server");
+ Free(fetch_buffer);
+ return FALSE;
+ }
+
+ Free(fetch_buffer);
+
+ JsonObject* user_object = Json.Parse(resp->body.data);
+ if (!user_object) {
+ @slon_log(LOG_HTTPD, "Could not fetch actor's public key, user object not present in response from remote server");
+ return FALSE;
+ }
+
+ JsonObject* pubkey_object = user_object->@("publicKey");
+ if (!pubkey_object) {
+ @slon_log(LOG_HTTPD, "Could not fetch actor's public key, publicKey object not present in user object");
+ return FALSE;
+ }
+
+ if (!pubkey_object->@("id")) {
+ @slon_log(LOG_HTTPD, "Could not fetch actor's public key, id not present in publicKey object");
+ return FALSE;
+ }
+ if (!pubkey_object->@("owner")) {
+ @slon_log(LOG_HTTPD, "Could not fetch actor's public key, owner not present in publicKey object");
+ return FALSE;
+ }
+ if (!pubkey_object->@("publicKeyPem")) {
+ @slon_log(LOG_HTTPD, "Could not fetch actor's public key, publicKeyPem not present in publicKey object");
+ return FALSE;
+ }
+
+ if (StrICmp(pubkey_object->@("id"), keyId)) {
+ @slon_log(LOG_HTTPD, "Could not fetch actor's public key, keyId does not match id present in publicKey object");
+ return FALSE;
+ }
+ if (StrICmp(pubkey_object->@("owner"), request_json->@("actor"))) {
+ @slon_log(LOG_HTTPD, "Could not fetch actor's public key, actor does not match owner present in publicKey object");
+ return FALSE;
+ }
+
+ U8* pem_string = pubkey_object->@("publicKeyPem");
+
+ // Convert Base64 PEM to single line
+ U8* pem_single_line = @slon_calloc(session, StrLen(pem_string));
+ I64 pem_lines_count = 0;
+ U8** pem_lines = String.Split(pem_string, '\n', &pem_lines_count);
+ i = 0;
+ while (i < pem_lines_count) {
+ if (pem_lines[i] && StrLen(pem_lines[i]) > 0) {
+ if (!StrFind("KEY", pem_lines[i])) {
+ StrCpy(pem_single_line + StrLen(pem_single_line), pem_lines[i]);
+ }
+ }
+ ++i;
+ }
+
+ // Decode PEM to DER
+ I64 der_buf_length = 0;
+ U8* der_buf = @base64_decode(pem_single_line, &der_buf_length);
+
+ // Cache the public key
+ JsonObject* cached_key = Json.CreateObject();
+ cached_key->set("key", der_buf, JSON_NUMBER);
+ cached_key->set("length", der_buf_length, JSON_NUMBER);
+ db->o("public_keys")->set(keyId, cached_key, JSON_OBJECT);
+
+ @slon_free(session, pem_single_line);
+
+ Json.Delete(user_object);
+ Json.Delete(http_headers);
+ }
+
+ // Calculate our signature string allocation
+ I64 sig_string_alloc_length = 0;
+
+ I64 headers_split_count = 0;
+ U8** headers_split = String.Split(headers, ' ', &headers_split_count);
+ i = 0;
+ while (i < headers_split_count) {
+ sig_string_alloc_length += StrLen(@slon_http_request_header(session, headers_split[i]));
+ ++i;
+ }
+ sig_string_alloc_length += StrLen(@slon_http_request_verb(session));
+ sig_string_alloc_length += StrLen(@slon_http_request_path(session));
+ sig_string_alloc_length *= 2;
+
+ // Construct our signature string
+ U8* sig_string = @slon_calloc(session, sig_string_alloc_length);
+ i = 0;
+ while (i < headers_split_count) {
+ if (StrLen(headers_split[i]) && headers_split[i][0] >= 'A' && headers_split[i][0] <= 'Z') {
+ headers_split[i][0] += 'a' - headers_split[i][0];
+ }
+ if (!StrCmp("(request-target)", headers_split[i])) {
+ String.Append(sig_string, "(request-target): %s %s", "post", @slon_http_request_path(session));
+ } else {
+ String.Append(sig_string, "%s: %s", headers_split[i], @slon_http_request_header(session, headers_split[i]));
+ }
+ ++i;
+ if (i < headers_split_count) {
+ String.Append(sig_string, "\n");
+ }
+ }
+
+ // Base64 decode request's signature
+ I64 verify_sig_buf_length = 0;
+ U8* verify_sig_buf = @base64_decode(signature, &verify_sig_buf_length);
+
+ // Hash our constructed signature string
+ U8 sig_string_hash[32];
+ calc_sha_256(sig_string_hash, sig_string, StrLen(sig_string));
+
+ // Import RSA key
+ U64 rsa_key = CAlloc(sizeof(U64) * 32, adam_task);
+ I64 res = @rsa_import(db->o("public_keys")->o(keyId)->@("key"), db->o("public_keys")->o(keyId)->@("length"), rsa_key);
+ if (res != 0) { // CRYPT_OK = 0
+ @slon_log(LOG_HTTPD, "Received error from @rsa_import: %d", res);
+ return FALSE;
+ }
+
+ // Verify signature
+ I32 stat = 0;
+ res = @rsa_verify_signature(verify_sig_buf, verify_sig_buf_length, sig_string_hash, 32, &stat, rsa_key);
+ if (res != 0) { // CRYPT_OK = 0
+ @slon_log(LOG_HTTPD, "Received error from @rsa_verify_signature: %d", res);
+ return FALSE;
+ }
+
+ Free(rsa_key);
+ Free(verify_sig_buf);
+ @slon_free(session, sig_string);
+
+ return stat;
+}
+
+U0 @slon_activitypub_users_get(SlonHttpSession* session)
+{
+ U8* path = @slon_strnew(session, @slon_http_request_path(session));
+ I64 path_segments_count = 0;
+ U8** path_segments = String.Split(path, '/', &path_segments_count);
+ if (path_segments_count == 3) {
+ JsonObject* actor = db->o("actors")->@(path_segments[1]);
+ if (actor) {
+ @slon_http_send_ap_json(session, actor);
+ } else {
+ @slon_http_set_status_code(session, 404);
+ }
+ } else {
+ @slon_http_set_status_code(session, 400);
+ }
+slon_activitypub_users_get_return:
+ @slon_free(session, path);
+}
+
+U0 @slon_activitypub_async_accept_request(JsonObject* o)
+{
+ JsonObject* request = o->o("request");
+ if (!StrICmp("accept", request->@("type")) || !StrICmp("reject", request->@("type"))) {
+ return;
+ }
+
+ Sleep(2000);
+
+ U8 scratch_buffer[2048];
+
+ U8* this_actor = db->o("actors")->o(o->@("user"))->@("id");
+
+ StrPrint(scratch_buffer, "%s/accept/%d", this_actor, Now);
+ JsonObject* accept_object = Json.CreateObject();
+ accept_object->set("@context", request->@("@context"), JSON_STRING);
+ accept_object->set("id", scratch_buffer, JSON_STRING);
+ accept_object->set("type", "Accept", JSON_STRING);
+ accept_object->set("actor", this_actor, JSON_STRING);
+ accept_object->set("object", request, JSON_OBJECT);
+
+ U8* accept_object_s = Json.Stringify(accept_object);
+
+ U8 content_hash[512];
+ calc_sha_256(content_hash, accept_object_s, StrLen(accept_object_s));
+ U8* computed_digest = @base64_encode(content_hash, 32);
+
+ JsonObject* http_headers = Json.CreateObject();
+
+ StrPrint(scratch_buffer, "%s/inbox", request->@("actor"));
+ HttpUrl* url = @http_parse_url(scratch_buffer);
+
+ CDateStruct ds;
+ Date2Struct(&ds, Now + 1043910000);
+ StrPrint(scratch_buffer, "%03tZ, %02d %03tZ %04d %02d:%02d:%02d GMT", ds.day_of_week, "ST_DAYS_OF_WEEK", ds.day_of_mon, ds.mon - 1, "ST_MONTHS",
+ ds.year, ds.hour, ds.min, ds.sec);
+ http_headers->set("Date", scratch_buffer, JSON_STRING);
+
+ StrPrint(scratch_buffer, "SHA-256=%s", computed_digest);
+ http_headers->set("Digest", scratch_buffer, JSON_STRING);
+
+ http_headers->set("Content-Type", "application/activity+json", JSON_STRING);
+
+ StrPrint(scratch_buffer, "");
+ String.Append(scratch_buffer, "(request-target): post %s\n", url->path);
+ String.Append(scratch_buffer, "host: %s\n", url->host);
+ String.Append(scratch_buffer, "date: %s\n", http_headers->@("Date"));
+ String.Append(scratch_buffer, "digest: %s\n", http_headers->@("Digest"));
+ String.Append(scratch_buffer, "content-type: %s", http_headers->@("Content-Type"));
+
+ AdamLog("headers_to_sign:\n```%s```\n", scratch_buffer);
+
+ calc_sha_256(content_hash, scratch_buffer, StrLen(scratch_buffer));
+
+ U8* user = StrFind("/users/", this_actor) + 7;
+ JsonObject* private_key_binary = db->o("private_keys_binary")->o(user);
+ if (!private_key_binary) {
+ I64 private_key_binary_size = 0;
+ private_key_binary = Json.CreateObject();
+ private_key_binary->set("data", @base64_decode(db->o("private_keys")->@(user), &private_key_binary_size), JSON_OBJECT);
+ private_key_binary->set("size", private_key_binary_size, JSON_NUMBER);
+ db->o("private_keys_binary")->set(user, private_key_binary, JSON_OBJECT);
+ }
+
+ I64 res;
+
+ // Import RSA key
+ U64 rsa_key = CAlloc(sizeof(U64) * 32, adam_task);
+ res = @rsa_import(private_key_binary->@("data"), private_key_binary->@("size"), rsa_key);
+ AdamLog("@rsa_import: res: %d\n", res);
+
+ U8 sig[256];
+ U64 siglen = 256;
+ res = @rsa_create_signature(sig, &siglen, content_hash, 32, rsa_key);
+ AdamLog("@rsa_create_signature: res: %d\n", res);
+ U8* computed_sig = @base64_encode(sig, 256);
+
+ StrCpy(scratch_buffer, "");
+ String.Append(scratch_buffer, "keyId=\"%s#main-key\",", this_actor);
+ String.Append(scratch_buffer, "algorithm=\"rsa-sha256\",");
+ String.Append(scratch_buffer, "headers=\"(request-target) host date digest content-type\",");
+ String.Append(scratch_buffer, "signature=\"%s\"", computed_sig);
+ http_headers->set("Signature", scratch_buffer, JSON_STRING);
+
+ U8* fetch_buffer = CAlloc(HTTP_FETCH_BUFFER_SIZE, adam_task);
+ @http_response* resp = Http.Post(url, fetch_buffer, accept_object_s, http_headers);
+
+ if (!resp) {
+ @slon_log(LOG_HTTPD, "Could not POST Accept, invalid response from remote server");
+ Free(fetch_buffer);
+ return;
+ }
+
+ while (resp->state != HTTP_STATE_DONE) {
+ Sleep(1);
+ }
+
+ AdamLog("code: %d\n", resp->status.code);
+
+ Free(fetch_buffer);
+}
+
+U0 @slon_activitypub_follow_request()
+{
+
+ U8 scratch_buffer[2048];
+
+ U8* this_actor = "https://error.checksum.fail/users/alec";
+
+ StrPrint(scratch_buffer, "%s/follow/%d", this_actor, Now);
+ JsonObject* follow_object = Json.CreateObject();
+ follow_object->set("@context", "https://www.w3.org/ns/activitystreams", JSON_STRING);
+ follow_object->set("id", scratch_buffer, JSON_STRING);
+ follow_object->set("type", "Follow", JSON_STRING);
+ follow_object->set("actor", this_actor, JSON_STRING);
+ follow_object->set("object", "https://techhub.social/users/ryeucrvtexw3", JSON_STRING);
+
+ U8* follow_object_s = Json.Stringify(follow_object);
+
+ U8 content_hash[512];
+ calc_sha_256(content_hash, follow_object_s, StrLen(follow_object_s));
+ U8* computed_digest = @base64_encode(content_hash, 32);
+
+ JsonObject* http_headers = Json.CreateObject();
+
+ StrPrint(scratch_buffer, "%s/inbox", "https://techhub.social/users/ryeucrvtexw3");
+ HttpUrl* url = @http_parse_url(scratch_buffer);
+
+ CDateStruct ds;
+ Date2Struct(&ds, Now + 1043910000);
+ StrPrint(scratch_buffer, "%03tZ, %02d %03tZ %04d %02d:%02d:%02d GMT", ds.day_of_week, "ST_DAYS_OF_WEEK", ds.day_of_mon, ds.mon - 1, "ST_MONTHS",
+ ds.year, ds.hour, ds.min, ds.sec);
+ http_headers->set("Date", scratch_buffer, JSON_STRING);
+
+ StrPrint(scratch_buffer, "SHA-256=%s", computed_digest);
+ http_headers->set("Digest", scratch_buffer, JSON_STRING);
+
+ http_headers->set("Content-Type", "application/activity+json", JSON_STRING);
+
+ StrPrint(scratch_buffer, "");
+ String.Append(scratch_buffer, "(request-target): post %s\n", url->path);
+ String.Append(scratch_buffer, "host: %s\n", url->host);
+ String.Append(scratch_buffer, "date: %s\n", http_headers->@("Date"));
+ String.Append(scratch_buffer, "digest: %s\n", http_headers->@("Digest"));
+ String.Append(scratch_buffer, "content-type: %s", http_headers->@("Content-Type"));
+
+ AdamLog("headers_to_sign:\n```%s```\n", scratch_buffer);
+
+ calc_sha_256(content_hash, scratch_buffer, StrLen(scratch_buffer));
+
+ U8* user = StrFind("/users/", this_actor) + 7;
+ JsonObject* private_key_binary = db->o("private_keys_binary")->o(user);
+ if (!private_key_binary) {
+ I64 private_key_binary_size = 0;
+ private_key_binary = Json.CreateObject();
+ private_key_binary->set("data", @base64_decode(db->o("private_keys")->@(user), &private_key_binary_size), JSON_OBJECT);
+ private_key_binary->set("size", private_key_binary_size, JSON_NUMBER);
+ db->o("private_keys_binary")->set(user, private_key_binary, JSON_OBJECT);
+ }
+
+ I64 res;
+
+ // Import RSA key
+ U64 rsa_key = CAlloc(sizeof(U64) * 32, adam_task);
+ res = @rsa_import(private_key_binary->@("data"), private_key_binary->@("size"), rsa_key);
+ AdamLog("@rsa_import: res: %d\n", res);
+
+ U8 sig[256];
+ U64 siglen = 256;
+ res = @rsa_create_signature(sig, &siglen, content_hash, 32, rsa_key);
+ AdamLog("@rsa_create_signature: res: %d\n", res);
+ U8* computed_sig = @base64_encode(sig, 256);
+
+ StrCpy(scratch_buffer, "");
+ String.Append(scratch_buffer, "keyId=\"%s#main-key\",", this_actor);
+ String.Append(scratch_buffer, "algorithm=\"rsa-sha256\",");
+ String.Append(scratch_buffer, "headers=\"(request-target) host date digest content-type\",");
+ String.Append(scratch_buffer, "signature=\"%s\"", computed_sig);
+ http_headers->set("Signature", scratch_buffer, JSON_STRING);
+
+ U8* fetch_buffer = CAlloc(HTTP_FETCH_BUFFER_SIZE, adam_task);
+ @http_response* resp = Http.Post(url, fetch_buffer, follow_object_s, http_headers);
+
+ if (!resp) {
+ @slon_log(LOG_HTTPD, "Could not POST Accept, invalid response from remote server");
+ Free(fetch_buffer);
+ return;
+ }
+
+ while (resp->state != HTTP_STATE_DONE) {
+ Sleep(1);
+ }
+
+ AdamLog("code: %d\n", resp->status.code);
+
+ Free(fetch_buffer);
+}
+
+U0 @slon_activitypub_async_create_status(JsonObject* status)
+{
+ Sleep(2000);
+ U8 scratch_buffer[2048];
+
+ U8* dest = "https://techhub.social/users/ryeucrvtexw3/inbox";
+ U8* this_actor = StrNew(status->@("uri"), adam_task);
+ StrFind("/statuses/", this_actor)[0] = NULL;
+
+ JsonObject* create_object = Json.CreateObject();
+
+ create_object->set("@context", "https://www.w3.org/ns/activitystreams", JSON_STRING);
+ StrPrint(scratch_buffer, "%s/activity", status->@("uri"));
+ create_object->set("id", scratch_buffer, JSON_STRING);
+ create_object->set("type", "Create", JSON_STRING);
+ create_object->set("actor", this_actor, JSON_STRING);
+ create_object->set("published", status->@("created_at"), JSON_STRING);
+ create_object->set("to", Json.Parse("[\"https://www.w3.org/ns/activitystreams#Public\"]"), JSON_ARRAY);
+ JsonArray* cc = Json.CreateArray();
+ StrPrint(scratch_buffer, "%s/followers", this_actor);
+ cc->append(Json.CreateItem(scratch_buffer, JSON_STRING));
+ create_object->set("cc", cc, JSON_ARRAY);
+
+ JsonObject* note_object = Json.CreateObject();
+ note_object->set("id", status->@("uri"), JSON_STRING);
+ note_object->set("type", "Note", JSON_STRING);
+ note_object->set("summary", NULL, JSON_NULL);
+ note_object->set("inReplyTo", NULL, JSON_NULL);
+ note_object->set("published", status->@("created_at"), JSON_STRING);
+ note_object->set("attributedTo", this_actor, JSON_STRING);
+ note_object->set("to", Json.Parse("[\"https://www.w3.org/ns/activitystreams#Public\"]"), JSON_ARRAY);
+ note_object->set("cc", cc, JSON_ARRAY);
+ note_object->set("sensitive", status->@("sensitive"), JSON_BOOLEAN);
+ note_object->set("atomUri", status->@("uri"), JSON_STRING);
+ note_object->set("inReplyToAtomUri", NULL, JSON_NULL);
+ note_object->set("content", status->@("content"), JSON_STRING);
+ JsonObject* content_map = Json.CreateObject();
+ content_map->set("en", status->@("content"), JSON_STRING);
+ note_object->set("contentMap", content_map, JSON_OBJECT);
+ note_object->set("attachment", SLON_EMPTY_JSON_ARRAY, JSON_ARRAY);
+ note_object->set("tag", SLON_EMPTY_JSON_ARRAY, JSON_ARRAY);
+ note_object->set("replies", SLON_EMPTY_JSON_OBJECT, JSON_OBJECT);
+ note_object->set("likes", SLON_EMPTY_JSON_OBJECT, JSON_OBJECT);
+ note_object->set("shares", SLON_EMPTY_JSON_OBJECT, JSON_OBJECT);
+
+ create_object->set("object", note_object, JSON_OBJECT);
+ U8* create_object_s = Json.Stringify(create_object);
+
+ U8 content_hash[512];
+ calc_sha_256(content_hash, create_object_s, StrLen(create_object_s));
+ U8* computed_digest = @base64_encode(content_hash, 32);
+
+ JsonObject* http_headers = Json.CreateObject();
+
+ HttpUrl* url = @http_parse_url(dest);
+
+ CDateStruct ds;
+ Date2Struct(&ds, Now + 1043910000);
+ StrPrint(scratch_buffer, "%03tZ, %02d %03tZ %04d %02d:%02d:%02d GMT", ds.day_of_week, "ST_DAYS_OF_WEEK", ds.day_of_mon, ds.mon - 1, "ST_MONTHS",
+ ds.year, ds.hour, ds.min, ds.sec);
+ http_headers->set("Date", scratch_buffer, JSON_STRING);
+
+ StrPrint(scratch_buffer, "SHA-256=%s", computed_digest);
+ http_headers->set("Digest", scratch_buffer, JSON_STRING);
+
+ http_headers->set("Content-Type", "application/activity+json", JSON_STRING);
+
+ StrPrint(scratch_buffer, "");
+ String.Append(scratch_buffer, "(request-target): post %s\n", url->path);
+ String.Append(scratch_buffer, "host: %s\n", url->host);
+ String.Append(scratch_buffer, "date: %s\n", http_headers->@("Date"));
+ String.Append(scratch_buffer, "digest: %s\n", http_headers->@("Digest"));
+ String.Append(scratch_buffer, "content-type: %s", http_headers->@("Content-Type"));
+
+ AdamLog("headers_to_sign:\n```%s```\n", scratch_buffer);
+
+ calc_sha_256(content_hash, scratch_buffer, StrLen(scratch_buffer));
+
+ U8* user = StrFind("/users/", this_actor) + 7;
+ JsonObject* private_key_binary = db->o("private_keys_binary")->o(user);
+ if (!private_key_binary) {
+ I64 private_key_binary_size = 0;
+ private_key_binary = Json.CreateObject();
+ private_key_binary->set("data", @base64_decode(db->o("private_keys")->@(user), &private_key_binary_size), JSON_OBJECT);
+ private_key_binary->set("size", private_key_binary_size, JSON_NUMBER);
+ db->o("private_keys_binary")->set(user, private_key_binary, JSON_OBJECT);
+ }
+
+ I64 res;
+
+ // Import RSA key
+ U64 rsa_key = CAlloc(sizeof(U64) * 32, adam_task);
+ res = @rsa_import(private_key_binary->@("data"), private_key_binary->@("size"), rsa_key);
+ AdamLog("@rsa_import: res: %d\n", res);
+
+ U8 sig[256];
+ U64 siglen = 256;
+ res = @rsa_create_signature(sig, &siglen, content_hash, 32, rsa_key);
+ AdamLog("@rsa_create_signature: res: %d\n", res);
+ U8* computed_sig = @base64_encode(sig, 256);
+
+ StrCpy(scratch_buffer, "");
+ String.Append(scratch_buffer, "keyId=\"%s#main-key\",", this_actor);
+ String.Append(scratch_buffer, "algorithm=\"rsa-sha256\",");
+ String.Append(scratch_buffer, "headers=\"(request-target) host date digest content-type\",");
+ String.Append(scratch_buffer, "signature=\"%s\"", computed_sig);
+ http_headers->set("Signature", scratch_buffer, JSON_STRING);
+
+ U8* fetch_buffer = CAlloc(HTTP_FETCH_BUFFER_SIZE, adam_task);
+ @http_response* resp = Http.Post(url, fetch_buffer, create_object_s, http_headers);
+
+ if (!resp) {
+ @slon_log(LOG_HTTPD, "Could not POST Accept, invalid response from remote server");
+ Free(fetch_buffer);
+ return;
+ }
+
+ while (resp->state != HTTP_STATE_DONE) {
+ Sleep(1);
+ }
+
+ AdamLog("code: %d\n", resp->status.code);
+
+ Free(fetch_buffer);
+}
+
+U0 @slon_activitypub_async_delete_status(JsonObject* status)
+{
+ Sleep(2000);
+ U8 scratch_buffer[2048];
+
+ U8* dest = "https://techhub.social/users/ryeucrvtexw3/inbox";
+ U8* this_actor = StrNew(status->@("uri"), adam_task);
+ StrFind("/statuses/", this_actor)[0] = NULL;
+
+ JsonObject* delete_object = Json.CreateObject();
+
+ delete_object->set("@context", "https://www.w3.org/ns/activitystreams", JSON_STRING);
+ StrPrint(scratch_buffer, "%s#delete", status->@("uri"));
+ delete_object->set("id", scratch_buffer, JSON_STRING);
+ delete_object->set("type", "Delete", JSON_STRING);
+ delete_object->set("actor", this_actor, JSON_STRING);
+ delete_object->set("to", Json.Parse("[\"https://www.w3.org/ns/activitystreams#Public\"]"), JSON_ARRAY);
+
+ JsonObject* ts_object = Json.CreateObject();
+ ts_object->set("id", status->@("uri"), JSON_STRING);
+ ts_object->set("type", "Tombstone", JSON_STRING);
+ ts_object->set("atomUri", status->@("uri"), JSON_STRING);
+
+ delete_object->set("object", ts_object, JSON_OBJECT);
+ U8* delete_object_s = Json.Stringify(delete_object);
+
+ U8 content_hash[512];
+ calc_sha_256(content_hash, delete_object_s, StrLen(delete_object_s));
+ U8* computed_digest = @base64_encode(content_hash, 32);
+
+ JsonObject* http_headers = Json.CreateObject();
+
+ HttpUrl* url = @http_parse_url(dest);
+
+ CDateStruct ds;
+ Date2Struct(&ds, Now + 1043910000);
+ StrPrint(scratch_buffer, "%03tZ, %02d %03tZ %04d %02d:%02d:%02d GMT", ds.day_of_week, "ST_DAYS_OF_WEEK", ds.day_of_mon, ds.mon - 1, "ST_MONTHS",
+ ds.year, ds.hour, ds.min, ds.sec);
+ http_headers->set("Date", scratch_buffer, JSON_STRING);
+
+ StrPrint(scratch_buffer, "SHA-256=%s", computed_digest);
+ http_headers->set("Digest", scratch_buffer, JSON_STRING);
+
+ http_headers->set("Content-Type", "application/activity+json", JSON_STRING);
+
+ StrPrint(scratch_buffer, "");
+ String.Append(scratch_buffer, "(request-target): post %s\n", url->path);
+ String.Append(scratch_buffer, "host: %s\n", url->host);
+ String.Append(scratch_buffer, "date: %s\n", http_headers->@("Date"));
+ String.Append(scratch_buffer, "digest: %s\n", http_headers->@("Digest"));
+ String.Append(scratch_buffer, "content-type: %s", http_headers->@("Content-Type"));
+
+ AdamLog("headers_to_sign:\n```%s```\n", scratch_buffer);
+
+ calc_sha_256(content_hash, scratch_buffer, StrLen(scratch_buffer));
+
+ U8* user = StrFind("/users/", this_actor) + 7;
+ JsonObject* private_key_binary = db->o("private_keys_binary")->o(user);
+ if (!private_key_binary) {
+ I64 private_key_binary_size = 0;
+ private_key_binary = Json.CreateObject();
+ private_key_binary->set("data", @base64_decode(db->o("private_keys")->@(user), &private_key_binary_size), JSON_OBJECT);
+ private_key_binary->set("size", private_key_binary_size, JSON_NUMBER);
+ db->o("private_keys_binary")->set(user, private_key_binary, JSON_OBJECT);
+ }
+
+ I64 res;
+
+ // Import RSA key
+ U64 rsa_key = CAlloc(sizeof(U64) * 32, adam_task);
+ res = @rsa_import(private_key_binary->@("data"), private_key_binary->@("size"), rsa_key);
+ AdamLog("@rsa_import: res: %d\n", res);
+
+ U8 sig[256];
+ U64 siglen = 256;
+ res = @rsa_create_signature(sig, &siglen, content_hash, 32, rsa_key);
+ AdamLog("@rsa_create_signature: res: %d\n", res);
+ U8* computed_sig = @base64_encode(sig, 256);
+
+ StrCpy(scratch_buffer, "");
+ String.Append(scratch_buffer, "keyId=\"%s#main-key\",", this_actor);
+ String.Append(scratch_buffer, "algorithm=\"rsa-sha256\",");
+ String.Append(scratch_buffer, "headers=\"(request-target) host date digest content-type\",");
+ String.Append(scratch_buffer, "signature=\"%s\"", computed_sig);
+ http_headers->set("Signature", scratch_buffer, JSON_STRING);
+
+ U8* fetch_buffer = CAlloc(HTTP_FETCH_BUFFER_SIZE, adam_task);
+ @http_response* resp = Http.Post(url, fetch_buffer, delete_object_s, http_headers);
+
+ if (!resp) {
+ @slon_log(LOG_HTTPD, "Could not POST Accept, invalid response from remote server");
+ Free(fetch_buffer);
+ return;
+ }
+
+ while (resp->state != HTTP_STATE_DONE) {
+ Sleep(1);
+ }
+
+ AdamLog("code: %d\n", resp->status.code);
+
+ Free(fetch_buffer);
+}
+
+U0 @slon_activitypub_create_status_fedi(JsonObject* status)
+{
+ Spawn(&@slon_activitypub_async_create_status, status, "SlonAsyncCreateTask");
+}
+
+U0 @slon_activitypub_delete_status_fedi(JsonObject* status)
+{
+ Spawn(&@slon_activitypub_async_delete_status, status, "SlonAsyncDeleteTask");
+}
+
+@slon_api_status_create_fedi = &@slon_activitypub_create_status_fedi;
+@slon_api_status_delete_fedi = &@slon_activitypub_delete_status_fedi;
+
+U0 @slon_activitypub_users_inbox(SlonHttpSession* session, U8* user)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn scratch_buffer;
+
+ I64 i;
+ JsonObject* account = @slon_api_account_by_username(user);
+
+ Bool already_following = FALSE;
+ JsonArray* followers = NULL;
+ JsonArray* statuses = NULL;
+
+ JsonObject* status = NULL;
+
+ if (!StrICmp("follow", request_json->@("type"))) {
+ if (!db->o("followers")->@(user)) {
+ db->o("followers")->set(user, Json.CreateArray(), JSON_ARRAY);
+ }
+ followers = db->o("followers")->a(user);
+ for (i = 0; i < followers->length; i++) {
+ if (!StrCmp(request_json->@("actor"), followers->@(i))) {
+ already_following = TRUE;
+ }
+ }
+ if (!already_following) {
+ followers->append(Json.CreateItem(request_json->@("actor"), JSON_STRING));
+ account->set("followers_count", account->@("followers_count") + 1);
+ @slon_db_save_to_disk;
+ }
+ }
+
+ if (!StrICmp("like", request_json->@("type"))) {
+ U8* status_id = StrFind("/", StrFind("/statuses/", request_json->@("object")) + 1) + 1;
+ statuses = db->o("statuses")->a(account->@("id"));
+ for (i = 0; i < statuses->length; i++) {
+ status = statuses->@(i);
+ if (!StrICmp(status_id, status->@("id"))) {
+ status->set("favourites_count", status->@("favourites_count") + 1);
+ break;
+ }
+ }
+ @slon_db_save_statuses_to_disk;
+ }
+
+ JsonObject* o = Json.CreateObject();
+ o->set("user", user, JSON_STRING);
+ o->set("request", Json.Clone(request_json), JSON_OBJECT);
+ Spawn(&@slon_activitypub_async_accept_request, o, "SlonAsyncAcceptTask");
+
+ @slon_http_set_status_code(session, 200);
+ return;
+}
+
+U0 @slon_activitypub_users_post(SlonHttpSession* session)
+{
+ if (!@slon_activitypub_http_signature_is_valid(session)) {
+ @slon_http_set_status_code(session, 401);
+ return;
+ }
+
+ U8* path = @slon_strnew(session, @slon_http_request_path(session));
+ I64 path_segments_count = 0;
+ U8** path_segments = String.Split(path, '/', &path_segments_count);
+
+ if (path_segments_count < 3) {
+ @slon_http_set_status_code(session, 400);
+ goto slon_activitypub_users_post_return;
+ }
+
+ U8* user = path_segments[1];
+ JsonObject* actor = db->o("actors")->@(user);
+ if (!actor) {
+ @slon_http_set_status_code(session, 404);
+ goto slon_activitypub_users_post_return;
+ }
+
+ U8* method = path_segments[2];
+ if (!StrICmp("inbox", method)) {
+ @slon_activitypub_users_inbox(session, user);
+ goto slon_activitypub_users_post_return;
+ }
+
+ @slon_http_set_status_code(session, 404);
+
+slon_activitypub_users_post_return:
+ @slon_free(session, path);
+}
diff --git a/Slon/Modules/Api.HC b/Slon/Modules/Api.HC
new file mode 100644
index 0000000..e5ec8b8
--- /dev/null
+++ b/Slon/Modules/Api.HC
@@ -0,0 +1,86 @@
+#define SLON_API_LOCAL_TIME_OFFSET 3550
+#define SLON_AUTH_ACCOUNT_ID U8* account_id = Json.Get(session->auth, "account_id");
+
+Bool @slon_api_authorized(SlonHttpSession* session)
+{
+ return session->auth > 0;
+}
+
+U8* @slon_api_generate_random_hex_string(SlonHttpSession* session, I64 size)
+{
+ U8* str = @slon_calloc(session, (size + 1) * 2);
+ I64 i;
+ for (i = 0; i < size; i++) {
+ String.Append(str, "%02x", RandU64 & 0xff);
+ }
+ return str;
+}
+
+U8* @slon_api_generate_unique_id(SlonHttpSession* session)
+{
+ U8* unique_id = @slon_calloc(session, 64);
+ U64 id = ((CDate2Unix(Now) + SLON_API_LOCAL_TIME_OFFSET) * 1000) << 16;
+ id += RandU64 & 0xffff;
+ StrPrint(unique_id, "%d", id);
+ return unique_id;
+}
+
+U8* @slon_api_timestamp_from_cdate(SlonHttpSession* session, CDate* date)
+{
+ CDateStruct ds;
+ Date2Struct(&ds, date);
+ U8* timestamp = @slon_calloc(session, 32);
+ StrPrint(timestamp, "%04d-%02d-%02dT%02d:%02d:%02d.000-05:00", ds.year, ds.mon, ds.day_of_mon, ds.hour, ds.min, ds.sec);
+ return timestamp;
+}
+
+Bool @slon_api_boolean_from_string(U8* s)
+{
+ // https://docs.joinmastodon.org/client/intro/#boolean
+ // True-or-false (Booleans)
+ // A boolean value is considered false for the values 0, f, F, false, FALSE, off, OFF; considered to not be provided for empty strings;
+ // and considered to be true for all other values. When using JSON data, use the literals true, false, and null instead.
+ return !(!StrICmp("0", s) || !StrICmp("f", s) || !StrICmp("false", s) || !StrICmp("off", s));
+}
+
+JsonObject* @slon_api_account_by_email(U8* email)
+{
+ if (!email || !StrLen(email))
+ return NULL;
+ JsonArray* accts = db->a("accounts");
+ I64 i;
+ for (i = 0; i < accts->length; i++) {
+ if (!StrICmp(accts->o(i)->@("email"), email)) {
+ return accts->o(i);
+ }
+ }
+ return NULL;
+}
+
+JsonObject* @slon_api_account_by_id(U8* id)
+{
+ if (!id || !StrLen(id))
+ return NULL;
+ JsonArray* accts = db->a("accounts");
+ I64 i;
+ for (i = 0; i < accts->length; i++) {
+ if (!StrICmp(accts->o(i)->@("id"), id)) {
+ return accts->o(i);
+ }
+ }
+ return NULL;
+}
+
+JsonObject* @slon_api_account_by_username(U8* username)
+{
+ if (!username || !StrLen(username))
+ return NULL;
+ JsonArray* accts = db->a("accounts");
+ I64 i;
+ for (i = 0; i < accts->length; i++) {
+ if (!StrICmp(accts->o(i)->@("username"), username)) {
+ return accts->o(i);
+ }
+ }
+ return NULL;
+}
diff --git a/Slon/Modules/Db.HC b/Slon/Modules/Db.HC
new file mode 100644
index 0000000..8d2d0c7
--- /dev/null
+++ b/Slon/Modules/Db.HC
@@ -0,0 +1,252 @@
+#define SLON_DB_PATH "A:/db"
+
+JsonObject* db = Json.CreateObject();
+
+U0 @slon_db_load_accounts_from_disk()
+{
+ U8 scratch_buffer[256];
+ StrPrint(scratch_buffer, "%s/accounts.json", SLON_DB_PATH);
+ db->set("accounts", Json.ParseFile(scratch_buffer), JSON_ARRAY);
+}
+
+U0 @slon_db_load_actors_from_disk()
+{
+ U8 scratch_buffer[256];
+ StrPrint(scratch_buffer, "%s/actors.json", SLON_DB_PATH);
+ db->set("actors", Json.ParseFile(scratch_buffer), JSON_OBJECT);
+}
+
+U0 @slon_db_load_apps_from_disk()
+{
+ U8 scratch_buffer[256];
+ StrPrint(scratch_buffer, "%s/apps.json", SLON_DB_PATH);
+ db->set("apps", Json.ParseFile(scratch_buffer), JSON_OBJECT);
+}
+
+U0 @slon_db_load_instance_from_disk()
+{
+ U8 scratch_buffer[256];
+ StrPrint(scratch_buffer, "%s/instance.json", SLON_DB_PATH);
+ db->set("instance", Json.ParseFile(scratch_buffer), JSON_OBJECT);
+}
+
+U0 @slon_db_load_oauth_from_disk()
+{
+ U8 scratch_buffer[256];
+ StrPrint(scratch_buffer, "%s/oauth.json", SLON_DB_PATH);
+ db->set("oauth", Json.ParseFile(scratch_buffer), JSON_OBJECT);
+}
+
+U0 @slon_db_load_private_keys_from_disk()
+{
+ U8 scratch_buffer[256];
+ StrPrint(scratch_buffer, "%s/private_keys.json", SLON_DB_PATH);
+ db->set("private_keys", Json.ParseFile(scratch_buffer), JSON_OBJECT);
+}
+
+U0 @slon_db_load_followers_from_disk()
+{
+ JsonObject* followers = Json.CreateObject();
+ U8 scratch_buffer[256];
+ StrPrint(scratch_buffer, "%s/followers/*.json", SLON_DB_PATH);
+ CDirEntry* files = FilesFind(scratch_buffer);
+ CDirEntry* de = files;
+ JsonArray* follower_array = NULL;
+ while (de) {
+ follower_array = Json.ParseFile(de->full_name);
+ if (follower_array) {
+ StrFind(".json", de->name)[0] = NULL;
+ followers->set(de->name, follower_array, JSON_ARRAY);
+ }
+ de = de->next;
+ }
+ DirTreeDel(files);
+ db->set("followers", followers, JSON_OBJECT);
+}
+
+U0 @slon_db_load_statuses_from_disk()
+{
+ JsonObject* statuses = Json.CreateObject();
+ U8 scratch_buffer[256];
+ StrPrint(scratch_buffer, "%s/statuses/*.json", SLON_DB_PATH);
+ CDirEntry* files = FilesFind(scratch_buffer);
+ CDirEntry* de = files;
+ JsonArray* status_array = NULL;
+ while (de) {
+ status_array = Json.ParseFile(de->full_name);
+ if (status_array) {
+ StrFind(".json", de->name)[0] = NULL;
+ statuses->set(de->name, status_array, JSON_ARRAY);
+ }
+ de = de->next;
+ }
+ DirTreeDel(files);
+ db->set("statuses", statuses, JSON_OBJECT);
+}
+
+U0 @slon_db_save_accounts_to_disk()
+{
+ U8 scratch_buffer[256];
+ StrPrint(scratch_buffer, "%s/accounts.json", SLON_DB_PATH);
+ Json.DumpToFile(scratch_buffer, db->a("accounts"));
+}
+
+U0 @slon_db_save_actors_to_disk()
+{
+ U8 scratch_buffer[256];
+ StrPrint(scratch_buffer, "%s/actors.json", SLON_DB_PATH);
+ Json.DumpToFile(scratch_buffer, db->o("actors"));
+}
+
+U0 @slon_db_save_apps_to_disk()
+{
+ U8 scratch_buffer[256];
+ StrPrint(scratch_buffer, "%s/apps.json", SLON_DB_PATH);
+ Json.DumpToFile(scratch_buffer, db->o("apps"));
+}
+
+U0 @slon_db_save_instance_to_disk()
+{
+ U8 scratch_buffer[256];
+ StrPrint(scratch_buffer, "%s/instance.json", SLON_DB_PATH);
+ Json.DumpToFile(scratch_buffer, db->o("instance"));
+}
+
+U0 @slon_db_save_oauth_to_disk()
+{
+ U8 scratch_buffer[256];
+ StrPrint(scratch_buffer, "%s/oauth.json", SLON_DB_PATH);
+ Json.DumpToFile(scratch_buffer, db->o("oauth"));
+}
+
+U0 @slon_db_save_private_keys_to_disk()
+{
+ U8 scratch_buffer[256];
+ StrPrint(scratch_buffer, "%s/private_keys.json", SLON_DB_PATH);
+ Json.DumpToFile(scratch_buffer, db->o("private_keys"));
+}
+
+U0 @slon_db_save_followers_to_disk()
+{
+ U8 scratch_buffer[256];
+ JsonKey* key = db->o("followers")->keys;
+ while (key) {
+ StrPrint(scratch_buffer, "%s/followers/%s.json", SLON_DB_PATH, key->name);
+ Json.DumpToFile(scratch_buffer, key->value);
+ key = key->next;
+ }
+}
+
+U0 @slon_db_save_statuses_to_disk()
+{
+ U8 scratch_buffer[256];
+ JsonKey* key = db->o("statuses")->keys;
+ while (key) {
+ StrPrint(scratch_buffer, "%s/statuses/%s.json", SLON_DB_PATH, key->name);
+ Json.DumpToFile(scratch_buffer, key->value);
+ key = key->next;
+ }
+}
+
+U0 @slon_db_save_to_disk()
+{
+ @slon_db_save_accounts_to_disk();
+ @slon_db_save_actors_to_disk();
+ @slon_db_save_apps_to_disk();
+ @slon_db_save_followers_to_disk();
+ @slon_db_save_instance_to_disk();
+ @slon_db_save_oauth_to_disk();
+ @slon_db_save_private_keys_to_disk();
+ @slon_db_save_statuses_to_disk();
+}
+
+U0 @slon_db_load_from_defaults()
+{
+ db->set("accounts", Json.CreateArray(), JSON_ARRAY);
+ db->set("actors", Json.CreateObject(), JSON_OBJECT);
+ db->set("apps", Json.CreateObject(), JSON_OBJECT);
+ db->set("idempotency_keys", Json.CreateObject(), JSON_OBJECT);
+ db->set("private_keys", Json.CreateObject(), JSON_OBJECT);
+ db->set("private_keys_binary", Json.CreateObject(), JSON_OBJECT);
+ db->set("public_keys", Json.CreateObject(), JSON_OBJECT);
+ db->set("followers", Json.CreateObject(), JSON_OBJECT);
+ db->set("instance", Json.ParseFile("M:/Slon/Static/defaults/instance.json"), JSON_OBJECT);
+ db->set("statuses", Json.CreateObject(), JSON_OBJECT);
+ JsonObject* oauth = Json.CreateObject();
+ oauth->set("codes", Json.CreateObject(), JSON_OBJECT);
+ oauth->set("requests", Json.CreateObject(), JSON_OBJECT);
+ oauth->set("responses", Json.CreateObject(), JSON_OBJECT);
+ oauth->set("tokens", Json.CreateObject(), JSON_OBJECT);
+ db->set("oauth", oauth, JSON_OBJECT);
+ db->set("setup", FALSE, JSON_BOOLEAN);
+}
+
+U0 @slon_db_load_from_disk()
+{
+ @slon_db_load_accounts_from_disk();
+ @slon_db_load_actors_from_disk();
+ @slon_db_load_apps_from_disk();
+ db->set("idempotency_keys", Json.CreateObject(), JSON_OBJECT);
+ @slon_db_load_private_keys_from_disk();
+ db->set("private_keys_binary", Json.CreateObject(), JSON_OBJECT);
+ db->set("public_keys", Json.CreateObject(), JSON_OBJECT);
+ @slon_db_load_followers_from_disk();
+ @slon_db_load_instance_from_disk();
+ @slon_db_load_oauth_from_disk();
+ @slon_db_load_statuses_from_disk();
+ db->set("setup", TRUE, JSON_BOOLEAN);
+}
+
+U0 @slon_db_instance_update_user_count()
+{
+ JsonObject* stats = db->o("instance")->o("stats");
+ stats->set("user_count", db->a("accounts")->length);
+}
+
+U0 @slon_db_instance_decrement_status_count()
+{
+ JsonObject* stats = db->o("instance")->o("stats");
+ stats->set("status_count", MaxI64(0, stats->@("status_count") - 1));
+}
+
+U0 @slon_db_instance_increment_status_count()
+{
+ JsonObject* stats = db->o("instance")->o("stats");
+ stats->set("status_count", stats->@("status_count") + 1);
+}
+
+U0 @slon_db_actors_update_user(JsonObject* acct)
+{
+ JsonObject* actors = db->o("actors");
+ JsonObject* actor = actors->o(acct->@("username"));
+
+ if (!actor) {
+ // FIXME: Handle this error
+ return;
+ }
+ actor->set("name", acct->@("display_name"));
+ actor->set("summary", acct->@("note"));
+ JsonObject* icon = actor->o("icon");
+ icon->set("url", acct->@("avatar"));
+ actor->set("attachment", acct->@("fields"));
+ @slon_db_save_actors_to_disk;
+}
+
+U0 @slon_db_init()
+{
+ if (FileFind(SLON_DB_PATH)) {
+ @slon_log(LOG_DB, "loading db from disk");
+ @slon_db_load_from_disk;
+ } else {
+ @slon_log(LOG_DB, "no db found; loading defaults");
+ @slon_db_load_from_defaults;
+ }
+}
+
+@slon_db_init;
+
+JsonArray* SLON_EMPTY_JSON_ARRAY = Json.CreateArray();
+JsonObject* SLON_EMPTY_JSON_OBJECT = Json.CreateObject();
+
+JsonObject* SLON_DEFAULT_ACCT_OBJECT = Json.ParseFile("M:/Slon/Static/defaults/account.json");
+JsonObject* SLON_DEFAULT_ACTOR_OBJECT = Json.ParseFile("M:/Slon/Static/defaults/actor.json");
diff --git a/Slon/Modules/Http.HC b/Slon/Modules/Http.HC
new file mode 100644
index 0000000..b8c777e
--- /dev/null
+++ b/Slon/Modules/Http.HC
@@ -0,0 +1,249 @@
+#define SLON_HTTP_BUFFER_SIZE 1048576
+#define SLON_HTTP_VERB_DELETE 1
+#define SLON_HTTP_VERB_GET 2
+#define SLON_HTTP_VERB_OPTIONS 3
+#define SLON_HTTP_VERB_PATCH 4
+#define SLON_HTTP_VERB_POST 5
+
+#define SLON_MULTIPART_PARSER_CONSUME_BOUNDARY 0
+#define SLON_MULTIPART_PARSER_CONSUME_CONTENT_DISPOSITION 1
+#define SLON_MULTIPART_PARSER_CONSUME_CONTENT 2
+#define SLON_MULTIPART_PARSER_DONE 3
+
+#define SLON_SCRATCH_BUFFER_AND_REQUEST_JSON \
+ U8 scratch_buffer[256]; \
+ JsonObject* request_json = @slon_http_request_json(session);
+
+#define SLON_DEBUG_PRINT_REQUEST_JSON \
+ JsonObject* request_json = @slon_http_request_json(session); \
+ U8* request_json_str = Json.Stringify(request_json); \
+ AdamLog("request_json: %s\n", request_json_str); \
+ Free(request_json_str);
+
+JsonObject* SLON_HTTP_STATUS_CODES = Json.ParseFile("M:/Slon/Settings/status_codes.json");
+
+class SlonHttpBuffer {
+ U8* data;
+ I64 size;
+ I64 capacity;
+};
+
+class SlonHttpRequest {
+ SlonHttpBuffer* buffer;
+ JsonObject* headers;
+ JsonObject* json;
+ U8* data;
+ I64 size;
+ U8* verb;
+ U8* raw_path;
+ U8* path;
+ Bool headers_have_been_parsed;
+};
+
+class SlonHttpResponse {
+ SlonHttpBuffer* buffer;
+ JsonObject* headers;
+ U8* data;
+ I64 size;
+ I64 status_code;
+};
+
+class SlonHttpSession {
+ U64 s;
+ SlonHttpRequest* request;
+ SlonHttpResponse* response;
+ I64 bytes_used;
+ JsonObject* auth;
+};
+
+U64 @slon_calloc(SlonHttpSession* session, I64 size)
+{
+ if (!session || !size)
+ return NULL;
+ U64 res = CAlloc(size, adam_task);
+ session->bytes_used += MSize2(res);
+ // AdamLog("@slon_calloc: requested %d, total used: %d\n", MSize2(res), session->bytes_used);
+ return res;
+}
+
+U0 @slon_free(SlonHttpSession* session, U64 ptr)
+{
+ if (!session || !ptr)
+ return;
+ session->bytes_used -= MSize2(ptr);
+ // AdamLog("@slon_free: freed %d, total used: %d\n", MSize2(ptr), session->bytes_used);
+ Free(ptr);
+}
+
+U64 @slon_malloc(SlonHttpSession* session, I64 size)
+{
+ if (!session || !size)
+ return NULL;
+ U64 res = MAlloc(size, adam_task);
+ session->bytes_used += MSize2(res);
+ // AdamLog("@slon_malloc: requested %d, total used: %d\n", MSize2(res), session->bytes_used);
+ return res;
+}
+
+U8* @slon_strnew(SlonHttpSession* session, U8* str)
+{
+ if (!session || !str)
+ return NULL;
+ U8* new = StrNew(str, adam_task);
+ session->bytes_used += MSize2(new);
+ // AdamLog("@slon_strnew: requested %d, total used: %d\n", MSize2(new), session->bytes_used);
+ // AdamLog("@slon_strnew: %s\n", new);
+ return new;
+}
+
+U8* @slon_http_decode_urlencoded_string(SlonHttpSession* session, U8* str)
+{
+ if (!StrFind("%", str) && !StrFind("+", str)) {
+ return @slon_strnew(session, str);
+ }
+ U8* decoded_string = @slon_calloc(session, StrLen(str));
+ I64 i = 0;
+ I64 j;
+ U32 code_point;
+ while (i < StrLen(str)) {
+ if (str[i] == '%') {
+ code_point = 0;
+ for (j = 2; j > 0; j--) {
+ if (str[i + j] >= '0' && str[i + j] <= '9')
+ code_point += (@t(j == 1, 16, 1) * (str[i + j] - '0'));
+ if (str[i + j] >= 'A' && str[i + j] <= 'F')
+ code_point += (@t(j == 1, 16, 1) * (10 + (str[i + j] - 'A')));
+ if (str[i + j] >= 'a' && str[i + j] <= 'f')
+ code_point += (@t(j == 1, 16, 1) * (10 + (str[i + j] - 'a')));
+ }
+ String.Append(decoded_string, "%c", code_point);
+ i += 3;
+ } else if (str[i] == '+') {
+ String.Append(decoded_string, " ");
+ i++;
+ } else {
+ String.Append(decoded_string, "%c", str[i]);
+ i++;
+ }
+ }
+ return decoded_string;
+}
+
+JsonObject* @slon_http_request_json(SlonHttpSession* session)
+{
+ if (!session->request->json)
+ return SLON_EMPTY_JSON_OBJECT;
+ return session->request->json;
+}
+
+U0 @slon_http_set_header(SlonHttpSession* session, U8* key, U8* value)
+{
+ Json.Set(session->response->headers, key, value, JSON_STRING);
+}
+
+U0 @slon_http_set_content_type(SlonHttpSession* session, U8* value)
+{
+ @slon_http_set_header(session, "content-type", value);
+}
+
+U0 @slon_http_set_status_code(SlonHttpSession* session, I64 status_code)
+{
+ session->response->status_code = status_code;
+}
+
+U0 @slon_http_send_ap_json(SlonHttpSession* session, U64 json)
+{
+ // a stringified copy of "json" is created, a strnew is sent, we clean up stringified copy, sender cleans up "json"
+ @slon_http_set_status_code(session, 200);
+ @slon_http_set_content_type(session, "application/activity+json; charset=utf-8");
+ U8* json_string = Json.Stringify(json);
+ session->response->data = @slon_strnew(session, json_string);
+ session->response->size = StrLen(session->response->data);
+ Free(json_string);
+}
+
+U0 @slon_http_send_json(SlonHttpSession* session, U64 json)
+{
+ // a stringified copy of "json" is created, a strnew is sent, we clean up stringified copy, sender cleans up "json"
+ @slon_http_set_status_code(session, 200);
+ @slon_http_set_content_type(session, "application/json; charset=utf-8");
+ U8* json_string = Json.Stringify(json);
+ session->response->data = @slon_strnew(session, json_string);
+ session->response->size = StrLen(session->response->data);
+ Free(json_string);
+}
+
+U0 @slon_http_send_string(SlonHttpSession* session, U8* str)
+{
+ // a strnew of "str" is sent, sender cleans up "str"
+ @slon_http_set_status_code(session, 200);
+ session->response->data = @slon_strnew(session, str);
+ session->response->size = StrLen(str);
+}
+
+U0 @slon_http_send(SlonHttpSession* session, U64 data, I64 size)
+{
+ // a malloc copy of "data" is sent, sender cleans up "data"
+ @slon_http_set_status_code(session, 200);
+ U8* data_new = @slon_malloc(session, size);
+ MemCpy(data_new, data, size);
+ session->response->data = data_new;
+ session->response->size = size;
+}
+
+U0 @slon_http_send_file(SlonHttpSession* session, U8* path)
+{
+ if (!session || !path)
+ return;
+ if (!FileFind(path))
+ return;
+ I64 size = 0;
+ U8* data = FileRead(path, &size);
+ @slon_http_send(session, data, size);
+ Free(data);
+}
+
+U0 @slon_http_send_html_file(SlonHttpSession* session, U8* path)
+{
+ @slon_http_set_content_type(session, "text/html");
+ @slon_http_send_file(session, path);
+}
+
+U0 @slon_http_send_json_file(SlonHttpSession* session, U8* path, U8* content_type = "application/json; charset=utf-8")
+{
+ @slon_http_set_content_type(session, content_type);
+ @slon_http_send_file(session, path);
+}
+
+U8* @slon_http_request_path(SlonHttpSession* session)
+{
+ return session->request->path;
+}
+
+I64 @slon_http_request_verb(SlonHttpSession* session)
+{
+ if (!StrCmp(session->request->verb, "DELETE"))
+ return SLON_HTTP_VERB_DELETE;
+ if (!StrCmp(session->request->verb, "GET"))
+ return SLON_HTTP_VERB_GET;
+ if (!StrCmp(session->request->verb, "OPTIONS"))
+ return SLON_HTTP_VERB_OPTIONS;
+ if (!StrCmp(session->request->verb, "PATCH"))
+ return SLON_HTTP_VERB_PATCH;
+ if (!StrCmp(session->request->verb, "POST"))
+ return SLON_HTTP_VERB_POST;
+ return 999;
+}
+
+U8* @slon_http_request_header(SlonHttpSession* session, U8* key)
+{
+ U64 value = Json.Get(session->request->headers, key);
+ if (!value)
+ return "";
+ return value;
+}
+
+Bool @slon_http_request_has_query_string(SlonHttpSession* session)
+{
+ return StrFind("?", session->request->raw_path) > 0 && !String.EndsWith("?", session->request->raw_path);
+}
diff --git a/Slon/Modules/Log.HC b/Slon/Modules/Log.HC
new file mode 100644
index 0000000..afb5110
--- /dev/null
+++ b/Slon/Modules/Log.HC
@@ -0,0 +1,22 @@
+#define LOG_DB 100
+#define LOG_HTTPD 101
+
+U0 @slon_log(I64 module, U8* fmt, ...)
+{
+ CDateStruct ds;
+ Date2Struct(&ds, Now);
+ AdamLog("[%02d:%02d]", ds.hour, ds.min);
+ switch (module) {
+ case LOG_DB:
+ AdamLog("[ slon/db] ");
+ break;
+ case LOG_HTTPD:
+ AdamLog("[slon/httpd] ");
+ break;
+ default:
+ AdamLog("[ slon/misc] ");
+ break;
+ }
+ AdamLog(fmt, argc, argv);
+ AdamLog("\n");
+}
diff --git a/Slon/Modules/Meta.HC b/Slon/Modules/Meta.HC
new file mode 100644
index 0000000..84ddce0
--- /dev/null
+++ b/Slon/Modules/Meta.HC
@@ -0,0 +1,9 @@
+U0 @slon_host_meta(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn request_json;
+
+ StrPrint(scratch_buffer, " ", db->o("instance")->@("uri"));
+ @slon_http_set_content_type(session, "application/xrd+xml; charset=utf-8");
+ @slon_http_send_string(session, scratch_buffer);
+}
diff --git a/Slon/Modules/OAuth.HC b/Slon/Modules/OAuth.HC
new file mode 100644
index 0000000..47e4dec
--- /dev/null
+++ b/Slon/Modules/OAuth.HC
@@ -0,0 +1,192 @@
+#define SLON_OAUTH_USERINFO_URL "https://app.simplelogin.io/oauth2/userinfo?access_token="
+
+U0 @slon_oauth_well_known(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn request_json;
+
+ StrPrint(scratch_buffer, "{\"issuer\":\"https://%s\",\"authorization_endpoint\":\"https://%s/oauth/authorize\",\"response_types_supported\":[\"code\"],\"app_registration_endpoint\":\"https://%s/api/v1/apps\"}",
+ db->o("instance")->@("uri"), db->o("instance")->@("uri"), db->o("instance")->@("uri"));
+ @slon_http_set_content_type(session, "application/json; charset=utf-8");
+ @slon_http_send_string(session, scratch_buffer);
+}
+
+U0 @slon_oauth_fetch_token(U8* client_id)
+{
+ if (!client_id || !StrLen(client_id))
+ return;
+
+ U8 url_string[256];
+ JsonObject* oauth_request = db->o("oauth")->o("requests")->@(client_id);
+ if (!oauth_request)
+ return;
+
+ U8* access_token = oauth_request->@("access_token");
+ if (!access_token) {
+ return;
+ }
+
+ U8* fetch_buffer = CAlloc(HTTP_FETCH_BUFFER_SIZE, adam_task);
+ StrPrint(url_string, "%s%s", SLON_OAUTH_USERINFO_URL, access_token);
+ @http_response* resp = fetch(url_string, fetch_buffer);
+
+ if (!resp)
+ goto oauth_free_and_return;
+
+ if (resp->body.length) {
+ // POSIX people think JSON should end with a new line, and the Jakt parser disagrees :^)
+ while (resp->body.data[StrLen(resp->body.data) - 1] == '\n')
+ resp->body.data[StrLen(resp->body.data) - 1] = NULL;
+ JsonObject* response = Json.Parse(resp->body.data);
+ db->o("oauth")->o("responses")->set(client_id, response, JSON_OBJECT);
+ }
+ // FIXME: Free resp
+
+oauth_free_and_return:
+ Free(fetch_buffer);
+ Free(client_id);
+}
+
+U0 @async_slon_oauth_fetch_token(U8* client_id)
+{
+ Spawn(&@slon_oauth_fetch_token, StrNew(client_id, adam_task), "OauthFetchTokenTask");
+}
+
+U8* @slon_oauth_generate_access_token(SlonHttpSession* session)
+{
+ return @slon_api_generate_random_hex_string(session, 16);
+}
+
+U8* @slon_oauth_generate_authorization_code(SlonHttpSession* session)
+{
+ return @slon_api_generate_random_hex_string(session, 16);
+}
+
+U0 @slon_oauth_verify_access_get(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+
+ U8* client_id = request_json->@("client_id");
+ U8* redirect_uri = request_json->@("redirect_uri");
+ JsonObject* app_object = db->o("apps")->@(client_id);
+ // If client_id or redirect_uri are empty, or if client app doesn't exist, Bad Request
+ if (!StrLen(client_id) || !StrLen(redirect_uri) || !app_object) {
+ @slon_http_set_status_code(session, 400);
+ return;
+ }
+ U8* client_secret = app_object->@("client_secret");
+ JsonObject* userinfo = db->o("oauth")->o("responses")->@(client_id);
+ if (userinfo) {
+ // If a userinfo with the client_id exists, read the userinfo Object.
+ U8* email = userinfo->@("email");
+ if (email && StrLen(email)) {
+ JsonObject* acct = @slon_api_account_by_email(email);
+ if (acct) {
+
+ // If the account exists,
+ // create a token that points to the account
+ U8* access_token = NULL;
+ Bool access_token_exists = TRUE;
+ while (access_token_exists) {
+ if (access_token) {
+ @slon_free(session, access_token);
+ }
+ access_token = @slon_oauth_generate_access_token(session);
+ access_token_exists = db->o("oauth")->o("tokens")->@(access_token) > 0;
+ }
+ I64 created_at = ToF64(CDate2Unix(Now));
+
+ JsonObject* token_object = Json.CreateObject();
+ token_object->set("access_token", access_token, JSON_STRING);
+ token_object->set("token_type", "Bearer", JSON_STRING);
+ token_object->set("scope", "read write follow push", JSON_STRING);
+ token_object->set("created_at", created_at, JSON_NUMBER);
+ token_object->set("account_id", acct->@("id"), JSON_STRING);
+ token_object->set("client_id", client_id, JSON_STRING);
+ token_object->set("email", email, JSON_STRING);
+ db->o("oauth")->o("tokens")->set(access_token, token_object, JSON_OBJECT);
+ // FIXME: We need to commit this to disk eventually? but not immediately
+
+ U8* authorization_code = NULL;
+ Bool authorization_code_exists = TRUE;
+ while (authorization_code_exists) {
+ if (authorization_code) {
+ @slon_free(session, authorization_code);
+ }
+ authorization_code = @slon_oauth_generate_authorization_code(session);
+ authorization_code_exists = db->o("oauth")->o("codes")->@(authorization_code) > 0;
+ }
+
+ JsonObject* code_object = Json.CreateObject();
+ code_object->set("access_token", access_token, JSON_STRING);
+ code_object->set("token_type", "Bearer", JSON_STRING);
+ code_object->set("scope", "read write follow push", JSON_STRING);
+ code_object->set("created_at", created_at, JSON_NUMBER);
+ code_object->set("account_id", acct->@("id"), JSON_STRING);
+ code_object->set("client_id", client_id, JSON_STRING);
+ code_object->set("client_secret", client_secret, JSON_STRING);
+ code_object->set("email", email, JSON_STRING);
+ db->o("oauth")->o("codes")->set(authorization_code, code_object, JSON_OBJECT);
+ @slon_db_save_oauth_to_disk;
+
+ StrPrint(scratch_buffer, "%s?code=%s", redirect_uri, authorization_code);
+ JsonObject* redirect_uri_object = Json.CreateObject();
+ redirect_uri_object->set("redirect_uri", scratch_buffer, JSON_STRING);
+ @slon_http_send_json(session, redirect_uri_object);
+ Json.Delete(redirect_uri_object);
+
+ @slon_free(session, authorization_code);
+ @slon_free(session, access_token);
+
+ } else {
+ // If the account does not exist, return Not Found
+ @slon_http_set_status_code(session, 404);
+ }
+ } else {
+ // Response doesn't contain an email, therefore user is Unauthorized.
+ @slon_http_set_status_code(session, 401);
+ }
+ return;
+ } else {
+ if (!db->o("oauth")->o("requests")->@(client_id)) {
+ // If a request with the client_id does not exist, create one, and spawn a fetch() instance to retrieve the OAuth2 token.
+ db->o("oauth")->o("requests")->set(client_id, request_json, JSON_OBJECT);
+ @async_slon_oauth_fetch_token(client_id);
+ }
+ @slon_http_set_status_code(session, 202);
+ }
+ Json.Delete(app_object);
+}
+
+U0 @slon_oauth_token_post(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn scratch_buffer;
+
+ U8* client_id = request_json->@("client_id");
+ U8* client_secret = request_json->@("client_secret");
+ U8* code = request_json->@("code");
+
+ JsonObject* code_object = db->o("oauth")->o("codes")->@(code);
+ if (!StrLen(client_id) || !StrLen(client_secret) || !code_object) {
+ // If client_id is empty, or client_secret is empty, or the code doesn't exist, it's a Bad Request.
+ @slon_http_set_status_code(session, 400);
+ return;
+ }
+
+ U8* access_token = code_object->@("access_token");
+ if (!StrCmp(client_id, code_object->@("client_id")) && !StrCmp(client_secret, code_object->@("client_secret"))) {
+ JsonObject* token = db->o("oauth")->o("tokens")->@(access_token);
+ if (token) {
+ @slon_http_send_json(session, token);
+ } else {
+ // If the token doesn't exist, Page Expired?
+ @slon_http_set_status_code(session, 419);
+ }
+ } else {
+ // If client_id and client_secret do not match, it's Unauthorized
+ @slon_http_set_status_code(session, 401);
+ }
+
+ Json.Delete(code_object);
+}
diff --git a/Slon/Modules/Web.HC b/Slon/Modules/Web.HC
new file mode 100644
index 0000000..901fa08
--- /dev/null
+++ b/Slon/Modules/Web.HC
@@ -0,0 +1,42 @@
+U0 @slon_web_user_get(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ no_warn scratch_buffer, request_json;
+
+ I64 path_segments_count = 0;
+ U8** path_segments = String.Split(StrFind("@", @slon_http_request_path(session)) + 1, '/', &path_segments_count);
+
+ U8* user = path_segments[0];
+
+ if (path_segments_count == 1) {
+ JsonObject* actor = db->o("actors")->@(user);
+ if (!actor) {
+ @slon_http_set_status_code(session, 404);
+ goto slon_web_user_get_return;
+ }
+ // gib profil pl0x
+
+ I64 html_file_size;
+ U8* html_file_data = FileRead("M:/Slon/Static/html/user.html", &html_file_size);
+ U8* user_file_data = Json.Stringify(actor);
+
+ U8* html_data = @slon_calloc(session, (html_file_size * 2) + (StrLen(user_file_data) * 2));
+ String.Append(html_data, html_file_data);
+ String.Append(html_data, "");
+ @slon_http_set_content_type(session, "text/html");
+ @slon_http_send(session, html_data, StrLen(html_data));
+
+ Free(html_file_data);
+ Free(user_file_data);
+ @slon_free(session, html_data);
+ goto slon_web_user_get_return;
+ } else {
+ // do something here (statuses, followers, media, etc.)
+ @slon_http_set_status_code(session, 404);
+ }
+
+slon_web_user_get_return:
+ Free(path_segments);
+}
diff --git a/Slon/Modules/Webfinger.HC b/Slon/Modules/Webfinger.HC
new file mode 100644
index 0000000..2d5d775
--- /dev/null
+++ b/Slon/Modules/Webfinger.HC
@@ -0,0 +1,39 @@
+U0 @slon_webfinger(SlonHttpSession* session)
+{
+ SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
+ U8* resource = request_json->@("resource");
+ StrPrint(scratch_buffer, "@%s", db->o("instance")->@("uri"));
+ if (resource && String.BeginsWith("acct:", resource) && String.EndsWith(scratch_buffer, resource)) {
+ resource = StrFind(":", resource) + 1;
+ StrFind("@", resource)[0] = NULL;
+ if (db->o("actors")->@(resource)) {
+ JsonObject* webfinger_object = Json.CreateObject();
+
+ StrPrint(scratch_buffer, "acct:%s@%s", resource, db->o("instance")->@("uri"));
+ webfinger_object->set("subject", scratch_buffer, JSON_STRING);
+
+ JsonArray* aliases = Json.CreateArray();
+ StrPrint(scratch_buffer, "https://%s/@%s", db->o("instance")->@("uri"), resource);
+ aliases->append(Json.CreateItem(scratch_buffer, JSON_STRING));
+ StrPrint(scratch_buffer, "https://%s/users/%s", db->o("instance")->@("uri"), resource);
+ aliases->append(Json.CreateItem(scratch_buffer, JSON_STRING));
+ webfinger_object->set("aliases", aliases, JSON_ARRAY);
+
+ JsonArray* links = Json.CreateArray();
+ JsonObject* link_object = Json.CreateObject();
+ link_object->set("rel", "self", JSON_STRING);
+ link_object->set("type", "application/activity+json", JSON_STRING);
+ StrPrint(scratch_buffer, "https://%s/users/%s", db->o("instance")->@("uri"), resource);
+ link_object->set("href", scratch_buffer, JSON_STRING);
+ links->append(Json.CreateItem(link_object, JSON_OBJECT));
+ webfinger_object->set("links", links, JSON_ARRAY);
+
+ @slon_http_send_json(session, webfinger_object);
+ Json.Delete(webfinger_object);
+ } else {
+ @slon_http_set_status_code(session, 404);
+ }
+ } else {
+ @slon_http_set_status_code(session, 400);
+ }
+}
diff --git a/Slon/Settings/status_codes.json b/Slon/Settings/status_codes.json
new file mode 100644
index 0000000..860c982
--- /dev/null
+++ b/Slon/Settings/status_codes.json
@@ -0,0 +1 @@
+{"100": "Continue", "101": "Switching Protocols", "102": "Processing", "103": "Early Hints", "200": "OK", "201": "Created", "202": "Accepted", "203": "Non-Authoritative Information", "204": "No Content", "205": "Reset Content", "206": "Partial Content", "207": "Multi-Status", "208": "Already Reported", "218": "This is fine", "226": "IM Used", "300": "Multiple Choices", "301": "Moved Permanently", "302": "Found", "303": "See Other", "304": "Not Modified", "306": "Switch Proxy", "307": "Temporary Redirect", "308": "Resume Incomplete", "400": "Bad Request", "401": "Unauthorized", "402": "Payment Required", "403": "Forbidden", "404": "Not Found", "405": "Method Not Allowed", "406": "Not Acceptable", "407": "Proxy Authentication Required", "408": "Request Timeout", "409": "Conflict", "410": "Gone", "411": "Length Required", "412": "Precondition Failed", "413": "Request Entity Too Large", "414": "Request-URI Too Long", "415": "Unsupported Media Type", "416": "Requested Range Not Satisfiable", "417": "Expectation Failed", "418": "I'm a teapot", "419": "Page Expired", "420": "Method Failure", "421": "Misdirected Request", "422": "Unprocessable Entity", "423": "Locked", "424": "Failed Dependency", "426": "Upgrade Required", "428": "Precondition Required", "429": "Too Many Requests", "431": "Request Header Fields Too Large", "440": "Login Time-out", "444": "Connection Closed Without Response", "449": "Retry With", "450": "Blocked by Windows Parental Controls", "451": "Unavailable For Legal Reasons", "494": "Request Header Too Large", "495": "SSL Certificate Error", "496": "SSL Certificate Required", "497": "HTTP Request Sent to HTTPS Port", "498": "Invalid Token", "499": "Client Closed Request", "500": "Internal Server Error", "501": "Not Implemented", "502": "Bad Gateway", "503": "Service Unavailable", "504": "Gateway Timeout", "505": "HTTP Version Not Supported", "506": "Variant Also Negotiates", "507": "Insufficient Storage", "508": "Loop Detected", "509": "Bandwidth Limit Exceeded", "510": "Not Extended", "511": "Network Authentication Required", "520": "Unknown Error", "521": "Web Server Is Down", "522": "Connection Timed Out", "523": "Origin Is Unreachable", "524": "A Timeout Occurred", "525": "SSL Handshake Failed", "526": "Invalid SSL Certificate", "527": "Railgun Listener to Origin Error", "530": "Origin DNS Error", "598": "Network Read Timeout Error"}
diff --git a/Slon/Static/css/main.css b/Slon/Static/css/main.css
new file mode 100644
index 0000000..e16df8f
--- /dev/null
+++ b/Slon/Static/css/main.css
@@ -0,0 +1,117 @@
+@font-face {
+ font-family: "TempleOS";
+ src: url("data:font/woff2;base64,d09GMk9UVE8AABAEAAoAAAAAipQAAA+3AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYKBfz9GRlRNHAZgAIdYATYCJAOEBgQGBYQHByAb34klbHcBbgfpZr0zj6JcbEZl//8tgcqQjcLTbnJVYIc1cTWSnogkqf54ZrKmcAvP7SyzM16GxUh4ES4rCkVx/Pu0N9fiHmnFA/htuqwo/Bg2EPeELUHCqe/OHxW7VzZwcyR46GGb7wNGFMPAi0KMKmbVxiKjWGTr81zut32RBNSLaA0d04quuRd4CV0+ce/elXBkASXeeWRjWuJZ+68xiich6VieUI5CFR3d5OHhlu/HYRpoZHm8+wI5ThzIxG7QCSWyxU1Bbbb/JGX3Gm/gH2w37wfDcUwoRSis776U3uRuyl/V8ESHhz8P56Li4uLi5r1oXjBryymDsGMJjd7xt1Z79Hr/jzcETzwK4gk6VP3ONBsxDMSzCpvLHZdhtxROUDtivq4foVmxTCPt79daPfnvsF3wKPaQEm8u0zr734n9xUySatyEeKmmkSGRKZHQKZnphJChWWjdIb6W3yxpvwLpU5SCqilq1Smk411Pi73ZIWWLMXbepUT4f0wtmr701vKWHbY1zwVRlvABCThOO+7uCIFMhhDCqJsuuntXhARB+n/yc7+QI5AjIx1l4xZFbiyU9Z9jX2eYsqLBOp3eCGRTniQELEP0p35fULjmMKi2pn2CwBAL7LlQUPIRLEy0JCky5chXplZT7XTV3wheMSVNAwsnAlum9w6EmXAQ7sJXhIhokSxbQ5W0Wuust8EIfnF5dX1zeySRKVR6iV8SgZXNw4k7XyGiJcvWUCWtVjrqpq8hRptgugVW2WiH/Y6hyBypl69++5+0tdGbvIDSyq/shwBXFLPks1LUmdpSxMUbFufgXhcdfMgsDk2O5k1SFQeY2kJGqKE9WMCiR/sEM6ksipb5yIs2qsiMTqYZFoIIRwbRz+xeD0Q3es0ZRzFnu+3+fA1RkWymolJ1SDTfFwWwUjT0jsQeHcKtUjYkbiOfeILkZdhalSxl+JQubT2cUhKitAsxGCANIG98X5a5XnSRfN5TBn1SfjCYCAQYFAISCjgzSUVilQ2MJUmZGcCMX1AVIo+FgABnRZU9c0lZRRNATCR1WUMgAoPCSDa1F6Xr2VF17yTL2Rq+0yE72yiAuChz4yeKs3WKreBdInG6rCajeJVVses1CiAO0JCb7ZPElAUY1JcY9AY3cVxdKbccaUnroJj516ZWhTYARvBoM4E2Bc6BbPgbjZYZZIp8mvq+KJdu2nZGXQGg8vWMwOEkUxxBdsnnrM+0Owyny3QjKIYTJEUzLMcLoiQrqqYDaFjs7B0cnZxdXN3cPTy9vH18/fyhq5pumJYfQTGcICmaYTleECVZUTUdGIFyFSpVqVajVp1GMSoiWk0008JYGjPlpYJIjFYXE5kskGS1XpdD38DC2tHGE4FEYcx9SHgCkUamUOlsBpPl4MvjC8SmMYaTi5uHl49fQFBIOLqYISomLiEpJS0jKyevoKikrKKqpq6hqaWto6unb2BoZGxiamZuYWllbWNr5+Ts4urm7oFAotAYLA5PIJLIFCqNzmCy2Bwujy8QisQSqUyuUKrUGq1O7ymEBEHjN+kejsR2E2xbKumo/vaxbM9g60XrNRq9x97DfvwNhp08wxVTOG9fk+u3yeT7uAamdWv1i2HW5u1sfuPZf9wtL5O25B2dwYrYHV4uhF1Hma2sHA7rcmPgdNRW2zOuj5e7/gzzord8U/Jv0D69zC6KAXlF8XI/USM97rSKdro36n+gEvZjMe4bFePuUnX7fZ7uXX0vH6l+284o/zVXRsCuOytwz+Mc1Cno0muvWggZ5nCa+it/k+Eq1eSwOeYQ3i/COcIlwjdiq4dEOUS9SU0M35haEfunje0Z8d/H+KlKPLFqkxsk7yOVqYIOTLNgi3hN+hdRlrFHpmauV5FZG7R39n4TmzPVOuTOdQl5p3yzAsMwoDCs8Glan5s2/FLGVvvigy27ZHcf0of2YZDgBm7iFm7jDnS4i3v4MebM4ApVxcOVXrl9u1YN3cP20ZNRffr8uWprllzP74jakfffp3Pdqde/0dbPvvGDahCF4ilr5EulqJ/O129TZR62wH5cc79r9TcMi5Vp3nZ7Vre7U8rKH/rirOOPevY2iUrbl1f2Tv3P/WfJfqMCTFRNN0xLKFLVdMO07Hv8O5T6vx/buz0GmWluE7jfj8kyRmoGgRyjxvNiphm0Wadr00ce0CTFK5on5lcdvQom/V28KnKvowCuZ6GqCSgUMmNxlBAmMSqF8DF/LPR6al0pReMqPyAXq+MOvb0vVFsbhtC2kGq8hK/TJ+Jsp0V31ItDcCUsikn4YymSGlSK8PilaQnoJJLJhe55rHVYTcyERnVP4fqmamDuxfHb56DIlYhfCP8TECJwBnIQSGzszC3qw2AVTwPCsQZFlnwixMn7VGyYoKUiw03+urv5jlEdh88RlMXR8yFExnnyETMB6JznEwV+TkcCvxyolmcSstZnQCnWaf1CW/XkoGP1qZ8++mRjAkC4pg2zMlpS93xnvuID/DRr6datkZlAJ3OO5nGQ4IICmRPU45bmAym3gnwVyV1rkWsEJwsO6QiZuPbVrISJSRv39TOHmv81AnaEzCSuzCcg3E7Bm8Hr3wBQT5fmRbg2LhxZUMFT7Lm6vX1kIXM2+cJ9IAnzTQOg6PZXQK96YVVPpn14HFMLDmVQxsKFe+QfhCdg2BIBrqZdkoDMGXxHc+Z4Z7G+qfO7IOE+7ppTB4hzXcU1BUjGMXDHse2bZJ34VKzWe/zaVAsJYJQgwCirGG7OlgSY/ArvwlK71gqA+LoLuh9b4MmKp9xlCYDaaZzPeZETMgNJvve/Q5i9kZzzJAaLy4sdap0Tr3MmCVZNDIxgqudmARFnTJr5Q+r8PWZC1anlO9imCVmjejPA4gdNfJIaKR+Y+gNJwfdgcn1+p9D3SkzI5IngVszfjoBDI1gDhCrsq5jivtvPeawUHcvw5bI88p+5Y3/aibaLXfDK4zDnt3WmPuhcMtTnUG/jM8PCt77vLpbbKxKsW9q6woLbT2s1ge7Y3N59iNxb2duMFQd8IVSpoZZxIlwpLKRHNjRX5j06wF6x5TwUeRYWXvuLxivDdAniCXPShD8MYEG2ZulOnvlw68eypOSxdB7HWjPBRX97w+ie7Hn3cp1GVmBIMZDMMd2jtS0hj9sV5OV4a/lvEVVB7G3Zmu0CLh0NPZp6kAMh7/lvuDW/0gqnLfRySNtWao7LcUF7OthDzvToMg12zuWvMXrb42UDVwmlNp7Re+Blh/C69rWfhdMeAPGwMdH0zQMUXmPCfXvcsiq47DqzxDpxmSWIQdiagSRtqq/U4ID2xn3xGMBfPFYRAFCppqL/OwyMliTNJ3S9jUtoejcMu1unnviOGPfQb5aF3nSqU5zDLp8yxydPTDi3EdLOUPiPMHCZhTztAg5fceTvSDBupU/Ncx7yywdC4b/tIKtLPtFKTNuUP2Vf1YsjWI8JVFMY+wCUpkm0z44Sn9/WhUFb36apm3kLzNr8yfcu4ZmQ7zYSBd3ugO2IeHXQtlLCaIYx0bIeJvr5eouR1qf2dG3cs8LJtDk5U7N8wwouN9y94XzjzMbxvdPKmXjPsdUeFyqfV/g8vhhjwHaygGuwAYMRjmyYrAZunnDeBCZwqF+ci4+fyTtUZ6M/heKv9BQFOFCekHSg7CBtPuXgXbMzYfoqc6aBJjbmZilgYTQSgnprtQM2Dtiel58+zF7aET/anLnIhw83S8pZcsqIhzuk/8HO9GeKRLMVGrqzC48uNThLyXgObiSjNgjbuNWsY4KzDdtvyIQC52DuJkhlwHHO7kCm86gyYLGaTzKegsoB8XUU5wR1jHZSTZ42S0QknzYgTd5KgbnJsfZx0E4zcJmZk60prvyZPrvifCk6beOstU3D1so+tGTh2ORygu39z4gAR1vL8PITyDzGAgmzW782POD5yGMrA54ZrcttXD4JQuTzAz+XiSQKOknPym9uvGYTmCaaL63xSMwhZuYuDCX5bXMcpR5sih2UwEUC0gyaTdUffWLs6pKUG5I3zg3M+XvweNH/fNWGDeUEzoOw2/Vz7zCX1e6SmeUkzgRrd8MLbduZY4M7Z3DfjDnZXubp46M/Va5tpqcBpsD3HJWUCejRRRdTPtlGlWfL6N7/3z43fNYnP+8+X57whJcXPgOAX16NPCAAHn+PAHjymzb7cb0MTUwKAwgTBUx2rxVvUmwhQc8IAAbBWOhVAwDQdNKbMQi8h5MGGKBBEjiUAXE0FBQEIAWkgCsBILmSIQJwJZPsCKiRRA9XMMBBdyJNpPcXUxpsrPWOe/l7KOIUA/PKMr2sqg08fD2yavMm8bT0dFdKlK5KL2WAMkwZp2wHU2ERrIMtsA+OwVm4jSnYdl0FUFjkVDE6Eq+kYvvd6ScZnYzaJIUSZRqhgUHGPbG3MMa78TOuPgKoX12/HNTPB/X6//T/SQDg4TZo89Dn4ez/HmzA/avXHu493DXuXAB0d3u2PcUea/cCXalL0cWDrtAtOg+AAKDCDADmASsAgH1uBLHtGtwKucMDQr8iQNg+fIS18AC0mmiqGWgOWgBoqZXWAKCNttoBaK+DjgA66ayLrqCb7nroqRforR+A/gYYaBA3Dy8fMFkAmCokLALATMmW/OeA+TIALJSTB2CJIrBMTx8A2GJoAsBOM2vggC0AcAgAOAwAHAEAjkoBgOcAwAsA4CUA8Eql1niCT7774ScA+AV++wPgL/jnPwCoF4DQB0HcRyKV6ekb/JUh9r83xP6Xx3wt3D08vZTejW2//Tiv+3kJLGrFI7+lautjrj+hjAuptLHOh5hyqa1jzLXPdfugukaN6b7p1btP334DBwwajG03YvjIUWNGjx03fuKESZMJ3abPQNNs1mJ5h/4A0NOW4c5dzdsDiyln84eia7TePGs7qjlau8gpolLR0WMWgICCqtfdRhZx37YmaGDldAANDTBEUsABFJTOKjotF83D7WDL+HOpnO11j+1hbuN4mvzhKYqo4BwUn3C1jub12D8J44QO2YD5ZO4dxZaeg2W5kCdG+ShHeG2JT6klJvRJ8jGswESiG2oIAZizAQUAcKROUWBEI5RgQG+U4uUsyjASMtTDQgRdr4+h0ESKkBkBKqQosJaLEsxpjVKyDEYZ1j6iHvIwfyrpYxkJpOqok966aK2lVrpR8NGULwU1FbVwARS02mmuKV4uxGFQqoNuWuuWb28zIXjpENcVaa6l7trR6oL/wagJMnQsQj+au2ipea0pg6goxIxeoe2u/lECqQUi6OaF5J6a0bFLy+YKdZBKEaNoVkmrogLVgWhOaK8rIyPhaY1QVwUVlRBE6LPgxSE1qFQhQaqL20A3zbXXSTvNddQVf6lJLlLg3Ww7fAc=") format("woff2")
+}
+html {
+ cursor: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUBAMAAAB/pwA+AAAAAXNSR0IArs4c6QAAABJQTFRFAAAAmZmZv7+/1NTU////AAAAlbJ9ggAAAAV0Uk5TAAAAAADCawaxAAAAOElEQVQIW23GMRUAIAxDwW8BC7WAhViIfysMdCC83nTYtkuSiHJl8dj+3/to0Y+K/lBMvW3Le2g6s9cS/2KdOksAAAAASUVORK5CYII="), default;
+}
+body {
+ margin: 0;
+ background: #0000a8;
+ font-family: "TempleOS";
+ font-size: 16px;
+ line-height: 16px
+}
+marquee {
+ width: 240px;
+ background: #0000a8;
+ color: #fff
+}
+url {
+ display: none;
+}
+a {
+ color: #800;
+ text-decoration-thickness: 2px;
+}
+.page-container {
+ width: 800px;
+ margin: auto;
+ padding-bottom: 16px
+}
+.page-content {
+ padding-top: 16px;
+ padding-bottom: 16px;
+}
+.page-header {
+ position: fixed;
+ background: #0000a8;
+ color: #fff;
+ z-index: 999;
+}
+.page-header b {
+ color: #ff0
+}
+.page-header::after {
+ content: "\25b2\25b2";
+}
+.page-footer {
+ margin: auto;
+ color:#0ff
+}
+.status-avatar {
+ float: left;
+ display: inline;
+ vertical-align: top;
+ width: 128px;
+ height: 128px;
+}
+.status-container {
+ margin: 0;
+ width: 720px;
+ color: #000;
+ background: #fff;
+}
+.status-container b {
+ font-weight: normal;
+ color: #0000a8;
+}
+.status-content {
+ position: absolute;
+ padding-top: 32px;
+ padding-left: 32px;
+ padding-right: 32px;
+ width: 720px;
+ overflow: hidden
+}
+.status-header {
+ vertical-align: top
+}
+.status-footer {
+ float: right;
+ padding-top: 16px;
+ padding-right: 32px
+}
+.status-counts {
+ color: #008080;
+ display: inline-block;
+ padding-top: 16px
+}
+.status-text {
+ color: #808080;
+ padding-top: 16px;
+ width: 672px
+}
+.nostatus {
+ width: 672px
+}
+.status-timestamp {
+ color: #008080;
+ float: right;
+ padding-right: 32px
+}
+.text-cursor {
+ border: 0;
+ content: url("data:image/gif;base64,R0lGODdhEAAQAHcAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQEFAAAACwAAAAAEAAQAID//wAAAAACDoSPqcvtD6OctNqLsz4FACH5BAgUAAAALAAAAAAQABAAgAAAAAAAAAIOhI+py+0Po5y02ouzPgUAOw==");
+}
+.text-error {
+ border: 0;
+ content: url("data:image/gif;base64,R0lGODdhYAAQAHcAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQEFAAAACwAAAAAYAAQAID/V1f///8CoASCqWi229pTMc5Qz8W5+51YWwZ+Ezl6qsisWuuEkhzDL1fjN6u7FeUC5njEmU24M/Z8Sh/ySUsOm9QpylZcQY/Rrc6b/UV5j+tJfDGHSyokGYI+v93SOtuErbZldGC/C9hCx6SHx/EnOGYXeKhIaLXHJfnFuFbZMVhnaDnJSdmJ6XiTpgdWOKpoqhZ6twgKmYP4uQT7aHuLm6u7y9vLWwAAIfkECBQAAAAsAAAAAGAAEACA/1dX////AqBMgKlottvaUzFOUM/FufudWFsGfhM5eqrIrFrrhJIcwy9X4zeruxXlAuZ4xJlNuDP2fEof8klLDpvUKcpWXEGP0a3Om/1FeY/rSXwxh0sqJBmCPr/d0jrbhK22ZXRgvwvYQsekh8fxJzhmF3ioSGi1xyX5xbhW2TFYZ2g5yUnZiel4k6YHVjiqaKoWercICpmD+LkE+2h7i5uru8vby1sAADs=");
+}
+.text-menu {
+ border: 0;
+ content: url("data:image/gif;base64,R0lGODdhQAAQAHcAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQEFAAAACwAAAAAQAAQAIAAAKj///8CeQSCYXtpCSOD0y167dMZ7Sh1ilh5pGiC38i0jbmW7DurdMq58bn3N2jB6FAjIU0mAwZzOpvxycHgpkCXEgo7nqgrW7LK0+644qHWW8YW12TzOeRTW5fZoTx8R5bt0X42v3X15zfTxWQ0JlhDOIfm9ggZKTlJWWlpUAAAIfkECBQAAAAsAAAAAEAAEACAAACo////AnlMgGB7aQkjg9Mteu3TGe0odYpYeaRogt/ItI25luw7q3TKufG59zdowehQIyFNJgMGczqb8cnB4KZAlxIKO56oK1uyytPuuOKh1lvGFtdk8znkU1uX2aE8fEeW7dF+Nr919ec308VkNCZYQziH5vYIGSk5SVlpaVAAADs=");
+ position: absolute;
+ top: 0px;
+ left: 16px
+}
\ No newline at end of file
diff --git a/Slon/Static/defaults/account.json b/Slon/Static/defaults/account.json
new file mode 100644
index 0000000..2c35240
--- /dev/null
+++ b/Slon/Static/defaults/account.json
@@ -0,0 +1,8 @@
+{
+ "username": "",
+ "display_name": "",
+ "email": "",
+ "note": "",
+ "avatar": "",
+ "header": ""
+}
\ No newline at end of file
diff --git a/Slon/Static/defaults/actor.json b/Slon/Static/defaults/actor.json
new file mode 100644
index 0000000..34ab3ec
--- /dev/null
+++ b/Slon/Static/defaults/actor.json
@@ -0,0 +1,44 @@
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ {
+ "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+ "toot": "http://joinmastodon.org/ns#",
+ "featured": {
+ "@id": "toot:featured",
+ "@type": "@id"
+ },
+ "featuredTags": {
+ "@id": "toot:featuredTags",
+ "@type": "@id"
+ },
+ "alsoKnownAs": {
+ "@id": "as:alsoKnownAs",
+ "@type": "@id"
+ },
+ "movedTo": {
+ "@id": "as:movedTo",
+ "@type": "@id"
+ },
+ "schema": "http://schema.org#",
+ "PropertyValue": "schema:PropertyValue",
+ "value": "schema:value",
+ "discoverable": "toot:discoverable",
+ "suspended": "toot:suspended",
+ "memorial": "toot:memorial",
+ "indexable": "toot:indexable",
+ "attributionDomains": {
+ "@id": "toot:attributionDomains",
+ "@type": "@id"
+ }
+ }
+ ],
+ "type": "Person",
+ "manuallyApprovesFollowers": false,
+ "discoverable": false,
+ "indexable": false,
+ "memorial": false,
+ "tag": [],
+ "endpoints": {}
+}
\ No newline at end of file
diff --git a/Slon/Static/defaults/instance.json b/Slon/Static/defaults/instance.json
new file mode 100644
index 0000000..e577c34
--- /dev/null
+++ b/Slon/Static/defaults/instance.json
@@ -0,0 +1,14 @@
+{
+ "uri": "error.checksum.fail",
+ "title": "error.checksum.fail",
+ "short_description": "A fediverse instance running on TempleOS",
+ "description": "A fediverse instance running on TempleOS",
+ "email": "alec@checksum.fail",
+ "version": "1.0.0",
+ "registrations": false,
+ "stats": {
+ "user_count": 0,
+ "status_count": 0,
+ "domain_count": 0
+ }
+}
\ No newline at end of file
diff --git a/Slon/Static/html/admin/main.html b/Slon/Static/html/admin/main.html
new file mode 100644
index 0000000..08ff19e
--- /dev/null
+++ b/Slon/Static/html/admin/main.html
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Slon/Static/html/admin/setup_instance.html b/Slon/Static/html/admin/setup_instance.html
new file mode 100644
index 0000000..d065942
--- /dev/null
+++ b/Slon/Static/html/admin/setup_instance.html
@@ -0,0 +1,83 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Slon/Static/html/user.html b/Slon/Static/html/user.html
new file mode 100644
index 0000000..bab12fa
--- /dev/null
+++ b/Slon/Static/html/user.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
Loading Statuses...
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Slon/Static/js/header.js b/Slon/Static/js/header.js
new file mode 100644
index 0000000..ca1f056
--- /dev/null
+++ b/Slon/Static/js/header.js
@@ -0,0 +1,3 @@
+let lastFrameTime=0,fps=0;function calculateFPS(){requestAnimationFrame(()=>{const a=performance.now();fps=1E3/(a-lastFrameTime);lastFrameTime=a;calculateFPS()})}
+function updatePageHeader(){var a=new Date,c=a.toString().substring(0,3);const d=String(a.getMonth()+1).padStart(2,"0"),e=String(a.getDate()).padStart(2,"0");a=a.toString().substring(16,24);const f="CPU "+(10*Math.random()).toFixed(0)+" ";if(navigator.userAgent.toLowerCase().includes("firefox"))var b=3735929054;else b=performance.memory,b=b.jsHeapSizeLimit-b.usedJSHeapSize;c=c+" "+d+"/"+e+" "+a+" FPS:"+fps.toFixed(0)+" Mem:"+b.toString(16).toUpperCase()+" "+f;document.getElementsByClassName("page-header")[0].innerHTML=
+c}calculateFPS();updatePageHeader();setInterval(updatePageHeader,300);
\ No newline at end of file
diff --git a/Slon/Static/js/statuses.js b/Slon/Static/js/statuses.js
new file mode 100644
index 0000000..0f97c7a
--- /dev/null
+++ b/Slon/Static/js/statuses.js
@@ -0,0 +1,132 @@
+function updateStatusContainers() {
+ let els = document.getElementsByClassName('status-container');
+ for (var n = 0; n < els.length; n++) {
+ let pc = els[n];
+ let post_html = ""
+
+ let horizontal_fill_count = ((pc.offsetWidth - 32) / 16);
+ let url = pc.getElementsByTagName('url')[0].textContent;
+ let marqueeHref = url == "Unnamed Task" ? window.location : url;
+
+ post_html += "\u2554";
+ for (var i = 0; i < (horizontal_fill_count / 2) - ((240 / 16) / 2); i++) {
+ post_html += "\u2550";
+ }
+
+ post_html += "" + url + "... ";
+ for (var i = 0; i < (horizontal_fill_count / 2) - ((240 / 16) / 2) - 2; i++) {
+ post_html += "\u2550";
+ }
+ post_html += "[X]\u2557 ";
+
+ let height = Math.ceil(pc.getElementsByClassName('status-content')[0].offsetHeight / 16);
+ let desc = "Term";
+
+ for (var y = 0; y < height; y++) {
+ let ch = y < 4 ? desc[y] : "\u2551";
+ post_html += "" + ch + "
\u2551 ";
+ }
+
+ post_html += "\u255a";
+ for (var i = 0; i < (horizontal_fill_count + 1); i++) {
+ post_html += "\u2550";
+ }
+ post_html += "\u255d ";
+
+ pc.innerHTML += post_html;
+ pc.style.display = "inline";
+ }
+}
+
+function smolDate(a){return a.split(" ago")[0].replace("a ","1").replace("an ","1").replace("days","d").replace("day","d").replace("hours","h").replace("hour","h").replace("minutes","m").replace("minute","m").replace("seconds","s").replace("second","s").replace("few","").replace(" ","")};
+
+function updateStatuses(user, statuses) {
+ let pageContent = document.getElementsByClassName("page-content")[0];
+ let elements = document.createElement('div');
+
+ let container = document.createElement('div');
+ container.className = "status-container";
+ // Render user profile
+ {
+ let content = document.createElement('div');
+ content.className = "status-content";
+ let content_html = "";
+ content_html += "
";
+ content_html += ""
+ content_html += "" + user["summary"] + "
";
+ content_html += "Joined " + new Date(Date.parse(user["published"])).toString().substr(0,15) + "
";
+ content.innerHTML = content_html;
+ let url = document.createElement('url');
+ url.textContent = window.location;
+ let menuImg = document.createElement('img');
+ menuImg.className = "text-menu";
+ content.appendChild(menuImg);
+ container.appendChild(content);
+ container.appendChild(url);
+ elements.appendChild(container);
+ let spacer = document.createElement('div');
+ spacer.style.height = "16px";
+ elements.appendChild(spacer);
+ }
+
+ elements.className = "statuses";
+ statuses.sort((a,b) => b.id - a.id);
+ for (var i = 0; i < statuses.length; i++) {
+ let status = statuses[i];
+ let container = document.createElement('div');
+ container.className = "status-container";
+ let content = document.createElement('div');
+ content.className = "status-content";
+ let content_html = "";
+ if (status["visibility"] == "public") {
+ content_html += "🌎 ";
+ }
+ content_html += "" + smolDate(dayjs(status["created_at"]).fromNow()) + " ";
+ content_html += "" + status["content"] + "
";
+ content_html += "💬 " + status["replies_count"] + " 🔁 " + status["reblogs_count"] + " ⭐ " + status["favourites_count"] + " ";
+ content_html += "";
+ content.innerHTML = content_html;
+ let url = document.createElement('url');
+ url.textContent = status["url"];
+ let menuImg = document.createElement('img');
+ menuImg.className = "text-menu";
+ content.appendChild(menuImg);
+ container.appendChild(content);
+ container.appendChild(url);
+ elements.appendChild(container);
+ if (i < statuses.length - 1) {
+ let spacer = document.createElement('div');
+ spacer.style.height = "16px";
+ elements.appendChild(spacer);
+ }
+ }
+ if (!statuses.length) {
+ let container = document.createElement('div');
+ container.className = "status-container";
+ let content = document.createElement('div');
+ content.className = "status-content";
+ let content_html = "";
+ content_html += "&FileRead &StatusRead &StatusPrint &ExeCmdLine No statuses found.\n " + Math.random().toFixed(6) + "s ans=0x00000000=0 C:/Home> ";
+ content.innerHTML = content_html;
+ let url = document.createElement('url');
+ url.textContent = "Unnamed Task";
+ let menuImg = document.createElement('img');
+ menuImg.className = "text-menu";
+ content.appendChild(menuImg);
+ container.appendChild(content);
+ container.appendChild(url);
+ elements.appendChild(container);
+ }
+ pageContent.innerHTML = "";
+ pageContent.appendChild(elements);
+ updateStatusContainers();
+}
+
+function getStatuses(user) {
+ fetch("https://error.checksum.fail/api/v1/accounts/" + user["accountId"] + "/statuses", {
+ method: 'GET',
+ headers: {'Accept': 'application/json' }
+ })
+ .then(response => response.json())
+ .then(data => updateStatuses(user, data));
+}
diff --git a/Slon/Static/oauth/authorize.html b/Slon/Static/oauth/authorize.html
new file mode 100644
index 0000000..3b85d29
--- /dev/null
+++ b/Slon/Static/oauth/authorize.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/System/Api/Dns.HC b/System/Api/Dns.HC
new file mode 100644
index 0000000..79b9985
--- /dev/null
+++ b/System/Api/Dns.HC
@@ -0,0 +1,25 @@
+#define DNS_REQUEST_PTR 0x300010
+
+MemSet(DNS_REQUEST_PTR, NULL, sizeof(U64));
+
+class DnsRequest {
+ U64 host;
+ U64 pointer_to_u32;
+};
+
+U32 @dns_query(U8* host)
+{
+ U32 res = 0;
+ if (!host)
+ return U32_MAX;
+ DnsRequest* request = CAlloc(sizeof(DnsRequest), Fs->code_heap);
+ request->host = StrNew(host, adam_task);
+ request->pointer_to_u32 = &res;
+ U64* request_ptr = DNS_REQUEST_PTR;
+ while (*request_ptr)
+ Sleep(1);
+ LXchgU32(request_ptr, request);
+ while (!res)
+ Sleep(1);
+ return res;
+}
diff --git a/System/Api/Icmp.HC b/System/Api/Icmp.HC
new file mode 100644
index 0000000..e52040c
--- /dev/null
+++ b/System/Api/Icmp.HC
@@ -0,0 +1,32 @@
+#define ICMP_REQUEST_PTR 0x300020
+
+MemSet(ICMP_REQUEST_PTR, NULL, sizeof(U64));
+
+class IcmpRequest {
+ U64 addr;
+ U64 iden;
+ U64 seq;
+ U64 pointer_to_u32;
+};
+
+U32 @icmp_echo_request(U32 addr, U16 iden, U16 seq, IcmpRequest* request, I64 count)
+{
+ U32 res = 0; // low 16 = ttl, hi 16 = payload size
+ request->addr = addr;
+ request->iden = iden;
+ request->seq = seq;
+ request->pointer_to_u32 = &res;
+ I64 start_jiffies = cnts.jiffies;
+ U64* request_ptr = ICMP_REQUEST_PTR;
+ if (!count)
+ *request_ptr = NULL;
+ while (*request_ptr) {
+ if (!(cnts.jiffies < start_jiffies + 1000))
+ return res;
+ Sleep(1);
+ }
+ LXchgU32(request_ptr, request);
+ while (!res && cnts.jiffies < start_jiffies + 1000)
+ Sleep(1);
+ return res;
+}
\ No newline at end of file
diff --git a/System/Api/Ipv4.HC b/System/Api/Ipv4.HC
new file mode 100644
index 0000000..105ebe3
--- /dev/null
+++ b/System/Api/Ipv4.HC
@@ -0,0 +1,9 @@
+U32 @ipv4_address(I64 o3, I64 o2, I64 o1, I64 o0)
+{
+ U32 addr = NULL;
+ addr.u8[3] = o3;
+ addr.u8[2] = o2;
+ addr.u8[1] = o1;
+ addr.u8[0] = o0;
+ return addr;
+}
diff --git a/System/Api/MD5.HC b/System/Api/MD5.HC
new file mode 100644
index 0000000..176cc93
--- /dev/null
+++ b/System/Api/MD5.HC
@@ -0,0 +1,144 @@
+/*
+ * Simple MD5 implementation
+ *
+ * https://gist.github.com/creationix/4710780
+ */
+
+U32 md5_r[64] = { 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
+ 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
+ 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
+ 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 };
+// Use binary integer part of the sines of integers (in radians) as constants//
+// Initialize variables:
+U32 md5_k[64] = {
+ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a,
+ 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+ 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340,
+ 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
+ 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8,
+ 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+ 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa,
+ 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92,
+ 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+ 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
+};
+
+// leftrotate function
+U32 LEFTROTATE(U32 x, U32 c) { return (((x) << (c)) | ((x) >> (32 - (c)))); }
+
+U0 md5(U8* initial_msg, U32 initial_len, U32* md5_h)
+{
+
+ // These vars will contain the hash
+ U32 md5_h0, md5_h1, md5_h2, md5_h3;
+
+ // Message (to prepare)
+ U8* msg = NULL;
+
+ // Note: All variables are unsigned 32 bit and wrap modulo 2^32 when
+ // calculating
+
+ // r specifies the per-round shift amounts
+
+ md5_h0 = 0x67452301;
+ md5_h1 = 0xefcdab89;
+ md5_h2 = 0x98badcfe;
+ md5_h3 = 0x10325476;
+
+ // Pre-processing: adding a single 1 bit
+ // append "1" bit to message
+ /* Notice: the input bytes are considered as bits strings,
+ where the first bit is the most significant bit of the byte.[37] */
+
+ // Pre-processing: padding with zeros
+ // append "0" bit until message length in bit ≡ 448 (mod 512)
+ // append length mod (2 pow 64) to message
+
+ U32 new_len;
+ for (new_len = initial_len * 8 + 1; new_len % 512 != 448; new_len++)
+ ;
+ new_len /= 8;
+
+ msg = CAlloc(new_len + 64, adam_task); // also appends "0" bits
+ // (we alloc also 64 extra bytes...)
+ MemCpy(msg, initial_msg, initial_len);
+ msg[initial_len] = 128; // write the "1" bit
+
+ U32 bits_len = 8 * initial_len; // note, we append the len
+ MemCpy(msg + new_len, &bits_len, 4); // in bits at the end of the buffer
+
+ // Process the message in successive 512-bit chunks:
+ // for each 512-bit chunk of message:
+ U32 offset;
+ for (offset = 0; offset < new_len; offset += (512 / 8)) {
+
+ // break chunk into sixteen 32-bit words w[j], 0 ≤ j ≤ 15
+ U32* w = (msg + offset)(U32*);
+
+ // Initialize hash value for this chunk:
+ U32 a = md5_h0;
+ U32 b = md5_h1;
+ U32 c = md5_h2;
+ U32 d = md5_h3;
+
+ // Main loop:
+ U32 i;
+ for (i = 0; i < 64; i++) {
+
+ U32 f, g;
+
+ if (i < 16) {
+ f = (b & c) | ((~b) & d);
+ g = i;
+ } else if (i < 32) {
+ f = (d & b) | ((~d) & c);
+ g = (5 * i + 1) % 16;
+ } else if (i < 48) {
+ f = b ^ c ^ d;
+ g = (3 * i + 5) % 16;
+ } else {
+ f = c ^ (b | (~d));
+ g = (7 * i) % 16;
+ }
+
+ U32 temp = d;
+ d = c;
+ c = b;
+ // printf("rotateLeft(%x + %x + %x + %x, %d)\n", a, f, k[i], w[g], r[i]);
+ b = b + LEFTROTATE((a + f + md5_k[i] + w[g]), md5_r[i]);
+ a = temp;
+ }
+
+ // Add this chunk's hash to result so far:
+
+ md5_h0 += a;
+ md5_h1 += b;
+ md5_h2 += c;
+ md5_h3 += d;
+ }
+
+ md5_h[0] = md5_h0;
+ md5_h[1] = md5_h1;
+ md5_h[2] = md5_h2;
+ md5_h[3] = md5_h3;
+
+ // cleanup
+ Free(msg);
+}
+
+U8* md5_string(U8* buf, I64 size)
+{
+ U32 md5_h[4];
+ md5(buf, size, &md5_h[0]);
+ U8* str = CAlloc(33, adam_task);
+ StrPrint(str + StrLen(str), "%02x%02x%02x%02x", md5_h[0].u8[0],
+ md5_h[0].u8[1], md5_h[0].u8[2], md5_h[0].u8[3]);
+ StrPrint(str + StrLen(str), "%02x%02x%02x%02x", md5_h[1].u8[0],
+ md5_h[1].u8[1], md5_h[1].u8[2], md5_h[1].u8[3]);
+ StrPrint(str + StrLen(str), "%02x%02x%02x%02x", md5_h[2].u8[0],
+ md5_h[2].u8[1], md5_h[2].u8[2], md5_h[2].u8[3]);
+ StrPrint(str + StrLen(str), "%02x%02x%02x%02x", md5_h[3].u8[0],
+ md5_h[3].u8[1], md5_h[3].u8[2], md5_h[3].u8[3]);
+ return str;
+}
diff --git a/System/Api/NetInfo.HC b/System/Api/NetInfo.HC
new file mode 100644
index 0000000..79f0384
--- /dev/null
+++ b/System/Api/NetInfo.HC
@@ -0,0 +1,32 @@
+#define NETINFO_REQUEST_PTR 0x300030
+
+MemSet(NETINFO_REQUEST_PTR, NULL, sizeof(U64));
+
+class NetInfoRequest {
+ U64 mac_address;
+ U64 ipv4_address;
+ U64 ipv4_netmask;
+ U64 ipv4_network;
+ U64 ipv4_gateway;
+ U64 dns_server_address;
+ U64 dns_server_port;
+ U64 rx_bytes;
+ U64 rx_frames;
+ U64 tx_bytes;
+ U64 tx_frames;
+ U64 pointer_to_u32;
+};
+
+NetInfoRequest* @net_info_request()
+{
+ U32 res = 0;
+ NetInfoRequest* req = CAlloc(sizeof(NetInfoRequest), Fs->code_heap);
+ req->pointer_to_u32 = &res;
+ U64* request_ptr = NETINFO_REQUEST_PTR;
+ while (*request_ptr)
+ Sleep(1);
+ LXchgU32(request_ptr, req);
+ while (!res)
+ Sleep(1);
+ return req;
+}
diff --git a/System/Api/Tcp.HC b/System/Api/Tcp.HC
new file mode 100644
index 0000000..ce39f4f
--- /dev/null
+++ b/System/Api/Tcp.HC
@@ -0,0 +1,210 @@
+#define TCP_SOCKET_REQUEST_PTR 0x300000
+#define TCP_BIND_REQUEST_PTR 0x300040
+#define TCP_ACCEPT_REQUEST_PTR 0x300050
+
+MemSet(TCP_SOCKET_REQUEST_PTR, NULL, sizeof(U64));
+
+// TcpSocket states
+
+#define TCP_SOCKET_STATE_IDLE 0
+#define TCP_SOCKET_STATE_ESTABLISHED 1
+#define TCP_SOCKET_STATE_CLOSED 2
+#define TCP_SOCKET_STATE_CONNECTING 4
+
+class TcpSocket {
+ U64 remote_addr;
+ U64 remote_port;
+ U64 state;
+ U64 receive_buffer_ptr; // Pointer to receive buffer in physical memory
+ U64 receive_buffer_size;
+ U64 receive_buffer_filled; // Number of bytes Net has put into buffer
+ U64 receive_buffer_kick; // Net sets this to 1 when it has data available for
+ // us, we set back to 0 when ready to receive
+ U64 send_buffer_ptr;
+ U64 send_buffer_size;
+ U64 send_buffer_filled;
+ U64 send_buffer_kick; // We set this to 1 when we have data available to net,
+ // Net sets back to 0 when ready to receive
+ U0(*close)
+ ();
+ U64(*receive)
+ (U64 buf, U64 length);
+ U0(*send)
+ (U64 buf, U64 length);
+};
+
+class TcpBind {
+ U64 port;
+ U64 function;
+ U64 response_code;
+};
+
+U8 @tcp_close_wrapper_function[16]
+ = { 0x55, 0x48, 0x8B, 0xEC, 0x68, 0x78,
+ 0x56, 0x34, 0x12, 0xE8, 0x02, 0x6D,
+ 0x02, 0x00, 0x5D, 0xC3 };
+
+U8 @tcp_receive_wrapper_function[32] = {
+ 0x55, 0x48, 0x8B, 0xEC, 0x56, 0x57, 0x48, 0x8B, 0x75, 0x18, 0x48,
+ 0x8B, 0x7D, 0x10, 0x56, 0x57, 0x68, 0x78, 0x56, 0x34, 0x12, 0xE8,
+ 0x5E, 0x62, 0x02, 0x00, 0x5F, 0x5E, 0x5D, 0xC2, 0x10, 0x00
+};
+
+U8 @tcp_send_wrapper_function[32] = {
+ 0x55, 0x48, 0x8B, 0xEC, 0x56, 0x57, 0x48, 0x8B, 0x75, 0x18, 0x48,
+ 0x8B, 0x7D, 0x10, 0x56, 0x57, 0x68, 0x78, 0x56, 0x34, 0x12, 0xE8,
+ 0x5E, 0x62, 0x02, 0x00, 0x5F, 0x5E, 0x5D, 0xC2, 0x10, 0x00
+};
+
+U0 @tcp_socket_send(TcpSocket* s, U64 buf, U64 length)
+{
+ while (s->send_buffer_kick)
+ Sleep(1);
+ U64 pos = 0;
+ U64 bytes_to_send = 0;
+ while (pos < length) {
+ if ((length - pos) > s->send_buffer_size)
+ bytes_to_send = s->send_buffer_size;
+ else
+ bytes_to_send = length - pos;
+ MemCpy(s->send_buffer_ptr, buf + pos, bytes_to_send);
+ s->send_buffer_filled = bytes_to_send;
+ s->send_buffer_kick = 1;
+ pos += bytes_to_send;
+ while (s->send_buffer_kick)
+ Sleep(1);
+ }
+}
+
+U64 @tcp_socket_receive(TcpSocket* s, U64 buf, U64 size)
+{
+ s->receive_buffer_size = size;
+ s->receive_buffer_kick = 0;
+ while (!s->receive_buffer_kick) {
+ if (s->state == TCP_SOCKET_STATE_CLOSED)
+ return NULL;
+ Sleep(1);
+ }
+ U64 bytes_received = s->receive_buffer_filled;
+ if (bytes_received > 0) {
+ MemCpy(buf, s->receive_buffer_ptr, bytes_received);
+ }
+ return bytes_received;
+}
+
+U0 @tcp_wait_for_connection_established(TcpSocket* s)
+{
+ while (s->state != TCP_SOCKET_STATE_ESTABLISHED)
+ Sleep(1);
+}
+
+U0 @tcp_socket_close(TcpSocket* s)
+{
+ if (s->close)
+ Free(s->close);
+ if (s->receive)
+ Free(s->receive);
+ if (s->send)
+ Free(s->send);
+ s->state = TCP_SOCKET_STATE_CLOSED;
+}
+
+TcpSocket* @tcp_socket_create(U8* host, U64 port)
+{
+ U64 addr = @dns_query(host);
+ TcpSocket* s = CAlloc(sizeof(TcpSocket), adam_task->code_heap);
+ s->remote_addr = addr;
+ s->remote_port = port;
+
+ U64 a;
+
+ s->close = MAlloc(16, adam_task->code_heap);
+ MemCpy(s->close, @tcp_close_wrapper_function, 16);
+ a = s->close;
+ a += 0x05;
+ MemSetU32(a, s, 1);
+ a = s->close;
+ a += 0x09;
+ @patch_call_rel32(a, &@tcp_socket_close);
+
+ s->receive = MAlloc(25, adam_task->code_heap);
+ MemCpy(s->receive, @tcp_receive_wrapper_function, 32);
+ a = s->receive;
+ a += 0x11;
+ MemSetU32(a, s, 1);
+ a = s->receive;
+ a += 0x15;
+ @patch_call_rel32(a, &@tcp_socket_receive);
+
+ s->send = MAlloc(32, adam_task->code_heap);
+ MemCpy(s->send, @tcp_send_wrapper_function, 32);
+ a = s->send;
+ a += 0x11;
+ MemSetU32(a, s, 1);
+ a = s->send;
+ a += 0x15;
+ @patch_call_rel32(a, &@tcp_socket_send);
+
+ U64* request_ptr = TCP_SOCKET_REQUEST_PTR;
+ while (*request_ptr)
+ Sleep(1);
+ LXchgU32(request_ptr, s);
+ return s;
+}
+
+U64 @tcp_socket_bind(U64 port, U64 function)
+{
+ if (!port || !function)
+ return NULL;
+
+ TcpBind* b = CAlloc(sizeof(TcpBind), adam_task->code_heap);
+ b->port = port;
+ b->function = function; // U0 my_spawn_wrapper_function(TcpSocket* s)
+
+ U64* request_ptr = TCP_BIND_REQUEST_PTR;
+ while (*request_ptr)
+ Sleep(1);
+ LXchgU32(request_ptr, b);
+ while (*request_ptr)
+ Sleep(1);
+ U64 res = b->response_code;
+ Free(b);
+ return res;
+}
+
+TcpSocket* @tcp_socket_accept(TcpSocket* s)
+{
+ if (!s || !s->remote_addr || !s->remote_port)
+ return NULL;
+
+ U64 a;
+
+ s->close = MAlloc(16, adam_task->code_heap);
+ MemCpy(s->close, @tcp_close_wrapper_function, 16);
+ a = s->close;
+ a += 0x05;
+ MemSetU32(a, s, 1);
+ a = s->close;
+ a += 0x09;
+ @patch_call_rel32(a, &@tcp_socket_close);
+
+ s->receive = MAlloc(25, adam_task->code_heap);
+ MemCpy(s->receive, @tcp_receive_wrapper_function, 32);
+ a = s->receive;
+ a += 0x11;
+ MemSetU32(a, s, 1);
+ a = s->receive;
+ a += 0x15;
+ @patch_call_rel32(a, &@tcp_socket_receive);
+
+ s->send = MAlloc(32, adam_task->code_heap);
+ MemCpy(s->send, @tcp_send_wrapper_function, 32);
+ a = s->send;
+ a += 0x11;
+ MemSetU32(a, s, 1);
+ a = s->send;
+ a += 0x15;
+ @patch_call_rel32(a, &@tcp_socket_send);
+
+ return s;
+}
diff --git a/System/Api/Tls.HC b/System/Api/Tls.HC
new file mode 100644
index 0000000..c5cecd4
--- /dev/null
+++ b/System/Api/Tls.HC
@@ -0,0 +1,99 @@
+#define TLS_CONNECT_TASK_STACK_SIZE 524288
+#define TLS_CLIENT_MESSAGE_BUFFER_SIZE 0xFFFF
+
+class TlsSocket : TcpSocket {
+ U64 ctx;
+ U8 client_message[TLS_CLIENT_MESSAGE_BUFFER_SIZE];
+};
+
+U0 @tls_send_pending(TlsSocket* s)
+{
+ U32 out_buffer_len = 0;
+ U8* out_buffer = @tls_get_write_buffer(s->ctx, &out_buffer_len);
+ if (out_buffer && out_buffer_len) {
+ @tcp_socket_send(s, out_buffer, out_buffer_len);
+ @tls_buffer_clear(s->ctx);
+ }
+}
+
+U0 @tls_socket_send(TlsSocket* s, U64 buf, U64 size)
+{
+ @tls_write(s->ctx, buf, size);
+ @tls_send_pending(s);
+}
+
+U64 @tls_socket_receive(TlsSocket* s, U8* buf, I64 size)
+{
+ I64 len = @tcp_socket_receive(s, s->client_message, TLS_CLIENT_MESSAGE_BUFFER_SIZE);
+ if (len) {
+ @tls_consume_stream(s->ctx, s->client_message, len, NULL);
+ @tls_send_pending(s);
+ }
+ return @tls_read(s->ctx, buf, size);
+}
+
+U0 @tls12_connect(TlsSocket* s)
+{
+ I64 len;
+ @tls_client_connect(s->ctx);
+ @tls_send_pending(s);
+ while (!@tls_established(s->ctx)) {
+ len = @tcp_socket_receive(s, &s->client_message, TLS_CLIENT_MESSAGE_BUFFER_SIZE);
+ if (len) {
+ @tls_consume_stream(s->ctx, &s->client_message, len, NULL);
+ @tls_send_pending(s);
+ }
+ Sleep(1);
+ }
+}
+
+TlsSocket* @tls_socket_create(U8* server_name, U64 port = 443)
+{
+ U64 addr = @dns_query(server_name);
+ TlsSocket* s = CAlloc(sizeof(TlsSocket), adam_task->code_heap);
+ s->remote_addr = addr;
+ s->remote_port = port;
+
+ U64 a;
+
+ s->close = MAlloc(16, adam_task->code_heap);
+ MemCpy(s->close, @tcp_close_wrapper_function, 16);
+ a = s->close;
+ a += 0x05;
+ MemSetU32(a, s, 1);
+ a = s->close;
+ a += 0x09;
+ @patch_call_rel32(a, &@tcp_socket_close);
+
+ s->receive = MAlloc(25, adam_task->code_heap);
+ MemCpy(s->receive, @tcp_receive_wrapper_function, 32);
+ a = s->receive;
+ a += 0x11;
+ MemSetU32(a, s, 1);
+ a = s->receive;
+ a += 0x15;
+ @patch_call_rel32(a, &@tls_socket_receive);
+
+ s->send = MAlloc(32, adam_task->code_heap);
+ MemCpy(s->send, @tcp_send_wrapper_function, 32);
+ a = s->send;
+ a += 0x11;
+ MemSetU32(a, s, 1);
+ a = s->send;
+ a += 0x15;
+ @patch_call_rel32(a, &@tls_socket_send);
+
+ U64* request_ptr = TCP_SOCKET_REQUEST_PTR;
+ while (*request_ptr)
+ Sleep(1);
+ LXchgU32(request_ptr, s);
+
+ while (s->state != TCP_SOCKET_STATE_ESTABLISHED)
+ Sleep(1);
+
+ s->ctx = @tls_create_context(0, TLS_V12);
+ @tls_sni_set(s->ctx, StrNew(server_name, adam_task->code_heap));
+ Spawn(&@tls12_connect, s, , , , TLS_CONNECT_TASK_STACK_SIZE);
+
+ return s;
+}
diff --git a/System/Config/Net.json b/System/Config/Net.json
new file mode 100644
index 0000000..9217064
--- /dev/null
+++ b/System/Config/Net.json
@@ -0,0 +1,10 @@
+{
+ "tcpip.ipv4_address": "10.20.0.10",
+ "tcpip.ipv4_netmask": "255.255.255.0",
+ "tcpip.ipv4_network": "10.20.0.0",
+ "tcpip.ipv4_gateway": "10.20.0.254",
+ "tcpip.ipv4_dns_server_address": "8.8.8.8",
+ "tcpip.ipv4_dns_server_port": "53",
+ "tcpip.mss_size": "1360",
+ "eof": "eof"
+}
\ No newline at end of file
diff --git a/System/Drivers/Virtio-blk.HC b/System/Drivers/Virtio-blk.HC
new file mode 100644
index 0000000..2008a88
--- /dev/null
+++ b/System/Drivers/Virtio-blk.HC
@@ -0,0 +1,473 @@
+// Virtio.HC
+
+//
+// PCI virtio I/O registers.
+//
+
+#define VIRTIO_PCI_HOST_FEATURES 0 // Features supported by the host
+#define VIRTIO_PCI_GUEST_FEATURES 4 // Features activated by the guest
+#define VIRTIO_PCI_QUEUE_PFN 8 // PFN for the currently selected queue
+#define VIRTIO_PCI_QUEUE_SIZE 12 // Queue size for the currently selected queue
+#define VIRTIO_PCI_QUEUE_SEL 14 // Queue selector
+#define VIRTIO_PCI_QUEUE_NOTIFY 16 // Queue notifier
+#define VIRTIO_PCI_STATUS 18 // Device status register
+#define VIRTIO_PCI_ISR 19 // Interrupt status register
+#define VIRTIO_PCI_CONFIG 20 // Configuration data block
+
+//
+// PCI virtio status register bits
+//
+
+#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1
+#define VIRTIO_CONFIG_S_DRIVER 2
+#define VIRTIO_CONFIG_S_DRIVER_OK 4
+#define VIRTIO_CONFIG_S_FAILED 0x80
+
+//
+// Ring descriptor flags
+//
+
+#define VRING_DESC_F_NEXT 1 // Buffer continues via the next field
+#define VRING_DESC_F_WRITE 2 // Buffer is write-only (otherwise read-only)
+#define VRING_DESC_F_INDIRECT 4 // Buffer contains a list of buffer descriptors
+
+class @virtio_queue_buf
+{
+ U64 address;
+ U32 length;
+ U16 flags;
+ U16 next;
+};
+class @virtio_avail
+{
+ U16 flags;
+ U16 index;
+ U16 ring[256];
+ U16 int_index;
+};
+class @virtio_used_item
+{
+ U32 index;
+ U32 length;
+};
+class @virtio_used
+{
+ U16 flags;
+ U16 index;
+ @virtio_used_item ring[256];
+ U16 int_index;
+};
+class @virtio_queue
+{
+ @virtio_queue_buf buffers[256];
+ @virtio_avail available;
+ U8 padding[3578];
+ @virtio_used used;
+};
+
+class @virtio_avail_buf
+{
+ U32 index;
+ U64 address;
+ U32 length;
+};
+
+class @virtio_buf_info
+{
+ U8* buffer;
+ U64 size;
+ U8 flags;
+
+ // If the user wants to keep same buffer as passed in this struct, use "true".
+ // otherwise, the supplied buffer will be copied in the queues' buffer
+ Bool copy;
+};
+
+// Virtio-blk.HC
+
+#define BDT_VIRTIO_BLK 10
+
+#define VIRTIO_BLK_T_IN 0
+#define VIRTIO_BLK_T_OUT 1
+#define VIRTIO_BLK_T_FLUSH 4
+
+#define VIRTIO_BLK_MAX_BLK 0x400000 // Limit blkdev to 2G max, set to NULL to use entire disk (not recommended for RedSea)
+
+class @virtio_blk
+{
+ U16 port;
+ U32 blks;
+ @virtio_queue* vq;
+ I64 vq_size;
+ I64 vq_index;
+ U8 status;
+};
+
+class @virtio_blk_request
+{
+ U32 type;
+ U32 priority;
+ U64 sector;
+};
+
+@virtio_blk virtio_blk;
+MemSet(&virtio_blk, 0, sizeof(@virtio_blk));
+
+I64 VirtioBlkInit()
+{
+ I64 j;
+
+ // Scan for device
+ j = PCIClassFind(0x010000, 0);
+ if (j < 0) {
+ "\n[virtio-blk] No device found\n";
+ return -1;
+ }
+ virtio_blk.port = PCIReadU32(j.u8[2],
+ j.u8[1], j.u8[0], 0x10)
+ & 0xFFFFFFFC;
+
+ virtio_blk.blks = InU32(virtio_blk.port + VIRTIO_PCI_CONFIG);
+
+ // Reset Device
+ OutU8(virtio_blk.port + VIRTIO_PCI_STATUS, 0);
+
+ // Found Driver
+ OutU8(virtio_blk.port + VIRTIO_PCI_STATUS, InU8(virtio_blk.port + VIRTIO_PCI_STATUS) | VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER);
+
+ // Set up virt queue
+ OutU16(virtio_blk.port + VIRTIO_PCI_QUEUE_SEL, 0);
+ virtio_blk.vq_size = InU16(virtio_blk.port + VIRTIO_PCI_QUEUE_SIZE); // 256
+ virtio_blk.vq = CAllocAligned(sizeof(@virtio_queue), 4096, adam_task->code_heap);
+ OutU32(virtio_blk.port + VIRTIO_PCI_QUEUE_PFN, virtio_blk.vq / 4096);
+
+ // Init OK
+ OutU8(virtio_blk.port + VIRTIO_PCI_STATUS, InU8(virtio_blk.port + VIRTIO_PCI_STATUS) | VIRTIO_CONFIG_S_DRIVER_OK);
+ virtio_blk.vq_index = 0;
+}
+
+// DskVIO.HC
+
+U0 VIOFlush()
+{
+ I64 j;
+ I64 vq_idx;
+ @virtio_blk_request* brq = CAlloc(sizeof(@virtio_blk_request), adam_task);
+ brq->type = VIRTIO_BLK_T_FLUSH;
+ brq->sector = NULL;
+ vq_idx = virtio_blk.vq->available.index % 256;
+ virtio_blk.vq->buffers[virtio_blk.vq_index % 256].address = brq;
+ virtio_blk.vq->buffers[virtio_blk.vq_index % 256].length = sizeof(@virtio_blk_request);
+ virtio_blk.vq->buffers[virtio_blk.vq_index % 256].flags = VRING_DESC_F_NEXT;
+ virtio_blk.vq->buffers[virtio_blk.vq_index % 256].next = (virtio_blk.vq_index + 1) % 256;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].address = &virtio_blk.status;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].length = 1;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].flags = VRING_DESC_F_WRITE;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].next = 0;
+ virtio_blk.vq->available.ring[vq_idx] = virtio_blk.vq_index % 256;
+ virtio_blk.vq_index += 2;
+ j = virtio_blk.vq->used.index;
+ virtio_blk.vq->available.index++;
+ OutU16(virtio_blk.port + VIRTIO_PCI_QUEUE_NOTIFY, 0);
+ while (j == virtio_blk.vq->used.index) {
+ Yield;
+ }
+ Free(brq);
+}
+
+Bool VIORBlks(CDrv* dv, U8* buf, I64 blk, I64 cnt)
+{
+ no_warn dv;
+ I64 i, j;
+ I64 vq_idx;
+ U64 addr;
+ @virtio_blk_request* brq = CAlloc(sizeof(@virtio_blk_request), adam_task);
+ for (i = 0; i < cnt; i++) {
+ brq->type = VIRTIO_BLK_T_IN;
+ brq->sector = blk + i;
+ vq_idx = virtio_blk.vq->available.index % 256;
+ addr = buf + (BLK_SIZE * i);
+ virtio_blk.vq->buffers[virtio_blk.vq_index % 256].address = brq;
+ virtio_blk.vq->buffers[virtio_blk.vq_index % 256].length = sizeof(@virtio_blk_request);
+ virtio_blk.vq->buffers[virtio_blk.vq_index % 256].flags = VRING_DESC_F_NEXT;
+ virtio_blk.vq->buffers[virtio_blk.vq_index % 256].next = (virtio_blk.vq_index + 1) % 256;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].address = addr;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].length = BLK_SIZE;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].flags = VRING_DESC_F_WRITE | VRING_DESC_F_NEXT;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].next = (virtio_blk.vq_index + 2) % 256;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].address = &virtio_blk.status;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].length = 1;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].flags = VRING_DESC_F_WRITE;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].next = 0;
+ virtio_blk.vq->available.ring[vq_idx] = virtio_blk.vq_index % 256;
+ virtio_blk.vq_index += 3;
+ j = virtio_blk.vq->used.index;
+ virtio_blk.vq->available.index++;
+ OutU16(virtio_blk.port + VIRTIO_PCI_QUEUE_NOTIFY, 0);
+ while (j == virtio_blk.vq->used.index) {
+ Yield;
+ }
+ }
+ Free(brq);
+ return TRUE;
+}
+
+Bool VIOWBlks(CDrv* dv, U8* buf, I64 blk, I64 cnt)
+{
+ no_warn dv;
+ I64 i, j;
+ I64 vq_idx;
+ U64 addr;
+ @virtio_blk_request* brq = CAlloc(sizeof(@virtio_blk_request), adam_task);
+ for (i = 0; i < cnt; i++) {
+ brq->type = VIRTIO_BLK_T_OUT;
+ brq->sector = blk + i;
+ vq_idx = virtio_blk.vq->available.index % 256;
+ addr = buf + (BLK_SIZE * i);
+ virtio_blk.vq->buffers[virtio_blk.vq_index % 256].address = brq;
+ virtio_blk.vq->buffers[virtio_blk.vq_index % 256].length = sizeof(@virtio_blk_request);
+ virtio_blk.vq->buffers[virtio_blk.vq_index % 256].flags = VRING_DESC_F_NEXT;
+ virtio_blk.vq->buffers[virtio_blk.vq_index % 256].next = (virtio_blk.vq_index + 1) % 256;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].address = addr;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].length = BLK_SIZE;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].flags = VRING_DESC_F_NEXT;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].next = (virtio_blk.vq_index + 2) % 256;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].address = &virtio_blk.status;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].length = 1;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].flags = VRING_DESC_F_WRITE;
+ virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].next = 0;
+ virtio_blk.vq->available.ring[vq_idx] = virtio_blk.vq_index % 256;
+ virtio_blk.vq_index += 3;
+ j = virtio_blk.vq->used.index;
+ virtio_blk.vq->available.index++;
+ OutU16(virtio_blk.port + VIRTIO_PCI_QUEUE_NOTIFY, 0);
+ while (j == virtio_blk.vq->used.index) {
+ Yield;
+ }
+ }
+ Free(brq);
+ VIOFlush;
+ return TRUE;
+}
+
+U0 RedSeaTryInit(CDrv* dv)
+{
+ CRedSeaBoot br;
+ Bool unlock;
+ try {
+ unlock = DrvLock(dv);
+ BlkRead(dv, &br, dv->drv_offset, 1);
+ if (br.signature != MBR_PT_REDSEA || br.signature2 != 0xAA55)
+ return;
+ dv->fs_type = FSt_REDSEA;
+ CallExtStr("RedSeaFreeFreeLst", dv);
+ dv->spc = 1;
+ dv->size = br.sects;
+ dv->data_area = dv->drv_offset + br.bitmap_sects;
+ dv->root_clus = br.root_clus;
+ dv->fat1 = dv->fat2 = dv->drv_offset + 1;
+ CallExtStr("DrvFATBlkAlloc", dv);
+ if (unlock)
+ DrvUnlock(dv);
+ } catch if (unlock)
+ DrvUnlock(dv);
+}
+
+U8 MountVirtioBlk()
+{ // Mount Virtio-blk device
+ CDrv* dv = DrvMakeFreeSlot(DrvNextFreeLet('A'));
+ CBlkDev* bd = BlkDevNextFreeSlot(dv->drv_let, BDT_RAM);
+ CRedSeaBoot* bs = CAlloc(BLK_SIZE, adam_task);
+ bd->max_blk = 512;
+ BlkDevAdd(bd, , TRUE, TRUE);
+ bd->type = BDT_VIRTIO_BLK;
+ if (VIRTIO_BLK_MAX_BLK) {
+ bd->max_blk = Min(VIRTIO_BLK_MAX_BLK, virtio_blk.blks);
+ } else {
+ bd->max_blk = virtio_blk.blks;
+ }
+ Free(bd->RAM_dsk);
+ dv->size = bd->max_blk + 1 - bd->drv_offset;
+ VIORBlks(dv, bs, 0, 1);
+ dv->root_clus = bs->root_clus;
+ dv->data_area = bs->bitmap_sects;
+ dv->next_free = NULL;
+ dv->last_free = NULL;
+ Free(bs);
+ RedSeaTryInit(dv);
+ return dv->drv_let;
+}
+
+// DskBlk2.HC
+
+Bool BlkRead2(CDrv* dv, U8* buf, I64 blk, I64 cnt)
+{ // Read blk cnt from Drv to buf.
+ Bool res = TRUE, unlock;
+ CBlkDev* bd = dv->bd;
+ if (cnt <= 0)
+ return TRUE;
+ DrvChk(dv);
+ try {
+ unlock = DrvLock(dv);
+ CallExtStr("BlkDevInit", bd);
+ if (dv->drv_offset && blk < dv->drv_offset || blk + cnt > dv->drv_offset + dv->size)
+ throw('Drv');
+ if (bd->flags & BDF_READ_CACHE)
+ CallExtStr("RCache", dv, &buf, &blk, &cnt);
+ if (cnt > 0) {
+ switch (bd->type) {
+ case BDT_RAM:
+ MemCpy(buf, bd->RAM_dsk + blk << BLK_SIZE_BITS, cnt << BLK_SIZE_BITS);
+ break;
+ case BDT_ISO_FILE_READ:
+ case BDT_ISO_FILE_WRITE:
+ FBlkRead(bd->file_dsk, buf, blk, cnt);
+ break;
+ case BDT_ATA:
+ case BDT_ATAPI:
+ res = CallExtStr("ATARBlks", dv, buf, blk, cnt);
+ break;
+ case BDT_VIRTIO_BLK:
+ res = VIORBlks(dv, buf, blk, cnt);
+ break;
+ }
+ bd->last_time = tS;
+ if (bd->flags & BDF_READ_CACHE)
+ CallExtStr("DskCacheAdd", dv, buf, blk, cnt);
+ }
+ if (unlock)
+ DrvUnlock(dv);
+ } catch if (unlock)
+ DrvUnlock(dv);
+ return res;
+}
+
+Bool BlkWrite2(CDrv* dv, U8* buf, I64 blk, I64 cnt)
+{ // Write blk cnt from buf to Drv.
+ Bool res = TRUE, unlock;
+ CBlkDev* bd = dv->bd;
+ if (cnt <= 0)
+ return TRUE;
+ DrvChk(dv);
+ try {
+ unlock = DrvLock(dv);
+ CallExtStr("BlkDevInit", bd);
+ if (bd->flags & BDF_READ_ONLY && !(bd->flags & BDF_READ_ONLY_OVERRIDE))
+ throw('BlkDev');
+ if (dv->drv_offset && blk < dv->drv_offset || blk + cnt > dv->drv_offset + dv->size)
+ throw('Drv');
+ if (cnt > 0) {
+ switch (bd->type) {
+ case BDT_RAM:
+ MemCpy(bd->RAM_dsk + blk << BLK_SIZE_BITS, buf, cnt << BLK_SIZE_BITS);
+ break;
+ case BDT_ISO_FILE_READ:
+ case BDT_ISO_FILE_WRITE:
+ FBlkWrite(bd->file_dsk, buf, blk, cnt);
+ break;
+ case BDT_ATA:
+ case BDT_ATAPI:
+ res = CallExtStr("ATAWBlks", dv, buf, blk, cnt);
+ break;
+ case BDT_VIRTIO_BLK:
+ res = VIOWBlks(dv, buf, blk, cnt);
+ break;
+ }
+ bd->last_time = tS;
+ if (bd->flags & BDF_READ_CACHE)
+ CallExtStr("DskCacheAdd", dv, buf, blk, cnt);
+ }
+ if (unlock)
+ DrvUnlock(dv);
+ } catch if (unlock)
+ DrvUnlock(dv);
+ return res;
+}
+
+@patch_jmp_rel32(&BlkRead, &BlkRead2);
+@patch_jmp_rel32(&BlkWrite, &BlkWrite2);
+
+// DskBlkDev2.HC
+
+CBlkDev* BlkDevChk2(CBlkDev* bd, Bool except = TRUE)
+{ // Check for valid BlkDev. Throw exception.
+ if (bd->type == BDT_VIRTIO_BLK)
+ return bd;
+ if (!bd || bd->bd_signature != BD_SIGNATURE_VAL || !(BDT_NULL < bd->type < BDT_TYPES_NUM)) {
+ if (except)
+ throw('BlkDev');
+ else
+ return NULL;
+ } else
+ return bd;
+}
+
+@patch_jmp_rel32(&BlkDevChk, &BlkDevChk2);
+
+// DskDrv2.HC
+
+DefineLstLoad("ST_BLKDEV_TYPES2",
+ "NULL\0RAM\0ATA\0FILE_READ\0FILE_WRITE\0ATAPI\0NULL\0NULL\0NULL\0NULL\0VIRTIO\0");
+
+U8 DrvTextAttrGet2(U8 drv_let = 0)
+{ // Get color of drive.
+ U8* blkdev_text_attr2 = blkdev_text_attr;
+ U8* drv_text_attr2 = drv_text_attr;
+ I64 dta_size = 3;
+ drv_let = Let2Let(drv_let);
+ if (drv_let == 'A')
+ return BLACK << 4 | WHITE;
+ if ('A' <= drv_let <= 'Z')
+ return blkdev_text_attr2[Let2BlkDevType(drv_let)] << 4 | drv_text_attr2[drv_let % dta_size];
+ else
+ return BLACK << 4 | WHITE;
+}
+
+U0 DrvRep2()
+{ // Drive report.
+ CDrv* dv;
+ CBlkDev* bd;
+ I64 ch, i, drv_let, attr;
+ U8* st;
+ "\nDefined Drives:\n";
+ for (i = 0, dv = blkdev.drvs; i < DRVS_NUM; i++, dv++) {
+ if (dv->dv_signature == DRV_SIGNATURE_VAL) {
+ bd = dv->bd;
+ drv_let = Drv2Let(dv);
+ if (Bt(&dv->fs_type, FStf_DISABLE))
+ ch = '-';
+ else if (drv_let == blkdev.boot_drv_let)
+ ch = ':';
+ else
+ ch = '+';
+ attr = DrvTextAttrGet(drv_let);
+ "\dFG,%d\d\dBG,%d\d%C %-8Z %-10Z %04X %04X %02X\n",
+ attr & 15, attr >> 4, drv_let, dv->fs_type &FSG_TYPE_MASK, "ST_DRV_TYPES",
+ bd->type, "ST_BLKDEV_TYPES2", bd->base0, bd->base1, bd->unit;
+ if (st = DrvModelNum(drv_let)) {
+ "Model#:%s\n", st;
+ Free(st);
+ }
+ if (st = DrvSerialNum(drv_let)) {
+ "Serial#:%s\n", st;
+ Free(st);
+ }
+ if (bd->type == BDT_ISO_FILE_READ || bd->type == BDT_ISO_FILE_WRITE)
+ "File=\"%s\"\n", bd->file_dsk_name;
+ "%016X-%016X\n\dFG\d\dBG\d", dv->drv_offset, dv->drv_offset + dv->size - 1;
+ }
+ }
+ "Home Dir:\"%s\"\n", blkdev.home_dir;
+}
+
+@patch_jmp_rel32(&DrvTextAttrGet, &DrvTextAttrGet2);
+@patch_jmp_rel32(&DrvRep, &DrvRep2);
+
+VirtioBlkInit;
+MountVirtioBlk;
+
+if (Let2Drv('A', 0) && !Let2Drv('A')->root_clus) {
+ "[virtio-blk] RedSea filesystem not initialized, formatting.\n";
+ Fmt('A', , FALSE, FSt_REDSEA);
+ Cd("M:/System/");
+}
diff --git a/System/FFI/Base.HC b/System/FFI/Base.HC
new file mode 100644
index 0000000..e013399
--- /dev/null
+++ b/System/FFI/Base.HC
@@ -0,0 +1,51 @@
+#define PUSH_SYSV_REGS \
+ asm {PUSH RCX PUSH RDX PUSH RBX PUSH RBP PUSH RSI PUSH RDI PUSH R8 PUSH R9 PUSH \
+ R10 PUSH R11 PUSH R12 PUSH R13 PUSH R14 PUSH R15}
+#define POP_SYSV_REGS \
+ p0 = p0; \
+ p1 = p1; \
+ p2 = p2; \
+ p3 = p3; \
+ p4 = p4; \
+ p5 = p5; \
+ asm {POP R15 POP R14 POP R13 POP R12 POP R11 POP R10 POP R9 POP R8 POP RDI POP \
+ RSI POP RBP POP RBX POP RDX POP RCX}
+#define GET_SYSV_ARGS \
+ asm {PUSH R9 PUSH R8 PUSH RCX PUSH RDX PUSH RSI PUSH RDI} \
+ I64 reg RDI p0; \
+ I64 reg RSI p1; \
+ I64 reg RDX p2; \
+ I64 reg RCX p3; \
+ I64 reg R8 p4; \
+ I64 reg R9 p5; \
+ asm {POP RDI POP RSI POP RDX POP RCX POP R8 POP R9}
+
+#define MOV_ANS_RAX asm { MOV[&ans], RAX }
+#define MOV_PARAM0_RDI asm {MOV [¶m0], RDI}
+
+I64 param0;
+I64 elf_argc;
+U8** elf_argv;
+
+asm {
+_ELF_CALL::
+ PUSH RBP
+ MOV RBP,RSP
+ MOV RAX,U64 SF_ARG1[RBP]
+ MOV RDI,U64 SF_ARG2[RBP]
+ MOV RSI,U64 SF_ARG3[RBP]
+ TEST RAX,RAX
+ JZ @@05
+ CALL RAX
+@@05: POP RBP
+ RET1 8
+}
+
+U0 _main()
+{
+ MOV_PARAM0_RDI
+ CallInd(_ELF_CALL, param0, elf_argc, elf_argv);
+ UserTaskCont;
+}
+
+U0 _exit() { UserTaskCont; }
diff --git a/System/FFI/ELF64.HC b/System/FFI/ELF64.HC
new file mode 100644
index 0000000..4392924
--- /dev/null
+++ b/System/FFI/ELF64.HC
@@ -0,0 +1,301 @@
+#define EI_NIDENT 16
+#define EM_X86_64 0x3E
+#define ET_EXEC 2
+#define ET_DYN 3
+
+U0 @elf64_debug_print(U8 fmt, ...)
+{
+ // FIXME: Remove unnecessary debug_print statements and PrintErr for errors.
+ no_warn fmt, argc, argv;
+}
+
+class Elf64_Ehdr {
+ U8 e_ident[EI_NIDENT]; /* Magic number and other info */
+ U16 e_type; /* Object file type */
+ U16 e_machine; /* Architecture */
+ U32 e_version; /* Object file version */
+ U64 e_entry; /* Entry point virtual address */
+ U64 e_phoff; /* Program header table file offset */
+ U64 e_shoff; /* Section header table file offset */
+ U32 e_flags; /* Processor-specific flags */
+ U16 e_ehsize; /* ELF header size in bytes */
+ U16 e_phentsize; /* Program header table entry size */
+ U16 e_phnum; /* Program header table entry count */
+ U16 e_shentsize; /* Section header table entry size */
+ U16 e_shnum; /* Section header table entry count */
+ U16 e_shstrndx; /* Section header string table index */
+};
+
+class Elf64_Shdr {
+ U32 sh_name; /* Section name (string tbl index) */
+ U32 sh_type; /* Section type */
+ U64 sh_flags; /* Section flags */
+ U64 sh_addr; /* Section virtual addr at execution */
+ U64 sh_offset; /* Section file offset */
+ U64 sh_size; /* Section size in bytes */
+ U32 sh_link; /* Link to another section */
+ U32 sh_info; /* Additional section information */
+ U64 sh_addralign; /* Section alignment */
+ U64 sh_entsize; /* Entry size if section holds table */
+};
+
+class Elf64_Sym {
+ U32 st_name; /* Symbol name (string tbl index) */
+ U8 st_info; /* Symbol type and binding */
+ U8 st_other; /* Symbol visibility */
+ U16 st_shndx; /* Section index */
+ U64 st_value; /* Symbol value */
+ U64 st_size; /* Symbol size */
+};
+
+class PLT_entry {
+ U8 pad[0x10];
+};
+
+class RELA_entry {
+ U64 r_offset;
+ U64 r_info;
+ I64 r_addend;
+};
+
+class Elf {
+ union {
+ U8* u8;
+ Elf64_Ehdr* ehdr;
+ } I64 size;
+ U8* dynstr;
+ Elf64_Sym* dynsym;
+ PLT_entry* plt;
+ RELA_entry* rela_dyn;
+ RELA_entry* rela_plt;
+ Elf64_Sym* strtab;
+ Elf64_Sym* symtab;
+ I64 rela_dyn_size;
+ I64 rela_plt_size;
+ I64 strtab_size;
+ I64 symtab_size;
+};
+
+U0(*_start)
+();
+
+U0 unimplemented_symbol()
+{
+ I32 s = 0xDEADF00D;
+ PrintWarn("Unimplemented symbol: %s\n", s);
+ Dbg;
+ while (1)
+ Sleep(1);
+}
+
+Bool is_valid_elf(Elf* elf)
+{
+ Bool res = TRUE;
+ if (MemCmp(elf->u8 + 1, "ELF", 3)) {
+ @elf64_debug_print("Invalid signature (not ELF).\n");
+ res = FALSE;
+ }
+ if (elf->ehdr->e_type != ET_EXEC && elf->ehdr->e_type != ET_DYN) {
+ @elf64_debug_print("Invalid object file type.\n");
+ res = FALSE;
+ }
+ if (elf->ehdr->e_machine != EM_X86_64) {
+ @elf64_debug_print("Invalid architecture.\n");
+ res = FALSE;
+ }
+ return res;
+}
+
+U0 process_elf_section_header_table(Elf* elf)
+{
+ Elf64_Shdr* shdr = elf->u8 + elf->ehdr->e_shoff;
+ Elf64_Shdr* shdr_shstrtab = shdr + elf->ehdr->e_shstrndx;
+ U8* shstrtab = elf->u8 + shdr_shstrtab->sh_offset;
+ I64 i = 0;
+ while (i < elf->ehdr->e_shnum) {
+ if (!StrCmp(shstrtab + shdr->sh_name, ".symtab")) {
+ @elf64_debug_print("found symtab at 0x%08x, size = %d\n", shdr->sh_offset,
+ shdr->sh_size);
+ elf->symtab = elf->u8 + shdr->sh_offset;
+ elf->symtab_size = shdr->sh_size;
+ }
+ if (!StrCmp(shstrtab + shdr->sh_name, ".strtab")) {
+ @elf64_debug_print("found strtab at 0x%08x, size = %d\n", shdr->sh_offset,
+ shdr->sh_size);
+ elf->strtab = elf->u8 + shdr->sh_offset;
+ elf->strtab_size = shdr->sh_size;
+ }
+ if (shdr->sh_addr) {
+ MemCpy(shdr->sh_addr, elf->u8 + shdr->sh_offset, shdr->sh_size);
+ if (!StrCmp(shstrtab + shdr->sh_name, ".dynstr"))
+ elf->dynstr = shdr->sh_addr;
+ if (!StrCmp(shstrtab + shdr->sh_name, ".dynsym"))
+ elf->dynsym = shdr->sh_addr;
+ if (!StrCmp(shstrtab + shdr->sh_name, ".plt"))
+ elf->plt = shdr->sh_addr;
+ if (!StrCmp(shstrtab + shdr->sh_name, ".rela.dyn")) {
+ elf->rela_dyn = shdr->sh_addr;
+ elf->rela_dyn_size = shdr->sh_size / shdr->sh_entsize;
+ }
+ if (!StrCmp(shstrtab + shdr->sh_name, ".rela.plt")) {
+ elf->rela_plt = shdr->sh_addr;
+ elf->rela_plt_size = shdr->sh_size / shdr->sh_entsize;
+ }
+ if (!StrCmp(shstrtab + shdr->sh_name, ".bss") || !StrCmp(shstrtab + shdr->sh_name, ".tbss")) {
+ MemSet(shdr->sh_addr, NULL, shdr->sh_size);
+ @elf64_debug_print(
+ "Zeroed out section '%s' at physical address 0x%06x, size = %d bytes\n",
+ shstrtab + shdr->sh_name, shdr->sh_addr, shdr->sh_size);
+ } else
+ @elf64_debug_print(
+ "MemCpy section '%s' to physical address 0x%06x, size = %d bytes\n",
+ shstrtab + shdr->sh_name, shdr->sh_addr, shdr->sh_size);
+ if (!StrCmp(shstrtab + shdr->sh_name, ".bss")) {
+ MemSet(shdr->sh_addr, NULL, shdr->sh_size);
+ @elf64_debug_print("MemSet section '%s' at physical address 0x%06x to NULL, "
+ "size = %d bytes\n",
+ shstrtab + shdr->sh_name, shdr->sh_addr, shdr->sh_size);
+ }
+ }
+ shdr++;
+ i++;
+ }
+}
+
+U0 process_elf_rela_dyn_entries(Elf* elf)
+{
+ I64 i;
+ U8* entry_name;
+ RELA_entry* rela_dyn = elf->rela_dyn;
+ for (i = 0; i < elf->rela_dyn_size; i++) {
+ entry_name = elf->dynstr + elf->dynsym[(rela_dyn->r_info >> 32)].st_name;
+ @elf64_debug_print("rela_dyn->r_offset = %08x\n", rela_dyn->r_offset);
+ @elf64_debug_print("entry name = '%s'\n", entry_name);
+ if (!StrCmp(entry_name, "__libc_start_main")) {
+ *(rela_dyn->r_offset)(U64*) = &_main;
+ @elf64_debug_print("Set value for .rela.dyn entry '%s' to: &_main\n",
+ entry_name);
+ }
+ if (!StrCmp(entry_name, "stdin")) {
+ *(rela_dyn->r_offset)(U64*) = 0;
+ @elf64_debug_print("Set value for .rela.dyn entry '%s' to: %d\n", entry_name, 0);
+ }
+ if (!StrCmp(entry_name, "stdout")) {
+ *(rela_dyn->r_offset)(U64*) = 1;
+ @elf64_debug_print("Set value for .rela.dyn entry '%s' to: %d\n", entry_name, 1);
+ }
+ if (!StrCmp(entry_name, "stderr")) {
+ *(rela_dyn->r_offset)(U64*) = 2;
+ @elf64_debug_print("Set value for .rela.dyn entry '%s' to: %d\n", entry_name, 2);
+ }
+ rela_dyn++;
+ }
+}
+
+CHashClass* get_symbol_hash_entry(U8* entry_name)
+{
+ I64 i;
+ CHashSrcSym* sym;
+ CHashTable* tbl = Fs->hash_table;
+ while (tbl) {
+ for (i = 0; i < tbl->mask; i++) {
+ sym = tbl->body[i];
+ while (sym) {
+ if (sym->type == HTT_CLASS)
+ if (!StrCmp(sym->str, entry_name))
+ return sym;
+ sym = sym->next;
+ }
+ }
+ tbl = tbl->next;
+ }
+ return NULL;
+}
+
+U64 get_symbol_address(U8* entry_name)
+{
+ CHash* h = HashFind(entry_name, Fs->hash_table, Fs->hash_table->mask);
+ if (!h)
+ return NULL;
+ switch (h->type) {
+ case HTT_GLBL_VAR:
+ return h(CHashGlblVar*)->data_addr;
+ break;
+ case HTT_FUN:
+ return h(CHashFun*)->exe_addr;
+ break;
+ default:
+ return NULL;
+ break;
+ }
+ return NULL;
+}
+
+U0 process_elf_rela_plt_entries(Elf* elf)
+{
+ I64 i;
+ U32 handler;
+ U32* patch;
+ U8* entry_name;
+ Bool symbol_exists;
+ PLT_entry* plt = elf->plt;
+ RELA_entry* rela_plt = elf->rela_plt;
+ plt++;
+ for (i = 0; i < elf->rela_plt_size; i++) {
+ symbol_exists = FALSE;
+ entry_name = elf->dynstr + elf->dynsym[(rela_plt->r_info >> 32)].st_name;
+ handler = MAlloc(sizeof(unimplemented_symbol), adam_task->code_heap);
+ MemCpy(handler, &unimplemented_symbol, sizeof(unimplemented_symbol));
+ patch = handler + 0x0A;
+ *patch = entry_name;
+ @patch_jmp_rel32(plt, handler);
+ @patch_call_rel32(handler + 0x16, &PrintErr);
+ //@patch_call_rel32(handler + 0x21, &_exit);
+ if (!StrCmp(entry_name, "__libc_start_main")) {
+ symbol_exists = TRUE;
+ @patch_jmp_rel32(plt, &_main);
+ @elf64_debug_print("Set value for .rela.plt entry '%s' to &_main\n", entry_name);
+ }
+ if (get_symbol_address(entry_name)) {
+ symbol_exists = TRUE;
+ @patch_jmp_rel32(plt, get_symbol_address(entry_name));
+ @elf64_debug_print("Set value for .rela.plt entry '%s' to &%s\n", entry_name,
+ entry_name);
+ }
+ if (!symbol_exists)
+ @elf64_debug_print(
+ "Set value for .rela.plt entry '%s' to &unimplemented_symbol\n",
+ entry_name);
+ rela_plt++;
+ plt++;
+ }
+}
+
+U0 load_elf(...)
+{
+ if (argc < 1) {
+ PrintErr("Not enough arguments.\n");
+ return;
+ }
+ if (!FileFind(argv[0])) {
+ PrintErr("File not found: %s\n", argv[0]);
+ return;
+ }
+
+ Elf elf;
+ elf.u8 = FileRead(argv[0], &elf.size);
+ @elf64_debug_print("Load file '%s', size = %d bytes\n", argv[0], elf.size);
+
+ if (!is_valid_elf(&elf)) {
+ PrintErr("File is not a valid ELF x86-64 executable.\n");
+ return;
+ }
+
+ process_elf_section_header_table(&elf);
+ process_elf_rela_dyn_entries(&elf);
+ process_elf_rela_plt_entries(&elf);
+
+ _start = elf.ehdr->e_entry;
+ elf_argc = argc;
+ elf_argv = argv;
+}
\ No newline at end of file
diff --git a/System/FFI/LibC.HC b/System/FFI/LibC.HC
new file mode 100644
index 0000000..e49e567
--- /dev/null
+++ b/System/FFI/LibC.HC
@@ -0,0 +1,317 @@
+#define stdin 0
+#define stdout 1
+#define stderr 2
+
+U0 bcmp()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ MemCmp(p0, p1, p2);
+ POP_SYSV_REGS
+}
+
+U0 calloc()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ CAlloc(p0 * p1, adam_task->code_heap);
+ POP_SYSV_REGS
+}
+
+U0 free()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ Free(p0);
+ POP_SYSV_REGS
+}
+
+I64 @isatty()
+{
+ return 0;
+}
+
+U0 isatty()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ // Dbg;
+ @isatty;
+ POP_SYSV_REGS
+}
+
+I64 @fwrite(U8* ptr, I64 size, I64 nmemb, U64 stream)
+{
+ U8* tmp;
+ switch (stream) {
+ case stdout:
+ case stderr:
+ tmp = CAlloc((size * nmemb) + 1, adam_task->code_heap);
+ MemCpy(tmp, ptr, (size * nmemb));
+#ifdef QEMU_RUN_TESTS
+ QemuDebugMsg(tmp);
+#endif
+ DocPutS(adam_task->put_doc, tmp);
+ Free(tmp);
+ // if (!MemCmp(tmp, "VERIFICATION FAILED", 19))
+ // Break;
+ break;
+ default:
+ break;
+ }
+ return size * nmemb;
+}
+
+U0 fwrite()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @fwrite(p0, p1, p2, p3);
+ POP_SYSV_REGS
+}
+
+U64 @getentropy(U8* buffer, U64 length)
+{
+ I64 i;
+ for (i = 0; i < length; i++)
+ buffer[i] = RandU64;
+ return 0;
+}
+
+U0 getentropy()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @getentropy(p0, p1);
+ POP_SYSV_REGS
+}
+
+U0 htonl()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ EndianU32(p0);
+ POP_SYSV_REGS
+}
+
+U0 ntohl()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ EndianU32(p0);
+ POP_SYSV_REGS
+}
+
+U0 htons()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ EndianU16(p0);
+ POP_SYSV_REGS
+}
+
+U0 ntohs()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ EndianU16(p0);
+ POP_SYSV_REGS
+}
+
+U0 malloc()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ MAlloc(p0, adam_task->code_heap);
+ POP_SYSV_REGS
+}
+
+U0 memcmp()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ MemCmp(p0, p1, p2);
+ POP_SYSV_REGS
+}
+
+U0 memcpy()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ MemCpy(p0, p1, p2);
+ POP_SYSV_REGS
+}
+
+U8* @memmove(U8* dest, U8* src, I64 n)
+{
+ I64 i;
+ U8* from = src;
+ U8* to = dest;
+ if (from == to || n == 0)
+ return dest;
+ if (to > from && to - from < n) {
+ /* to overlaps with from */
+ /* */
+ /* */
+ /* copy in reverse, to avoid overwriting from */
+ for (i = n - 1; i >= 0; i--)
+ to[i] = from[i];
+ return dest;
+ }
+ if (from > to && from - to < n) {
+ /* to overlaps with from */
+ /* */
+ /* */
+ /* copy forwards, to avoid overwriting from */
+ for (i = 0; i < n; i++)
+ to[i] = from[i];
+ return dest;
+ }
+ MemCpy(dest, src, n);
+ return dest;
+}
+
+U0 memmove()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @memmove(p0, p1, p2);
+ POP_SYSV_REGS
+}
+
+U0 memset()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ MemSet(p0, p1, p2);
+ POP_SYSV_REGS
+}
+
+U0 putc()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ PutChars(p0);
+ POP_SYSV_REGS
+}
+
+U0 rand()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ RandU64;
+ POP_SYSV_REGS
+}
+
+U8* @realloc(U8* ptr, I64 size)
+{
+ U8* new;
+ if (!ptr) {
+ new = MAlloc(size, adam_task->code_heap);
+ } else {
+ new = MAlloc(size, adam_task->code_heap);
+ MemCpy(new, ptr, size);
+ Free(ptr);
+ }
+ return new;
+}
+
+U0 realloc()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @realloc(p0, p1);
+ POP_SYSV_REGS
+}
+
+// FIXME: It is non-obvious how to take a [u8] and convert it to a
+// formatted string in Jakt, so we have to do this hack for
+// now. Hopefully, this will change soon.
+U0 sprintf()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ StrPrint(p0, p1, p2, p3, p4, p5);
+ POP_SYSV_REGS
+}
+
+I64 @strncmp(U8* s1, U8* s2, I32 n)
+{
+ U64 u1, u2;
+
+ while (n-- > 0) {
+ u1 = *s1++;
+ u2 = *s2++;
+ u1 = u1 & 0xff;
+ u2 = u2 & 0xff;
+ if (u1 != u2)
+ return u1 - u2;
+ if (u1 == '\0')
+ return 0;
+ }
+ return 0;
+}
+
+U0 strncmp()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @strncmp(p0, p1, p2);
+ POP_SYSV_REGS
+}
+
+U0 strcmp()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ StrCmp(p0, p1);
+ POP_SYSV_REGS
+}
+
+U0 strlen()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ StrLen(p0);
+ POP_SYSV_REGS
+}
+
+I64 tos_nist_offset = 5020;
+#define NIST_TIME_OFFSET (tos_nist_offset - local_time_offset / CDATE_FREQ)
+
+public
+I64 CDate2Unix(CDate dt)
+{ // TempleOS datetime to Unix timestamp.
+ return ToI64((dt - Str2Date("1/1/1970")) / CDATE_FREQ + NIST_TIME_OFFSET);
+}
+
+I64 @time(I64* ptr)
+{
+ no_warn ptr;
+ return CDate2Unix(Now);
+}
+
+U0 time()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @time(p0);
+ POP_SYSV_REGS
+}
+
+U0 toupper()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ ToUpper(p0);
+ POP_SYSV_REGS
+}
+
+U0 __assert_fail()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ "%s:%d: %s: %s\n", p1, p2, p3, p0;
+ Break;
+ POP_SYSV_REGS
+}
diff --git a/System/FFI/New.HC b/System/FFI/New.HC
new file mode 100644
index 0000000..e8cdd42
--- /dev/null
+++ b/System/FFI/New.HC
@@ -0,0 +1,35 @@
+U0 _ZdlPv()
+{
+ // operator delete(void*)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ Free(p0);
+ POP_SYSV_REGS
+}
+
+U0 _ZdlPvm()
+{
+ // operator delete(void*, unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ Free(p0);
+ POP_SYSV_REGS
+}
+
+U0 _Znwm()
+{
+ // operator new(unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ MAlloc(p0, adam_task);
+ POP_SYSV_REGS
+}
+
+U0 _ZnwmRKSt9nothrow_t()
+{
+ // operator new(unsigned long, std::nothrow_t const&)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ MAlloc(p0, adam_task);
+ POP_SYSV_REGS
+}
diff --git a/System/Jakt/DC.HC b/System/Jakt/DC.HC
new file mode 100644
index 0000000..ea20b52
--- /dev/null
+++ b/System/Jakt/DC.HC
@@ -0,0 +1,288 @@
+U0 _Z8dc_aliasm()
+{
+ // dc_alias(unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ DCAlias(p0);
+ POP_SYSV_REGS
+}
+
+U0 _Z7dc_blotmmmm()
+{
+ // dc_blot(unsigned long, unsigned long, unsigned long, unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ GrBlot(p0, p1, p2, p3);
+ POP_SYSV_REGS
+}
+
+U8* @dc_buffer(CDC* dc) { return dc->body; }
+
+U0 _Z9dc_bufferm()
+{
+ // dc_buffer(unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @dc_buffer(p0);
+ POP_SYSV_REGS
+}
+
+I64 @dc_color(CDC* dc) { return dc->color; }
+
+U0 _Z8dc_colorm()
+{
+ // dc_color(unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @dc_color(p0);
+ POP_SYSV_REGS
+}
+
+U0 @dc_copy(CDC* dest, I64 x, I64 y, CDC* src)
+{
+
+ // If position is off-screen, return
+ if (x > dest->width - 1 || y > dest->height - 1)
+ return;
+
+ // If device context dimensions match, MemCpy and return
+ if (dest->width_internal == src->width_internal && dest->height == src->height) {
+ MemCpy(dest->body, src->body, dest->width_internal * dest->height);
+ return;
+ }
+
+ CDC* dc1 = DCAlias(dest);
+ CDC* dc2 = DCAlias(src);
+
+ I64 src_line = 0;
+ I64 src_row = 0;
+ I64 clip_y = 0;
+
+ // Handle horizontal clipping left
+ while (x < 0) {
+ dc2->x0++;
+ x++;
+ }
+
+ // Handle vertical clipping top
+ while (y < 0) {
+ dc2->body += src->width_internal;
+ dc2->y0++;
+ y++;
+ }
+
+ // default, clip line to copy as width-left off screen
+ src_line = src->width - dc2->x0;
+
+ if (-dc2->x0 + x + src->width >= dest->width) {
+ src_line -= ((-dc2->x0 + x + src->width) - dest->width);
+ }
+
+ dc2->body += dc2->x0;
+ clip_y = y;
+
+ while (src_row < (src->height - dc2->y0) && clip_y < dest->height) {
+ MemCpy(dc1->body + (y * dest->width) + x, dc2->body, src_line);
+ dc2->body += src->width_internal;
+ dc1->body += dest->width_internal;
+ clip_y++;
+ src_row++;
+ }
+
+ Free(dc2);
+ Free(dc1);
+}
+
+U0 _Z7dc_copymmmm()
+{
+ // dc_copy(unsigned long, unsigned long, unsigned long, unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @dc_copy(p0, p1, p2, p3);
+ POP_SYSV_REGS
+}
+
+U0 _Z10dc_destroym()
+{
+ // dc_destroy(unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ DCDel(p0);
+ POP_SYSV_REGS
+}
+
+U0 _Z14dc_draw_circlemlll()
+{
+ // dc_draw_circle(unsigned long, long, long, long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ GrCircle3(p0, p1, p2, 0, p3);
+ POP_SYSV_REGS
+}
+
+U0 _Z19dc_draw_filled_rectmllll()
+{
+ // dc_draw_filled_rect(unsigned long, long, long, long, long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ GrRect(p0, p1, p2, p3, p4);
+ POP_SYSV_REGS
+}
+
+U0 _Z12dc_draw_linemllll()
+{
+ // dc_draw_line(unsigned long, long, long, long, long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ GrLine3(p0, p1, p2, 0, p3, p4, 0);
+ POP_SYSV_REGS
+}
+
+U0 _Z13dc_draw_pixelmll()
+{
+ // dc_draw_pixel(unsigned long, long, long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ GrPlot(p0, p1, p2);
+ POP_SYSV_REGS
+}
+
+U0 _Z7dc_fillmm()
+{
+ // dc_fill(unsigned long, unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ DCFill(p0, p1);
+ POP_SYSV_REGS
+}
+
+CDC* @dc_gr_dc() { return gr.dc; }
+
+U0 _Z8dc_gr_dcv()
+{
+ // dc_gr_dc()
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @dc_gr_dc();
+ POP_SYSV_REGS
+}
+
+I64 @dc_height(CDC* dc) { return dc->height; }
+
+U0 _Z9dc_heightm()
+{
+ // dc_height(unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @dc_height(p0);
+ POP_SYSV_REGS
+}
+
+U0 _Z17dc_load_from_filePKc()
+{
+ // dc_load_from_file(char const*)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ GRRead(p0);
+ POP_SYSV_REGS
+}
+
+U0 _Z6dc_newmm()
+{
+ // dc_new(unsigned long, unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ DCNew(p0, p1);
+ POP_SYSV_REGS
+}
+
+U0 _Z11dc_pixel_atmll()
+{
+ // dc_pixel_at(unsigned long, long, long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ GrPeek(p0, p1, p2);
+ POP_SYSV_REGS
+}
+
+U0 _Z16dc_replace_colormmm()
+{
+ // dc_replace_color(unsigned long, unsigned long, unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ DCColorChg(p0, p1, p2);
+ POP_SYSV_REGS
+}
+
+U0 _Z13dc_screenshotv()
+{
+ // dc_screenshot()
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ DCScrnCapture(1);
+ POP_SYSV_REGS
+}
+
+U0 _Z15dc_save_to_filePKcm()
+{
+ // dc_save_to_file(char const*, unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ GRWrite(p0, p1);
+ POP_SYSV_REGS
+}
+
+U0 @dc_set_color(CDC* dc, I64 color) { dc->color = color; }
+
+U0 _Z12dc_set_colorml()
+{
+ // dc_set_color(unsigned long, long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @dc_set_color(p0, p1);
+ POP_SYSV_REGS
+}
+
+U0 @dc_set_thickness(CDC* dc, I64 thickness) { dc->thick = thickness; }
+
+U0 _Z16dc_set_thicknessml()
+{
+ // dc_set_thickness(unsigned long, long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @dc_set_thickness(p0, p1);
+ POP_SYSV_REGS
+}
+
+I64 @dc_thickness(CDC* dc) { return dc->thick; }
+
+U0 _Z12dc_thicknessm()
+{
+ // dc_thickness(unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @dc_thickness(p0);
+ POP_SYSV_REGS
+}
+
+I64 @dc_width(CDC* dc) { return dc->width; }
+
+U0 _Z8dc_widthm()
+{
+ // dc_width(unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @dc_width(p0);
+ POP_SYSV_REGS
+}
+
+I64 @dc_width_internal(CDC* dc) { return dc->width_internal; }
+
+U0 _Z17dc_width_internalm()
+{
+ // dc_width_internal(unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @dc_width_internal(p0);
+ POP_SYSV_REGS
+}
\ No newline at end of file
diff --git a/System/Jakt/IOPort.HC b/System/Jakt/IOPort.HC
new file mode 100644
index 0000000..e192c14
--- /dev/null
+++ b/System/Jakt/IOPort.HC
@@ -0,0 +1,53 @@
+U0 _Z14ioport_read_u8t()
+{
+ // ioport_read_u8(unsigned short)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ InU8(p0);
+ POP_SYSV_REGS
+}
+
+U0 _Z15ioport_read_u16t()
+{
+ // ioport_read_u16(unsigned short)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ InU16(p0);
+ POP_SYSV_REGS
+}
+
+U0 _Z15ioport_read_u32t()
+{
+ // ioport_read_u32(unsigned short)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ InU32(p0);
+ POP_SYSV_REGS
+}
+
+U0 _Z15ioport_write_u8th()
+{
+ // ioport_write_u8(unsigned short, unsigned char)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ OutU8(p0, p1);
+ POP_SYSV_REGS
+}
+
+U0 _Z16ioport_write_u16tt()
+{
+ // ioport_write_u16(unsigned short, unsigned short)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ OutU16(p0, p1);
+ POP_SYSV_REGS
+}
+
+U0 _Z16ioport_write_u32tj()
+{
+ // ioport_write_u32(unsigned short, unsigned int)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ OutU32(p0, p1);
+ POP_SYSV_REGS
+}
diff --git a/System/Jakt/Input.HC b/System/Jakt/Input.HC
new file mode 100644
index 0000000..b427a40
--- /dev/null
+++ b/System/Jakt/Input.HC
@@ -0,0 +1,72 @@
+U0 _Z16input_get_stringPKc()
+{
+ // input_get_string(char const*)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ GetStr(p0);
+ POP_SYSV_REGS
+}
+
+Bool @input_key_down(U8 scancode) { return Bt(kbd.down_bitmap, scancode); }
+
+U0 _Z14input_key_downh()
+{
+ // input_key_down(unsigned char)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @input_key_down(p0);
+ POP_SYSV_REGS
+}
+
+Bool @input_mouse_left() { return ms.lb; }
+
+U0 _Z16input_mouse_leftv()
+{
+ // input_mouse_left()
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @input_mouse_left();
+ POP_SYSV_REGS
+}
+
+Bool @input_mouse_right() { return ms.rb; }
+
+U0 _Z17input_mouse_rightv()
+{
+ // input_mouse_right()
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @input_mouse_right();
+ POP_SYSV_REGS
+}
+
+I64 @input_mouse_x() { return ms.pos.x; }
+
+U0 _Z13input_mouse_xv()
+{
+ // input_mouse_x()
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @input_mouse_x();
+ POP_SYSV_REGS
+}
+
+I64 @input_mouse_y() { return ms.pos.y; }
+
+U0 _Z13input_mouse_yv()
+{
+ // input_mouse_y()
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @input_mouse_y();
+ POP_SYSV_REGS
+}
+
+U0 _Z17input_press_a_keyv()
+{
+ // input_press_a_key()
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ PressAKey;
+ POP_SYSV_REGS
+}
\ No newline at end of file
diff --git a/System/Jakt/OS.HC b/System/Jakt/OS.HC
new file mode 100644
index 0000000..f54d6c0
--- /dev/null
+++ b/System/Jakt/OS.HC
@@ -0,0 +1,289 @@
+U0 _Z8os_blinkPKc()
+{
+ // os_blink(char const*)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ F64 frequency = Str2F64(p0);
+ Print("called os_blink(%.1f)\n", frequency);
+ Blink(frequency);
+ POP_SYSV_REGS
+}
+
+U64 @os_call(U8* function_name, U64 arg)
+{
+ if (!function_name)
+ return NULL;
+ if (!StrLen(function_name))
+ return NULL;
+ CHash* h = HashFind(function_name, Fs->hash_table, Fs->hash_table->mask);
+ if (!h)
+ return NULL;
+ if (h->type & HTT_FUN == HTT_FUN) {
+ CallInd(h(CHashFun*)->exe_addr, arg);
+ } else {
+ return NULL;
+ }
+}
+
+U0 _Z7os_callmm()
+{
+ // os_call(unsigned long, unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @os_call(p0, p1);
+ POP_SYSV_REGS
+}
+
+U0 _Z16os_device_callocj()
+{
+ // os_device_calloc(unsigned int)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ CAllocAligned(p0, 4096, adam_task->code_heap);
+ POP_SYSV_REGS
+}
+
+U0 _Z7os_exitv()
+{
+ // os_exit()
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ UserTaskCont;
+ POP_SYSV_REGS
+}
+
+U8* @os_file_picker(U8* path, U8* glob)
+{
+ U8* full_path = CAlloc(StrLen(path) + StrLen(glob) + 4, adam_task);
+ CatPrint(full_path, "%s/%s", path, glob);
+
+ CDirEntry* de = FilesFind(full_path);
+ Free(full_path);
+
+ CDirEntry* tmpde;
+ U8* file_list = NULL;
+ U8* selected_file = NULL;
+ I64 list_pos = 0;
+ I64 list_size = 0;
+
+ tmpde = de;
+ while (tmpde) {
+ list_size += StrLen(tmpde->name) + 2;
+ tmpde = tmpde->next;
+ }
+
+ file_list = CAlloc(list_size, adam_task);
+
+ tmpde = de;
+ while (tmpde) {
+ StrCpy(file_list + list_pos, tmpde->name);
+ list_pos += StrLen(tmpde->name) + 1;
+ tmpde = tmpde->next;
+ }
+
+ I64 list_index = Adam("PopUpPickLst(0x%08x);\n", file_list);
+ Free(file_list);
+ list_pos = 0;
+
+ if (list_index < 0) {
+ DirTreeDel(de);
+ return StrNew("", adam_task);
+ }
+
+ tmpde = de;
+ while (tmpde) {
+ if (list_index == list_pos) {
+ selected_file = CAlloc(StrLen(path) + StrLen(tmpde->name) + 4, adam_task);
+ CatPrint(selected_file, "%s/%s", path, tmpde->name);
+ break;
+ }
+ StrCpy(file_list + list_pos, tmpde->name);
+ list_pos++;
+ tmpde = tmpde->next;
+ }
+
+ DirTreeDel(de);
+ return selected_file;
+}
+
+U0 _Z14os_file_pickerPKcS0_()
+{
+ // os_file_picker(char const*, char const*)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @os_file_picker(p0, p1);
+ POP_SYSV_REGS
+}
+
+U8* @os_files_list(U8* path)
+{
+ U8* full_path = CAlloc(StrLen(path) + 4, adam_task);
+ CatPrint(full_path, "%s", path);
+
+ CDirEntry* de = FilesFind(full_path);
+ Free(full_path);
+
+ CDateStruct ds;
+ CDirEntry* tmpde;
+ U8* file_list = NULL;
+ I64 list_size = 0;
+
+ tmpde = de;
+ while (tmpde) {
+ list_size += StrLen(tmpde->name) + 48; // Should be enough for filename, date,
+ // filesize + semicolon separators
+ tmpde = tmpde->next;
+ }
+
+ if (!list_size)
+ return NULL;
+
+ file_list = CAlloc(list_size, adam_task);
+
+ tmpde = de;
+ I64 counter = 0;
+
+ while (tmpde) {
+ if (counter > 0) {
+ StrCpy(file_list + StrLen(file_list), "|");
+ }
+ StrCpy(file_list + StrLen(file_list), tmpde->name);
+ if (tmpde->attr & RS_ATTR_DIR)
+ StrCpy(file_list + StrLen(file_list), "/");
+ StrCpy(file_list + StrLen(file_list), ";");
+ Date2Struct(&ds, tmpde->datetime);
+ StrPrint(file_list + StrLen(file_list), "%04d-%02d-%02d %02d:%02d", ds.year,
+ ds.mon, ds.day_of_mon, ds.hour, ds.min);
+ StrCpy(file_list + StrLen(file_list), ";");
+ StrPrint(file_list + StrLen(file_list), "%d", tmpde->size);
+ tmpde = tmpde->next;
+ counter++;
+ }
+
+ DirTreeDel(de);
+ return file_list;
+}
+
+U0 _Z14os_path_existsPKc()
+{
+ // os_path_exists(char const*)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ FileFind(p0);
+ POP_SYSV_REGS
+}
+
+U0 _Z13os_files_listPKc()
+{
+ // os_files_list(char const*)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @os_files_list(p0);
+ POP_SYSV_REGS
+}
+
+Bool @os_is_vm()
+{
+ CRAXRBCRCXRDX res;
+ CPUId(0x40000000, &res);
+ if (res.rbx == 0x4B4D564B)
+ return TRUE;
+ return FALSE;
+}
+
+U0 _Z8os_is_vmv()
+{
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @os_is_vm;
+ POP_SYSV_REGS
+}
+
+U0 @os_pc_speaker(F64 frequency)
+{
+ I64 period;
+ if (!frequency)
+ OutU8(0x61, InU8(0x61) & ~3);
+ else {
+ period = ClampI64(SYS_TIMER_FREQ / frequency, 1, U16_MAX);
+ OutU8(0x43, 0xB6);
+ OutU8(0x42, period);
+ OutU8(0x42, period.u8[1]);
+ OutU8(0x61, 3 | InU8(0x61));
+ }
+}
+
+U0 _Z13os_pc_speakerPKc()
+{
+ // os_pc_speaker(char const*)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ F64 frequency = Str2F64(p0);
+ @os_pc_speaker(frequency);
+ POP_SYSV_REGS
+}
+
+U0 _Z9os_randomv()
+{
+ // os_random()
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ RandU64;
+ POP_SYSV_REGS
+}
+
+U0 _Z19os_read_entire_filePKcPl()
+{
+ // os_read_entire_file(char const*, long*)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ FileRead(p0, p1);
+ POP_SYSV_REGS
+}
+
+U0 @os_screenshot()
+{
+ CDC* dc = DCScrnCapture(, adam_task);
+ Image.Write("B:/screenshot.png", dc);
+ DCDel(dc);
+}
+
+U0 _Z13os_screenshotv()
+{
+ // os_screenshot()
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @os_screenshot;
+ POP_SYSV_REGS
+}
+
+U8* @os_to_uppercase(U8* instr)
+{
+ if (!instr)
+ return NULL;
+ if (!StrLen(instr))
+ return NULL;
+ U8* outstr = CAlloc(StrLen(instr) + 1, adam_task);
+ I64 i;
+ for (i = 0; i < StrLen(instr); i++)
+ outstr[i] = ToUpper(instr[i]);
+ return outstr;
+}
+
+U0 _Z15os_to_uppercasePKc()
+{
+ // os_to_uppercase(char const*)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @os_to_uppercase(p0);
+ POP_SYSV_REGS
+}
+
+U0 _Z20os_write_entire_filePKcPhl()
+{
+ // os_write_entire_file(char const*, unsigned char*, long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ FileWrite(p0, p1, p2);
+ POP_SYSV_REGS
+}
diff --git a/System/Jakt/PCI.HC b/System/Jakt/PCI.HC
new file mode 100644
index 0000000..1a37903
--- /dev/null
+++ b/System/Jakt/PCI.HC
@@ -0,0 +1,62 @@
+U0 _Z8pci_findl()
+{
+ // pci_find(long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ PCIClassFind(p0, 0);
+ POP_SYSV_REGS
+}
+
+U0 _Z11pci_read_u8llll()
+{
+ // pci_read_u8(long, long, long, long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ PCIReadU8(p0, p1, p2, p3);
+ POP_SYSV_REGS
+}
+
+U0 _Z12pci_read_u16llll()
+{
+ // pci_read_u16(long, long, long, long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ PCIReadU16(p0, p1, p2, p3);
+ POP_SYSV_REGS
+}
+
+U0 _Z12pci_read_u32llll()
+{
+ // pci_read_u32(long, long, long, long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ PCIReadU32(p0, p1, p2, p3);
+ POP_SYSV_REGS
+}
+
+U0 _Z12pci_write_u8llllh()
+{
+ // pci_write_u8(long, long, long, long, unsigned char)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ PCIWriteU8(p0, p1, p2, p3, p4);
+ POP_SYSV_REGS
+}
+
+U0 _Z13pci_write_u16llllt()
+{
+ // pci_write_u16(long, long, long, long, unsigned short)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ PCIWriteU16(p0, p1, p2, p3, p4);
+ POP_SYSV_REGS
+}
+
+U0 _Z13pci_write_u32llllj()
+{
+ // pci_write_u32(long, long, long, long, unsigned int)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ PCIWriteU32(p0, p1, p2, p3, p4);
+ POP_SYSV_REGS
+}
diff --git a/System/Jakt/Time.HC b/System/Jakt/Time.HC
new file mode 100644
index 0000000..f6f3f31
--- /dev/null
+++ b/System/Jakt/Time.HC
@@ -0,0 +1,37 @@
+U0 _Z9time_busyl()
+{
+ // time_busy(long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ Busy(p0);
+ POP_SYSV_REGS
+}
+
+I64 @time_jiffies() { return cnts.jiffies; }
+
+U0 _Z12time_jiffiesv()
+{
+ // time_jiffies()
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @time_jiffies;
+ POP_SYSV_REGS
+}
+
+U0 _Z8time_nowv()
+{
+ // time_now()
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ Now;
+ POP_SYSV_REGS
+}
+
+U0 _Z10time_sleepl()
+{
+ // time_sleep(long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ Sleep(p0);
+ POP_SYSV_REGS
+}
\ No newline at end of file
diff --git a/System/Jakt/Window.HC b/System/Jakt/Window.HC
new file mode 100644
index 0000000..7e91097
--- /dev/null
+++ b/System/Jakt/Window.HC
@@ -0,0 +1,94 @@
+U0 @window_draw_it(CTask* task, CDC* dc)
+{
+ if (task->user_data)
+ @dc_copy(dc, task->pix_left, task->pix_top, task->user_data);
+}
+
+CTask* @window_user()
+{
+ CTask* task = Spawn(&UserCmdLine, , , 0);
+ TaskWait(task);
+ XTalk(task,
+ "while (1) { StrCpy(Fs->task_title, Fs->task_name); Sleep(1); };\n");
+ return task;
+}
+
+CTask* @window_create()
+{
+ CTask* task = @window_user;
+ task->draw_it = &@window_draw_it;
+ return task;
+}
+
+U0 _Z13window_createv()
+{
+ // window_create()
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @window_create();
+ POP_SYSV_REGS
+}
+
+U0 _Z14window_destroym()
+{
+ // window_destroy(unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ Kill(p0);
+ POP_SYSV_REGS
+}
+
+Bool @window_is_focused(CTask* task) { return task == sys_focus_task; }
+
+U0 _Z17window_is_focusedm()
+{
+ // window_is_focused(unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @window_is_focused(p0);
+ POP_SYSV_REGS
+}
+
+U0 @window_set_coordinates(CTask* task, I64 top, I64 left, I64 bottom,
+ I64 right)
+{
+ task->win_top = top;
+ task->win_left = left;
+ task->win_bottom = bottom;
+ task->win_right = right;
+}
+
+U0 _Z22window_set_coordinatesmllll()
+{
+ // window_set_coordinates(unsigned long, long, long, long, long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @window_set_coordinates(p0, p1, p2, p3, p4);
+ POP_SYSV_REGS
+}
+
+U0 @window_set_context(CTask* task, CDC* dc) { task->user_data = dc; }
+
+U0 _Z18window_set_contextmm()
+{
+ // window_set_context(unsigned long, unsigned long)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @window_set_context(p0, p1);
+ POP_SYSV_REGS
+}
+
+U0 @window_set_title(CTask* task, U8* title)
+{
+ StrCpy(task->task_name, title);
+ StrCpy(task->task_title, title);
+}
+
+U0 _Z16window_set_titlemPKc()
+{
+ // window_set_title(unsigned long, char const*)
+ PUSH_SYSV_REGS
+ GET_SYSV_ARGS
+ @window_set_title(p0, p1);
+ POP_SYSV_REGS
+}
diff --git a/System/Libraries/Base64.HC b/System/Libraries/Base64.HC
new file mode 100644
index 0000000..50e5667
--- /dev/null
+++ b/System/Libraries/Base64.HC
@@ -0,0 +1,88 @@
+
+U8* @base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+U8* @base64_decode(U8* input, I64* output_length)
+{
+ I64 input_length = StrLen(input);
+ if (input_length % 4 != 0) {
+ return NULL; // Invalid Base64 input length
+ }
+
+ // Calculate the expected output length
+ *output_length = (3 * input_length) / 4;
+ if (input[input_length - 1] == '=') {
+ (*output_length)--;
+ }
+ if (input[input_length - 2] == '=') {
+ (*output_length)--;
+ }
+
+ // Allocate memory for the decoded data
+ U8* decoded_data = CAlloc(*output_length, adam_task);
+ if (decoded_data == NULL) {
+ return NULL; // Memory allocation failed
+ }
+
+ // Initialize variables for decoding process
+ I32 i, j = 0;
+ U32 sextet_bits = 0;
+ I64 sextet_count = 0;
+ U32 base64_value;
+ U8* char_pointer;
+ U8 input_find_buf[2];
+ input_find_buf[1] = NULL;
+
+ // Loop through the Base64 input and decode it
+ for (i = 0; i < input_length; i++) {
+ // Convert Base64 character to a 6-bit value
+ base64_value = 0;
+ if (input[i] == '=') {
+ base64_value = 0;
+ } else {
+ input_find_buf[0] = input[i];
+ char_pointer = StrFirstOcc(@base64_chars, input_find_buf);
+ if (char_pointer == NULL) {
+ Free(decoded_data);
+ return NULL; // Invalid Base64 character
+ }
+ base64_value = char_pointer - @base64_chars;
+ }
+
+ // Combine 6-bit values into a 24-bit sextet
+ sextet_bits = (sextet_bits << 6) | base64_value;
+ sextet_count++;
+
+ // When a sextet is complete, decode it into three bytes
+ if (sextet_count == 4) {
+ decoded_data[j++] = (sextet_bits >> 16) & 0xFF;
+ decoded_data[j++] = (sextet_bits >> 8) & 0xFF;
+ decoded_data[j++] = sextet_bits & 0xFF;
+ sextet_bits = 0;
+ sextet_count = 0;
+ }
+ }
+
+ return decoded_data;
+}
+
+U8* @base64_encode(U8* input, I64 input_length)
+{
+ I64 i;
+ U8 buf[3];
+ I64 c = 0;
+ U8* output = CAlloc(input_length * 2, adam_task);
+
+ for (i = 0; i < input_length; i += 3) {
+ buf[0] = input[i];
+ buf[1] = @t((i + 1 < input_length), input[i + 1], 0);
+ buf[2] = @t((i + 2 < input_length), input[i + 2], 0);
+
+ output[c++] = @base64_chars[(buf[0] & 0xfc) >> 2];
+ output[c++] = @base64_chars[((buf[0] & 0x03) << 4) + ((buf[1] & 0xf0) >> 4)];
+ output[c++] = @t((i + 1 < input_length), @base64_chars[((buf[1] & 0x0f) << 2) + ((buf[2] & 0xc0) >> 6)], '=');
+ output[c++] = @t((i + 2 < input_length), @base64_chars[buf[2] & 0x3f], '=');
+ }
+
+ output[c] = '\0';
+ return output;
+}
diff --git a/System/Libraries/Http.HC b/System/Libraries/Http.HC
new file mode 100644
index 0000000..991ff9b
--- /dev/null
+++ b/System/Libraries/Http.HC
@@ -0,0 +1,628 @@
+#define HTTP_TMP_DIRECTORY "B:/Tmp"
+#define HTTP_CACHE_DIRECTORY "B:/Tmp/Cache"
+#define HTTP_FETCH_BUFFER_SIZE 1024 << 15
+
+#define SEDECIM_USER_AGENT_STRING "Mozilla/5.0 (compatible; Sedecim/1.0; TempleOS) (KHTML, like Gecko)"
+
+class @http_buffer
+{
+ I64 length;
+ U8* data;
+};
+
+class @http_status
+{
+ U8* protocol;
+ I64 code;
+ U8* text;
+};
+
+class @http_response
+{
+ I64 state;
+ TlsSocket* s;
+ @http_status status;
+ JsonObject* headers;
+ @http_buffer body;
+ Bool headers_parsed;
+};
+
+class @http_url
+{
+ U8* scheme;
+ U8* host;
+ I64 port;
+ U8* path;
+ U8* query;
+ U8* fragment;
+};
+
+class @http_request
+{
+ @http_url* url;
+ U8* buf;
+ U8* data;
+ I64 type;
+ JsonObject* headers;
+ @http_response* response;
+};
+
+#define HttpResponse @http_response
+#define HttpUrl @http_url
+
+#define HTTP_FETCH_BUFFER_SIZE 16777216
+
+#define HTTP_PARSE_SCHEME 0
+#define HTTP_PARSE_SCHEME_FS 1
+#define HTTP_PARSE_HOST 2
+#define HTTP_PARSE_PORT 3
+#define HTTP_PARSE_PATH 4
+#define HTTP_PARSE_QUERY 5
+#define HTTP_PARSE_FRAGMENT 6
+
+#define HTTP_MIN_REQUEST_BUFFER_SIZE 16384
+#define HTTP_PARSE_URL_FIFO_SIZE 1024
+
+#define HTTP_REQ_GET 0
+#define HTTP_REQ_HEAD 1
+#define HTTP_REQ_POST 2
+#define HTTP_REQ_PUT 3
+
+#define HTTP_STATE_UNSENT 0
+#define HTTP_STATE_OPENED 1
+#define HTTP_STATE_HEADERS_RECEIVED 2
+#define HTTP_STATE_LOADING 3
+#define HTTP_STATE_DONE 4
+
+U8* @http_string_from_fifo(CFifoU8* f)
+{
+ U8 ch;
+ I64 i = 0;
+ U8* str = CAlloc(FifoU8Cnt(f) + 1, adam_task);
+ while (FifoU8Cnt(f)) {
+ FifoU8Rem(f, &ch);
+ str[i] = ch;
+ i++;
+ }
+ FifoU8Flush(f);
+ return str;
+}
+
+U0 @http_free_url(@http_url* url)
+{
+ if (!url)
+ return;
+ if (url->scheme)
+ Free(url->scheme);
+ if (url->host)
+ Free(url->host);
+ if (url->path)
+ Free(url->path);
+ if (url->query)
+ Free(url->query);
+ if (url->fragment)
+ Free(url->fragment);
+ Free(url);
+}
+
+U0 @http_free_response(@http_response* resp)
+{
+ if (!resp)
+ return;
+ // FIXME: Free response headers JSON object
+ Free(resp);
+}
+
+@http_url* @http_parse_url(U8* str)
+{
+ if (!str)
+ return NULL;
+ U8* buf = NULL;
+ U8 hex[3];
+ I64 i = 0;
+ I64 state = HTTP_PARSE_SCHEME;
+ CFifoU8* consume_fifo = FifoU8New(HTTP_PARSE_URL_FIFO_SIZE, adam_task);
+ @http_url* url = CAlloc(sizeof(@http_url), adam_task);
+ while (1) {
+ switch (str[i]) {
+ case 0:
+ switch (state) {
+ case HTTP_PARSE_HOST:
+ url->host = @http_string_from_fifo(consume_fifo);
+ url->path = StrNew("/", adam_task);
+ goto done_parsing_url;
+ break;
+ case HTTP_PARSE_PORT:
+ buf = @http_string_from_fifo(consume_fifo);
+ url->port = Str2I64(buf);
+ Free(buf);
+ url->path = StrNew("/", adam_task);
+ goto done_parsing_url;
+ break;
+ case HTTP_PARSE_PATH:
+ url->path = @http_string_from_fifo(consume_fifo);
+ goto done_parsing_url;
+ break;
+ case HTTP_PARSE_QUERY:
+ url->query = @http_string_from_fifo(consume_fifo);
+ goto done_parsing_url;
+ break;
+ case HTTP_PARSE_FRAGMENT:
+ url->fragment = @http_string_from_fifo(consume_fifo);
+ goto done_parsing_url;
+ break;
+ default:
+ goto done_parsing_url;
+ break;
+ }
+ break;
+ case '#':
+ switch (state) {
+ case HTTP_PARSE_PATH:
+ url->path = @http_string_from_fifo(consume_fifo);
+ FifoU8Ins(consume_fifo, str[i]);
+ state = HTTP_PARSE_FRAGMENT;
+ break;
+ case HTTP_PARSE_QUERY:
+ url->query = @http_string_from_fifo(consume_fifo);
+ FifoU8Ins(consume_fifo, str[i]);
+ state = HTTP_PARSE_FRAGMENT;
+ break;
+ }
+ break;
+ case '?':
+ switch (state) {
+ case HTTP_PARSE_PATH:
+ url->path = @http_string_from_fifo(consume_fifo);
+ FifoU8Ins(consume_fifo, str[i]);
+ state = HTTP_PARSE_QUERY;
+ break;
+ }
+ break;
+ case '/':
+ switch (state) {
+ case HTTP_PARSE_SCHEME:
+ state = HTTP_PARSE_SCHEME_FS;
+ goto keep_consuming_url_chars;
+ break;
+ case HTTP_PARSE_SCHEME_FS:
+ FifoU8Ins(consume_fifo, str[i]);
+ url->scheme = @http_string_from_fifo(consume_fifo);
+ if (!StrCmp(url->scheme, "http://"))
+ url->port = 80;
+ if (!StrCmp(url->scheme, "https://"))
+ url->port = 443;
+ state = HTTP_PARSE_HOST;
+ break;
+ case HTTP_PARSE_HOST:
+ url->host = @http_string_from_fifo(consume_fifo);
+ FifoU8Ins(consume_fifo, str[i]);
+ state = HTTP_PARSE_PATH;
+ break;
+ case HTTP_PARSE_PORT:
+ buf = @http_string_from_fifo(consume_fifo);
+ url->port = Str2I64(buf);
+ Free(buf);
+ FifoU8Ins(consume_fifo, str[i]);
+ state = HTTP_PARSE_PATH;
+ break;
+ case HTTP_PARSE_PATH:
+ goto keep_consuming_url_chars;
+ break;
+ }
+ break;
+ case ':':
+ switch (state) {
+ case HTTP_PARSE_SCHEME:
+ case HTTP_PARSE_PATH:
+ case HTTP_PARSE_QUERY:
+ case HTTP_PARSE_FRAGMENT:
+ goto keep_consuming_url_chars;
+ break;
+ case HTTP_PARSE_HOST:
+ url->host = @http_string_from_fifo(consume_fifo);
+ state = HTTP_PARSE_PORT;
+ break;
+ }
+ break;
+ default:
+ keep_consuming_url_chars:
+ switch (state) {
+ case HTTP_PARSE_PATH:
+ case HTTP_PARSE_QUERY:
+ switch (str[i]) {
+ case '0' ... '9':
+ case 'A' ... 'Z':
+ case 'a' ... 'z':
+ case '?':
+ case '&':
+ case '/':
+ case '=':
+ // !'()*-._~
+ case '!':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '-':
+ case '.':
+ case '_':
+ case '~':
+ case '%':
+ FifoU8Ins(consume_fifo, str[i]);
+ break;
+ default:
+ FifoU8Ins(consume_fifo, '%');
+ StrPrint(hex, "%02X", str[i]);
+ FifoU8Ins(consume_fifo, hex[0]);
+ FifoU8Ins(consume_fifo, hex[1]);
+ break;
+ }
+ break;
+ default:
+ FifoU8Ins(consume_fifo, str[i]);
+ break;
+ }
+ break;
+ }
+ i++;
+ }
+done_parsing_url:
+ FifoU8Flush(consume_fifo);
+ FifoU8Del(consume_fifo);
+ return url;
+}
+
+U0 @http_parse_response_headers(@http_response* resp, U8* buffer, I64 length)
+{
+ if (!resp || !buffer || !length)
+ return;
+ U64 response_data_ptr = StrFind("\r\n\r\n", buffer);
+ if (!response_data_ptr)
+ return;
+ resp->body.data = response_data_ptr + 4;
+ resp->body.data[-4] = NULL;
+ JsonObject* headers = Json.CreateObject();
+ U8** lines = NULL;
+ I64 lines_count = 0;
+ I64 i;
+ I64 j;
+ U8* key_ptr = NULL;
+ U8* value_ptr = NULL;
+ lines = String.Split(buffer, '\n', &lines_count);
+
+ U8* resp_protocol = lines[0];
+ U8* resp_status_code = StrFind(" ", resp_protocol) + 1;
+ U8* resp_text = StrFind(" ", resp_status_code + 1);
+ (*StrFind(" ", resp_protocol)) = NULL;
+ (*StrFind(" ", resp_status_code)) = NULL;
+
+ resp->status.protocol = StrNew(resp_protocol, adam_task);
+ resp->status.code = Str2I64(resp_status_code);
+ resp->status.text = StrNew(resp_text, adam_task);
+
+ for (i = 1; i < lines_count; i++) {
+ for (j = 0; j < StrLen(lines[i]); j++) {
+ if (lines[i][j] == ':' && lines[i][j + 1] == ' ') {
+ lines[i][j] = NULL;
+ key_ptr = lines[i];
+ value_ptr = lines[i] + j + 2;
+ (*StrFind("\r", value_ptr)) = NULL;
+ Json.Set(headers, key_ptr, StrNew(value_ptr, adam_task), JSON_STRING);
+ goto @http_next_header_line;
+ }
+ }
+ @http_next_header_line:
+ }
+ resp->headers = headers;
+ resp->headers_parsed = TRUE;
+}
+
+Bool @http_detect_response_headers(U8* buf, I64 len)
+{
+ if (len < 4)
+ return FALSE;
+ I64 i;
+ for (i = 0; i < len - 4; i++) {
+ if (!MemCmp(buf + i, "\r\n\r\n", 4))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+I64 @http_req(@http_request* req)
+{
+ if (!req)
+ return NULL;
+ if (!req->url || !req->buf || !req->response)
+ return NULL;
+ if (!req->url->scheme || !req->url->host || !req->url->path)
+ return NULL;
+ if (req->type == HTTP_REQ_POST && !req->data)
+ return NULL;
+ if (req->type == HTTP_REQ_PUT && !req->data)
+ return NULL;
+
+ @http_response* resp = req->response;
+
+ U8* buf = NULL;
+ U8* headers_buf = "";
+ I64 cnt = 1;
+ I64 len = NULL;
+
+ buf = CAlloc(HTTP_MIN_REQUEST_BUFFER_SIZE, adam_task);
+ if (req->headers) {
+ headers_buf = CAlloc(HTTP_MIN_REQUEST_BUFFER_SIZE, adam_task);
+ JsonKey* key = req->headers->keys;
+ while (key) {
+ StrPrint(headers_buf + StrLen(headers_buf), "%s: %s\r\n", key->name, key->value);
+ key = key->next;
+ }
+ }
+
+ switch (req->type) {
+ case HTTP_REQ_GET:
+ StrPrint(buf,
+ "GET %s%s HTTP/1.0\r\n"
+ "Host: %s\r\n"
+ "%s"
+ "User-Agent: " SEDECIM_USER_AGENT_STRING
+ "\r\n\r\n",
+ req->url->path, req->url->query, req->url->host, headers_buf);
+ break;
+ case HTTP_REQ_HEAD:
+ StrPrint(buf,
+ "HEAD %s%s HTTP/1.0\r\n"
+ "Host: %s\r\n"
+ "%s"
+ "User-Agent: " SEDECIM_USER_AGENT_STRING
+ "\r\n\r\n",
+ req->url->path, req->url->query, req->url->host, headers_buf);
+ break;
+ case HTTP_REQ_POST:
+ StrPrint(buf,
+ "POST %s%s HTTP/1.0\r\n"
+ "Host: %s\r\n"
+ "%s"
+ "User-Agent: " SEDECIM_USER_AGENT_STRING
+ "\r\n"
+ "Content-Length: %d\r\n\r\n",
+ req->url->path, req->url->query, req->url->host, headers_buf,
+ StrLen(req->data));
+ StrPrint(buf + StrLen(buf), req->data);
+ break;
+ case HTTP_REQ_PUT:
+ StrPrint(buf,
+ "PUT %s%s HTTP/1.0\r\n"
+ "Host: %s\r\n"
+ "%s"
+ "User-Agent: " SEDECIM_USER_AGENT_STRING
+ "\r\n"
+ "Content-Length: %d\r\n\r\n",
+ req->url->path, req->url->query, req->url->host, headers_buf,
+ StrLen(req->data));
+ StrPrint(buf + StrLen(buf), req->data);
+ break;
+ }
+
+ TlsSocket* s = NULL;
+ resp->s = NULL;
+
+ if (!StrCmp(req->url->scheme, "http://")) {
+ s = @tcp_socket_create(req->url->host, req->url->port);
+ resp->s = s;
+ while (s->state != TCP_SOCKET_STATE_ESTABLISHED)
+ Sleep(1);
+ }
+
+ if (!StrCmp(req->url->scheme, "https://")) {
+ s = @tls_socket_create(req->url->host, req->url->port);
+ resp->s = s;
+ while (!@tls_established(s->ctx))
+ Sleep(1);
+ }
+
+ resp->state = HTTP_STATE_OPENED;
+ s->send(buf, StrLen(buf));
+ while (cnt || s->state != TCP_SOCKET_STATE_CLOSED) {
+ cnt = s->receive(req->buf + len, 1024);
+ len += cnt;
+ switch (resp->state) {
+ case HTTP_STATE_LOADING:
+ resp->body.length += cnt;
+ break;
+ case HTTP_STATE_HEADERS_RECEIVED:
+ resp->body.length = (req->buf + len) - resp->body.data;
+ resp->state = HTTP_STATE_LOADING;
+ break;
+ case HTTP_STATE_OPENED:
+ if (@http_detect_response_headers(req->buf, len)) {
+ @http_parse_response_headers(resp, req->buf, len);
+ resp->state = HTTP_STATE_HEADERS_RECEIVED;
+ }
+ break;
+ }
+ Sleep(1);
+ }
+ if (!resp->headers_parsed)
+ @http_parse_response_headers(resp, req->buf, len);
+ resp->state = HTTP_STATE_DONE;
+ req->buf[len] = NULL;
+ Free(buf);
+ if (StrLen(headers_buf) > 0) {
+ Free(headers_buf);
+ }
+ s->close();
+ resp->s = NULL;
+ Free(req);
+ return len;
+}
+
+Bool @http_scheme_is_https(@http_url* url)
+{
+ if (!url || !url->scheme)
+ return FALSE;
+ return !MemCmp(url->scheme, "https", 5);
+}
+
+@http_response* @http_get(@http_url* url, U8* buf, U64 return_req = NULL, JsonObject* headers = NULL)
+{
+ @http_response* resp = CAlloc(sizeof(@http_response), adam_task);
+ @http_request* req = CAlloc(sizeof(@http_request), adam_task);
+ if (return_req)
+ MemCpy(return_req, &req, sizeof(U64));
+ req->url = url;
+ req->buf = buf;
+ req->type = HTTP_REQ_GET;
+ req->headers = headers;
+ req->response = resp;
+ Spawn(&@http_req, req, "HTTPGetRequest");
+ return resp;
+}
+
+@http_response* @http_head(@http_url* url, U8* buf, JsonObject* headers = NULL)
+{
+ @http_response* resp = CAlloc(sizeof(@http_response), adam_task);
+ @http_request* req = CAlloc(sizeof(@http_request), adam_task);
+ req->url = url;
+ req->buf = buf;
+ req->type = HTTP_REQ_HEAD;
+ req->headers = headers;
+ req->response = resp;
+ Spawn(&@http_req, req, "HTTPHeadRequest");
+ return resp;
+}
+
+@http_response* @http_post(@http_url* url, U8* buf, U8* data, JsonObject* headers = NULL)
+{
+ @http_response* resp = CAlloc(sizeof(@http_response), adam_task);
+ @http_request* req = CAlloc(sizeof(@http_request), adam_task);
+ req->url = url;
+ req->buf = buf;
+ req->type = HTTP_REQ_POST;
+ req->headers = headers;
+ req->data = data;
+ req->response = resp;
+ Spawn(&@http_req, req, "HTTPPostRequest");
+ return resp;
+}
+
+@http_response* @http_put(@http_url* url, U8* buf, U8* data, JsonObject* headers = NULL)
+{
+ @http_response* resp = CAlloc(sizeof(@http_response), adam_task);
+ @http_request* req = CAlloc(sizeof(@http_request), adam_task);
+ req->url = url;
+ req->buf = buf;
+ req->type = HTTP_REQ_PUT;
+ req->headers = headers;
+ req->data = data;
+ req->response = resp;
+ Spawn(&@http_req, req, "HTTPPutRequest");
+ return resp;
+}
+
+class @http
+{
+ @http_response* (*Get)(@http_url* url, U8* buf, U64* req = NULL, JsonObject* headers = NULL);
+ @http_response* (*Head)(@http_url* url, U8* buf, JsonObject* headers = NULL);
+ @http_response* (*Post)(@http_url* url, U8* buf, U8* data, JsonObject* headers = NULL);
+ @http_response* (*Put)(@http_url* url, U8* buf, U8* data, JsonObject* headers = NULL);
+};
+
+@http Http;
+
+Http.Get = &@http_get;
+Http.Head = &@http_head;
+Http.Post = &@http_post;
+Http.Put = &@http_put;
+
+Bool @http_is_resource_cached(U8* src, U8* cache_directory = HTTP_CACHE_DIRECTORY)
+{
+ U8 buf[512];
+ U8* src_md5 = md5_string(src, StrLen(src));
+ StrCpy(buf, cache_directory);
+ StrPrint(buf + StrLen(buf), "/%s", src_md5);
+ Free(src_md5);
+ return FileFind(buf);
+}
+
+U0 @http_cache_resource(U8* src, U8* data, I64 size, U8* cache_directory = HTTP_CACHE_DIRECTORY)
+{
+ U8 buf[512];
+ U8* src_md5 = md5_string(src, StrLen(src));
+ StrCpy(buf, cache_directory);
+ StrPrint(buf + StrLen(buf), "/%s", src_md5);
+ Free(src_md5);
+ FileWrite(buf, data, size);
+}
+
+U8* @http_get_cached_resource_filename(U8* src, U8* cache_directory = HTTP_CACHE_DIRECTORY)
+{
+ U8* buf = CAlloc(512, adam_task);
+ U8* src_md5 = md5_string(src, StrLen(src));
+ StrCpy(buf, cache_directory);
+ StrPrint(buf + StrLen(buf), "/%s", src_md5);
+ Free(src_md5);
+ return buf;
+}
+
+U0 @http_init_tmp_and_cache_directories()
+{
+ if (!FileFind(HTTP_TMP_DIRECTORY))
+ DirMk(HTTP_TMP_DIRECTORY);
+ if (!FileFind(HTTP_CACHE_DIRECTORY))
+ DirMk(HTTP_CACHE_DIRECTORY);
+}
+
+@http_response* fetch(U8* url_string, U8* fetch_buffer)
+{
+ if (!url_string || !fetch_buffer)
+ return NULL;
+ HttpUrl* url = @http_parse_url(url_string);
+ if (!url)
+ return NULL;
+ @http_response* resp = Http.Get(url, fetch_buffer);
+ while (resp->state != HTTP_STATE_DONE)
+ Sleep(1);
+ return resp;
+}
+
+I64 curl(U8* url_string)
+{
+ if (!url_string)
+ return 0;
+ U8* fetch_buffer = CAlloc(HTTP_FETCH_BUFFER_SIZE, adam_task);
+ @http_response* resp = fetch(url_string, fetch_buffer);
+ if (!resp)
+ return 0;
+ if (resp->body.length) {
+ U8* buf = resp->body.data;
+ while (*buf) {
+ if (*buf == '\d')
+ "\d\d";
+ else
+ "%c", *buf;
+ ++buf;
+ }
+ "\n";
+ }
+ Free(fetch_buffer);
+ return resp->body.length;
+}
+
+I64 download(U8* path, U8* url_string)
+{
+ if (!path || !url_string)
+ return 0;
+ U8* fetch_buffer = CAlloc(HTTP_FETCH_BUFFER_SIZE, adam_task);
+ @http_response* resp = fetch(url_string, fetch_buffer);
+ if (!resp)
+ return 0;
+ if (resp->body.length) {
+ FileWrite(path, resp->body.data, resp->body.length);
+ }
+ Free(fetch_buffer);
+ return resp->body.length;
+}
+
+@http_init_tmp_and_cache_directories;
diff --git a/System/Libraries/Json.HC b/System/Libraries/Json.HC
new file mode 100644
index 0000000..c5f5d8d
--- /dev/null
+++ b/System/Libraries/Json.HC
@@ -0,0 +1,1354 @@
+#define JSON_SAME -1
+#define JSON_UNDEFINED 0
+#define JSON_OBJECT 1
+#define JSON_ARRAY 2
+#define JSON_STRING 3
+#define JSON_NUMBER 4
+#define JSON_BOOLEAN 5
+#define JSON_NULL 6
+#define JSON_HTML 7
+
+#define JSON_STATE_OBJECT_OR_ARRAY 0
+
+#define JSON_STATE_OBJECT 100
+#define JSON_STATE_OBJECT_KEY 101
+#define JSON_STATE_OBJECT_SEPARATOR 102
+#define JSON_STATE_OBJECT_TYPE 103
+#define JSON_STATE_OBJECT_NEXT 104
+
+#define JSON_STATE_OBJECT_OBJECT 105
+#define JSON_STATE_OBJECT_ARRAY 106
+#define JSON_STATE_OBJECT_STRING 107
+#define JSON_STATE_OBJECT_NUMBER 108
+#define JSON_STATE_OBJECT_BOOLEAN 109
+#define JSON_STATE_OBJECT_NULL 110
+
+#define JSON_STATE_ARRAY 200
+#define JSON_STATE_ARRAY_TYPE 201
+#define JSON_STATE_ARRAY_NEXT 202
+
+#define JSON_STATE_ARRAY_OBJECT 203
+#define JSON_STATE_ARRAY_ARRAY 204
+#define JSON_STATE_ARRAY_STRING 205
+#define JSON_STATE_ARRAY_NUMBER 206
+#define JSON_STATE_ARRAY_BOOLEAN 207
+#define JSON_STATE_ARRAY_NULL 208
+
+#define JSON_PARSER_FIFO_SIZE 16384
+#define JSON_STRINGIFY_BUF_SIZE 1048576
+
+#define JSON_WRAPPER_MAGIC_NUMBER 0xDEADC0DEDEADC0DE
+
+class @json_element
+{
+ @json_element* prev;
+ @json_element* next;
+ I64 type;
+};
+
+class @json_key : @json_element
+{
+ U8* name;
+ U64 value;
+};
+
+class @json_item : @json_element { U64 value; };
+
+class @json_object : @json_element
+{
+ I64 length;
+ @json_key* keys;
+};
+
+class @json_array : @json_element
+{
+ I64 length;
+ @json_item* items;
+ @json_item* last_item;
+};
+
+extern class @json_callable_object;
+
+class @json_callable_array : @json_array
+{
+ U64 (*@)(I64 index, Bool return_item = FALSE);
+ @json_callable_array* (*a)(I64 index, Bool return_item = FALSE);
+ @json_callable_object* (*o)(I64 index, Bool return_item = FALSE);
+ U0 (*append)(@json_item* append_item);
+ U0 (*insert)(@json_item* insert_item, I64 index);
+ U0 (*prepend)(@json_item* prepend_item);
+ U0 (*remove)(I64 index);
+};
+
+class @json_callable_object : @json_object
+{
+ U64 (*@)(U8* key, Bool return_key = FALSE);
+ @json_callable_array* (*a)(U8* key, Bool return_key = FALSE);
+ @json_callable_object* (*o)(U8* key, Bool return_key = FALSE);
+ U0 (*set)(U8* key, U64 value, I64 type = JSON_SAME);
+ U0 (*unset)(U8* key);
+};
+
+class @json_parser
+{
+ U8* stream;
+ U8 token;
+ CFifoU8* consumed;
+ I64 pos;
+ I64 state;
+ Bool debug;
+};
+
+#define JsonArray @json_callable_array
+#define JsonElement @json_element
+#define JsonItem @json_item
+#define JsonKey @json_key
+#define JsonObject @json_callable_object
+
+U0 @json_debug_parser_state(@json_parser* parser)
+{
+ switch (parser->state) {
+ case JSON_STATE_OBJECT:
+ "JSON_STATE_OBJECT\n";
+ break;
+ case JSON_STATE_OBJECT_KEY:
+ "JSON_STATE_OBJECT_KEY\n";
+ break;
+ case JSON_STATE_OBJECT_SEPARATOR:
+ "JSON_STATE_OBJECT_SEPARATOR\n";
+ break;
+ case JSON_STATE_OBJECT_TYPE:
+ "JSON_STATE_OBJECT_TYPE\n";
+ break;
+ case JSON_STATE_OBJECT_NEXT:
+ "JSON_STATE_OBJECT_NEXT\n";
+ break;
+ case JSON_STATE_OBJECT_STRING:
+ "JSON_STATE_OBJECT_STRING\n";
+ break;
+ case JSON_STATE_OBJECT_NUMBER:
+ "JSON_STATE_OBJECT_NUMBER\n";
+ break;
+ case JSON_STATE_ARRAY:
+ "JSON_STATE_ARRAY\n";
+ break;
+ case JSON_STATE_ARRAY_TYPE:
+ "JSON_STATE_ARRAY_TYPE\n";
+ break;
+ case JSON_STATE_ARRAY_NEXT:
+ "JSON_STATE_ARRAY_NEXT\n";
+ break;
+ case JSON_STATE_ARRAY_STRING:
+ "JSON_STATE_ARRAY_STRING\n";
+ break;
+ case JSON_STATE_ARRAY_NUMBER:
+ "JSON_STATE_ARRAY_NUMBER\n";
+ break;
+ }
+}
+
+U0 @json_append_item(@json_array* arr, @json_item* append_item)
+{
+ if (!arr || !append_item)
+ return;
+ if (arr->type != JSON_ARRAY)
+ return;
+ @json_item* item = arr->last_item;
+ if (!item) {
+ append_item->prev = NULL;
+ append_item->next = NULL;
+ arr->items = append_item;
+ arr->last_item = append_item;
+ arr->length++;
+ return;
+ }
+ item->next = append_item;
+ append_item->prev = item;
+ arr->last_item = append_item;
+ arr->length++;
+}
+
+U8* @json_string_from_fifo(CFifoU8* f)
+{
+ U8 ch;
+ I64 i = 0;
+ U8* str = CAlloc(FifoU8Cnt(f) + 1, adam_task);
+ while (FifoU8Cnt(f)) {
+ FifoU8Rem(f, &ch);
+ str[i] = ch;
+ i++;
+ }
+ FifoU8Flush(f);
+ return str;
+}
+
+U0 @json_insert_key(@json_object* obj, @json_key* key)
+{
+ if (!obj)
+ return;
+ if (!obj->keys) {
+ obj->keys = key;
+ obj->length++;
+ return;
+ }
+ @json_key* k = obj->keys;
+ while (k->next)
+ k = k->next;
+ k->next = key;
+ key->prev = k;
+ obj->length++;
+}
+
+U0 @json_rstrip(U8* str)
+{
+ if (!str || !StrLen(str))
+ return;
+ while (str[StrLen(str) - 1] == ' ' || str[StrLen(str) - 1] == '\r' || str[StrLen(str) - 1] == '\n' || str[StrLen(str) - 1] == '\t ')
+ str[StrLen(str) - 1] = NULL;
+}
+
+extern @json_element* @json_parse_object_or_array(@json_parser* parser);
+
+I64 @json_unescape_char(I64 escape_ch)
+{
+ I64 return_ch = escape_ch;
+
+ // FIXME: unicode
+ switch (escape_ch) {
+ case 'b':
+ return_ch = 0x08;
+ break;
+ case 'f':
+ return_ch = 0x0c;
+ break;
+ case 'n':
+ return_ch = 0x0a;
+ break;
+ case 'r':
+ return_ch = 0x0d;
+ break;
+ case 't':
+ return_ch = 0x09;
+ break;
+ default:
+ break;
+ }
+
+ return return_ch;
+}
+
+U0 @json_parse_object(@json_parser* parser, @json_object* obj)
+{
+ @json_key* key = NULL;
+ while (1) {
+ switch (parser->stream[parser->pos]) {
+ case '\\':
+ if (parser->state == JSON_STATE_OBJECT_STRING)
+ FifoU8Ins(parser->consumed, @json_unescape_char(parser->stream[++parser->pos]));
+ break;
+ case '}':
+ switch (parser->state) {
+ case JSON_STATE_OBJECT_KEY:
+ case JSON_STATE_OBJECT_STRING:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ case JSON_STATE_OBJECT_NUMBER:
+ key->value = @json_string_from_fifo(parser->consumed);
+ @json_rstrip(key->value);
+ key->value = Str2F64(key->value);
+ @json_insert_key(obj, key);
+ return;
+ break;
+ case JSON_STATE_OBJECT_BOOLEAN:
+ key->value = @json_string_from_fifo(parser->consumed);
+ @json_rstrip(key->value);
+ if (StrCmp("true", key->value) && StrCmp("false", key->value)) {
+ PrintErr("@json_parse_object: Illegal boolean value at position %d",
+ parser->pos);
+ while (1)
+ Sleep(1);
+ }
+ if (!StrCmp("true", key->value))
+ key->value = TRUE;
+ else
+ key->value = FALSE;
+ @json_insert_key(obj, key);
+ return;
+ break;
+ case JSON_STATE_OBJECT_NULL:
+ key->value = @json_string_from_fifo(parser->consumed);
+ @json_rstrip(key->value);
+ if (StrCmp("null", key->value)) {
+ PrintErr("@json_parse_object: Illegal null value at position %d",
+ parser->pos);
+ while (1)
+ Sleep(1);
+ }
+ key->value = NULL;
+ @json_insert_key(obj, key);
+ return;
+ break;
+ case JSON_STATE_OBJECT:
+ case JSON_STATE_OBJECT_NEXT:
+ return;
+ break;
+ }
+ break;
+ case ',':
+ switch (parser->state) {
+ case JSON_STATE_OBJECT_KEY:
+ case JSON_STATE_OBJECT_STRING:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ case JSON_STATE_OBJECT_NUMBER:
+ key->value = @json_string_from_fifo(parser->consumed);
+ key->value = Str2F64(key->value);
+ @json_insert_key(obj, key);
+ parser->state = JSON_STATE_OBJECT;
+ break;
+ case JSON_STATE_OBJECT_BOOLEAN:
+ key->value = @json_string_from_fifo(parser->consumed);
+ @json_rstrip(key->value);
+ if (StrCmp("true", key->value) && StrCmp("false", key->value)) {
+ PrintErr("@json_parse_object: Illegal boolean value at position %d",
+ parser->pos);
+ while (1)
+ Sleep(1);
+ }
+ if (!StrCmp("true", key->value))
+ key->value = TRUE;
+ else
+ key->value = FALSE;
+ @json_insert_key(obj, key);
+ parser->state = JSON_STATE_OBJECT;
+ break;
+ case JSON_STATE_OBJECT_NULL:
+ key->value = @json_string_from_fifo(parser->consumed);
+ @json_rstrip(key->value);
+ if (StrCmp("null", key->value)) {
+ PrintErr("@json_parse_object: Illegal null value at position %d",
+ parser->pos);
+ while (1)
+ Sleep(1);
+ }
+ key->value = NULL;
+ @json_insert_key(obj, key);
+ parser->state = JSON_STATE_OBJECT;
+ break;
+ case JSON_STATE_OBJECT_NEXT:
+ parser->state = JSON_STATE_OBJECT;
+ break;
+ }
+ break;
+ case ':':
+ switch (parser->state) {
+ case JSON_STATE_OBJECT_KEY:
+ case JSON_STATE_OBJECT_STRING:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ case JSON_STATE_OBJECT_SEPARATOR:
+ parser->state = JSON_STATE_OBJECT_TYPE;
+ break;
+ }
+ break;
+ case '[':
+ switch (parser->state) {
+ case JSON_STATE_OBJECT_KEY:
+ case JSON_STATE_OBJECT_STRING:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ case JSON_STATE_OBJECT_TYPE:
+ key->type = JSON_ARRAY;
+ key->value = @json_parse_object_or_array(parser);
+ @json_insert_key(obj, key);
+ parser->state = JSON_STATE_OBJECT_NEXT;
+ break;
+ }
+ break;
+ case '{':
+ switch (parser->state) {
+ case JSON_STATE_OBJECT_KEY:
+ case JSON_STATE_OBJECT_STRING:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ case JSON_STATE_OBJECT_TYPE:
+ key->type = JSON_OBJECT;
+ key->value = @json_parse_object_or_array(parser);
+ @json_insert_key(obj, key);
+ parser->state = JSON_STATE_OBJECT_NEXT;
+ break;
+ }
+ break;
+ case '"':
+ switch (parser->state) {
+ case JSON_STATE_OBJECT_STRING:
+ key->value = @json_string_from_fifo(parser->consumed);
+ @json_insert_key(obj, key);
+ parser->state = JSON_STATE_OBJECT_NEXT;
+ break;
+ case JSON_STATE_OBJECT_TYPE:
+ key->type = JSON_STRING;
+ parser->state = JSON_STATE_OBJECT_STRING;
+ break;
+ case JSON_STATE_OBJECT_KEY:
+ key->name = @json_string_from_fifo(parser->consumed);
+ parser->state = JSON_STATE_OBJECT_SEPARATOR;
+ break;
+ case JSON_STATE_OBJECT:
+ key = CAlloc(sizeof(@json_key), adam_task);
+ parser->state = JSON_STATE_OBJECT_KEY;
+ break;
+ }
+ break;
+ case '-':
+ case '0' ... '9':
+ case '.':
+ switch (parser->state) {
+ case JSON_STATE_OBJECT_KEY:
+ case JSON_STATE_OBJECT_STRING:
+ case JSON_STATE_OBJECT_NUMBER:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ case JSON_STATE_OBJECT_TYPE:
+ key->type = JSON_NUMBER;
+ parser->state = JSON_STATE_OBJECT_NUMBER;
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ }
+ break;
+ case 't':
+ case 'f':
+ switch (parser->state) {
+ case JSON_STATE_OBJECT_KEY:
+ case JSON_STATE_OBJECT_STRING:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ case JSON_STATE_OBJECT_TYPE:
+ key->type = JSON_BOOLEAN;
+ parser->state = JSON_STATE_OBJECT_BOOLEAN;
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ }
+ break;
+ case 'n':
+ switch (parser->state) {
+ case JSON_STATE_OBJECT_KEY:
+ case JSON_STATE_OBJECT_STRING:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ case JSON_STATE_OBJECT_TYPE:
+ key->type = JSON_NULL;
+ parser->state = JSON_STATE_OBJECT_NULL;
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ }
+ break;
+ default:
+ switch (parser->state) {
+ case JSON_STATE_OBJECT_KEY:
+ case JSON_STATE_OBJECT_STRING:
+ case JSON_STATE_OBJECT_BOOLEAN:
+ case JSON_STATE_OBJECT_NULL:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ }
+ break;
+ }
+ if (parser->debug) {
+ @json_debug_parser_state(parser);
+ "Object: %08X, Pos: %d, Token: %c\n", obj, parser->pos,
+ parser->stream[parser->pos];
+ Sleep(50);
+ }
+ parser->pos++;
+ }
+}
+
+U0 @json_parse_array(@json_parser* parser, @json_array* arr)
+{
+ @json_item* item = NULL;
+ while (1) {
+ if (parser->state == JSON_STATE_ARRAY) {
+ switch (parser->stream[parser->pos]) {
+ case 0:
+ PrintErr("@json_parse_array: Malformed array");
+ while (1)
+ Sleep(1);
+ break;
+ case ']':
+ return;
+ break;
+ }
+ item = CAlloc(sizeof(@json_item), adam_task);
+ parser->state = JSON_STATE_ARRAY_TYPE;
+ }
+ switch (parser->stream[parser->pos]) {
+ case '\\':
+ if (parser->state == JSON_STATE_ARRAY_STRING)
+ FifoU8Ins(parser->consumed, @json_unescape_char(parser->stream[++parser->pos]));
+ break;
+ case ']':
+ switch (parser->state) {
+ case JSON_STATE_ARRAY_STRING:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ case JSON_STATE_ARRAY_NUMBER:
+ item->value = @json_string_from_fifo(parser->consumed);
+ @json_rstrip(item->value);
+ item->value = Str2F64(item->value);
+ @json_append_item(arr, item);
+ return;
+ break;
+ case JSON_STATE_ARRAY_BOOLEAN:
+ item->value = @json_string_from_fifo(parser->consumed);
+ @json_rstrip(item->value);
+ if (StrCmp("true", item->value) && StrCmp("false", item->value)) {
+ PrintErr("@json_parse_array: Illegal boolean value at position %d",
+ parser->pos);
+ while (1)
+ Sleep(1);
+ }
+ if (!StrCmp("true", item->value))
+ item->value = TRUE;
+ else
+ item->value = FALSE;
+ @json_append_item(arr, item);
+ break;
+ case JSON_STATE_ARRAY_NULL:
+ item->value = @json_string_from_fifo(parser->consumed);
+ @json_rstrip(item->value);
+ if (StrCmp("null", item->value)) {
+ PrintErr("@json_parse_array: Illegal null value at position %d",
+ parser->pos);
+ while (1)
+ Sleep(1);
+ }
+ item->value = NULL;
+ @json_append_item(arr, item);
+ break;
+ case JSON_STATE_ARRAY:
+ case JSON_STATE_ARRAY_NEXT:
+ return;
+ break;
+ }
+ break;
+ case ',':
+ switch (parser->state) {
+ case JSON_STATE_ARRAY_STRING:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ case JSON_STATE_ARRAY_NUMBER:
+ item->value = @json_string_from_fifo(parser->consumed);
+ item->value = Str2F64(item->value);
+ @json_append_item(arr, item);
+ parser->state = JSON_STATE_ARRAY;
+ break;
+ case JSON_STATE_ARRAY_BOOLEAN:
+ item->value = @json_string_from_fifo(parser->consumed);
+ @json_rstrip(item->value);
+ if (StrCmp("true", item->value) && StrCmp("false", item->value)) {
+ PrintErr("@json_parse_array: Illegal boolean value at position %d",
+ parser->pos);
+ while (1)
+ Sleep(1);
+ }
+ if (!StrCmp("true", item->value))
+ item->value = TRUE;
+ else
+ item->value = FALSE;
+ @json_append_item(arr, item);
+ parser->state = JSON_STATE_ARRAY;
+ break;
+ case JSON_STATE_ARRAY_NULL:
+ item->value = @json_string_from_fifo(parser->consumed);
+ @json_rstrip(item->value);
+ if (StrCmp("null", item->value)) {
+ PrintErr("@json_parse_array: Illegal null value at position %d",
+ parser->pos);
+ while (1)
+ Sleep(1);
+ }
+ item->value = NULL;
+ @json_append_item(arr, item);
+ parser->state = JSON_STATE_ARRAY;
+ break;
+ case JSON_STATE_ARRAY_NEXT:
+ parser->state = JSON_STATE_ARRAY;
+ break;
+ }
+ break;
+ case '[':
+ switch (parser->state) {
+ case JSON_STATE_ARRAY_STRING:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ case JSON_STATE_ARRAY_TYPE:
+ item->type = JSON_ARRAY;
+ item->value = @json_parse_object_or_array(parser);
+ @json_append_item(arr, item);
+ parser->state = JSON_STATE_ARRAY_NEXT;
+ break;
+ }
+ break;
+ case '{':
+ switch (parser->state) {
+ case JSON_STATE_ARRAY_STRING:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ case JSON_STATE_ARRAY_TYPE:
+ item->type = JSON_OBJECT;
+ item->value = @json_parse_object_or_array(parser);
+ @json_append_item(arr, item);
+ parser->state = JSON_STATE_ARRAY_NEXT;
+ break;
+ }
+ break;
+ case '"':
+ switch (parser->state) {
+ case JSON_STATE_ARRAY_STRING:
+ item->value = @json_string_from_fifo(parser->consumed);
+ @json_append_item(arr, item);
+ parser->state = JSON_STATE_ARRAY_NEXT;
+ break;
+ case JSON_STATE_ARRAY_TYPE:
+ item->type = JSON_STRING;
+ parser->state = JSON_STATE_ARRAY_STRING;
+ break;
+ }
+ break;
+ case '-':
+ case '0' ... '9':
+ case '.':
+ switch (parser->state) {
+ case JSON_STATE_ARRAY_STRING:
+ case JSON_STATE_ARRAY_NUMBER:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ case JSON_STATE_ARRAY_TYPE:
+ item->type = JSON_NUMBER;
+ parser->state = JSON_STATE_ARRAY_NUMBER;
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ }
+ break;
+ case 't':
+ case 'f':
+ switch (parser->state) {
+ case JSON_STATE_ARRAY_STRING:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ case JSON_STATE_ARRAY_TYPE:
+ item->type = JSON_BOOLEAN;
+ parser->state = JSON_STATE_ARRAY_BOOLEAN;
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ }
+ break;
+ case 'n':
+ switch (parser->state) {
+ case JSON_STATE_ARRAY_STRING:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ case JSON_STATE_OBJECT_TYPE:
+ item->type = JSON_NULL;
+ parser->state = JSON_STATE_ARRAY_NULL;
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ }
+ break;
+ default:
+ switch (parser->state) {
+ case JSON_STATE_ARRAY_STRING:
+ case JSON_STATE_ARRAY_BOOLEAN:
+ case JSON_STATE_ARRAY_NULL:
+ FifoU8Ins(parser->consumed, parser->stream[parser->pos]);
+ break;
+ }
+ break;
+ }
+ if (parser->debug) {
+ @json_debug_parser_state(parser);
+ "Array: %08X, Pos: %d, Token: %c\n", arr, parser->pos,
+ parser->stream[parser->pos];
+ Sleep(50);
+ }
+ parser->pos++;
+ }
+}
+
+extern @json_callable_array* @json_create_callable_array(@json_array* arr);
+extern @json_callable_object* @json_create_callable_object(@json_object* obj);
+
+@json_element* @json_parse_object_or_array(@json_parser* parser)
+{
+ @json_element* el = CAlloc(sizeof(@json_element) * 2, adam_task);
+ while (1) {
+ switch (parser->stream[parser->pos]) {
+ case 0:
+ return el;
+ break;
+ case ' ':
+ case '\r':
+ case '\n':
+ case '\t':
+ break;
+ case '{':
+ el->type = JSON_OBJECT;
+ parser->pos++;
+ parser->state = JSON_STATE_OBJECT;
+ @json_parse_object(parser, el);
+ return @json_create_callable_object(el);
+ break;
+ case '[':
+ el->type = JSON_ARRAY;
+ parser->pos++;
+ parser->state = JSON_STATE_ARRAY;
+ @json_parse_array(parser, el);
+ return @json_create_callable_array(el);
+ break;
+ default:
+ PrintErr("@json_parse_object_or_array: Invalid token: '%c'", parser->stream[parser->pos]);
+ while (1) {
+ Sleep(1);
+ };
+ break;
+ }
+ parser->pos++;
+ Sleep(1);
+ }
+}
+
+@json_element* @json_parse(U8* str)
+{
+ @json_parser* parser = CAlloc(sizeof(@json_parser), adam_task);
+ parser->consumed = FifoU8New(JSON_PARSER_FIFO_SIZE, adam_task);
+ // parser->debug = TRUE;
+ parser->stream = str;
+ @json_element* root = @json_parse_object_or_array(parser);
+ FifoU8Flush(parser->consumed);
+ FifoU8Del(parser->consumed);
+ Free(parser);
+ return root;
+}
+
+U0 @json_prepend_item(@json_array* arr, @json_item* prepend_item)
+{
+ if (!arr || !prepend_item)
+ return;
+ if (arr->type != JSON_ARRAY)
+ return;
+ @json_item* items = arr->items;
+ arr->items = prepend_item;
+ arr->items->next = items;
+ arr->length++;
+}
+
+U0 @json_insert_item(@json_array* arr, @json_item* insert_item, I64 index)
+{
+ if (!arr || !insert_item)
+ return;
+ if (arr->type != JSON_ARRAY)
+ return;
+ if (index <= 0) {
+ @json_prepend_item(arr, insert_item);
+ return;
+ }
+ if (index >= arr->length) {
+ @json_append_item(arr, insert_item);
+ return;
+ }
+ @json_item* insert_at_item = arr->items;
+ @json_item* insert_after_item = NULL;
+ I64 i;
+ for (i = 0; i < index; i++)
+ insert_at_item = insert_at_item->next;
+ insert_after_item = insert_at_item->prev;
+ insert_after_item->next = insert_item;
+ insert_item->prev = insert_after_item;
+ insert_item->next = insert_at_item;
+ insert_at_item->prev = insert_item;
+ arr->length++;
+}
+
+U0 @json_stringify_append_char(U8* str, U8 char)
+{
+ // FIXME: unicode
+ I64 len = StrLen(str);
+ switch (char) {
+ case '\\':
+ str[len] = '\\';
+ str[len + 1] = '\\';
+ str[len + 2] = NULL;
+ break;
+ case 0x08:
+ str[len] = '\\';
+ str[len + 1] = 'b';
+ str[len + 2] = NULL;
+ break;
+ case 0x0c:
+ str[len] = '\\';
+ str[len + 1] = 'f';
+ str[len + 2] = NULL;
+ break;
+ case 0x0a:
+ str[len] = '\\';
+ str[len + 1] = 'n';
+ str[len + 2] = NULL;
+ break;
+ case 0x0d:
+ str[len] = '\\';
+ str[len + 1] = 'r';
+ str[len + 2] = NULL;
+ break;
+ case 0x09:
+ str[len] = '\\';
+ str[len + 1] = 't';
+ str[len + 2] = NULL;
+ break;
+ default:
+ str[len] = char;
+ str[len + 1] = NULL;
+ break;
+ }
+}
+
+U0 @json_stringify_append_str(U8* str, U8* str2)
+{
+ while (*str2) {
+ @json_stringify_append_char(str, *str2);
+ str2++;
+ }
+}
+
+U0 @json_stringify_append_number(U8* str, F64 num)
+{
+ U8 buf[256];
+ MemSet(buf, 0, 256);
+ StrPrint(buf, "%.6f", num);
+ I64 i = StrLen(buf) - 1;
+ while (buf[i] == '0') {
+ buf[i] = NULL;
+ i--;
+ }
+ i = StrLen(buf) - 1;
+ if (buf[i] == '.')
+ buf[i] = NULL;
+ @json_stringify_append_str(str, buf);
+}
+
+extern U0 @json_stringify_object_or_array(U8* str, @json_element* el);
+
+U0 @json_stringify_object(U8* str, @json_object* obj)
+{
+ @json_stringify_append_char(str, '{');
+ @json_key* key = obj->keys;
+ while (key) {
+ @json_stringify_append_char(str, '"');
+ @json_stringify_append_str(str, key->name);
+ @json_stringify_append_char(str, '"');
+ @json_stringify_append_char(str, ':');
+ switch (key->type) {
+ case JSON_OBJECT:
+ case JSON_ARRAY:
+ @json_stringify_object_or_array(str, key->value);
+ break;
+ case JSON_STRING:
+ @json_stringify_append_char(str, '"');
+ @json_stringify_append_str(str, key->value);
+ @json_stringify_append_char(str, '"');
+ break;
+ case JSON_NUMBER:
+ @json_stringify_append_number(str, key->value);
+ break;
+ case JSON_BOOLEAN:
+ @json_stringify_append_str(str, @t(key->value, "true", "false"));
+ break;
+ case JSON_NULL:
+ @json_stringify_append_str(str, "null");
+ break;
+ default:
+ PrintErr("@json_stringify_object: Invalid element type: %d", key->type);
+ while (1) {
+ Sleep(1);
+ };
+ }
+ if (key->next)
+ @json_stringify_append_char(str, ',');
+ key = key->next;
+ }
+ @json_stringify_append_char(str, '}');
+}
+
+U0 @json_stringify_array(U8* str, @json_array* arr)
+{
+ @json_stringify_append_char(str, '[');
+ @json_item* item = arr->items;
+ while (item) {
+ switch (item->type) {
+ case JSON_OBJECT:
+ case JSON_ARRAY:
+ @json_stringify_object_or_array(str, item->value);
+ break;
+ case JSON_STRING:
+ @json_stringify_append_char(str, '"');
+ @json_stringify_append_str(str, item->value);
+ @json_stringify_append_char(str, '"');
+ break;
+ case JSON_NUMBER:
+ @json_stringify_append_number(str, item->value);
+ break;
+ case JSON_BOOLEAN:
+ @json_stringify_append_str(str, @t(item->value, "true", "false"));
+ break;
+ case JSON_NULL:
+ @json_stringify_append_str(str, "null");
+ break;
+ default:
+ PrintErr("@json_stringify_array: Invalid element type: %d", item->type);
+ while (1) {
+ Sleep(1);
+ };
+ }
+ if (item->next)
+ @json_stringify_append_char(str, ',');
+ item = item->next;
+ }
+ @json_stringify_append_char(str, ']');
+}
+
+U0 @json_stringify_object_or_array(U8* str, @json_element* el)
+{
+ while (el) {
+ switch (el->type) {
+ case JSON_OBJECT:
+ @json_stringify_object(str, el);
+ break;
+ case JSON_ARRAY:
+ @json_stringify_array(str, el);
+ break;
+ default:
+ PrintErr("@json_stringify_object_or_array: Invalid element type: %d", el->type);
+ while (1) {
+ Sleep(1);
+ };
+ break;
+ }
+ el = el->next;
+ }
+}
+
+U8* @json_stringify(@json_element* el, I64 buf_size = JSON_STRINGIFY_BUF_SIZE)
+{
+ U8* str = CAlloc(buf_size, adam_task);
+ @json_stringify_object_or_array(str, el);
+ return str;
+}
+
+U64 @json_get(@json_object* obj, U8* key, Bool return_key = FALSE)
+{
+ if (!obj || !key)
+ return NULL;
+ if (!obj->keys || obj->type != JSON_OBJECT)
+ return NULL;
+ @json_key* iter_key = obj->keys;
+ while (iter_key) {
+ if (!StrICmp(iter_key->name, key))
+ if (return_key)
+ return iter_key;
+ else
+ return iter_key->value;
+ iter_key = iter_key->next;
+ }
+ return NULL;
+}
+
+U0 @json_set(@json_object* obj, U8* key, U64 value, I64 type = JSON_SAME)
+{
+ if (!obj || !key || !type)
+ return;
+ if (obj->type != JSON_OBJECT)
+ return;
+ @json_key* iter_key = obj->keys;
+ while (iter_key) {
+ if (!StrCmp(iter_key->name, key)) {
+ if (type != JSON_SAME)
+ iter_key->type = type;
+ iter_key->value = value;
+ return;
+ }
+ iter_key = iter_key->next;
+ }
+ @json_key* new_key = CAlloc(sizeof(@json_key), adam_task);
+ new_key->name = StrNew(key, adam_task);
+ new_key->type = type;
+ if (new_key->type == JSON_STRING)
+ new_key->value = StrNew(value, adam_task);
+ else
+ new_key->value = value;
+ @json_insert_key(obj, new_key);
+}
+
+U0 @json_unset(@json_object* obj, U8* key)
+{
+ if (!obj || !key)
+ return;
+ if (obj->type != JSON_OBJECT)
+ return;
+ @json_key* iter_key = obj->keys;
+ @json_key* prev_key = NULL;
+ @json_key* next_key = NULL;
+ while (iter_key) {
+ if (!StrCmp(iter_key->name, key)) {
+ prev_key = iter_key->prev;
+ next_key = iter_key->next;
+ if (prev_key)
+ prev_key->next = next_key;
+ if (next_key)
+ next_key->prev = prev_key;
+ // FIXME: Delete the unset key
+ return;
+ }
+ iter_key = iter_key->next;
+ }
+}
+
+U64 @json_callable_object_get_wrapper_function(U8* key, Bool return_key = FALSE)
+{
+ return @json_get(JSON_WRAPPER_MAGIC_NUMBER, key, return_key);
+}
+
+U0 @json_callable_object_set_wrapper_function(U8* key, U64 value, I64 type = JSON_SAME)
+{
+ @json_set(JSON_WRAPPER_MAGIC_NUMBER, key, value, type);
+}
+
+U0 @json_callable_object_unset_wrapper_function(U8* key)
+{
+ @json_unset(JSON_WRAPPER_MAGIC_NUMBER, key);
+}
+
+@json_callable_object* @json_create_callable_object(@json_object* obj)
+{
+ // Alloc callable object and copy instance
+ @json_callable_object* cobj = CAlloc(sizeof(@json_callable_object), adam_task);
+ MemCpy(cobj, obj, sizeof(@json_object));
+
+ // Create a copy of function and patch Get
+ U64 a;
+ I64 code_size = MSize(&@json_callable_object_get_wrapper_function);
+ cobj->@ = CAlloc(code_size, adam_task->code_heap);
+ MemCpy(cobj->@, &@json_callable_object_get_wrapper_function, code_size);
+
+ a = cobj->@;
+ a += 0x13;
+ MemSetI64(a, cobj, 1);
+
+ a = cobj->@;
+ a += 0x1c;
+ @patch_call_rel32(a, &@json_get);
+
+ // Create a copy of function and patch Set
+ code_size = MSize(&@json_callable_object_set_wrapper_function);
+ cobj->set = CAlloc(code_size, adam_task->code_heap);
+ MemCpy(cobj->set, &@json_callable_object_set_wrapper_function, code_size);
+
+ a = cobj->set;
+ a += 0x1a;
+ MemSetI64(a, cobj, 1);
+
+ a = cobj->set;
+ a += 0x23;
+ @patch_call_rel32(a, &@json_set);
+
+ // Create a copy of function and patch Unset
+ code_size = MSize(&@json_callable_object_unset_wrapper_function);
+ cobj->unset = CAlloc(code_size, adam_task->code_heap);
+ MemCpy(cobj->unset, &@json_callable_object_unset_wrapper_function, code_size);
+
+ a = cobj->unset;
+ a += 0x0c;
+ MemSetI64(a, cobj, 1);
+
+ a = cobj->unset;
+ a += 0x15;
+ @patch_call_rel32(a, &@json_unset);
+
+ cobj->a = cobj->@;
+ cobj->o = cobj->@;
+
+ return cobj;
+}
+
+@json_callable_object* @json_create_object()
+{
+ @json_object* obj = CAlloc(sizeof(@json_object), adam_task);
+ obj->type = JSON_OBJECT;
+ return @json_create_callable_object(obj);
+}
+
+@json_item* @json_create_item(U64 value, I64 type)
+{
+ @json_item* item = CAlloc(sizeof(@json_item), adam_task);
+ item->type = type;
+ if (item->type == JSON_STRING)
+ item->value = StrNew(value, adam_task);
+ else
+ item->value = value;
+ return item;
+}
+
+U64 @json_array_index(@json_array* arr, I64 index, Bool return_item = FALSE)
+{
+ if (!arr)
+ return NULL;
+ if (arr->type != JSON_ARRAY)
+ return NULL;
+ if (index < 0)
+ return NULL;
+ if (!arr->length)
+ return NULL;
+ if (index > 0 && index > arr->length - 1)
+ return NULL;
+ @json_item* item = arr->items;
+ if (!item)
+ return NULL;
+ I64 i;
+ for (i = 0; i < index; i++)
+ item = item->next;
+ if (return_item)
+ return item;
+ else
+ return item->value;
+}
+
+U0 @json_remove_item(@json_array* arr, I64 index)
+{
+ if (!arr)
+ return;
+ if (arr->type != JSON_ARRAY)
+ return;
+ if (index < 0)
+ return;
+ if (!arr->length)
+ return;
+ if (index > 0 && index > arr->length - 1)
+ return;
+ @json_item* item = arr->items;
+ if (!item)
+ return;
+ @json_item* prev_item = NULL;
+ @json_item* next_item = NULL;
+ I64 i;
+ for (i = 0; i < index; i++)
+ item = item->next;
+ if (!index) {
+ arr->items = item->next;
+ goto @json_remove_item_final;
+ }
+ prev_item = item->prev;
+ next_item = item->next;
+ if (arr->last_item == item)
+ arr->last_item = item->prev;
+ prev_item->next = next_item;
+ if (next_item)
+ next_item->prev = prev_item;
+ @json_remove_item_final : Free(item);
+ --arr->length;
+}
+
+U64 @json_callable_array_index_wrapper_function(I64 index, Bool return_item = FALSE)
+{
+ return @json_get(JSON_WRAPPER_MAGIC_NUMBER, index, return_item);
+}
+
+U0 @json_callable_array_append_wrapper_function(@json_item* append_item)
+{
+ @json_append_item(JSON_WRAPPER_MAGIC_NUMBER, append_item);
+}
+
+U0 @json_callable_array_insert_wrapper_function(@json_item* insert_item, I64 index)
+{
+ @json_insert_item(JSON_WRAPPER_MAGIC_NUMBER, insert_item, index);
+}
+
+U0 @json_callable_array_prepend_wrapper_function(@json_item* prepend_item)
+{
+ @json_prepend_item(JSON_WRAPPER_MAGIC_NUMBER, prepend_item);
+}
+
+U0 @json_callable_array_remove_wrapper_function(I64 index)
+{
+ @json_remove_item(JSON_WRAPPER_MAGIC_NUMBER, index);
+}
+
+@json_callable_array* @json_create_callable_array(@json_array* arr)
+{
+ // Alloc callable object and copy instance
+ @json_callable_array* carr = CAlloc(sizeof(@json_callable_array), adam_task);
+ MemCpy(carr, arr, sizeof(@json_array));
+
+ // Create a copy of function and patch Index
+ U64 a;
+ I64 code_size = MSize(&@json_callable_array_index_wrapper_function);
+ carr->@ = CAlloc(code_size, adam_task->code_heap);
+ MemCpy(carr->@, &@json_callable_array_index_wrapper_function, code_size);
+
+ a = carr->@;
+ a += 0x13;
+ MemSetI64(a, carr, 1);
+
+ a = carr->@;
+ a += 0x1c;
+ @patch_call_rel32(a, &@json_array_index);
+
+ carr->a = carr->@;
+ carr->o = carr->@;
+
+ // Create a copy of function and patch Append
+ code_size = MSize(&@json_callable_array_append_wrapper_function);
+ carr->append = CAlloc(code_size, adam_task->code_heap);
+ MemCpy(carr->append, &@json_callable_array_append_wrapper_function, code_size);
+
+ a = carr->append;
+ a += 0x0c;
+ MemSetI64(a, carr, 1);
+
+ a = carr->append;
+ a += 0x15;
+ @patch_call_rel32(a, &@json_append_item);
+
+ // Create a copy of function and patch Prepend
+ code_size = MSize(&@json_callable_array_prepend_wrapper_function);
+ carr->prepend = CAlloc(code_size, adam_task->code_heap);
+ MemCpy(carr->prepend, &@json_callable_array_prepend_wrapper_function, code_size);
+
+ a = carr->prepend;
+ a += 0x0c;
+ MemSetI64(a, carr, 1);
+
+ a = carr->prepend;
+ a += 0x15;
+ @patch_call_rel32(a, &@json_prepend_item);
+
+ // Create a copy of function and patch Insert
+ code_size = MSize(&@json_callable_array_insert_wrapper_function);
+ carr->insert = CAlloc(code_size, adam_task->code_heap);
+ MemCpy(carr->insert, &@json_callable_array_insert_wrapper_function, code_size);
+
+ a = carr->insert;
+ a += 0x12;
+ MemSetI64(a, carr, 1);
+
+ a = carr->insert;
+ a += 0x1b;
+ @patch_call_rel32(a, &@json_insert_item);
+
+ // Create a copy of function and patch Remove
+ code_size = MSize(&@json_callable_array_remove_wrapper_function);
+ carr->remove = CAlloc(code_size, adam_task->code_heap);
+ MemCpy(carr->remove, &@json_callable_array_remove_wrapper_function, code_size);
+
+ a = carr->remove;
+ a += 0x0c;
+ MemSetI64(a, carr, 1);
+
+ a = carr->remove;
+ a += 0x15;
+ @patch_call_rel32(a, &@json_remove_item);
+
+ return carr;
+}
+
+@json_array* @json_create_array()
+{
+ @json_array* arr = CAlloc(sizeof(@json_array), adam_task);
+ arr->type = JSON_ARRAY;
+ return @json_create_callable_array(arr);
+}
+
+U64 @json_parse_file(U8* path)
+{
+ U64 res = NULL;
+ U8* json_string = FileRead(path);
+ if (json_string) {
+ res = @json_parse(json_string);
+ Free(json_string);
+ }
+ return res;
+}
+
+U0 @json_delete(@json_element* el)
+{
+ // FIXME: Implement this
+ no_warn el;
+}
+
+U0 @json_dump_to_file(U8* path, @json_element* el)
+{
+ if (!path || !el)
+ return;
+ U8* json_string = @json_stringify(el);
+ FileWrite(path, json_string, StrLen(json_string));
+ Free(json_string);
+}
+
+@json_element* @json_clone(@json_element* el)
+{
+ if (!el)
+ return NULL;
+ U8* tmp = @json_stringify(el);
+ if (!tmp)
+ return NULL;
+ @json_element* clone = @json_parse(tmp);
+ Free(tmp);
+ return clone;
+}
+
+class @json
+{
+ U0(*AppendItem)
+ (@json_array * arr, @json_item * append_item);
+ U64(*ArrayIndex)
+ (@json_array * arr, I64 index, Bool return_item = FALSE);
+ @json_element* (*Clone)(@json_element* el);
+ @json_object* (*CreateObject)();
+ @json_callable_object* (*CreateCallableObject)(@json_object* obj);
+ @json_item* (*CreateItem)(U64 value, I64 type);
+ @json_array* (*CreateArray)();
+ U0 (*Delete)(@json_element* el);
+ U0 (*DumpToFile)(U8* path, @json_element* el);
+ U0(*InsertItem)
+ (@json_array * arr, @json_item * insert_item, I64 index);
+ @json_element* (*Parse)(U8* str);
+ U64 (*ParseFile)(U8* path);
+ U0(*PrependItem)
+ (@json_array * arr, @json_item * prepend_item);
+ U0(*RemoveItem)
+ (@json_array * arr, I64 index);
+ U64(*Get)
+ (@json_object * obj, U8 * key, Bool return_key = FALSE);
+ U0(*Set)
+ (@json_object * obj, U8 * key, U64 value, I64 type);
+ U8* (*Stringify)(@json_element* el, I64 buf_size = JSON_STRINGIFY_BUF_SIZE);
+ U0(*Unset)
+ (@json_object * obj, U8 * key);
+};
+
+@json Json;
+Json.AppendItem = &@json_append_item;
+Json.ArrayIndex = &@json_array_index;
+Json.Clone = &@json_clone;
+Json.CreateArray = &@json_create_array;
+Json.CreateItem = &@json_create_item;
+Json.CreateObject = &@json_create_object;
+Json.Delete = &@json_delete;
+Json.DumpToFile = &@json_dump_to_file;
+Json.Get = &@json_get;
+Json.InsertItem = &@json_insert_item;
+Json.Parse = &@json_parse;
+Json.ParseFile = &@json_parse_file;
+Json.PrependItem = &@json_prepend_item;
+Json.RemoveItem = &@json_remove_item;
+Json.Set = &@json_set;
+Json.Stringify = &@json_stringify;
+Json.Unset = &@json_unset;
diff --git a/System/Libraries/Rsa.HC b/System/Libraries/Rsa.HC
new file mode 100644
index 0000000..ad3dbde
--- /dev/null
+++ b/System/Libraries/Rsa.HC
@@ -0,0 +1,44 @@
+Silent(1); // This is needed to suppress "Function should return val" warnings for wrappers to non-HolyC functions
+
+I64 @rsa_import(U8* der_bytes, I64 der_len, U64 key)
+{
+ U64 reg RDI rdi = der_bytes;
+ U64 reg RSI rsi = der_len;
+ U64 reg RDX rdx = key;
+ no_warn rdi, rsi, rdx;
+ asm {
+ MOV RAX, RSA_IMPORT
+ CALL RAX
+ }
+}
+
+I64 @rsa_create_signature(U8* sig, I64* siglen, U8* hash, I64 hashlen, U64 key)
+{
+ U64 reg RDI rdi = sig;
+ U64 reg RSI rsi = siglen;
+ U64 reg RDX rdx = hash;
+ U64 reg RCX rcx = hashlen;
+ U64 reg R8 r8 = key;
+ no_warn rdi, rsi, rdx, rcx, r8;
+ asm {
+ MOV RAX, RSA_CREATE_SIGNATURE
+ CALL RAX
+ }
+}
+
+I64 @rsa_verify_signature(U8* sig, I64 siglen, U8* hash, I64 hashlen, I32* stat, U64 key)
+{
+ U64 reg RDI rdi = sig;
+ U64 reg RSI rsi = siglen;
+ U64 reg RDX rdx = hash;
+ U64 reg RCX rcx = hashlen;
+ U64 reg R8 r8 = stat;
+ U64 reg R9 r9 = key;
+ no_warn rdi, rsi, rdx, rcx, r8, r9;
+ asm {
+ MOV RAX, RSA_VERIFY_SIGNATURE
+ CALL RAX
+ }
+}
+
+Silent(0);
diff --git a/System/Libraries/Sha256.HC b/System/Libraries/Sha256.HC
new file mode 100644
index 0000000..42dc43f
--- /dev/null
+++ b/System/Libraries/Sha256.HC
@@ -0,0 +1,233 @@
+#define CHUNK_SIZE 64
+#define TOTAL_LEN_LEN 8
+
+/*
+ * ABOUT bool: this file does not use bool in order to be as pre-C99 compatible
+ * as possible.
+ */
+
+/*
+ * Comments from pseudo-code at https://en.wikipedia.org/wiki/SHA-2 are
+ * reproduced here. When useful for clarification, portions of the pseudo-code
+ * are reproduced here too.
+ */
+
+/*
+ * Initialize array of round constants:
+ * (first 32 bits of the fractional parts of the cube roots of the first 64
+ * primes 2..311):
+ */
+U32 k[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
+ 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
+ 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
+ 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
+ 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
+ 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+U32 _h[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
+ 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 };
+
+class buffer_state {
+ U8* p;
+ I32 len;
+ I32 total_len;
+ I32 single_one_delivered; /* bool */
+ I32 total_len_delivered; /* bool */
+};
+
+U32 right_rot(U32 value, U32 count)
+{
+ /*
+ * Defined behaviour in standard C for all count where 0 < count < 32,
+ * which is what we need here.
+ */
+ return value >> count | value << (32 - count);
+}
+
+U0 init_buf_state(buffer_state* state, U8* input, I32 len)
+{
+ state->p = input;
+ state->len = len;
+ state->total_len = len;
+ state->single_one_delivered = 0;
+ state->total_len_delivered = 0;
+}
+
+/* Return value: bool */
+I32 calc_chunk(U8* chunk, buffer_state* state)
+{
+ I32 space_in_chunk;
+
+ if (state->total_len_delivered) {
+ return 0;
+ }
+
+ if (state->len >= CHUNK_SIZE) {
+ MemCpy(chunk, state->p, CHUNK_SIZE);
+ state->p += CHUNK_SIZE;
+ state->len -= CHUNK_SIZE;
+ return 1;
+ }
+
+ MemCpy(chunk, state->p, state->len);
+ chunk += state->len;
+ space_in_chunk = CHUNK_SIZE - state->len;
+ state->p += state->len;
+ state->len = 0;
+
+ /* If we are here, space_in_chunk is one at minimum. */
+ if (!state->single_one_delivered) {
+ *chunk++ = 0x80;
+ space_in_chunk -= 1;
+ state->single_one_delivered = 1;
+ }
+
+ /*
+ * Now:
+ * - either there is enough space left for the total length, and we can
+ * conclude,
+ * - or there is too little space left, and we have to pad the rest of this
+ * chunk with zeroes. In the latter case, we will conclude at the next
+ * invokation of this function.
+ */
+ if (space_in_chunk >= TOTAL_LEN_LEN) {
+ I32 left = space_in_chunk - TOTAL_LEN_LEN;
+ I32 len = state->total_len;
+ I32 i;
+ MemSet(chunk, 0x00, left);
+ chunk += left;
+
+ /* Storing of len * 8 as a big endian 64-bit without overflow. */
+ chunk[7] = (len << 3);
+ len >>= 5;
+ for (i = 6; i >= 0; i--) {
+ chunk[i] = len;
+ len >>= 8;
+ }
+ state->total_len_delivered = 1;
+ } else {
+ MemSet(chunk, 0x00, space_in_chunk);
+ }
+
+ return 1;
+}
+
+/*
+ * Limitations:
+ * - Since input is a pointer in RAM, the data to hash should be in RAM, which
+ * could be a problem for large data sizes.
+ * - SHA algorithms theoretically operate on bit strings. However, this
+ * implementation has no support for bit string lengths that are not multiples
+ * of eight, and it really operates on arrays of bytes. In particular, the len
+ * parameter is a number of bytes.
+ */
+U0 calc_sha_256(U8* hash, U8* input, I32 len)
+{
+ /*
+ * Note 1: All integers (expect indexes) are 32-bit U32 integers and addition
+ * is calculated modulo 2^32. Note 2: For each round, there is one round
+ * constant k[i] and one entry in the message schedule array w[i], 0 = i = 63
+ * Note 3: The compression function uses 8 working variables, a through h
+ * Note 4: Big-endian convention is used when expressing the constants in this
+ * pseudocode, and when parsing message block data from bytes to words, for
+ * example, the first word of the input message "abc" after padding is
+ * 0x61626380
+ */
+
+ /*
+ * Initialize hash values:
+ * (first 32 bits of the fractional parts of the square roots of the first 8
+ * primes 2..19):
+ */
+
+ U32 h[8];
+ U32 i, j;
+ MemCpy(h, _h, sizeof(U32) * 8);
+
+ /* 512-bit chunks is what we will operate on. */
+ U8 chunk[64];
+
+ buffer_state state;
+
+ init_buf_state(&state, input, len);
+
+ while (calc_chunk(chunk, &state)) {
+ U32 ah[8];
+
+ U8* p = chunk;
+
+ /* Initialize working variables to current hash value: */
+ for (i = 0; i < 8; i++)
+ ah[i] = h[i];
+
+ /* Compression function main loop: */
+ for (i = 0; i < 4; i++) {
+ /*
+ * The w-array is really w[64], but since we only need
+ * 16 of them at a time, we save stack by calculating
+ * 16 at a time.
+ *
+ * This optimization was not there initially and the
+ * rest of the comments about w[64] are kept in their
+ * initial state.
+ */
+
+ /*
+ * create a 64-entry message schedule array w[0..63] of 32-bit words
+ * (The initial values in w[0..63] don't matter, so many implementations
+ * zero them here) copy chunk into first 16 words w[0..15] of the message
+ * schedule array
+ */
+ U32 w[16];
+
+ U32 s0, s1;
+
+ for (j = 0; j < 16; j++) {
+ if (i == 0) {
+ w[j] = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+ p += 4;
+ } else {
+ /* Extend the first 16 words into the remaining 48 words w[16..63] of
+ * the message schedule array: */
+ s0 = right_rot(w[(j + 1) & 0xf], 7) ^ right_rot(w[(j + 1) & 0xf], 18) ^ (w[(j + 1) & 0xf] >> 3);
+ s1 = right_rot(w[(j + 14) & 0xf], 17) ^ right_rot(w[(j + 14) & 0xf], 19) ^ (w[(j + 14) & 0xf] >> 10);
+ w[j] = w[j] + s0 + w[(j + 9) & 0xf] + s1;
+ }
+ s1 = right_rot(ah[4], 6) ^ right_rot(ah[4], 11) ^ right_rot(ah[4], 25);
+ U32 ch = (ah[4] & ah[5]) ^ (~ah[4] & ah[6]);
+ U32 temp1 = ah[7] + s1 + ch + k[i << 4 | j] + w[j];
+ s0 = right_rot(ah[0], 2) ^ right_rot(ah[0], 13) ^ right_rot(ah[0], 22);
+ U32 maj = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]);
+ U32 temp2 = s0 + maj;
+
+ ah[7] = ah[6];
+ ah[6] = ah[5];
+ ah[5] = ah[4];
+ ah[4] = ah[3] + temp1;
+ ah[3] = ah[2];
+ ah[2] = ah[1];
+ ah[1] = ah[0];
+ ah[0] = temp1 + temp2;
+ }
+ }
+
+ /* Add the compressed chunk to the current hash value: */
+ for (i = 0; i < 8; i++)
+ h[i] += ah[i];
+ }
+
+ /* Produce the final hash value (big-endian): */
+ for (i = 0, j = 0; i < 8; i++) {
+ hash[j++] = (h[i] >> 24);
+ hash[j++] = (h[i] >> 16);
+ hash[j++] = (h[i] >> 8);
+ hash[j++] = h[i];
+ }
+}
diff --git a/System/Libraries/String.HC b/System/Libraries/String.HC
new file mode 100644
index 0000000..7b45f41
--- /dev/null
+++ b/System/Libraries/String.HC
@@ -0,0 +1,133 @@
+U0 @string_append(U8* dst, U8* fmt, ...)
+{
+ U8* buf;
+ if (argc) {
+ buf = StrPrintJoin(NULL, fmt, argc, argv);
+ } else {
+ buf = StrNew(fmt, adam_task);
+ }
+ U8* src = buf;
+ StrCpy(dst + StrLen(dst), src);
+ Free(buf);
+}
+
+Bool @string_is_number(U8* s)
+{
+ while (*s) {
+ switch (*s) {
+ case '-':
+ case '.':
+ case '0' ... '9':
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+ s++;
+ }
+ return TRUE;
+}
+
+Bool @string_begins_with(U8* fragment, U8* str)
+{
+ if (!fragment || !str)
+ return FALSE;
+ if (StrLen(fragment) > StrLen(str))
+ return FALSE;
+ return !MemCmp(fragment, str, StrLen(fragment));
+}
+
+Bool @string_ends_with(U8* fragment, U8* str)
+{
+ if (!fragment || !str)
+ return FALSE;
+ if (StrLen(fragment) > StrLen(str))
+ return FALSE;
+ return !MemCmp(fragment, str + StrLen(str) - StrLen(fragment), StrLen(fragment));
+}
+
+U8* @string_replace(U8* s, U8* oldW, U8* newW)
+{
+ if (!StrFind(oldW, s)) {
+ return StrNew(s, adam_task);
+ }
+ U8* result;
+ I64 i, cnt = 0;
+ I64 newWlen = StrLen(newW);
+ I64 oldWlen = StrLen(oldW);
+ for (i = 0; s[i] != NULL; i++) {
+ if (StrFind(oldW, &s[i]) == &s[i]) {
+ cnt++;
+
+ i += oldWlen - 1;
+ }
+ }
+ result = MAlloc(i + cnt * (newWlen - oldWlen) + 1, adam_task);
+ i = 0;
+ while (*s) {
+ if (StrFind(oldW, s) == s) {
+ StrCpy(&result[i], newW);
+ i += newWlen;
+ s += oldWlen;
+ } else
+ result[i++] = *s++;
+ }
+ result[i] = NULL;
+ return result;
+}
+
+U8** @string_split(U8* s, U8 ch = '\n', I64* cnt)
+{
+ U8 check_buf[4];
+ StrPrint(check_buf, "%c", ch);
+ if (!StrFind(check_buf, s)) {
+ U8** same_arr = CAlloc(sizeof(U8*) * 1, adam_task);
+ same_arr[0] = s;
+ *cnt = 1;
+ return same_arr;
+ }
+ U8* p = s;
+ cnt[0] = 0;
+ while (*p) {
+ if (*p == ch)
+ cnt[0]++;
+ p++;
+ }
+ if (!(cnt[0]))
+ return NULL;
+ cnt[0]++;
+ I64 i = -1;
+ U8** arr = CAlloc(sizeof(U8*) * cnt[0], adam_task);
+ p = s;
+ while (*p) {
+ if (*p == ch || i < 0) {
+ i++;
+ arr[i] = p;
+ if (*p == ch) {
+ arr[i]++;
+ *p = NULL;
+ }
+ }
+ p++;
+ }
+ return arr;
+}
+
+class @string
+{
+ U0(*Append)
+ (U8 * dst, U8 * fmt, ...);
+ Bool (*BeginsWith)(U8* fragment, U8* str);
+ Bool (*EndsWith)(U8* fragment, U8* str);
+ Bool (*IsNumber)(U8* s);
+ U8* (*Replace)(U8* s, U8* oldW, U8* newW);
+ U8** (*Split)(U8* s, U8 ch = '\n', I64 * cnt);
+};
+
+@string String;
+String.Append = &@string_append;
+String.BeginsWith = &@string_begins_with;
+String.EndsWith = &@string_ends_with;
+String.IsNumber = &@string_is_number;
+String.Replace = &@string_replace;
+String.Split = &@string_split;
diff --git a/System/Libraries/Tlse.HC b/System/Libraries/Tlse.HC
new file mode 100644
index 0000000..62fb3d3
--- /dev/null
+++ b/System/Libraries/Tlse.HC
@@ -0,0 +1,115 @@
+#define TLS_V12 0x0303
+
+Silent(1); // This is needed to suppress "Function should return val" warnings for wrappers to non-HolyC functions
+
+U64 @tls_create_context(U8 is_server, U16 version)
+{
+ U64 reg RDI rdi = is_server;
+ U64 reg RSI rsi = version;
+ no_warn rdi, rsi;
+ asm {
+ MOV RAX, TLS_CREATE_CONTEXT
+ CALL RAX
+ }
+}
+
+I32 @tls_sni_set(U64 context, U8* sni)
+{
+ U64 reg RDI rdi = context;
+ U64 reg RSI rsi = sni;
+ no_warn rdi, rsi;
+ asm {
+ MOV RAX, TLS_SNI_SET
+ CALL RAX
+ }
+}
+
+I32 @tls_client_connect(U64 context)
+{
+ U64 reg RDI rdi = context;
+ no_warn rdi;
+ asm {
+ MOV RAX, TLS_CLIENT_CONNECT
+ CALL RAX
+ }
+}
+
+U8* @tls_get_write_buffer(U64 context, U32* outlen)
+{
+ U64 reg RDI rdi = context;
+ U64 reg RSI rsi = outlen;
+ no_warn rdi, rsi;
+ asm {
+ MOV RAX, TLS_GET_WRITE_BUFFER
+ CALL RAX
+ }
+}
+
+U0 @tls_buffer_clear(U64 context)
+{
+ U64 reg RDI rdi = context;
+ no_warn rdi;
+ asm {
+ MOV RAX, TLS_BUFFER_CLEAR
+ CALL RAX
+ }
+}
+
+I32 @tls_connection_status(U64 context)
+{
+ U64 reg RDI rdi = context;
+ no_warn rdi;
+ asm {
+ MOV RAX, TLS_CONNECTION_STATUS
+ CALL RAX
+ }
+}
+
+U0 @tls_consume_stream(U64 context, U8* buf, I32 buf_len, U64 certificate_verify)
+{
+ U64 reg RDI rdi = context;
+ U64 reg RSI rsi = buf;
+ U64 reg RDX rdx = buf_len;
+ U64 reg RCX rcx = certificate_verify;
+ no_warn rdi, rsi, rdx, rcx;
+ asm {
+ MOV RAX, TLS_CONSUME_STREAM
+ CALL RAX
+ }
+}
+
+I32 @tls_read(U64 context, U8* buf, U32 size)
+{
+ U64 reg RDI rdi = context;
+ U64 reg RSI rsi = buf;
+ U64 reg RDX rdx = size;
+ no_warn rdi, rsi, rdx;
+ asm {
+ MOV RAX, TLS_READ
+ CALL RAX
+ }
+}
+
+I32 @tls_write(U64 context, U8* data, U32 len)
+{
+ U64 reg RDI rdi = context;
+ U64 reg RSI rsi = data;
+ U64 reg RDX rdx = len;
+ no_warn rdi, rsi, rdx;
+ asm {
+ MOV RAX, TLS_WRITE
+ CALL RAX
+ }
+}
+
+I32 @tls_established(U64 context)
+{
+ U64 reg RDI rdi = context;
+ no_warn rdi;
+ asm {
+ MOV RAX, TLS_ESTABLISHED
+ CALL RAX
+ }
+}
+
+Silent(0);
diff --git a/System/MakeSystem.HC b/System/MakeSystem.HC
new file mode 100644
index 0000000..4a6ae95
--- /dev/null
+++ b/System/MakeSystem.HC
@@ -0,0 +1,56 @@
+/* clang-format off */
+
+DocMax(adam_task);
+WinMax(adam_task);
+WinToTop(adam_task);
+
+#include "Setup/Environment";
+#include "Drivers/Virtio-blk";
+
+// FFI support files
+#include "FFI/Base";
+#include "FFI/LibC";
+#include "FFI/New";
+#include "FFI/ELF64";
+
+// stb_image library
+#include "Utilities/Image";
+load_elf("M:/build/bin/image");
+
+// Jakt support files
+#include "Jakt/OS";
+#include "Jakt/IOPort";
+#include "Jakt/PCI";
+#include "Jakt/Time";
+
+#include "Libraries/Tlse";
+load_elf("M:/build/bin/tlse");
+
+// Networking APIs
+#include "Api/Dns.HC";
+#include "Api/Icmp.HC";
+#include "Api/Ipv4.HC";
+#include "Api/MD5.HC";
+#include "Api/NetInfo.HC";
+#include "Api/Tcp.HC";
+#include "Api/Tls.HC";
+
+// Libraries
+#include "Libraries/Base64";
+#include "Libraries/Json";
+#include "Libraries/Rsa";
+#include "Libraries/Sha256";
+#include "Libraries/String";
+#include "Libraries/Http";
+
+load_elf("M:/build/bin/net");
+
+// Networking Utilities
+#include "Utilities/Dns";
+#include "Utilities/NetRep";
+#include "Utilities/Ping";
+#include "Utilities/Time";
+
+Spawn(_start, , "Net Task");
+
+/* clang-format on */
diff --git a/System/Setup/Environment.HC b/System/Setup/Environment.HC
new file mode 100644
index 0000000..d07a9cc
--- /dev/null
+++ b/System/Setup/Environment.HC
@@ -0,0 +1,98 @@
+AutoComplete(0);
+
+U0 @patch_call_rel32(U32 from, U32 to)
+{
+ *(from(U8*)) = 0xE8;
+ *((from + 1)(I32*)) = to - from - 5;
+}
+
+U0 @patch_jmp_rel32(U32 from, U32 to)
+{
+ *(from(U8*)) = 0xE9;
+ *((from + 1)(I32*)) = to - from - 5;
+}
+
+CMemBlk* ShrinkMemBlkByPags(CMemBlk* from, I64 count)
+{
+ from->pags -= count;
+ U64 to = from;
+ to += count * MEM_PAG_SIZE;
+ MemCpy(to, from, MEM_PAG_SIZE);
+ return to;
+}
+
+U0 @sse_enable()
+{
+ /* clang-format off */
+ asm
+ {
+ MOV_EAX_CR0
+ AND AX, 0xFFFB // clear coprocessor emulation CR0.EM
+ OR AX, 0x2 // set coprocessor monitoring CR0.MP
+ MOV_CR0_EAX
+ MOV_EAX_CR4
+ OR AX, 3 << 9 // set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
+ MOV_CR4_EAX
+ }
+ /* clang-format on */
+}
+
+U0 @sse_enable_on_all_cores()
+{
+ I64 i;
+ for (i = 1; i < mp_cnt; i++)
+ Spawn(&@sse_enable, , , i);
+}
+
+I64 @t(Bool _condition, I64 _true, I64 _false)
+{
+ if (_condition)
+ return _true;
+ return _false;
+}
+
+// Before doing anything else, we:
+
+// 1. Mark memory in code heap below 0x1000000 as used.
+sys_code_bp->mem_free_lst->next->pags = 0;
+
+// 2. Free up 64MB at bottom of code heap for non-HolyC programs
+sys_code_bp->mem_free_lst = ShrinkMemBlkByPags(sys_code_bp->mem_free_lst, 131072);
+
+// 3. Enable SSE
+@sse_enable;
+
+U0 dd() { DocDump(adam_task->put_doc); }
+//@patch_jmp_rel32(&Dbg2, &Reboot); // Reboot instead of crashing to the debugger
+U0 NoBeep(I8, Bool) {};
+@patch_jmp_rel32(&Beep, &NoBeep); // Don't delay on beep when entering debugger
+
+Bool BlkDevLock2(CBlkDev* bd)
+{
+ BlkDevChk(bd);
+ while (bd->lock_fwding)
+ bd = bd->lock_fwding;
+ if (!Bt(&bd->locked_flags, BDlf_LOCKED) || bd->owning_task != Fs) {
+ while (LBts(&bd->locked_flags, BDlf_LOCKED))
+ Sleep(Rand * 10);
+ bd->owning_task = Fs;
+ return TRUE;
+ } else
+ return FALSE;
+}
+
+Bool DrvLock2(CDrv* dv)
+{
+ DrvChk(dv);
+ BlkDevLock2(dv->bd);
+ if (!Bt(&dv->locked_flags, DVlf_LOCKED) || dv->owning_task != Fs) {
+ while (LBts(&dv->locked_flags, DVlf_LOCKED))
+ Sleep(Rand * 10);
+ dv->owning_task = Fs;
+ return TRUE;
+ } else
+ return FALSE;
+}
+
+@patch_jmp_rel32(&BlkDevLock, &BlkDevLock2); // Patch BlkDevLock so we don't deadlock on multiple tasks reading from virtio disk
+@patch_jmp_rel32(&DrvLock, &DrvLock2); // Patch DrvLock so we don't deadlock on multiple tasks reading from virtio disk
diff --git a/System/Utilities/Dns.HC b/System/Utilities/Dns.HC
new file mode 100644
index 0000000..42c97a7
--- /dev/null
+++ b/System/Utilities/Dns.HC
@@ -0,0 +1,10 @@
+U0 DnsQuery(U8* host)
+{
+ U32 result = @dns_query(host);
+ if (result == U32_MAX) {
+ "Error looking up host %s\n", host;
+ return;
+ }
+ "Query for %s: %d.%d.%d.%d\n", host, result.u8[3], result.u8[2], result.u8[1],
+ result.u8[0];
+}
diff --git a/System/Utilities/Image.HC b/System/Utilities/Image.HC
new file mode 100644
index 0000000..d420bdc
--- /dev/null
+++ b/System/Utilities/Image.HC
@@ -0,0 +1,414 @@
+Silent(1); // This is needed to suppress "Function should return val" warnings for wrappers to non-HolyC functions
+
+class @image
+{
+ CDC* (*FromBuffer)(U8* buffer, I64 len);
+ CDC* (*Load)(U8* filename);
+ CDC* (*Write)(U8* filename, CDC* dc);
+};
+
+@image Image;
+
+class @image_frame
+{
+ CDC* dc;
+ CSprite* sprite;
+ I64 delay;
+};
+
+class @image_collection
+{
+ @image_frame** frames;
+ I64 count;
+ I64 current;
+ I64 jiffies;
+ I64 index;
+ @image_collection* next;
+};
+
+I64 @image_cbgr24_to_4_bit(CBGR24* ptr, Bool dither_probability)
+{
+ I64 res, k;
+ if (dither_probability) {
+ k = RandU32;
+ if (SqrI64(ptr->r) + SqrI64(ptr->g) + SqrI64(ptr->b) >= 3 * SqrI64(k.u8[0]))
+ res = 8;
+ else
+ res = 0;
+ if (ptr->r >= k.u8[1])
+ res |= RED;
+ if (ptr->g >= k.u8[2])
+ res |= GREEN;
+ if (ptr->b >= k.u8[3])
+ res |= BLUE;
+ } else {
+ if (SqrI64(ptr->r) + SqrI64(ptr->g) + SqrI64(ptr->b) >= SqrI64(0x80)) {
+ res = 8;
+ if (ptr->r >= 0x80)
+ res |= RED;
+ if (ptr->g >= 0x80)
+ res |= GREEN;
+ if (ptr->b >= 0x80)
+ res |= BLUE;
+ } else {
+ res = 0;
+ if (ptr->r >= 0x40)
+ res |= RED;
+ if (ptr->g >= 0x40)
+ res |= GREEN;
+ if (ptr->b >= 0x40)
+ res |= BLUE;
+ }
+ }
+ return res;
+}
+
+#define IMAGE_DITHER_NONE 0
+#define IMAGE_DITHER_NATIVE 1
+#define IMAGE_DITHER_FLOYDSTEINBERG 2
+
+U0 @image_render_4bit_floydstein(U8* buffer, I32 width, I32 height)
+{
+ U64 reg RDI rdi = buffer;
+ U64 reg RSI rsi = width;
+ U64 reg RDX rdx = height;
+ no_warn rdi, rsi, rdx;
+ asm {
+ MOV RAX, RENDER_4BIT_FLOYDSTEIN
+ CALL RAX
+ }
+}
+
+CDC* @image_render_16color_native(U8* pixels, I32 x, I32 y, Bool dither)
+{
+ I64 i;
+ I64 j;
+ I64 cnt = 0;
+ CBGR24 cbgr24;
+ CDC* dc = DCNew(x, y);
+ for (i = 0; i < y; i++)
+ for (j = 0; j < x; j++) {
+ cbgr24.r = pixels[cnt];
+ cbgr24.g = pixels[cnt + 1];
+ cbgr24.b = pixels[cnt + 2];
+ if (!pixels[cnt + 3])
+ dc->color = TRANSPARENT;
+ else
+ dc->color = @image_cbgr24_to_4_bit(&cbgr24, dither);
+ GrPlot(dc, j, y - i - 1);
+ cnt += 4;
+ }
+ return dc;
+}
+
+CBGR24 @image_palette_std[COLORS_NUM] = {
+ 0x000000, 0x0000AA, 0x00AA00, 0x00AAAA,
+ 0xAA0000, 0xAA00AA, 0xAA5500, 0xAAAAAA,
+ 0x555555, 0x5555FF, 0x55FF55, 0x55FFFF,
+ 0xFF5555, 0xFF55FF, 0xFFFF55, 0xFFFFFF
+};
+
+CBGR24 @image_dif_rgb(CBGR24 from, CBGR24 to)
+{
+ CBGR24 dif;
+ dif.r = to.r - from.r;
+ dif.g = to.g - from.g;
+ dif.b = to.b - from.b;
+ return dif;
+}
+
+F64 @image_dist_rgb(CBGR24 from, CBGR24 to)
+{
+ CBGR24 dif = @image_dif_rgb(from, to);
+ F64 dist = dif.r * dif.r + dif.g * dif.g + dif.b * dif.b;
+ return dist;
+}
+
+I64 @image_get_4bit_color(CBGR24* cbgr24)
+{
+ F64 dist = -1, tempDist;
+ I64 i;
+ I64 color = TRANSPARENT;
+ for (i = 0; i < COLORS_NUM; i++) {
+ tempDist = @image_dist_rgb(*cbgr24, @image_palette_std[i]);
+ if (tempDist < dist || dist < 0) {
+ dist = tempDist;
+ color = i;
+ }
+ }
+ return color;
+}
+
+CDC* @image_render_16color_floydsteinberg(U8* pixels, I32 width, I32 height)
+{
+ @image_render_4bit_floydstein(pixels, width, height);
+ I64 i;
+ I64 j;
+ I64 cnt = 0;
+ CBGR24 cbgr24;
+ CDC* dc = DCNew(width, height);
+ for (i = 0; i < height; i++)
+ for (j = 0; j < width; j++) {
+ cbgr24.r = pixels[cnt];
+ cbgr24.g = pixels[cnt + 1];
+ cbgr24.b = pixels[cnt + 2];
+ if (!pixels[cnt + 3])
+ dc->color = TRANSPARENT;
+ else
+ dc->color = @image_get_4bit_color(&cbgr24);
+ GrPlot(dc, j, height - i - 1);
+ cnt += 4;
+ }
+ return dc;
+}
+
+CDC* @image_generate_dc_from_pixels(U8* pixels, I32 width, I32 height, Bool dither = IMAGE_DITHER_FLOYDSTEINBERG)
+{
+ switch (dither) {
+ case IMAGE_DITHER_NONE:
+ case IMAGE_DITHER_NATIVE:
+ return @image_render_16color_native(pixels, width, height, dither);
+ break;
+ case IMAGE_DITHER_FLOYDSTEINBERG:
+ return @image_render_16color_floydsteinberg(pixels, width, height);
+ break;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+U8* @image_load_gif_from_memory(U8* buffer, I64 len, I64** delays, I64* x, I64* y,
+ I64* z)
+{
+ U64 reg RDI rdi = buffer;
+ U64 reg RSI rsi = len;
+ U64 reg RDX rdx = delays;
+ U64 reg RCX rcx = x;
+ U64 reg R8 r8 = y;
+ U64 reg R9 r9 = z;
+ no_warn rdi, rsi, rdx, rcx, r8, r9;
+ asm {
+ MOV RAX, IMAGE_LOAD_GIF_FROM_MEMORY
+ CALL RAX
+ }
+}
+
+U8* @stbi_failure_reason()
+{
+ asm {
+ MOV RAX, STBI_FAILURE_REASON
+ CALL RAX
+ }
+}
+
+I32 @stbi_info_from_memory(U8* buffer, I64 len, I64* x, I64* y, I64* comp)
+{
+ U64 reg RDI rdi = buffer;
+ U64 reg RSI rsi = len;
+ U64 reg RDX rdx = x;
+ U64 reg RCX rcx = y;
+ U64 reg R8 r8 = comp;
+ no_warn rdi, rsi, rdx, rcx, r8;
+ asm {
+ MOV RAX, STBI_INFO_FROM_MEMORY
+ CALL RAX
+ }
+}
+
+U8* @stbi_load_from_memory(U8* buffer, I64 len, I64* x, I64* y,
+ I64* channels_in_file, I64 desired_channels)
+{
+ U64 reg RDI rdi = buffer;
+ U64 reg RSI rsi = len;
+ U64 reg RDX rdx = x;
+ U64 reg RCX rcx = y;
+ U64 reg R8 r8 = channels_in_file;
+ U64 reg R9 r9 = desired_channels;
+ no_warn rdi, rsi, rdx, rcx, r8, r9;
+ asm {
+ MOV RAX, STBI_LOAD_FROM_MEMORY
+ CALL RAX
+ }
+}
+
+U32* @stbi_write_png_to_mem(U32* pixels, I32 stride_bytes, I32 x, I32 y, I32 n, I32* out_len)
+{
+ U64 reg RDI rdi = pixels;
+ U64 reg RSI rsi = stride_bytes;
+ U64 reg RDX rdx = x;
+ U64 reg RCX rcx = y;
+ U64 reg R8 r8 = n;
+ U64 reg R9 r9 = out_len;
+ no_warn rdi, rsi, rdx, rcx, r8, r9;
+ asm {
+ MOV RAX, STBI_WRITE_PNG_TO_MEM
+ CALL RAX
+ }
+}
+
+CDC* @image_load(U8* filename)
+{
+ if (!filename || !FileFind(filename)) {
+ PrintErr("Image file not found.\n");
+ return NULL;
+ }
+ I64 len;
+ I32 x;
+ I32 y;
+ I32 comp;
+ U8* buffer = FileRead(filename, &len);
+ I32 code = @stbi_info_from_memory(buffer, len, &x, &y, &comp);
+ if (code != 1) {
+ Free(buffer);
+ return NULL;
+ }
+ U8* pixels = @stbi_load_from_memory(buffer, len, &x, &y, &comp, 4);
+ Free(buffer);
+ CDC* dc = @image_generate_dc_from_pixels(pixels, x, y);
+ Free(pixels);
+ return dc;
+}
+
+U32 @image_rgba_color_table[16] = {
+ 0xff000000, 0xffaa0000, 0xff00aa00, 0xffaaaa00,
+ 0xff0000aa, 0xffaa00aa, 0xff0055aa, 0xffaaaaaa,
+ 0xff555555, 0xffff5555, 0xff55ff55, 0xffffff55,
+ 0xff5555ff, 0xffff55ff, 0xff55ffff, 0xffffffff
+};
+
+U32 @image_get_rgba_color(I64 color)
+{
+ if (color > 15)
+ return 0;
+ return @image_rgba_color_table[color];
+}
+
+U32* @image_get_rgba_buffer_from_dc_body(CDC* dc)
+{
+ if (!dc)
+ return NULL;
+ U32* pixels = CAlloc((dc->width * dc->height) * 4, adam_task);
+ I64 x;
+ I64 y;
+ I64 p = 0;
+ for (y = 0; y < dc->height; y++)
+ for (x = 0; x < dc->width; x++)
+ pixels[p++] = @image_get_rgba_color(GrPeek(dc, x, y));
+ return pixels;
+}
+
+U0 @image_write(U8* filename, CDC* dc)
+{
+ if (!dc) {
+ PrintErr("Device context is NULL.\n");
+ return;
+ }
+ I32 out_len;
+ U32* rgba_buffer = @image_get_rgba_buffer_from_dc_body(dc);
+ if (!rgba_buffer) {
+ PrintErr("RGBA buffer is NULL.\n");
+ return;
+ }
+ U8* png_buffer = @stbi_write_png_to_mem(rgba_buffer, dc->width * 4, dc->width, dc->height, 4, &out_len);
+ if (!png_buffer) {
+ PrintErr("PNG buffer is NULL.\n");
+ Free(rgba_buffer);
+ return;
+ }
+ FileWrite(filename, png_buffer, out_len);
+ Free(rgba_buffer);
+ Free(png_buffer);
+}
+
+U32 @image_pixel_flip_rgb_bgr(U32 src)
+{
+ U32 dst;
+ dst.u8[0] = src.u8[2];
+ dst.u8[1] = src.u8[1];
+ dst.u8[2] = src.u8[0];
+ dst.u8[3] = src.u8[3];
+ return dst;
+}
+
+CDC* @image_from_buffer(U8* buffer, I64 len)
+{
+ I32 x = 0;
+ I32 y = 0;
+ U8* pixels = NULL;
+ CDC* dc = NULL;
+
+ I32 comp;
+ I32 code = @stbi_info_from_memory(buffer, len, &x, &y, &comp);
+ if (code != 1) {
+ return NULL;
+ }
+ pixels = @stbi_load_from_memory(buffer, len, &x, &y, &comp, 4);
+ if (!pixels)
+ PopUpOk(@stbi_failure_reason);
+ dc = @image_generate_dc_from_pixels(pixels, x, y);
+ Free(pixels);
+ return dc;
+}
+
+@image_collection* @image_collection_from_buffer(U8* buffer, I64 len)
+{
+ I64 i;
+ I32* delays;
+ I32 x;
+ I32 y;
+ I32 z;
+ I32 comp;
+ I32 code = @stbi_info_from_memory(buffer, len, &x, &y, &comp);
+ if (code != 1) {
+ return NULL;
+ }
+ U64 pixels = @image_load_gif_from_memory(buffer, len, &delays, &x, &y, &z);
+ if (!pixels)
+ PopUpOk(@stbi_failure_reason);
+ if (!z)
+ return NULL; // no frames?
+ @image_collection* collection = CAlloc(sizeof(@image_collection), adam_task);
+ @image_frame* frame;
+ collection->frames = CAlloc(sizeof(@image_frame*) * z, adam_task);
+ collection->count = z;
+ for (i = 0; i < z; i++) {
+ frame = CAlloc(sizeof(@image_frame), adam_task);
+ frame->dc = @image_generate_dc_from_pixels(pixels, x, y);
+ frame->sprite = DC2Sprite(frame->dc);
+ frame->delay = delays[i];
+ collection->frames[i] = frame;
+ pixels += (x * y) * 4;
+ }
+ return collection;
+}
+
+Image.FromBuffer = &@image_from_buffer;
+Image.Load = &@image_load;
+Image.Write = &@image_write;
+
+Silent(0);
+
+U0 Screenshot(U8* custom_filename = NULL, Bool output_filename_to_focus_task = FALSE)
+{
+ CDC* dc = DCScrnCapture;
+ U8 filename[256];
+ CDateStruct ds;
+ if (custom_filename)
+ StrCpy(filename, custom_filename);
+ else {
+ Date2Struct(&ds, Now);
+ StrPrint(filename, "C:/Tmp/ScrnShots/%04d-%02d-%02d-%02d-%02d-%02d.png", ds.year, ds.mon, ds.day_of_mon, ds.hour, ds.min, ds.sec);
+ }
+ Image.Write(filename, dc);
+ DCDel(dc);
+ if (output_filename_to_focus_task)
+ XTalk(sys_focus_task, filename);
+};
+
+U0 @screenshot_hotkey(I64)
+{
+ Screenshot("C:/Home/Screenshot.png", TRUE);
+}
+
+CtrlAltCBSet('S', &@screenshot_hotkey, "", , FALSE);
diff --git a/System/Utilities/NetRep.HC b/System/Utilities/NetRep.HC
new file mode 100644
index 0000000..f56bacd
--- /dev/null
+++ b/System/Utilities/NetRep.HC
@@ -0,0 +1,22 @@
+U0 NetRep()
+{
+ NetInfoRequest* req = @net_info_request;
+ "MAC address : %02x:%02x:%02x:%02x:%02x:%02x\n", req->mac_address.u8[5], req->mac_address.u8[4],
+ req->mac_address.u8[3], req->mac_address.u8[2],
+ req->mac_address.u8[1], req->mac_address.u8[0];
+ "IPv4 address : %d.%d.%d.%d\n", req->ipv4_address.u8[3], req->ipv4_address.u8[2],
+ req->ipv4_address.u8[1], req->ipv4_address.u8[0];
+ "IPv4 netmask : %d.%d.%d.%d\n", req->ipv4_netmask.u8[3], req->ipv4_netmask.u8[2],
+ req->ipv4_netmask.u8[1], req->ipv4_netmask.u8[0];
+ "IPv4 network : %d.%d.%d.%d\n", req->ipv4_network.u8[3], req->ipv4_network.u8[2],
+ req->ipv4_network.u8[1], req->ipv4_network.u8[0];
+ "IPv4 gateway : %d.%d.%d.%d\n", req->ipv4_gateway.u8[3], req->ipv4_gateway.u8[2],
+ req->ipv4_gateway.u8[1], req->ipv4_gateway.u8[0];
+ "DNS server (port) : %d.%d.%d.%d (%d)\n", req->dns_server_address.u8[3], req->dns_server_address.u8[2],
+ req->dns_server_address.u8[1], req->dns_server_address.u8[0], req->dns_server_port;
+ "RX bytes : %d\n", req->rx_bytes;
+ "RX frames : %d\n", req->rx_frames;
+ "TX bytes : %d\n", req->tx_bytes;
+ "TX frames : %d\n", req->tx_frames;
+ Free(req);
+}
diff --git a/System/Utilities/Ping.HC b/System/Utilities/Ping.HC
new file mode 100644
index 0000000..386c441
--- /dev/null
+++ b/System/Utilities/Ping.HC
@@ -0,0 +1,72 @@
+#define PING_ERR_INVALID_HOST 1
+#define PING_ERR_HOST_NOT_FOUND 2
+
+#define PING_PAYLOAD_SIZE 56
+
+I64 @ping_err(I64 code)
+{
+ switch (code) {
+ case PING_ERR_INVALID_HOST:
+ "Invalid host specified\n";
+ return 1;
+ break;
+ case PING_ERR_HOST_NOT_FOUND:
+ "Host not found\n";
+ return 2;
+ break;
+ default:
+ "Unspecified error\n";
+ return -1;
+ }
+}
+
+I64 Ping(U8* host, I64 count = 4)
+{
+ if (!host)
+ return @ping_err(PING_ERR_INVALID_HOST);
+ if (!StrLen(host))
+ return @ping_err(PING_ERR_INVALID_HOST);
+
+ U32 addr = @dns_query(host);
+ if (addr == U32_MAX)
+ return @ping_err(PING_ERR_HOST_NOT_FOUND);
+
+ U16 iden = (RandU16 * SysTimerRead) & 0xFFFF;
+ I64 start_jiffies;
+ U32 reply = NULL;
+ I64 res = 0;
+ U16 seq = 0;
+ I64 loss = 0;
+
+ IcmpRequest* request = CAlloc(sizeof(IcmpRequest), Fs->code_heap);
+
+ "PING %s (%d.%d.%d.%d): %d data bytes\n",
+ host, addr.u8[3], addr.u8[2], addr.u8[1], addr.u8[0], PING_PAYLOAD_SIZE;
+
+ I64 i;
+ for (i = 0; i < count; i++) {
+ start_jiffies = cnts.jiffies;
+ reply = @icmp_echo_request(addr, iden, seq, request, i);
+ if (!reply) {
+ "Request timeout for icmp_seq %d\n", seq;
+ ++loss;
+ res = 1;
+ } else {
+ "%d bytes from %d.%d.%d.%d: icmp_seq=%d ttl=%d time=%d ms\n",
+ reply.u16[1], addr.u8[3], addr.u8[2], addr.u8[1], addr.u8[0], seq, reply.u16[0], cnts.jiffies - start_jiffies;
+ }
+ while (cnts.jiffies < start_jiffies + 1000 && i < (count - 1))
+ Sleep(1);
+ ++seq;
+ }
+
+ Free(request);
+
+ "--- %d.%d.%d.%d ping statistics ---\n", addr.u8[3], addr.u8[2], addr.u8[1], addr.u8[0];
+ "%d packets transmitted, %d packets received, %0f",
+ seq, seq - loss, (loss * 1.0 / seq * 1.0) * 100;
+ PutChars(37);
+ " packet loss\n";
+
+ return res;
+}
\ No newline at end of file
diff --git a/System/Utilities/Time.HC b/System/Utilities/Time.HC
new file mode 100644
index 0000000..ce5328d
--- /dev/null
+++ b/System/Utilities/Time.HC
@@ -0,0 +1,112 @@
+U0 @time_cmos_update_byte(I64 time_reg, I64 val)
+{
+ OutU8(0x70, time_reg);
+ OutU8(0x71, val);
+}
+
+I64 @time_dec_to_bcd(I64 val)
+{
+ return (((val / 10) << 4) | (val % 10));
+}
+
+U0 @time_update(U8* date_str, I64 mS_delta, I64 hour_offset)
+{
+ no_warn mS_delta;
+ Bool is_bcd;
+ OutU8(0x70, 0x0B);
+ if (InU8(0x71) & 4)
+ is_bcd = FALSE;
+ else
+ is_bcd = TRUE;
+
+ I64 date_argc;
+ U8** date_argv = String.Split(date_str, ' ', &date_argc);
+
+ I64 month = DefineMatch(date_argv[2], "ST_MONTHS") + 1;
+ I64 day = Str2I64(date_argv[1]);
+ I64 year = Str2I64(date_argv[3] + 2);
+ I64 century = 20;
+
+ date_argv[4][2] = NULL;
+ date_argv[4][5] = NULL;
+
+ I64 hour = Str2I64(date_argv[4]);
+ I64 minute = Str2I64(date_argv[4] + 3);
+ I64 second = Str2I64(date_argv[4] + 6);
+
+ // FIXME: Handle month boundaries, and 12 hour time
+ hour += hour_offset;
+ if (hour < 0) {
+ hour += 24;
+ --day;
+ } else if (hour > 23) {
+ hour -= 24;
+ ++day;
+ }
+
+ if (is_bcd) {
+ century = @time_dec_to_bcd(century);
+ year = @time_dec_to_bcd(year);
+ month = @time_dec_to_bcd(month);
+ day = @time_dec_to_bcd(day);
+ hour = @time_dec_to_bcd(hour);
+ minute = @time_dec_to_bcd(minute);
+ second = @time_dec_to_bcd(second);
+ }
+
+ @time_cmos_update_byte(0x32, century);
+ @time_cmos_update_byte(0x09, year);
+ @time_cmos_update_byte(0x08, month);
+ @time_cmos_update_byte(0x07, day);
+ @time_cmos_update_byte(0x04, hour);
+ @time_cmos_update_byte(0x02, minute);
+ @time_cmos_update_byte(0x00, second);
+}
+
+Bool @time_in_dst()
+{
+ CDateStruct ds;
+ Date2Struct(&ds, Now);
+ if (ds.mon > 3 && ds.mon < 11)
+ return TRUE;
+ if (ds.mon < 3 || ds.mon > 11)
+ return FALSE;
+ if (ds.mon == 3 && ds.day_of_mon != 8)
+ return ds.day_of_mon > 8;
+ if (ds.mon == 3)
+ return ds.hour > 1;
+ if (ds.mon == 11 && ds.day_of_mon != 1)
+ return FALSE;
+ if (ds.mon == 11)
+ return ds.hour < 2;
+}
+
+I64 @time_tz_offset()
+{
+ if (@time_in_dst)
+ return -4;
+ return -5;
+}
+
+U0 @time_query(Bool set = FALSE)
+{
+ U8 buf[1024];
+ @http_url* url = @http_parse_url("http://time.google.com");
+ @http_response* resp = Http.Head(url, &buf);
+ while (resp->state != HTTP_STATE_DONE)
+ Sleep(1);
+ I64 mS_delta = cnts.jiffies;
+ "Set current date and time to %s ", Json.Get(resp->headers, "Date");
+ if (!set)
+ set = YorN;
+ else
+ "\n";
+ if (set)
+ @time_update(Json.Get(resp->headers, "Date"), mS_delta, @time_tz_offset);
+}
+
+U0 TimeSync()
+{
+ Sleep(500);
+ @time_query(1);
+}
diff --git a/scripts/build-all b/scripts/build-all
new file mode 100755
index 0000000..ee5a664
--- /dev/null
+++ b/scripts/build-all
@@ -0,0 +1,188 @@
+#!/usr/bin/python3
+from pathlib import Path
+import glob
+import os
+import subprocess
+import sys
+import time
+
+if len(sys.argv) < 2:
+ raise ValueError('wrong number of arguments')
+
+project_path = sys.argv[1] + '/'
+project_name = project_path.rsplit('/')[-2]
+
+isoc_file = project_path + 'build/isoc/Slon.ISO.C'
+redsea_path = project_path + 'build/redsea'
+
+home_path = str(Path.home()) + '/'
+
+jakt_compiler_path = home_path + 'cloned/jakt/build/bin/jakt'
+jakt_runtime_path = home_path + 'cloned/jakt/runtime'
+jakt_lib_path = home_path + 'cloned/jakt/build/lib/x86_64-unknown-linux-unknown/'
+
+qemu_slipstream_iso_file = project_path + 'build/isoc/bootable.iso'
+qemu_virtio_disk_path = home_path + 'virtio-disk.qcow2'
+
+qemu_bin_path = home_path + "/Programs/qemu-9.1.2/build/qemu-system-x86_64"
+qemu_display = "-display sdl,grab-mod=rctrl"
+
+templeos_iso_file = home_path + 'iso/TempleOS.ISO'
+
+qemu_run_cmd = qemu_bin_path + ' ' + qemu_display + ' -enable-kvm -m 1024 -netdev tap,id=mynet0,ifname=tap0,script=no,downscript=no -device virtio-net,netdev=mynet0 -drive file=' + qemu_virtio_disk_path + ',format=qcow2,if=none,index=0,media=disk,id=virtio-disk -device virtio-blk-pci,drive=virtio-disk -cdrom ' + qemu_slipstream_iso_file + ' -debugcon stdio -boot d'
+
+def clang_format_src_files():
+ print("build-all: clang-format-src-files")
+ exclude_paths = ["stb_", "tlse", ".iso.c"]
+ format_file_extensions = [".c", ".cpp", ".h", ".hc"]
+ for src_file in glob.glob(project_path + "**", recursive=True):
+ exclude_file = False
+ for exclude_path in exclude_paths:
+ if src_file.lower().find(exclude_path) > 0:
+ exclude_file = True
+ if exclude_file:
+ continue
+ for format_file_extension in format_file_extensions:
+ if src_file.lower().endswith(format_file_extension):
+ print(src_file)
+ res = os.system('clang-format -i --style=file:' + project_path + '.clang-format ' + src_file)
+ if res:
+ raise ValueError("build-all: step 'clang-format-src-files' failed, error code " + str(res))
+
+def refresh_build_path():
+ print("build-all: refresh-build-path")
+ res = os.system('rm -rf ' + project_path + 'build && mkdir -p ' + project_path + 'build/bin && mkdir -p ' + project_path + 'build/isoc && mkdir -p ' + project_path + 'build/lib && mkdir -p ' + project_path + 'build/redsea')
+ if res:
+ raise ValueError("build-all: step 'refresh-build-path' failed, error code " + str(res))
+
+def build_image():
+ print("build-all: build-image")
+ build_specific_options = '-Wl,--section-start=.text=0x1004000 -Wl,--section-start=.plt=0x1002020 -no-pie'
+ res = os.system('cd ' + project_path + '&& cd src/image && gcc -o ../../build/bin/image ' + build_specific_options + ' -O0 -mno-mmx -mno-red-zone image.c')
+ if res:
+ raise ValueError("build-all: step 'build-image' failed, error code " + str(res))
+
+def build_libtemple():
+ print("build-all: build-libtemple")
+ res = os.system('cd ' + project_path + 'src/libtemple && g++ -c -o ../../build/libtemple.o libtemple.cpp && gcc -shared -o ../../build/lib/libtemple.so ../../build/libtemple.o && rm ' + project_path + 'build/libtemple.o')
+ if res:
+ raise ValueError("build-all: step 'build-libtemple' failed, error code " + str(res))
+
+def build_tlse():
+ print("build-all: build-tlse")
+ build_specific_options = '-Wl,--section-start=.text=0x1204000 -Wl,--section-start=.plt=0x1202020 -no-pie'
+ res = os.system('cd ' + project_path + '&& cd src/tlse && gcc -o ../../build/bin/tlse ' + build_specific_options + ' -O0 -mno-mmx -mno-red-zone -DTLS_AMALGAMATION tlse.c')
+ if res:
+ raise ValueError("build-all: step 'build-tlse' failed, error code " + str(res))
+
+def transpile_net_to_sepples():
+ print("build-all: transpile-net-to-sepples")
+ res = os.system('cd ' + project_path + 'src/net && ' + jakt_compiler_path + ' -S -R ' + jakt_runtime_path + ' -B ' + project_path + 'build/net -O net.jakt')
+ if res:
+ raise ValueError("build-all: step 'transpile-net-to-sepples' failed, error code " + str(res))
+
+def build_net():
+ print("build-all: build-net")
+ build_specific_options = '-Wno-invalid-offsetof -Wl,--section-start=.text=0x1404000 -Wl,--section-start=.plt=0x1402020 -no-pie'
+ res = os.system('cd ' + project_path + 'build/net && clang++-19 ' + build_specific_options + ' -O3 -I ' + jakt_runtime_path + ' -I ' + project_path + '/src/libtemple -fcolor-diagnostics -std=c++20 -fno-exceptions -Wno-user-defined-literals -Wno-deprecated-declarations -Wno-parentheses-equality -Wno-unqualified-std-cast-call -Wno-unknown-warning-option -Wno-int-to-pointer-cast -mno-red-zone -o ../bin/net *.cpp ../lib/libtemple.so ' + jakt_lib_path + 'libjakt_runtime_x86_64-unknown-linux-unknown.a ' + jakt_lib_path + 'libjakt_main_x86_64-unknown-linux-unknown.a && cd .. && rm -rf net')
+ if res:
+ raise ValueError("build-all: step 'build-net' failed, error code " + str(res))
+
+def address_string_for_symbol(file, symbol):
+ p = subprocess.Popen('readelf -s --wide "' + file + '" | grep \'' + symbol + '$\' | awk \'{sub("000000000", "0x", $2); print $2}\'', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+ return str(p.communicate()[0][:-1].decode(encoding='utf-8'))
+
+def image_hc_fixup(macro, symbol, image_bin_path, image_hc_path):
+ os.system('echo -e "#define ' + macro + ' ' + address_string_for_symbol(image_bin_path, symbol) + '\n" | cat - ' + image_hc_path + ' | sponge ' + image_hc_path)
+ return
+
+def tlse_hc_fixup(macro, symbol, tlse_bin_path, tlse_hc_path):
+ os.system('echo -e "#define ' + macro + ' ' + address_string_for_symbol(tlse_bin_path, symbol) + '\n" | cat - ' + tlse_hc_path + ' | sponge ' + tlse_hc_path)
+ return
+
+def generate_iso_c_file():
+ print("build-all: generate-iso-c-file")
+ step_error_message = "build-all: step 'generate-iso-c-file' failed, error code "
+
+ res = os.system('isoc-mount --rw ' + isoc_file + ' ' + redsea_path)
+ if res:
+ raise ValueError(step_error_message + str(res))
+ time.sleep(0.25)
+
+ copy_files_cmd_line = 'rsync -av --inplace --progress ' + project_path + ' ' + redsea_path
+ copy_files_cmd_line += ' --exclude .clang-format'
+ copy_files_cmd_line += ' --exclude .git'
+ copy_files_cmd_line += ' --exclude .gitignore'
+ copy_files_cmd_line += ' --exclude .vscode'
+ copy_files_cmd_line += ' --exclude build/isoc'
+ copy_files_cmd_line += ' --exclude build/lib'
+ copy_files_cmd_line += ' --exclude build/redsea'
+ copy_files_cmd_line += ' --exclude scripts'
+ copy_files_cmd_line += ' --exclude src'
+ res = os.system(copy_files_cmd_line)
+ if res:
+ raise ValueError(step_error_message + str(res))
+
+ # Fixup addresses for Image.HC
+ image_bin_path = redsea_path + '/build/bin/image'
+ image_hc_path = redsea_path + '/System/Utilities/Image.HC'
+
+ image_hc_fixup('IMAGE_LOAD_GIF_FROM_MEMORY', 'image_load_gif_from_memory', image_bin_path, image_hc_path)
+ image_hc_fixup('STBI_WRITE_PNG_TO_MEM', 'stbi_write_png_to_mem', image_bin_path, image_hc_path)
+ image_hc_fixup('STBI_LOAD_FROM_MEMORY', 'stbi_load_from_memory', image_bin_path, image_hc_path)
+ image_hc_fixup('STBI_INFO_FROM_MEMORY', 'stbi_info_from_memory', image_bin_path, image_hc_path)
+ image_hc_fixup('STBI_FAILURE_REASON', 'stbi_failure_reason', image_bin_path, image_hc_path)
+ image_hc_fixup('RENDER_4BIT_FLOYDSTEIN', 'render_4bit_floydstein', image_bin_path, image_hc_path)
+
+ # Fixup addresses for Tlse.HC
+
+ rsa_hc_path = redsea_path + '/System/Libraries/Rsa.HC'
+ tlse_bin_path = redsea_path + '/build/bin/tlse'
+ tlse_hc_path = redsea_path + '/System/Libraries/Tlse.HC'
+
+ tlse_hc_fixup('RSA_IMPORT', 'rsa_import', tlse_bin_path, rsa_hc_path)
+ tlse_hc_fixup('RSA_CREATE_SIGNATURE', 'rsa_create_signature', tlse_bin_path, rsa_hc_path)
+ tlse_hc_fixup('RSA_VERIFY_SIGNATURE', 'rsa_verify_signature', tlse_bin_path, rsa_hc_path)
+ tlse_hc_fixup('TLS_CREATE_CONTEXT', 'tls_create_context', tlse_bin_path, tlse_hc_path)
+ tlse_hc_fixup('TLS_SNI_SET', 'tls_sni_set', tlse_bin_path, tlse_hc_path)
+ tlse_hc_fixup('TLS_CLIENT_CONNECT', 'tls_client_connect', tlse_bin_path, tlse_hc_path)
+ tlse_hc_fixup('TLS_CONNECTION_STATUS', 'tls_connection_status', tlse_bin_path, tlse_hc_path)
+ tlse_hc_fixup('TLS_GET_WRITE_BUFFER', 'tls_get_write_buffer', tlse_bin_path, tlse_hc_path)
+ tlse_hc_fixup('TLS_BUFFER_CLEAR', 'tls_buffer_clear', tlse_bin_path, tlse_hc_path)
+ tlse_hc_fixup('TLS_CONSUME_STREAM', 'tls_consume_stream', tlse_bin_path, tlse_hc_path)
+ tlse_hc_fixup('TLS_READ', 'tls_read', tlse_bin_path, tlse_hc_path)
+ tlse_hc_fixup('TLS_WRITE', 'tls_write', tlse_bin_path, tlse_hc_path)
+ tlse_hc_fixup('TLS_ESTABLISHED', 'tls_established', tlse_bin_path, tlse_hc_path)
+
+ time.sleep(0.25)
+
+ res = os.system('sync && fusermount -u ' + redsea_path)
+ if res:
+ raise ValueError(step_error_message + str(res))
+ time.sleep(0.25)
+
+def generate_slipstream_iso_file():
+ print("build-all: generate-slipstream-iso-file")
+ res = os.system('templeos-slipstream ' + templeos_iso_file + ' ' + isoc_file + ' ' + qemu_slipstream_iso_file)
+ if res:
+ raise ValueError("build-all: step 'generate-slipstream-iso-file' failed, error code " + str(res))
+
+def run():
+ print("build-all: run")
+ res = os.system(qemu_run_cmd)
+ if res:
+ raise ValueError("build-all: step 'run' failed, error code " + str(res))
+
+def build_all():
+ clang_format_src_files()
+ #refresh_build_path()
+ #build_image()
+ #build_libtemple()
+ #build_tlse()
+ #transpile_net_to_sepples()
+ #build_net()
+ generate_iso_c_file()
+ generate_slipstream_iso_file()
+ run()
+
+build_all()
\ No newline at end of file
diff --git a/src/image/image.c b/src/image/image.c
new file mode 100644
index 0000000..42f031c
--- /dev/null
+++ b/src/image/image.c
@@ -0,0 +1,209 @@
+#define STBI_WRITE_NO_STDIO
+#define STB_IMAGE_WRITE_STATIC
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+#define STB_IMAGE_IMPLEMENTATION
+#define STBI_NO_LINEAR
+#define STBI_NO_STDIO
+#define STBI_NO_SIMD
+#define STBI_NO_HDR
+
+#include "stb_image.h"
+#include "stb_image_write.h"
+
+int main() { return 0; }
+
+STBIDEF stbi_uc* image_load_gif_from_memory(stbi_uc const* buffer, int len,
+ int** delays, int* x, int* y,
+ int* z)
+{
+ int comp;
+ return stbi_load_gif_from_memory(buffer, len, delays, x, y, z, &comp, 4);
+}
+
+/* dither.c: MIT License
+
+Copyright (c) 2016 jonmortiboy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+*/
+
+typedef struct RGB {
+ int r;
+ int g;
+ int b;
+} RGB;
+
+int imgw, imgh;
+
+// Define the 4bit colour palette
+int numCols = 16;
+RGB cols4bit[] = {
+ { 0, 0, 0 }, { 0, 0, 170 }, { 0, 170, 0 }, { 0, 170, 170 },
+ { 170, 0, 0 }, { 170, 0, 170 }, { 170, 85, 0 }, { 170, 170, 170 },
+ { 85, 85, 85 }, { 85, 85, 255 }, { 85, 255, 85 }, { 85, 255, 255 },
+ { 255, 85, 85 }, { 255, 85, 255 }, { 255, 255, 85 }, { 255, 255, 255 }
+};
+RGB* cols = cols4bit;
+
+RGB getRGB(uint32_t* pixels, int x, int y);
+void setRGB(uint32_t* pixels, int x, int y, RGB rgb);
+RGB difRGB(RGB from, RGB to);
+RGB addRGB(RGB a, RGB b);
+RGB divRGB(RGB rgb, double d);
+RGB mulRGB(RGB rgb, double d);
+RGB nearestRGB(RGB rgb, RGB* rgbs, int numRGBs);
+double distRGB(RGB from, RGB to);
+
+void render_4bit_floydstein(uint32_t* pixels, int width, int height);
+
+RGB getRGB(uint32_t* pixels, int x, int y)
+{
+ RGB rgb;
+ rgb.r = 0;
+ rgb.g = 0;
+ rgb.b = 0;
+
+ if (x < 0 || x >= imgw || y < 0 || y >= imgh)
+ return rgb;
+
+ rgb.r = (pixels[y * imgw + x] & 0xff);
+ rgb.g = (pixels[y * imgw + x] & 0xff00) >> 8;
+ rgb.b = (pixels[y * imgw + x] & 0xff0000) >> 16;
+
+ return rgb;
+}
+
+void setRGB(uint32_t* pixels, int x, int y, RGB rgb)
+{
+ if (x < 0 || x >= imgw || y < 0 || y >= imgh)
+ return;
+
+ uint32_t alpha = pixels[y * imgw + x] & 0xff000000;
+ pixels[y * imgw + x] = alpha + (rgb.r) + (rgb.g << 8) + (rgb.b << 16);
+}
+
+RGB difRGB(RGB from, RGB to)
+{
+ RGB dif;
+ dif.r = to.r - from.r;
+ dif.g = to.g - from.g;
+ dif.b = to.b - from.b;
+
+ return dif;
+}
+
+RGB addRGB(RGB a, RGB b)
+{
+ RGB sum;
+ sum.r = a.r + b.r;
+ sum.g = a.g + b.g;
+ sum.b = a.b + b.b;
+
+ if (sum.r > 255)
+ sum.r = 255;
+ if (sum.r < 0)
+ sum.r = 0;
+ if (sum.g > 255)
+ sum.g = 255;
+ if (sum.g < 0)
+ sum.g = 0;
+ if (sum.b > 255)
+ sum.b = 255;
+ if (sum.b < 0)
+ sum.b = 0;
+
+ return sum;
+}
+
+RGB divRGB(RGB rgb, double d)
+{
+ RGB div;
+ div.r = (int)((double)rgb.r / d);
+ div.g = (int)((double)rgb.g / d);
+ div.b = (int)((double)rgb.b / d);
+
+ return div;
+}
+
+RGB mulRGB(RGB rgb, double d)
+{
+ RGB mul;
+ mul.r = (int)((double)rgb.r * d);
+ mul.g = (int)((double)rgb.g * d);
+ mul.b = (int)((double)rgb.b * d);
+
+ return mul;
+}
+
+double distRGB(RGB from, RGB to)
+{
+ RGB dif = difRGB(from, to);
+ double dist = dif.r * dif.r + dif.g * dif.g + dif.b * dif.b;
+
+ return dist;
+}
+
+RGB nearestRGB(RGB rgb, RGB rgbs[], int numRGBs)
+{
+ double dist = -1, tempDist;
+ RGB nearest;
+
+ int i;
+ for (i = 0; i < numRGBs; i++) {
+ tempDist = distRGB(rgb, rgbs[i]);
+
+ if (tempDist < dist || dist < 0) {
+ dist = tempDist;
+ nearest = rgbs[i];
+ }
+ }
+
+ return nearest;
+}
+
+void render_4bit_floydstein(uint32_t* pixels, int width, int height)
+{
+
+ int i, x, y;
+ imgw = width;
+ imgh = height;
+ RGB rgb, nearest, rgberror;
+ for (i = 0; i < imgw * imgh; i++) {
+ rgb = getRGB(pixels, i % imgw, i / imgw);
+ nearest = nearestRGB(rgb, cols, numCols);
+
+ rgberror = difRGB(nearest, rgb);
+ rgberror = divRGB(rgberror, 16);
+
+ x = i % imgw;
+ y = i / imgw;
+
+ setRGB(pixels, x + 1, y,
+ addRGB(getRGB(pixels, x + 1, y), mulRGB(rgberror, 7)));
+ setRGB(pixels, x - 1, y + 1,
+ addRGB(getRGB(pixels, x - 1, y + 1), mulRGB(rgberror, 3)));
+ setRGB(pixels, x, y + 1,
+ addRGB(getRGB(pixels, x, y + 1), mulRGB(rgberror, 5)));
+ setRGB(pixels, x + 1, y + 1,
+ addRGB(getRGB(pixels, x + 1, y + 1), rgberror));
+
+ setRGB(pixels, i % imgw, i / imgw, nearest);
+ }
+}
\ No newline at end of file
diff --git a/src/image/stb_image.h b/src/image/stb_image.h
new file mode 100644
index 0000000..cc42440
--- /dev/null
+++ b/src/image/stb_image.h
@@ -0,0 +1,8632 @@
+/* stb_image - v2.30 - public domain image loader - http://nothings.org/stb
+ no warranty implied; use at your own risk
+
+ Do this:
+ #define STB_IMAGE_IMPLEMENTATION
+ before you include this file in *one* C or C++ file to create the implementation.
+
+ // i.e. it should look like this:
+ #include ...
+ #include ...
+ #include ...
+ #define STB_IMAGE_IMPLEMENTATION
+ #include "stb_image.h"
+
+ You can #define STBI_ASSERT(x) before the #include to avoid using assert.h.
+ And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free
+
+
+ QUICK NOTES:
+ Primarily of interest to game developers and other people who can
+ avoid problematic images and only need the trivial interface
+
+ JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib)
+ PNG 1/2/4/8/16-bit-per-channel
+
+ TGA (not sure what subset, if a subset)
+ BMP non-1bpp, non-RLE
+ PSD (composited view only, no extra channels, 8/16 bit-per-channel)
+
+ GIF (*comp always reports as 4-channel)
+ HDR (radiance rgbE format)
+ PIC (Softimage PIC)
+ PNM (PPM and PGM binary only)
+
+ Animated GIF still needs a proper API, but here's one way to do it:
+ http://gist.github.com/urraka/685d9a6340b26b830d49
+
+ - decode from memory or through FILE (define STBI_NO_STDIO to remove code)
+ - decode from arbitrary I/O callbacks
+ - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON)
+
+ Full documentation under "DOCUMENTATION" below.
+
+
+LICENSE
+
+ See end of file for license information.
+
+RECENT REVISION HISTORY:
+
+ 2.30 (2024-05-31) avoid erroneous gcc warning
+ 2.29 (2023-05-xx) optimizations
+ 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff
+ 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
+ 2.26 (2020-07-13) many minor fixes
+ 2.25 (2020-02-02) fix warnings
+ 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically
+ 2.23 (2019-08-11) fix clang static analysis warning
+ 2.22 (2019-03-04) gif fixes, fix warnings
+ 2.21 (2019-02-25) fix typo in comment
+ 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs
+ 2.19 (2018-02-11) fix warning
+ 2.18 (2018-01-30) fix warnings
+ 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings
+ 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes
+ 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC
+ 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs
+ 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes
+ 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes
+ 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64
+ RGB-format JPEG; remove white matting in PSD;
+ allocate large structures on the stack;
+ correct channel count for PNG & BMP
+ 2.10 (2016-01-22) avoid warning introduced in 2.09
+ 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED
+
+ See end of file for full revision history.
+
+
+ ============================ Contributors =========================
+
+ Image formats Extensions, features
+ Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info)
+ Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info)
+ Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG)
+ Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks)
+ Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG)
+ Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip)
+ Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD)
+ github:urraka (animated gif) Junggon Kim (PNM comments)
+ Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA)
+ socks-the-fox (16-bit PNG)
+ Jeremy Sawicki (handle all ImageNet JPGs)
+ Optimizations & bugfixes Mikhail Morozov (1-bit BMP)
+ Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query)
+ Arseny Kapoulkine Simon Breuss (16-bit PNM)
+ John-Mark Allen
+ Carmelo J Fdez-Aguera
+
+ Bug & warning fixes
+ Marc LeBlanc David Woo Guillaume George Martins Mozeiko
+ Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski
+ Phil Jordan Dave Moore Roy Eltham
+ Hayaki Saito Nathan Reed Won Chun
+ Luke Graham Johan Duparc Nick Verigakis the Horde3D community
+ Thomas Ruf Ronny Chevalier github:rlyeh
+ Janez Zemva John Bartholomew Michal Cichon github:romigrou
+ Jonathan Blow Ken Hamada Tero Hanninen github:svdijk
+ Eugene Golushkov Laurent Gomila Cort Stratton github:snagar
+ Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex
+ Cass Everitt Ryamond Barbiero github:grim210
+ Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw
+ Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus
+ Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo
+ Julian Raschke Gregory Mullen Christian Floisand github:darealshinji
+ Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007
+ Brad Weinberger Matvey Cherevko github:mosra
+ Luca Sas Alexander Veselov Zack Middleton [reserved]
+ Ryan C. Gordon [reserved] [reserved]
+ DO NOT ADD YOUR NAME HERE
+
+ Jacko Dirks
+
+ To add your name to the credits, pick a random blank space in the middle and fill it.
+ 80% of merge conflicts on stb PRs are due to people adding their name at the end
+ of the credits.
+*/
+
+#ifndef STBI_INCLUDE_STB_IMAGE_H
+# define STBI_INCLUDE_STB_IMAGE_H
+
+// DOCUMENTATION
+//
+// Limitations:
+// - no 12-bit-per-channel JPEG
+// - no JPEGs with arithmetic coding
+// - GIF always returns *comp=4
+//
+// Basic usage (see HDR discussion below for HDR usage):
+// int x,y,n;
+// unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
+// // ... process data if not NULL ...
+// // ... x = width, y = height, n = # 8-bit components per pixel ...
+// // ... replace '0' with '1'..'4' to force that many components per pixel
+// // ... but 'n' will always be the number that it would have been if you said 0
+// stbi_image_free(data);
+//
+// Standard parameters:
+// int *x -- outputs image width in pixels
+// int *y -- outputs image height in pixels
+// int *channels_in_file -- outputs # of image components in image file
+// int desired_channels -- if non-zero, # of image components requested in result
+//
+// The return value from an image loader is an 'unsigned char *' which points
+// to the pixel data, or NULL on an allocation failure or if the image is
+// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels,
+// with each pixel consisting of N interleaved 8-bit components; the first
+// pixel pointed to is top-left-most in the image. There is no padding between
+// image scanlines or between pixels, regardless of format. The number of
+// components N is 'desired_channels' if desired_channels is non-zero, or
+// *channels_in_file otherwise. If desired_channels is non-zero,
+// *channels_in_file has the number of components that _would_ have been
+// output otherwise. E.g. if you set desired_channels to 4, you will always
+// get RGBA output, but you can check *channels_in_file to see if it's trivially
+// opaque because e.g. there were only 3 channels in the source image.
+//
+// An output image with N components has the following components interleaved
+// in this order in each pixel:
+//
+// N=#comp components
+// 1 grey
+// 2 grey, alpha
+// 3 red, green, blue
+// 4 red, green, blue, alpha
+//
+// If image loading fails for any reason, the return value will be NULL,
+// and *x, *y, *channels_in_file will be unchanged. The function
+// stbi_failure_reason() can be queried for an extremely brief, end-user
+// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS
+// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly
+// more user-friendly ones.
+//
+// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.
+//
+// To query the width, height and component count of an image without having to
+// decode the full file, you can use the stbi_info family of functions:
+//
+// int x,y,n,ok;
+// ok = stbi_info(filename, &x, &y, &n);
+// // returns ok=1 and sets x, y, n if image is a supported format,
+// // 0 otherwise.
+//
+// Note that stb_image pervasively uses ints in its public API for sizes,
+// including sizes of memory buffers. This is now part of the API and thus
+// hard to change without causing breakage. As a result, the various image
+// loaders all have certain limits on image size; these differ somewhat
+// by format but generally boil down to either just under 2GB or just under
+// 1GB. When the decoded image would be larger than this, stb_image decoding
+// will fail.
+//
+// Additionally, stb_image will reject image files that have any of their
+// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS,
+// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit,
+// the only way to have an image with such dimensions load correctly
+// is for it to have a rather extreme aspect ratio. Either way, the
+// assumption here is that such larger images are likely to be malformed
+// or malicious. If you do need to load an image with individual dimensions
+// larger than that, and it still fits in the overall size limit, you can
+// #define STBI_MAX_DIMENSIONS on your own to be something larger.
+//
+// ===========================================================================
+//
+// UNICODE:
+//
+// If compiling for Windows and you wish to use Unicode filenames, compile
+// with
+// #define STBI_WINDOWS_UTF8
+// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert
+// Windows wchar_t filenames to utf8.
+//
+// ===========================================================================
+//
+// Philosophy
+//
+// stb libraries are designed with the following priorities:
+//
+// 1. easy to use
+// 2. easy to maintain
+// 3. good performance
+//
+// Sometimes I let "good performance" creep up in priority over "easy to maintain",
+// and for best performance I may provide less-easy-to-use APIs that give higher
+// performance, in addition to the easy-to-use ones. Nevertheless, it's important
+// to keep in mind that from the standpoint of you, a client of this library,
+// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all.
+//
+// Some secondary priorities arise directly from the first two, some of which
+// provide more explicit reasons why performance can't be emphasized.
+//
+// - Portable ("ease of use")
+// - Small source code footprint ("easy to maintain")
+// - No dependencies ("ease of use")
+//
+// ===========================================================================
+//
+// I/O callbacks
+//
+// I/O callbacks allow you to read from arbitrary sources, like packaged
+// files or some other source. Data read from callbacks are processed
+// through a small internal buffer (currently 128 bytes) to try to reduce
+// overhead.
+//
+// The three functions you must define are "read" (reads some bytes of data),
+// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end).
+//
+// ===========================================================================
+//
+// SIMD support
+//
+// The JPEG decoder will try to automatically use SIMD kernels on x86 when
+// supported by the compiler. For ARM Neon support, you must explicitly
+// request it.
+//
+// (The old do-it-yourself SIMD API is no longer supported in the current
+// code.)
+//
+// On x86, SSE2 will automatically be used when available based on a run-time
+// test; if not, the generic C versions are used as a fall-back. On ARM targets,
+// the typical path is to have separate builds for NEON and non-NEON devices
+// (at least this is true for iOS and Android). Therefore, the NEON support is
+// toggled by a build flag: define STBI_NEON to get NEON loops.
+//
+// If for some reason you do not want to use any of SIMD code, or if
+// you have issues compiling it, you can disable it entirely by
+// defining STBI_NO_SIMD.
+//
+// ===========================================================================
+//
+// HDR image support (disable by defining STBI_NO_HDR)
+//
+// stb_image supports loading HDR images in general, and currently the Radiance
+// .HDR file format specifically. You can still load any file through the existing
+// interface; if you attempt to load an HDR file, it will be automatically remapped
+// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1;
+// both of these constants can be reconfigured through this interface:
+//
+// stbi_hdr_to_ldr_gamma(2.2f);
+// stbi_hdr_to_ldr_scale(1.0f);
+//
+// (note, do not use _inverse_ constants; stbi_image will invert them
+// appropriately).
+//
+// Additionally, there is a new, parallel interface for loading files as
+// (linear) floats to preserve the full dynamic range:
+//
+// float *data = stbi_loadf(filename, &x, &y, &n, 0);
+//
+// If you load LDR images through this interface, those images will
+// be promoted to floating point values, run through the inverse of
+// constants corresponding to the above:
+//
+// stbi_ldr_to_hdr_scale(1.0f);
+// stbi_ldr_to_hdr_gamma(2.2f);
+//
+// Finally, given a filename (or an open file or memory block--see header
+// file for details) containing image data, you can query for the "most
+// appropriate" interface to use (that is, whether the image is HDR or
+// not), using:
+//
+// stbi_is_hdr(char *filename);
+//
+// ===========================================================================
+//
+// iPhone PNG support:
+//
+// We optionally support converting iPhone-formatted PNGs (which store
+// premultiplied BGRA) back to RGB, even though they're internally encoded
+// differently. To enable this conversion, call
+// stbi_convert_iphone_png_to_rgb(1).
+//
+// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per
+// pixel to remove any premultiplied alpha *only* if the image file explicitly
+// says there's premultiplied data (currently only happens in iPhone images,
+// and only if iPhone convert-to-rgb processing is on).
+//
+// ===========================================================================
+//
+// ADDITIONAL CONFIGURATION
+//
+// - You can suppress implementation of any of the decoders to reduce
+// your code footprint by #defining one or more of the following
+// symbols before creating the implementation.
+//
+// STBI_NO_JPEG
+// STBI_NO_PNG
+// STBI_NO_BMP
+// STBI_NO_PSD
+// STBI_NO_TGA
+// STBI_NO_GIF
+// STBI_NO_HDR
+// STBI_NO_PIC
+// STBI_NO_PNM (.ppm and .pgm)
+//
+// - You can request *only* certain decoders and suppress all other ones
+// (this will be more forward-compatible, as addition of new decoders
+// doesn't require you to disable them explicitly):
+//
+// STBI_ONLY_JPEG
+// STBI_ONLY_PNG
+// STBI_ONLY_BMP
+// STBI_ONLY_PSD
+// STBI_ONLY_TGA
+// STBI_ONLY_GIF
+// STBI_ONLY_HDR
+// STBI_ONLY_PIC
+// STBI_ONLY_PNM (.ppm and .pgm)
+//
+// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still
+// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB
+//
+// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater
+// than that size (in either width or height) without further processing.
+// This is to let programs in the wild set an upper bound to prevent
+// denial-of-service attacks on untrusted data, as one could generate a
+// valid image of gigantic dimensions and force stb_image to allocate a
+// huge block of memory and spend disproportionate time decoding it. By
+// default this is set to (1 << 24), which is 16777216, but that's still
+// very big.
+
+# ifndef STBI_NO_STDIO
+# include
+# endif // STBI_NO_STDIO
+
+# define STBI_VERSION 1
+
+enum {
+ STBI_default = 0, // only used for desired_channels
+
+ STBI_grey = 1,
+ STBI_grey_alpha = 2,
+ STBI_rgb = 3,
+ STBI_rgb_alpha = 4
+};
+
+# include
+typedef unsigned char stbi_uc;
+typedef unsigned short stbi_us;
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+# ifndef STBIDEF
+# ifdef STB_IMAGE_STATIC
+# define STBIDEF static
+# else
+# define STBIDEF extern
+# endif
+# endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// PRIMARY API - works on images of any type
+//
+
+//
+// load image by filename, open file, or memory buffer
+//
+
+typedef struct
+{
+ int (*read)(void* user, char* data, int size); // fill 'data' with 'size' bytes. return number of bytes actually read
+ void (*skip)(void* user, int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative
+ int (*eof)(void* user); // returns nonzero if we are at end of file/data
+} stbi_io_callbacks;
+
+////////////////////////////////////
+//
+// 8-bits-per-channel interface
+//
+
+STBIDEF stbi_uc* stbi_load_from_memory(stbi_uc const* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels);
+STBIDEF stbi_uc* stbi_load_from_callbacks(stbi_io_callbacks const* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels);
+
+# ifndef STBI_NO_STDIO
+STBIDEF stbi_uc* stbi_load(char const* filename, int* x, int* y, int* channels_in_file, int desired_channels);
+STBIDEF stbi_uc* stbi_load_from_file(FILE* f, int* x, int* y, int* channels_in_file, int desired_channels);
+// for stbi_load_from_file, file pointer is left pointing immediately after image
+# endif
+
+# ifndef STBI_NO_GIF
+STBIDEF stbi_uc* stbi_load_gif_from_memory(stbi_uc const* buffer, int len, int** delays, int* x, int* y, int* z, int* comp, int req_comp);
+# endif
+
+# ifdef STBI_WINDOWS_UTF8
+STBIDEF int stbi_convert_wchar_to_utf8(char* buffer, size_t bufferlen, wchar_t const* input);
+# endif
+
+////////////////////////////////////
+//
+// 16-bits-per-channel interface
+//
+
+STBIDEF stbi_us* stbi_load_16_from_memory(stbi_uc const* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels);
+STBIDEF stbi_us* stbi_load_16_from_callbacks(stbi_io_callbacks const* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels);
+
+# ifndef STBI_NO_STDIO
+STBIDEF stbi_us* stbi_load_16(char const* filename, int* x, int* y, int* channels_in_file, int desired_channels);
+STBIDEF stbi_us* stbi_load_from_file_16(FILE* f, int* x, int* y, int* channels_in_file, int desired_channels);
+# endif
+
+////////////////////////////////////
+//
+// float-per-channel interface
+//
+# ifndef STBI_NO_LINEAR
+STBIDEF float* stbi_loadf_from_memory(stbi_uc const* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels);
+STBIDEF float* stbi_loadf_from_callbacks(stbi_io_callbacks const* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels);
+
+# ifndef STBI_NO_STDIO
+STBIDEF float* stbi_loadf(char const* filename, int* x, int* y, int* channels_in_file, int desired_channels);
+STBIDEF float* stbi_loadf_from_file(FILE* f, int* x, int* y, int* channels_in_file, int desired_channels);
+# endif
+# endif
+
+# ifndef STBI_NO_HDR
+STBIDEF void stbi_hdr_to_ldr_gamma(float gamma);
+STBIDEF void stbi_hdr_to_ldr_scale(float scale);
+# endif // STBI_NO_HDR
+
+# ifndef STBI_NO_LINEAR
+STBIDEF void stbi_ldr_to_hdr_gamma(float gamma);
+STBIDEF void stbi_ldr_to_hdr_scale(float scale);
+# endif // STBI_NO_LINEAR
+
+// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR
+STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const* clbk, void* user);
+STBIDEF int stbi_is_hdr_from_memory(stbi_uc const* buffer, int len);
+# ifndef STBI_NO_STDIO
+STBIDEF int stbi_is_hdr(char const* filename);
+STBIDEF int stbi_is_hdr_from_file(FILE* f);
+# endif // STBI_NO_STDIO
+
+// get a VERY brief reason for failure
+// on most compilers (and ALL modern mainstream compilers) this is threadsafe
+STBIDEF const char* stbi_failure_reason(void);
+
+// free the loaded image -- this is just free()
+STBIDEF void stbi_image_free(void* retval_from_stbi_load);
+
+// get image dimensions & components without fully decoding
+STBIDEF int stbi_info_from_memory(stbi_uc const* buffer, int len, int* x, int* y, int* comp);
+STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const* clbk, void* user, int* x, int* y, int* comp);
+STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const* buffer, int len);
+STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const* clbk, void* user);
+
+# ifndef STBI_NO_STDIO
+STBIDEF int stbi_info(char const* filename, int* x, int* y, int* comp);
+STBIDEF int stbi_info_from_file(FILE* f, int* x, int* y, int* comp);
+STBIDEF int stbi_is_16_bit(char const* filename);
+STBIDEF int stbi_is_16_bit_from_file(FILE* f);
+# endif
+
+// for image formats that explicitly notate that they have premultiplied alpha,
+// we just return the colors as stored in the file. set this flag to force
+// unpremultiplication. results are undefined if the unpremultiply overflow.
+STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);
+
+// indicate whether we should process iphone images back to canonical format,
+// or just pass them through "as-is"
+STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);
+
+// flip the image vertically, so the first pixel in the output array is the bottom left
+STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);
+
+// as above, but only applies to images loaded on the thread that calls the function
+// this function is only available if your compiler supports thread-local variables;
+// calling it will fail to link if your compiler doesn't
+STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply);
+STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert);
+STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip);
+
+// ZLIB client - used by PNG, available for other purposes
+
+STBIDEF char* stbi_zlib_decode_malloc_guesssize(char const* buffer, int len, int initial_size, int* outlen);
+STBIDEF char* stbi_zlib_decode_malloc_guesssize_headerflag(char const* buffer, int len, int initial_size, int* outlen, int parse_header);
+STBIDEF char* stbi_zlib_decode_malloc(char const* buffer, int len, int* outlen);
+STBIDEF int stbi_zlib_decode_buffer(char* obuffer, int olen, char const* ibuffer, int ilen);
+
+STBIDEF char* stbi_zlib_decode_noheader_malloc(char const* buffer, int len, int* outlen);
+STBIDEF int stbi_zlib_decode_noheader_buffer(char* obuffer, int olen, char const* ibuffer, int ilen);
+
+# ifdef __cplusplus
+}
+# endif
+
+//
+//
+//// end header file /////////////////////////////////////////////////////
+#endif // STBI_INCLUDE_STB_IMAGE_H
+
+#ifdef STB_IMAGE_IMPLEMENTATION
+
+# if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \
+ || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \
+ || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \
+ || defined(STBI_ONLY_ZLIB)
+# ifndef STBI_ONLY_JPEG
+# define STBI_NO_JPEG
+# endif
+# ifndef STBI_ONLY_PNG
+# define STBI_NO_PNG
+# endif
+# ifndef STBI_ONLY_BMP
+# define STBI_NO_BMP
+# endif
+# ifndef STBI_ONLY_PSD
+# define STBI_NO_PSD
+# endif
+# ifndef STBI_ONLY_TGA
+# define STBI_NO_TGA
+# endif
+# ifndef STBI_ONLY_GIF
+# define STBI_NO_GIF
+# endif
+# ifndef STBI_ONLY_HDR
+# define STBI_NO_HDR
+# endif
+# ifndef STBI_ONLY_PIC
+# define STBI_NO_PIC
+# endif
+# ifndef STBI_ONLY_PNM
+# define STBI_NO_PNM
+# endif
+# endif
+
+# if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB)
+# define STBI_NO_ZLIB
+# endif
+
+# include
+# include
+# include // ptrdiff_t on osx
+# include
+# include
+
+# if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
+# include // ldexp, pow
+# endif
+
+# ifndef STBI_NO_STDIO
+# include
+# endif
+
+# ifndef STBI_ASSERT
+# include
+# define STBI_ASSERT(x) assert(x)
+# endif
+
+# ifdef __cplusplus
+# define STBI_EXTERN extern "C"
+# else
+# define STBI_EXTERN extern
+# endif
+
+# ifndef _MSC_VER
+# ifdef __cplusplus
+# define stbi_inline inline
+# else
+# define stbi_inline
+# endif
+# else
+# define stbi_inline __forceinline
+# endif
+
+# ifndef STBI_NO_THREAD_LOCALS
+# if defined(__cplusplus) && __cplusplus >= 201103L
+# define STBI_THREAD_LOCAL thread_local
+# elif defined(__GNUC__) && __GNUC__ < 5
+# define STBI_THREAD_LOCAL __thread
+# elif defined(_MSC_VER)
+# define STBI_THREAD_LOCAL __declspec(thread)
+# elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
+# define STBI_THREAD_LOCAL _Thread_local
+# endif
+
+# ifndef STBI_THREAD_LOCAL
+# if defined(__GNUC__)
+# define STBI_THREAD_LOCAL __thread
+# endif
+# endif
+# endif
+
+# if defined(_MSC_VER) || defined(__SYMBIAN32__)
+typedef unsigned short stbi__uint16;
+typedef signed short stbi__int16;
+typedef unsigned int stbi__uint32;
+typedef signed int stbi__int32;
+# else
+# include
+typedef uint16_t stbi__uint16;
+typedef int16_t stbi__int16;
+typedef uint32_t stbi__uint32;
+typedef int32_t stbi__int32;
+# endif
+
+// should produce compiler error if size is wrong
+typedef unsigned char validate_uint32[sizeof(stbi__uint32) == 4 ? 1 : -1];
+
+# ifdef _MSC_VER
+# define STBI_NOTUSED(v) (void)(v)
+# else
+# define STBI_NOTUSED(v) (void)sizeof(v)
+# endif
+
+# ifdef _MSC_VER
+# define STBI_HAS_LROTL
+# endif
+
+# ifdef STBI_HAS_LROTL
+# define stbi_lrot(x, y) _lrotl(x, y)
+# else
+# define stbi_lrot(x, y) (((x) << (y)) | ((x) >> (-(y) & 31)))
+# endif
+
+# if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED))
+// ok
+# elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED)
+// ok
+# else
+# error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)."
+# endif
+
+# ifndef STBI_MALLOC
+# define STBI_MALLOC(sz) malloc(sz)
+# define STBI_REALLOC(p, newsz) realloc(p, newsz)
+# define STBI_FREE(p) free(p)
+# endif
+
+# ifndef STBI_REALLOC_SIZED
+# define STBI_REALLOC_SIZED(p, oldsz, newsz) STBI_REALLOC(p, newsz)
+# endif
+
+// x86/x64 detection
+# if defined(__x86_64__) || defined(_M_X64)
+# define STBI__X64_TARGET
+# elif defined(__i386) || defined(_M_IX86)
+# define STBI__X86_TARGET
+# endif
+
+# if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD)
+// gcc doesn't support sse2 intrinsics unless you compile with -msse2,
+// which in turn means it gets to use SSE2 everywhere. This is unfortunate,
+// but previous attempts to provide the SSE2 functions with runtime
+// detection caused numerous issues. The way architecture extensions are
+// exposed in GCC/Clang is, sadly, not really suited for one-file libs.
+// New behavior: if compiled with -msse2, we use SSE2 without any
+// detection; if not, we don't use it at all.
+# define STBI_NO_SIMD
+# endif
+
+# if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD)
+// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET
+//
+// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the
+// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant.
+// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not
+// simultaneously enabling "-mstackrealign".
+//
+// See https://github.com/nothings/stb/issues/81 for more information.
+//
+// So default to no SSE2 on 32-bit MinGW. If you've read this far and added
+// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2.
+# define STBI_NO_SIMD
+# endif
+
+# if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET))
+# define STBI_SSE2
+# include
+
+# ifdef _MSC_VER
+
+# if _MSC_VER >= 1400 // not VC6
+# include // __cpuid
+static int stbi__cpuid3(void)
+{
+ int info[4];
+ __cpuid(info, 1);
+ return info[3];
+}
+# else
+static int stbi__cpuid3(void)
+{
+ int res;
+ __asm {
+ mov eax,1
+ cpuid
+ mov res,edx
+ }
+ return res;
+}
+# endif
+
+# define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name
+
+# if !defined(STBI_NO_JPEG) && defined(STBI_SSE2)
+static int stbi__sse2_available(void)
+{
+ int info3 = stbi__cpuid3();
+ return ((info3 >> 26) & 1) != 0;
+}
+# endif
+
+# else // assume GCC-style if not VC++
+# define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
+
+# if !defined(STBI_NO_JPEG) && defined(STBI_SSE2)
+static int stbi__sse2_available(void)
+{
+ // If we're even attempting to compile this on GCC/Clang, that means
+ // -msse2 is on, which means the compiler is allowed to use SSE2
+ // instructions at will, and so are we.
+ return 1;
+}
+# endif
+
+# endif
+# endif
+
+// ARM NEON
+# if defined(STBI_NO_SIMD) && defined(STBI_NEON)
+# undef STBI_NEON
+# endif
+
+# ifdef STBI_NEON
+# include
+# ifdef _MSC_VER
+# define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name
+# else
+# define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
+# endif
+# endif
+
+# ifndef STBI_SIMD_ALIGN
+# define STBI_SIMD_ALIGN(type, name) type name
+# endif
+
+# ifndef STBI_MAX_DIMENSIONS
+# define STBI_MAX_DIMENSIONS (1 << 24)
+# endif
+
+///////////////////////////////////////////////
+//
+// stbi__context struct and start_xxx functions
+
+// stbi__context structure is our basic context used by all images, so it
+// contains all the IO context, plus some basic image information
+typedef struct
+{
+ stbi__uint32 img_x, img_y;
+ int img_n, img_out_n;
+
+ stbi_io_callbacks io;
+ void* io_user_data;
+
+ int read_from_callbacks;
+ int buflen;
+ stbi_uc buffer_start[128];
+ int callback_already_read;
+
+ stbi_uc *img_buffer, *img_buffer_end;
+ stbi_uc *img_buffer_original, *img_buffer_original_end;
+} stbi__context;
+
+static void stbi__refill_buffer(stbi__context* s);
+
+// initialize a memory-decode context
+static void stbi__start_mem(stbi__context* s, stbi_uc const* buffer, int len)
+{
+ s->io.read = NULL;
+ s->read_from_callbacks = 0;
+ s->callback_already_read = 0;
+ s->img_buffer = s->img_buffer_original = (stbi_uc*)buffer;
+ s->img_buffer_end = s->img_buffer_original_end = (stbi_uc*)buffer + len;
+}
+
+// initialize a callback-based context
+static void stbi__start_callbacks(stbi__context* s, stbi_io_callbacks* c, void* user)
+{
+ s->io = *c;
+ s->io_user_data = user;
+ s->buflen = sizeof(s->buffer_start);
+ s->read_from_callbacks = 1;
+ s->callback_already_read = 0;
+ s->img_buffer = s->img_buffer_original = s->buffer_start;
+ stbi__refill_buffer(s);
+ s->img_buffer_original_end = s->img_buffer_end;
+}
+
+# ifndef STBI_NO_STDIO
+
+static int stbi__stdio_read(void* user, char* data, int size)
+{
+ return (int)fread(data, 1, size, (FILE*)user);
+}
+
+static void stbi__stdio_skip(void* user, int n)
+{
+ int ch;
+ fseek((FILE*)user, n, SEEK_CUR);
+ ch = fgetc((FILE*)user); /* have to read a byte to reset feof()'s flag */
+ if (ch != EOF) {
+ ungetc(ch, (FILE*)user); /* push byte back onto stream if valid. */
+ }
+}
+
+static int stbi__stdio_eof(void* user)
+{
+ return feof((FILE*)user) || ferror((FILE*)user);
+}
+
+static stbi_io_callbacks stbi__stdio_callbacks = {
+ stbi__stdio_read,
+ stbi__stdio_skip,
+ stbi__stdio_eof,
+};
+
+static void stbi__start_file(stbi__context* s, FILE* f)
+{
+ stbi__start_callbacks(s, &stbi__stdio_callbacks, (void*)f);
+}
+
+// static void stop_file(stbi__context *s) { }
+
+# endif // !STBI_NO_STDIO
+
+static void stbi__rewind(stbi__context* s)
+{
+ // conceptually rewind SHOULD rewind to the beginning of the stream,
+ // but we just rewind to the beginning of the initial buffer, because
+ // we only use it after doing 'test', which only ever looks at at most 92 bytes
+ s->img_buffer = s->img_buffer_original;
+ s->img_buffer_end = s->img_buffer_original_end;
+}
+
+enum {
+ STBI_ORDER_RGB,
+ STBI_ORDER_BGR
+};
+
+typedef struct
+{
+ int bits_per_channel;
+ int num_channels;
+ int channel_order;
+} stbi__result_info;
+
+# ifndef STBI_NO_JPEG
+static int stbi__jpeg_test(stbi__context* s);
+static void* stbi__jpeg_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri);
+static int stbi__jpeg_info(stbi__context* s, int* x, int* y, int* comp);
+# endif
+
+# ifndef STBI_NO_PNG
+static int stbi__png_test(stbi__context* s);
+static void* stbi__png_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri);
+static int stbi__png_info(stbi__context* s, int* x, int* y, int* comp);
+static int stbi__png_is16(stbi__context* s);
+# endif
+
+# ifndef STBI_NO_BMP
+static int stbi__bmp_test(stbi__context* s);
+static void* stbi__bmp_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri);
+static int stbi__bmp_info(stbi__context* s, int* x, int* y, int* comp);
+# endif
+
+# ifndef STBI_NO_TGA
+static int stbi__tga_test(stbi__context* s);
+static void* stbi__tga_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri);
+static int stbi__tga_info(stbi__context* s, int* x, int* y, int* comp);
+# endif
+
+# ifndef STBI_NO_PSD
+static int stbi__psd_test(stbi__context* s);
+static void* stbi__psd_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri, int bpc);
+static int stbi__psd_info(stbi__context* s, int* x, int* y, int* comp);
+static int stbi__psd_is16(stbi__context* s);
+# endif
+
+# ifndef STBI_NO_HDR
+static int stbi__hdr_test(stbi__context* s);
+static float* stbi__hdr_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri);
+static int stbi__hdr_info(stbi__context* s, int* x, int* y, int* comp);
+# endif
+
+# ifndef STBI_NO_PIC
+static int stbi__pic_test(stbi__context* s);
+static void* stbi__pic_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri);
+static int stbi__pic_info(stbi__context* s, int* x, int* y, int* comp);
+# endif
+
+# ifndef STBI_NO_GIF
+static int stbi__gif_test(stbi__context* s);
+static void* stbi__gif_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri);
+static void* stbi__load_gif_main(stbi__context* s, int** delays, int* x, int* y, int* z, int* comp, int req_comp);
+static int stbi__gif_info(stbi__context* s, int* x, int* y, int* comp);
+# endif
+
+# ifndef STBI_NO_PNM
+static int stbi__pnm_test(stbi__context* s);
+static void* stbi__pnm_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri);
+static int stbi__pnm_info(stbi__context* s, int* x, int* y, int* comp);
+static int stbi__pnm_is16(stbi__context* s);
+# endif
+
+static
+# ifdef STBI_THREAD_LOCAL
+ STBI_THREAD_LOCAL
+# endif
+ char const* stbi__g_failure_reason;
+
+STBIDEF const char* stbi_failure_reason(void)
+{
+ return stbi__g_failure_reason;
+}
+
+# ifndef STBI_NO_FAILURE_STRINGS
+static int stbi__err(char const* str)
+{
+ stbi__g_failure_reason = str;
+ return 0;
+}
+# endif
+
+static void* stbi__malloc(size_t size)
+{
+ return STBI_MALLOC(size);
+}
+
+// stb_image uses ints pervasively, including for offset calculations.
+// therefore the largest decoded image size we can support with the
+// current code, even on 64-bit targets, is INT_MAX. this is not a
+// significant limitation for the intended use case.
+//
+// we do, however, need to make sure our size calculations don't
+// overflow. hence a few helper functions for size calculations that
+// multiply integers together, making sure that they're non-negative
+// and no overflow occurs.
+
+// return 1 if the sum is valid, 0 on overflow.
+// negative terms are considered invalid.
+static int stbi__addsizes_valid(int a, int b)
+{
+ if (b < 0)
+ return 0;
+ // now 0 <= b <= INT_MAX, hence also
+ // 0 <= INT_MAX - b <= INTMAX.
+ // And "a + b <= INT_MAX" (which might overflow) is the
+ // same as a <= INT_MAX - b (no overflow)
+ return a <= INT_MAX - b;
+}
+
+// returns 1 if the product is valid, 0 on overflow.
+// negative factors are considered invalid.
+static int stbi__mul2sizes_valid(int a, int b)
+{
+ if (a < 0 || b < 0)
+ return 0;
+ if (b == 0)
+ return 1; // mul-by-0 is always safe
+ // portable way to check for no overflows in a*b
+ return a <= INT_MAX / b;
+}
+
+# if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR)
+// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow
+static int stbi__mad2sizes_valid(int a, int b, int add)
+{
+ return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a * b, add);
+}
+# endif
+
+// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow
+static int stbi__mad3sizes_valid(int a, int b, int c, int add)
+{
+ return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a * b, c) && stbi__addsizes_valid(a * b * c, add);
+}
+
+// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow
+# if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
+static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add)
+{
+ return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a * b, c) && stbi__mul2sizes_valid(a * b * c, d) && stbi__addsizes_valid(a * b * c * d, add);
+}
+# endif
+
+# if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR)
+// mallocs with size overflow checking
+static void* stbi__malloc_mad2(int a, int b, int add)
+{
+ if (!stbi__mad2sizes_valid(a, b, add))
+ return NULL;
+ return stbi__malloc(a * b + add);
+}
+# endif
+
+static void* stbi__malloc_mad3(int a, int b, int c, int add)
+{
+ if (!stbi__mad3sizes_valid(a, b, c, add))
+ return NULL;
+ return stbi__malloc(a * b * c + add);
+}
+
+# if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
+static void* stbi__malloc_mad4(int a, int b, int c, int d, int add)
+{
+ if (!stbi__mad4sizes_valid(a, b, c, d, add))
+ return NULL;
+ return stbi__malloc(a * b * c * d + add);
+}
+# endif
+
+// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow.
+static int stbi__addints_valid(int a, int b)
+{
+ if ((a >= 0) != (b >= 0))
+ return 1; // a and b have different signs, so no overflow
+ if (a < 0 && b < 0)
+ return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0.
+ return a <= INT_MAX - b;
+}
+
+// returns 1 if the product of two ints fits in a signed short, 0 on overflow.
+static int stbi__mul2shorts_valid(int a, int b)
+{
+ if (b == 0 || b == -1)
+ return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow
+ if ((a >= 0) == (b >= 0))
+ return a <= SHRT_MAX / b; // product is positive, so similar to mul2sizes_valid
+ if (b < 0)
+ return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN
+ return a >= SHRT_MIN / b;
+}
+
+// stbi__err - error
+// stbi__errpf - error returning pointer to float
+// stbi__errpuc - error returning pointer to unsigned char
+
+# ifdef STBI_NO_FAILURE_STRINGS
+# define stbi__err(x, y) 0
+# elif defined(STBI_FAILURE_USERMSG)
+# define stbi__err(x, y) stbi__err(y)
+# else
+# define stbi__err(x, y) stbi__err(x)
+# endif
+
+# define stbi__errpf(x, y) ((float*)(size_t)(stbi__err(x, y) ? NULL : NULL))
+# define stbi__errpuc(x, y) ((unsigned char*)(size_t)(stbi__err(x, y) ? NULL : NULL))
+
+STBIDEF void stbi_image_free(void* retval_from_stbi_load)
+{
+ STBI_FREE(retval_from_stbi_load);
+}
+
+# ifndef STBI_NO_LINEAR
+static float* stbi__ldr_to_hdr(stbi_uc* data, int x, int y, int comp);
+# endif
+
+# ifndef STBI_NO_HDR
+static stbi_uc* stbi__hdr_to_ldr(float* data, int x, int y, int comp);
+# endif
+
+static int stbi__vertically_flip_on_load_global = 0;
+
+STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip)
+{
+ stbi__vertically_flip_on_load_global = flag_true_if_should_flip;
+}
+
+# ifndef STBI_THREAD_LOCAL
+# define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global
+# else
+static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set;
+
+STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip)
+{
+ stbi__vertically_flip_on_load_local = flag_true_if_should_flip;
+ stbi__vertically_flip_on_load_set = 1;
+}
+
+# define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \
+ ? stbi__vertically_flip_on_load_local \
+ : stbi__vertically_flip_on_load_global)
+# endif // STBI_THREAD_LOCAL
+
+static void* stbi__load_main(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri, int bpc)
+{
+ memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields
+ ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed
+ ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order
+ ri->num_channels = 0;
+
+// test the formats with a very explicit header first (at least a FOURCC
+// or distinctive magic number first)
+# ifndef STBI_NO_PNG
+ if (stbi__png_test(s))
+ return stbi__png_load(s, x, y, comp, req_comp, ri);
+# endif
+# ifndef STBI_NO_BMP
+ if (stbi__bmp_test(s))
+ return stbi__bmp_load(s, x, y, comp, req_comp, ri);
+# endif
+# ifndef STBI_NO_GIF
+ if (stbi__gif_test(s))
+ return stbi__gif_load(s, x, y, comp, req_comp, ri);
+# endif
+# ifndef STBI_NO_PSD
+ if (stbi__psd_test(s))
+ return stbi__psd_load(s, x, y, comp, req_comp, ri, bpc);
+# else
+ STBI_NOTUSED(bpc);
+# endif
+# ifndef STBI_NO_PIC
+ if (stbi__pic_test(s))
+ return stbi__pic_load(s, x, y, comp, req_comp, ri);
+# endif
+
+// then the formats that can end up attempting to load with just 1 or 2
+// bytes matching expectations; these are prone to false positives, so
+// try them later
+# ifndef STBI_NO_JPEG
+ if (stbi__jpeg_test(s))
+ return stbi__jpeg_load(s, x, y, comp, req_comp, ri);
+# endif
+# ifndef STBI_NO_PNM
+ if (stbi__pnm_test(s))
+ return stbi__pnm_load(s, x, y, comp, req_comp, ri);
+# endif
+
+# ifndef STBI_NO_HDR
+ if (stbi__hdr_test(s)) {
+ float* hdr = stbi__hdr_load(s, x, y, comp, req_comp, ri);
+ return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
+ }
+# endif
+
+# ifndef STBI_NO_TGA
+ // test tga last because it's a crappy test!
+ if (stbi__tga_test(s))
+ return stbi__tga_load(s, x, y, comp, req_comp, ri);
+# endif
+
+ return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt");
+}
+
+static stbi_uc* stbi__convert_16_to_8(stbi__uint16* orig, int w, int h, int channels)
+{
+ int i;
+ int img_len = w * h * channels;
+ stbi_uc* reduced;
+
+ reduced = (stbi_uc*)stbi__malloc(img_len);
+ if (reduced == NULL)
+ return stbi__errpuc("outofmem", "Out of memory");
+
+ for (i = 0; i < img_len; ++i)
+ reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling
+
+ STBI_FREE(orig);
+ return reduced;
+}
+
+static stbi__uint16* stbi__convert_8_to_16(stbi_uc* orig, int w, int h, int channels)
+{
+ int i;
+ int img_len = w * h * channels;
+ stbi__uint16* enlarged;
+
+ enlarged = (stbi__uint16*)stbi__malloc(img_len * 2);
+ if (enlarged == NULL)
+ return (stbi__uint16*)stbi__errpuc("outofmem", "Out of memory");
+
+ for (i = 0; i < img_len; ++i)
+ enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff
+
+ STBI_FREE(orig);
+ return enlarged;
+}
+
+static void stbi__vertical_flip(void* image, int w, int h, int bytes_per_pixel)
+{
+ int row;
+ size_t bytes_per_row = (size_t)w * bytes_per_pixel;
+ stbi_uc temp[2048];
+ stbi_uc* bytes = (stbi_uc*)image;
+
+ for (row = 0; row < (h >> 1); row++) {
+ stbi_uc* row0 = bytes + row * bytes_per_row;
+ stbi_uc* row1 = bytes + (h - row - 1) * bytes_per_row;
+ // swap row0 with row1
+ size_t bytes_left = bytes_per_row;
+ while (bytes_left) {
+ size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp);
+ memcpy(temp, row0, bytes_copy);
+ memcpy(row0, row1, bytes_copy);
+ memcpy(row1, temp, bytes_copy);
+ row0 += bytes_copy;
+ row1 += bytes_copy;
+ bytes_left -= bytes_copy;
+ }
+ }
+}
+
+# ifndef STBI_NO_GIF
+static void stbi__vertical_flip_slices(void* image, int w, int h, int z, int bytes_per_pixel)
+{
+ int slice;
+ int slice_size = w * h * bytes_per_pixel;
+
+ stbi_uc* bytes = (stbi_uc*)image;
+ for (slice = 0; slice < z; ++slice) {
+ stbi__vertical_flip(bytes, w, h, bytes_per_pixel);
+ bytes += slice_size;
+ }
+}
+# endif
+
+static unsigned char* stbi__load_and_postprocess_8bit(stbi__context* s, int* x, int* y, int* comp, int req_comp)
+{
+ stbi__result_info ri;
+ void* result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8);
+
+ if (result == NULL)
+ return NULL;
+
+ // it is the responsibility of the loaders to make sure we get either 8 or 16 bit.
+ STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16);
+
+ if (ri.bits_per_channel != 8) {
+ result = stbi__convert_16_to_8((stbi__uint16*)result, *x, *y, req_comp == 0 ? *comp : req_comp);
+ ri.bits_per_channel = 8;
+ }
+
+ // @TODO: move stbi__convert_format to here
+
+ if (stbi__vertically_flip_on_load) {
+ int channels = req_comp ? req_comp : *comp;
+ stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc));
+ }
+
+ return (unsigned char*)result;
+}
+
+static stbi__uint16* stbi__load_and_postprocess_16bit(stbi__context* s, int* x, int* y, int* comp, int req_comp)
+{
+ stbi__result_info ri;
+ void* result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16);
+
+ if (result == NULL)
+ return NULL;
+
+ // it is the responsibility of the loaders to make sure we get either 8 or 16 bit.
+ STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16);
+
+ if (ri.bits_per_channel != 16) {
+ result = stbi__convert_8_to_16((stbi_uc*)result, *x, *y, req_comp == 0 ? *comp : req_comp);
+ ri.bits_per_channel = 16;
+ }
+
+ // @TODO: move stbi__convert_format16 to here
+ // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision
+
+ if (stbi__vertically_flip_on_load) {
+ int channels = req_comp ? req_comp : *comp;
+ stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16));
+ }
+
+ return (stbi__uint16*)result;
+}
+
+# if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR)
+static void stbi__float_postprocess(float* result, int* x, int* y, int* comp, int req_comp)
+{
+ if (stbi__vertically_flip_on_load && result != NULL) {
+ int channels = req_comp ? req_comp : *comp;
+ stbi__vertical_flip(result, *x, *y, channels * sizeof(float));
+ }
+}
+# endif
+
+# ifndef STBI_NO_STDIO
+
+# if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
+STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, char const* str, int cbmb, wchar_t* widestr, int cchwide);
+STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, wchar_t const* widestr, int cchwide, char* str, int cbmb, char const* defchar, int* used_default);
+# endif
+
+# if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
+STBIDEF int stbi_convert_wchar_to_utf8(char* buffer, size_t bufferlen, wchar_t const* input)
+{
+ return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int)bufferlen, NULL, NULL);
+}
+# endif
+
+static FILE* stbi__fopen(char const* filename, char const* mode)
+{
+ FILE* f;
+# if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
+ wchar_t wMode[64];
+ wchar_t wFilename[1024];
+ if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename) / sizeof(*wFilename)))
+ return 0;
+
+ if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode) / sizeof(*wMode)))
+ return 0;
+
+# if defined(_MSC_VER) && _MSC_VER >= 1400
+ if (0 != _wfopen_s(&f, wFilename, wMode))
+ f = 0;
+# else
+ f = _wfopen(wFilename, wMode);
+# endif
+
+# elif defined(_MSC_VER) && _MSC_VER >= 1400
+ if (0 != fopen_s(&f, filename, mode))
+ f = 0;
+# else
+ f = fopen(filename, mode);
+# endif
+ return f;
+}
+
+STBIDEF stbi_uc* stbi_load(char const* filename, int* x, int* y, int* comp, int req_comp)
+{
+ FILE* f = stbi__fopen(filename, "rb");
+ unsigned char* result;
+ if (!f)
+ return stbi__errpuc("can't fopen", "Unable to open file");
+ result = stbi_load_from_file(f, x, y, comp, req_comp);
+ fclose(f);
+ return result;
+}
+
+STBIDEF stbi_uc* stbi_load_from_file(FILE* f, int* x, int* y, int* comp, int req_comp)
+{
+ unsigned char* result;
+ stbi__context s;
+ stbi__start_file(&s, f);
+ result = stbi__load_and_postprocess_8bit(&s, x, y, comp, req_comp);
+ if (result) {
+ // need to 'unget' all the characters in the IO buffer
+ fseek(f, -(int)(s.img_buffer_end - s.img_buffer), SEEK_CUR);
+ }
+ return result;
+}
+
+STBIDEF stbi__uint16* stbi_load_from_file_16(FILE* f, int* x, int* y, int* comp, int req_comp)
+{
+ stbi__uint16* result;
+ stbi__context s;
+ stbi__start_file(&s, f);
+ result = stbi__load_and_postprocess_16bit(&s, x, y, comp, req_comp);
+ if (result) {
+ // need to 'unget' all the characters in the IO buffer
+ fseek(f, -(int)(s.img_buffer_end - s.img_buffer), SEEK_CUR);
+ }
+ return result;
+}
+
+STBIDEF stbi_us* stbi_load_16(char const* filename, int* x, int* y, int* comp, int req_comp)
+{
+ FILE* f = stbi__fopen(filename, "rb");
+ stbi__uint16* result;
+ if (!f)
+ return (stbi_us*)stbi__errpuc("can't fopen", "Unable to open file");
+ result = stbi_load_from_file_16(f, x, y, comp, req_comp);
+ fclose(f);
+ return result;
+}
+
+# endif //! STBI_NO_STDIO
+
+STBIDEF stbi_us* stbi_load_16_from_memory(stbi_uc const* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels)
+{
+ stbi__context s;
+ stbi__start_mem(&s, buffer, len);
+ return stbi__load_and_postprocess_16bit(&s, x, y, channels_in_file, desired_channels);
+}
+
+STBIDEF stbi_us* stbi_load_16_from_callbacks(stbi_io_callbacks const* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels)
+{
+ stbi__context s;
+ stbi__start_callbacks(&s, (stbi_io_callbacks*)clbk, user);
+ return stbi__load_and_postprocess_16bit(&s, x, y, channels_in_file, desired_channels);
+}
+
+STBIDEF stbi_uc* stbi_load_from_memory(stbi_uc const* buffer, int len, int* x, int* y, int* comp, int req_comp)
+{
+ stbi__context s;
+ stbi__start_mem(&s, buffer, len);
+ return stbi__load_and_postprocess_8bit(&s, x, y, comp, req_comp);
+}
+
+STBIDEF stbi_uc* stbi_load_from_callbacks(stbi_io_callbacks const* clbk, void* user, int* x, int* y, int* comp, int req_comp)
+{
+ stbi__context s;
+ stbi__start_callbacks(&s, (stbi_io_callbacks*)clbk, user);
+ return stbi__load_and_postprocess_8bit(&s, x, y, comp, req_comp);
+}
+
+# ifndef STBI_NO_GIF
+STBIDEF stbi_uc* stbi_load_gif_from_memory(stbi_uc const* buffer, int len, int** delays, int* x, int* y, int* z, int* comp, int req_comp)
+{
+ unsigned char* result;
+ stbi__context s;
+ stbi__start_mem(&s, buffer, len);
+
+ result = (unsigned char*)stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp);
+ if (stbi__vertically_flip_on_load) {
+ stbi__vertical_flip_slices(result, *x, *y, *z, *comp);
+ }
+
+ return result;
+}
+# endif
+
+# ifndef STBI_NO_LINEAR
+static float* stbi__loadf_main(stbi__context* s, int* x, int* y, int* comp, int req_comp)
+{
+ unsigned char* data;
+# ifndef STBI_NO_HDR
+ if (stbi__hdr_test(s)) {
+ stbi__result_info ri;
+ float* hdr_data = stbi__hdr_load(s, x, y, comp, req_comp, &ri);
+ if (hdr_data)
+ stbi__float_postprocess(hdr_data, x, y, comp, req_comp);
+ return hdr_data;
+ }
+# endif
+ data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp);
+ if (data)
+ return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
+ return stbi__errpf("unknown image type", "Image not of any known type, or corrupt");
+}
+
+STBIDEF float* stbi_loadf_from_memory(stbi_uc const* buffer, int len, int* x, int* y, int* comp, int req_comp)
+{
+ stbi__context s;
+ stbi__start_mem(&s, buffer, len);
+ return stbi__loadf_main(&s, x, y, comp, req_comp);
+}
+
+STBIDEF float* stbi_loadf_from_callbacks(stbi_io_callbacks const* clbk, void* user, int* x, int* y, int* comp, int req_comp)
+{
+ stbi__context s;
+ stbi__start_callbacks(&s, (stbi_io_callbacks*)clbk, user);
+ return stbi__loadf_main(&s, x, y, comp, req_comp);
+}
+
+# ifndef STBI_NO_STDIO
+STBIDEF float* stbi_loadf(char const* filename, int* x, int* y, int* comp, int req_comp)
+{
+ float* result;
+ FILE* f = stbi__fopen(filename, "rb");
+ if (!f)
+ return stbi__errpf("can't fopen", "Unable to open file");
+ result = stbi_loadf_from_file(f, x, y, comp, req_comp);
+ fclose(f);
+ return result;
+}
+
+STBIDEF float* stbi_loadf_from_file(FILE* f, int* x, int* y, int* comp, int req_comp)
+{
+ stbi__context s;
+ stbi__start_file(&s, f);
+ return stbi__loadf_main(&s, x, y, comp, req_comp);
+}
+# endif // !STBI_NO_STDIO
+
+# endif // !STBI_NO_LINEAR
+
+// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is
+// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always
+// reports false!
+
+STBIDEF int stbi_is_hdr_from_memory(stbi_uc const* buffer, int len)
+{
+# ifndef STBI_NO_HDR
+ stbi__context s;
+ stbi__start_mem(&s, buffer, len);
+ return stbi__hdr_test(&s);
+# else
+ STBI_NOTUSED(buffer);
+ STBI_NOTUSED(len);
+ return 0;
+# endif
+}
+
+# ifndef STBI_NO_STDIO
+STBIDEF int stbi_is_hdr(char const* filename)
+{
+ FILE* f = stbi__fopen(filename, "rb");
+ int result = 0;
+ if (f) {
+ result = stbi_is_hdr_from_file(f);
+ fclose(f);
+ }
+ return result;
+}
+
+STBIDEF int stbi_is_hdr_from_file(FILE* f)
+{
+# ifndef STBI_NO_HDR
+ long pos = ftell(f);
+ int res;
+ stbi__context s;
+ stbi__start_file(&s, f);
+ res = stbi__hdr_test(&s);
+ fseek(f, pos, SEEK_SET);
+ return res;
+# else
+ STBI_NOTUSED(f);
+ return 0;
+# endif
+}
+# endif // !STBI_NO_STDIO
+
+STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const* clbk, void* user)
+{
+# ifndef STBI_NO_HDR
+ stbi__context s;
+ stbi__start_callbacks(&s, (stbi_io_callbacks*)clbk, user);
+ return stbi__hdr_test(&s);
+# else
+ STBI_NOTUSED(clbk);
+ STBI_NOTUSED(user);
+ return 0;
+# endif
+}
+
+# ifndef STBI_NO_LINEAR
+static float stbi__l2h_gamma = 2.2f, stbi__l2h_scale = 1.0f;
+
+STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; }
+STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; }
+# endif
+
+static float stbi__h2l_gamma_i = 1.0f / 2.2f, stbi__h2l_scale_i = 1.0f;
+
+STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1 / gamma; }
+STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1 / scale; }
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Common code used by all image loaders
+//
+
+enum {
+ STBI__SCAN_load = 0,
+ STBI__SCAN_type,
+ STBI__SCAN_header
+};
+
+static void stbi__refill_buffer(stbi__context* s)
+{
+ int n = (s->io.read)(s->io_user_data, (char*)s->buffer_start, s->buflen);
+ s->callback_already_read += (int)(s->img_buffer - s->img_buffer_original);
+ if (n == 0) {
+ // at end of file, treat same as if from memory, but need to handle case
+ // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file
+ s->read_from_callbacks = 0;
+ s->img_buffer = s->buffer_start;
+ s->img_buffer_end = s->buffer_start + 1;
+ *s->img_buffer = 0;
+ } else {
+ s->img_buffer = s->buffer_start;
+ s->img_buffer_end = s->buffer_start + n;
+ }
+}
+
+stbi_inline static stbi_uc stbi__get8(stbi__context* s)
+{
+ if (s->img_buffer < s->img_buffer_end)
+ return *s->img_buffer++;
+ if (s->read_from_callbacks) {
+ stbi__refill_buffer(s);
+ return *s->img_buffer++;
+ }
+ return 0;
+}
+
+# if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)
+// nothing
+# else
+stbi_inline static int stbi__at_eof(stbi__context* s)
+{
+ if (s->io.read) {
+ if (!(s->io.eof)(s->io_user_data))
+ return 0;
+ // if feof() is true, check if buffer = end
+ // special case: we've only got the special 0 character at the end
+ if (s->read_from_callbacks == 0)
+ return 1;
+ }
+
+ return s->img_buffer >= s->img_buffer_end;
+}
+# endif
+
+# if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC)
+// nothing
+# else
+static void stbi__skip(stbi__context* s, int n)
+{
+ if (n == 0)
+ return; // already there!
+ if (n < 0) {
+ s->img_buffer = s->img_buffer_end;
+ return;
+ }
+ if (s->io.read) {
+ int blen = (int)(s->img_buffer_end - s->img_buffer);
+ if (blen < n) {
+ s->img_buffer = s->img_buffer_end;
+ (s->io.skip)(s->io_user_data, n - blen);
+ return;
+ }
+ }
+ s->img_buffer += n;
+}
+# endif
+
+# if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM)
+// nothing
+# else
+static int stbi__getn(stbi__context* s, stbi_uc* buffer, int n)
+{
+ if (s->io.read) {
+ int blen = (int)(s->img_buffer_end - s->img_buffer);
+ if (blen < n) {
+ int res, count;
+
+ memcpy(buffer, s->img_buffer, blen);
+
+ count = (s->io.read)(s->io_user_data, (char*)buffer + blen, n - blen);
+ res = (count == (n - blen));
+ s->img_buffer = s->img_buffer_end;
+ return res;
+ }
+ }
+
+ if (s->img_buffer + n <= s->img_buffer_end) {
+ memcpy(buffer, s->img_buffer, n);
+ s->img_buffer += n;
+ return 1;
+ } else
+ return 0;
+}
+# endif
+
+# if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC)
+// nothing
+# else
+static int stbi__get16be(stbi__context* s)
+{
+ int z = stbi__get8(s);
+ return (z << 8) + stbi__get8(s);
+}
+# endif
+
+# if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC)
+// nothing
+# else
+static stbi__uint32 stbi__get32be(stbi__context* s)
+{
+ stbi__uint32 z = stbi__get16be(s);
+ return (z << 16) + stbi__get16be(s);
+}
+# endif
+
+# if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF)
+// nothing
+# else
+static int stbi__get16le(stbi__context* s)
+{
+ int z = stbi__get8(s);
+ return z + (stbi__get8(s) << 8);
+}
+# endif
+
+# ifndef STBI_NO_BMP
+static stbi__uint32 stbi__get32le(stbi__context* s)
+{
+ stbi__uint32 z = stbi__get16le(s);
+ z += (stbi__uint32)stbi__get16le(s) << 16;
+ return z;
+}
+# endif
+
+# define STBI__BYTECAST(x) ((stbi_uc)((x) & 255)) // truncate int to byte without warnings
+
+# if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)
+// nothing
+# else
+//////////////////////////////////////////////////////////////////////////////
+//
+// generic converter from built-in img_n to req_comp
+// individual types do this automatically as much as possible (e.g. jpeg
+// does all cases internally since it needs to colorspace convert anyway,
+// and it never has alpha, so very few cases ). png can automatically
+// interleave an alpha=255 channel, but falls back to this for other cases
+//
+// assume data buffer is malloced, so malloc a new one and free that one
+// only failure mode is malloc failing
+
+static stbi_uc stbi__compute_y(int r, int g, int b)
+{
+ return (stbi_uc)(((r * 77) + (g * 150) + (29 * b)) >> 8);
+}
+# endif
+
+# if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)
+// nothing
+# else
+static unsigned char* stbi__convert_format(unsigned char* data, int img_n, int req_comp, unsigned int x, unsigned int y)
+{
+ int i, j;
+ unsigned char* good;
+
+ if (req_comp == img_n)
+ return data;
+ STBI_ASSERT(req_comp >= 1 && req_comp <= 4);
+
+ good = (unsigned char*)stbi__malloc_mad3(req_comp, x, y, 0);
+ if (good == NULL) {
+ STBI_FREE(data);
+ return stbi__errpuc("outofmem", "Out of memory");
+ }
+
+ for (j = 0; j < (int)y; ++j) {
+ unsigned char* src = data + j * x * img_n;
+ unsigned char* dest = good + j * x * req_comp;
+
+# define STBI__COMBO(a, b) ((a) * 8 + (b))
+# define STBI__CASE(a, b) \
+ case STBI__COMBO(a, b): \
+ for (i = x - 1; i >= 0; --i, src += a, dest += b)
+ // convert source image with img_n components to one with req_comp components;
+ // avoid switch per pixel, so use switch per scanline and massive macros
+ switch (STBI__COMBO(img_n, req_comp)) {
+ STBI__CASE(1, 2)
+ {
+ dest[0] = src[0];
+ dest[1] = 255;
+ }
+ break;
+ STBI__CASE(1, 3) { dest[0] = dest[1] = dest[2] = src[0]; }
+ break;
+ STBI__CASE(1, 4)
+ {
+ dest[0] = dest[1] = dest[2] = src[0];
+ dest[3] = 255;
+ }
+ break;
+ STBI__CASE(2, 1) { dest[0] = src[0]; }
+ break;
+ STBI__CASE(2, 3) { dest[0] = dest[1] = dest[2] = src[0]; }
+ break;
+ STBI__CASE(2, 4)
+ {
+ dest[0] = dest[1] = dest[2] = src[0];
+ dest[3] = src[1];
+ }
+ break;
+ STBI__CASE(3, 4)
+ {
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+ dest[3] = 255;
+ }
+ break;
+ STBI__CASE(3, 1) { dest[0] = stbi__compute_y(src[0], src[1], src[2]); }
+ break;
+ STBI__CASE(3, 2)
+ {
+ dest[0] = stbi__compute_y(src[0], src[1], src[2]);
+ dest[1] = 255;
+ }
+ break;
+ STBI__CASE(4, 1) { dest[0] = stbi__compute_y(src[0], src[1], src[2]); }
+ break;
+ STBI__CASE(4, 2)
+ {
+ dest[0] = stbi__compute_y(src[0], src[1], src[2]);
+ dest[1] = src[3];
+ }
+ break;
+ STBI__CASE(4, 3)
+ {
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+ }
+ break;
+ default:
+ STBI_ASSERT(0);
+ STBI_FREE(data);
+ STBI_FREE(good);
+ return stbi__errpuc("unsupported", "Unsupported format conversion");
+ }
+# undef STBI__CASE
+ }
+
+ STBI_FREE(data);
+ return good;
+}
+# endif
+
+# if defined(STBI_NO_PNG) && defined(STBI_NO_PSD)
+// nothing
+# else
+static stbi__uint16 stbi__compute_y_16(int r, int g, int b)
+{
+ return (stbi__uint16)(((r * 77) + (g * 150) + (29 * b)) >> 8);
+}
+# endif
+
+# if defined(STBI_NO_PNG) && defined(STBI_NO_PSD)
+// nothing
+# else
+static stbi__uint16* stbi__convert_format16(stbi__uint16* data, int img_n, int req_comp, unsigned int x, unsigned int y)
+{
+ int i, j;
+ stbi__uint16* good;
+
+ if (req_comp == img_n)
+ return data;
+ STBI_ASSERT(req_comp >= 1 && req_comp <= 4);
+
+ good = (stbi__uint16*)stbi__malloc(req_comp * x * y * 2);
+ if (good == NULL) {
+ STBI_FREE(data);
+ return (stbi__uint16*)stbi__errpuc("outofmem", "Out of memory");
+ }
+
+ for (j = 0; j < (int)y; ++j) {
+ stbi__uint16* src = data + j * x * img_n;
+ stbi__uint16* dest = good + j * x * req_comp;
+
+# define STBI__COMBO(a, b) ((a) * 8 + (b))
+# define STBI__CASE(a, b) \
+ case STBI__COMBO(a, b): \
+ for (i = x - 1; i >= 0; --i, src += a, dest += b)
+ // convert source image with img_n components to one with req_comp components;
+ // avoid switch per pixel, so use switch per scanline and massive macros
+ switch (STBI__COMBO(img_n, req_comp)) {
+ STBI__CASE(1, 2)
+ {
+ dest[0] = src[0];
+ dest[1] = 0xffff;
+ }
+ break;
+ STBI__CASE(1, 3) { dest[0] = dest[1] = dest[2] = src[0]; }
+ break;
+ STBI__CASE(1, 4)
+ {
+ dest[0] = dest[1] = dest[2] = src[0];
+ dest[3] = 0xffff;
+ }
+ break;
+ STBI__CASE(2, 1) { dest[0] = src[0]; }
+ break;
+ STBI__CASE(2, 3) { dest[0] = dest[1] = dest[2] = src[0]; }
+ break;
+ STBI__CASE(2, 4)
+ {
+ dest[0] = dest[1] = dest[2] = src[0];
+ dest[3] = src[1];
+ }
+ break;
+ STBI__CASE(3, 4)
+ {
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+ dest[3] = 0xffff;
+ }
+ break;
+ STBI__CASE(3, 1) { dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); }
+ break;
+ STBI__CASE(3, 2)
+ {
+ dest[0] = stbi__compute_y_16(src[0], src[1], src[2]);
+ dest[1] = 0xffff;
+ }
+ break;
+ STBI__CASE(4, 1) { dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); }
+ break;
+ STBI__CASE(4, 2)
+ {
+ dest[0] = stbi__compute_y_16(src[0], src[1], src[2]);
+ dest[1] = src[3];
+ }
+ break;
+ STBI__CASE(4, 3)
+ {
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+ }
+ break;
+ default:
+ STBI_ASSERT(0);
+ STBI_FREE(data);
+ STBI_FREE(good);
+ return (stbi__uint16*)stbi__errpuc("unsupported", "Unsupported format conversion");
+ }
+# undef STBI__CASE
+ }
+
+ STBI_FREE(data);
+ return good;
+}
+# endif
+
+# ifndef STBI_NO_LINEAR
+static float* stbi__ldr_to_hdr(stbi_uc* data, int x, int y, int comp)
+{
+ int i, k, n;
+ float* output;
+ if (!data)
+ return NULL;
+ output = (float*)stbi__malloc_mad4(x, y, comp, sizeof(float), 0);
+ if (output == NULL) {
+ STBI_FREE(data);
+ return stbi__errpf("outofmem", "Out of memory");
+ }
+ // compute number of non-alpha components
+ if (comp & 1)
+ n = comp;
+ else
+ n = comp - 1;
+ for (i = 0; i < x * y; ++i) {
+ for (k = 0; k < n; ++k) {
+ output[i * comp + k] = (float)(pow(data[i * comp + k] / 255.0f, stbi__l2h_gamma) * stbi__l2h_scale);
+ }
+ }
+ if (n < comp) {
+ for (i = 0; i < x * y; ++i) {
+ output[i * comp + n] = data[i * comp + n] / 255.0f;
+ }
+ }
+ STBI_FREE(data);
+ return output;
+}
+# endif
+
+# ifndef STBI_NO_HDR
+# define stbi__float2int(x) ((int)(x))
+static stbi_uc* stbi__hdr_to_ldr(float* data, int x, int y, int comp)
+{
+ int i, k, n;
+ stbi_uc* output;
+ if (!data)
+ return NULL;
+ output = (stbi_uc*)stbi__malloc_mad3(x, y, comp, 0);
+ if (output == NULL) {
+ STBI_FREE(data);
+ return stbi__errpuc("outofmem", "Out of memory");
+ }
+ // compute number of non-alpha components
+ if (comp & 1)
+ n = comp;
+ else
+ n = comp - 1;
+ for (i = 0; i < x * y; ++i) {
+ for (k = 0; k < n; ++k) {
+ float z = (float)pow(data[i * comp + k] * stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f;
+ if (z < 0)
+ z = 0;
+ if (z > 255)
+ z = 255;
+ output[i * comp + k] = (stbi_uc)stbi__float2int(z);
+ }
+ if (k < comp) {
+ float z = data[i * comp + k] * 255 + 0.5f;
+ if (z < 0)
+ z = 0;
+ if (z > 255)
+ z = 255;
+ output[i * comp + k] = (stbi_uc)stbi__float2int(z);
+ }
+ }
+ STBI_FREE(data);
+ return output;
+}
+# endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// "baseline" JPEG/JFIF decoder
+//
+// simple implementation
+// - doesn't support delayed output of y-dimension
+// - simple interface (only one output format: 8-bit interleaved RGB)
+// - doesn't try to recover corrupt jpegs
+// - doesn't allow partial loading, loading multiple at once
+// - still fast on x86 (copying globals into locals doesn't help x86)
+// - allocates lots of intermediate memory (full size of all components)
+// - non-interleaved case requires this anyway
+// - allows good upsampling (see next)
+// high-quality
+// - upsampled channels are bilinearly interpolated, even across blocks
+// - quality integer IDCT derived from IJG's 'slow'
+// performance
+// - fast huffman; reasonable integer IDCT
+// - some SIMD kernels for common paths on targets with SSE2/NEON
+// - uses a lot of intermediate memory, could cache poorly
+
+# ifndef STBI_NO_JPEG
+
+// huffman decoding acceleration
+# define FAST_BITS 9 // larger handles more cases; smaller stomps less cache
+
+typedef struct
+{
+ stbi_uc fast[1 << FAST_BITS];
+ // weirdly, repacking this into AoS is a 10% speed loss, instead of a win
+ stbi__uint16 code[256];
+ stbi_uc values[256];
+ stbi_uc size[257];
+ unsigned int maxcode[18];
+ int delta[17]; // old 'firstsymbol' - old 'firstcode'
+} stbi__huffman;
+
+typedef struct
+{
+ stbi__context* s;
+ stbi__huffman huff_dc[4];
+ stbi__huffman huff_ac[4];
+ stbi__uint16 dequant[4][64];
+ stbi__int16 fast_ac[4][1 << FAST_BITS];
+
+ // sizes for components, interleaved MCUs
+ int img_h_max, img_v_max;
+ int img_mcu_x, img_mcu_y;
+ int img_mcu_w, img_mcu_h;
+
+ // definition of jpeg image component
+ struct
+ {
+ int id;
+ int h, v;
+ int tq;
+ int hd, ha;
+ int dc_pred;
+
+ int x, y, w2, h2;
+ stbi_uc* data;
+ void *raw_data, *raw_coeff;
+ stbi_uc* linebuf;
+ short* coeff; // progressive only
+ int coeff_w, coeff_h; // number of 8x8 coefficient blocks
+ } img_comp[4];
+
+ stbi__uint32 code_buffer; // jpeg entropy-coded buffer
+ int code_bits; // number of valid bits
+ unsigned char marker; // marker seen while filling entropy buffer
+ int nomore; // flag if we saw a marker so must stop
+
+ int progressive;
+ int spec_start;
+ int spec_end;
+ int succ_high;
+ int succ_low;
+ int eob_run;
+ int jfif;
+ int app14_color_transform; // Adobe APP14 tag
+ int rgb;
+
+ int scan_n, order[4];
+ int restart_interval, todo;
+
+ // kernels
+ void (*idct_block_kernel)(stbi_uc* out, int out_stride, short data[64]);
+ void (*YCbCr_to_RGB_kernel)(stbi_uc* out, stbi_uc const* y, stbi_uc const* pcb, stbi_uc const* pcr, int count, int step);
+ stbi_uc* (*resample_row_hv_2_kernel)(stbi_uc* out, stbi_uc* in_near, stbi_uc* in_far, int w, int hs);
+} stbi__jpeg;
+
+static int stbi__build_huffman(stbi__huffman* h, int* count)
+{
+ int i, j, k = 0;
+ unsigned int code;
+ // build size list for each symbol (from JPEG spec)
+ for (i = 0; i < 16; ++i) {
+ for (j = 0; j < count[i]; ++j) {
+ h->size[k++] = (stbi_uc)(i + 1);
+ if (k >= 257)
+ return stbi__err("bad size list", "Corrupt JPEG");
+ }
+ }
+ h->size[k] = 0;
+
+ // compute actual symbols (from jpeg spec)
+ code = 0;
+ k = 0;
+ for (j = 1; j <= 16; ++j) {
+ // compute delta to add to code to compute symbol id
+ h->delta[j] = k - code;
+ if (h->size[k] == j) {
+ while (h->size[k] == j)
+ h->code[k++] = (stbi__uint16)(code++);
+ if (code - 1 >= (1u << j))
+ return stbi__err("bad code lengths", "Corrupt JPEG");
+ }
+ // compute largest code + 1 for this size, preshifted as needed later
+ h->maxcode[j] = code << (16 - j);
+ code <<= 1;
+ }
+ h->maxcode[j] = 0xffffffff;
+
+ // build non-spec acceleration table; 255 is flag for not-accelerated
+ memset(h->fast, 255, 1 << FAST_BITS);
+ for (i = 0; i < k; ++i) {
+ int s = h->size[i];
+ if (s <= FAST_BITS) {
+ int c = h->code[i] << (FAST_BITS - s);
+ int m = 1 << (FAST_BITS - s);
+ for (j = 0; j < m; ++j) {
+ h->fast[c + j] = (stbi_uc)i;
+ }
+ }
+ }
+ return 1;
+}
+
+// build a table that decodes both magnitude and value of small ACs in
+// one go.
+static void stbi__build_fast_ac(stbi__int16* fast_ac, stbi__huffman* h)
+{
+ int i;
+ for (i = 0; i < (1 << FAST_BITS); ++i) {
+ stbi_uc fast = h->fast[i];
+ fast_ac[i] = 0;
+ if (fast < 255) {
+ int rs = h->values[fast];
+ int run = (rs >> 4) & 15;
+ int magbits = rs & 15;
+ int len = h->size[fast];
+
+ if (magbits && len + magbits <= FAST_BITS) {
+ // magnitude code followed by receive_extend code
+ int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits);
+ int m = 1 << (magbits - 1);
+ if (k < m)
+ k += (~0U << magbits) + 1;
+ // if the result is small enough, we can fit it in fast_ac table
+ if (k >= -128 && k <= 127)
+ fast_ac[i] = (stbi__int16)((k * 256) + (run * 16) + (len + magbits));
+ }
+ }
+ }
+}
+
+static void stbi__grow_buffer_unsafe(stbi__jpeg* j)
+{
+ do {
+ unsigned int b = j->nomore ? 0 : stbi__get8(j->s);
+ if (b == 0xff) {
+ int c = stbi__get8(j->s);
+ while (c == 0xff)
+ c = stbi__get8(j->s); // consume fill bytes
+ if (c != 0) {
+ j->marker = (unsigned char)c;
+ j->nomore = 1;
+ return;
+ }
+ }
+ j->code_buffer |= b << (24 - j->code_bits);
+ j->code_bits += 8;
+ } while (j->code_bits <= 24);
+}
+
+// (1 << n) - 1
+static stbi__uint32 const stbi__bmask[17] = { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535 };
+
+// decode a jpeg huffman value from the bitstream
+stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg* j, stbi__huffman* h)
+{
+ unsigned int temp;
+ int c, k;
+
+ if (j->code_bits < 16)
+ stbi__grow_buffer_unsafe(j);
+
+ // look at the top FAST_BITS and determine what symbol ID it is,
+ // if the code is <= FAST_BITS
+ c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS) - 1);
+ k = h->fast[c];
+ if (k < 255) {
+ int s = h->size[k];
+ if (s > j->code_bits)
+ return -1;
+ j->code_buffer <<= s;
+ j->code_bits -= s;
+ return h->values[k];
+ }
+
+ // naive test is to shift the code_buffer down so k bits are
+ // valid, then test against maxcode. To speed this up, we've
+ // preshifted maxcode left so that it has (16-k) 0s at the
+ // end; in other words, regardless of the number of bits, it
+ // wants to be compared against something shifted to have 16;
+ // that way we don't need to shift inside the loop.
+ temp = j->code_buffer >> 16;
+ for (k = FAST_BITS + 1;; ++k)
+ if (temp < h->maxcode[k])
+ break;
+ if (k == 17) {
+ // error! code not found
+ j->code_bits -= 16;
+ return -1;
+ }
+
+ if (k > j->code_bits)
+ return -1;
+
+ // convert the huffman code to the symbol id
+ c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];
+ if (c < 0 || c >= 256) // symbol id out of bounds!
+ return -1;
+ STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);
+
+ // convert the id to a symbol
+ j->code_bits -= k;
+ j->code_buffer <<= k;
+ return h->values[c];
+}
+
+// bias[n] = (-1<code_bits < n)
+ stbi__grow_buffer_unsafe(j);
+ if (j->code_bits < n)
+ return 0; // ran out of bits from stream, return 0s intead of continuing
+
+ sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative)
+ k = stbi_lrot(j->code_buffer, n);
+ j->code_buffer = k & ~stbi__bmask[n];
+ k &= stbi__bmask[n];
+ j->code_bits -= n;
+ return k + (stbi__jbias[n] & (sgn - 1));
+}
+
+// get some unsigned bits
+stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg* j, int n)
+{
+ unsigned int k;
+ if (j->code_bits < n)
+ stbi__grow_buffer_unsafe(j);
+ if (j->code_bits < n)
+ return 0; // ran out of bits from stream, return 0s intead of continuing
+ k = stbi_lrot(j->code_buffer, n);
+ j->code_buffer = k & ~stbi__bmask[n];
+ k &= stbi__bmask[n];
+ j->code_bits -= n;
+ return k;
+}
+
+stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg* j)
+{
+ unsigned int k;
+ if (j->code_bits < 1)
+ stbi__grow_buffer_unsafe(j);
+ if (j->code_bits < 1)
+ return 0; // ran out of bits from stream, return 0s intead of continuing
+ k = j->code_buffer;
+ j->code_buffer <<= 1;
+ --j->code_bits;
+ return k & 0x80000000;
+}
+
+// given a value that's at position X in the zigzag stream,
+// where does it appear in the 8x8 matrix coded as row-major?
+static stbi_uc const stbi__jpeg_dezigzag[64 + 15] = {
+ 0, 1, 8, 16, 9, 2, 3, 10,
+ 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34,
+ 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36,
+ 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46,
+ 53, 60, 61, 54, 47, 55, 62, 63,
+ // let corrupt input sample past end
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63
+};
+
+// decode one 64-entry block--
+static int stbi__jpeg_decode_block(stbi__jpeg* j, short data[64], stbi__huffman* hdc, stbi__huffman* hac, stbi__int16* fac, int b, stbi__uint16* dequant)
+{
+ int diff, dc, k;
+ int t;
+
+ if (j->code_bits < 16)
+ stbi__grow_buffer_unsafe(j);
+ t = stbi__jpeg_huff_decode(j, hdc);
+ if (t < 0 || t > 15)
+ return stbi__err("bad huffman code", "Corrupt JPEG");
+
+ // 0 all the ac values now so we can do it 32-bits at a time
+ memset(data, 0, 64 * sizeof(data[0]));
+
+ diff = t ? stbi__extend_receive(j, t) : 0;
+ if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff))
+ return stbi__err("bad delta", "Corrupt JPEG");
+ dc = j->img_comp[b].dc_pred + diff;
+ j->img_comp[b].dc_pred = dc;
+ if (!stbi__mul2shorts_valid(dc, dequant[0]))
+ return stbi__err("can't merge dc and ac", "Corrupt JPEG");
+ data[0] = (short)(dc * dequant[0]);
+
+ // decode AC components, see JPEG spec
+ k = 1;
+ do {
+ unsigned int zig;
+ int c, r, s;
+ if (j->code_bits < 16)
+ stbi__grow_buffer_unsafe(j);
+ c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS) - 1);
+ r = fac[c];
+ if (r) { // fast-AC path
+ k += (r >> 4) & 15; // run
+ s = r & 15; // combined length
+ if (s > j->code_bits)
+ return stbi__err("bad huffman code", "Combined length longer than code bits available");
+ j->code_buffer <<= s;
+ j->code_bits -= s;
+ // decode into unzigzag'd location
+ zig = stbi__jpeg_dezigzag[k++];
+ data[zig] = (short)((r >> 8) * dequant[zig]);
+ } else {
+ int rs = stbi__jpeg_huff_decode(j, hac);
+ if (rs < 0)
+ return stbi__err("bad huffman code", "Corrupt JPEG");
+ s = rs & 15;
+ r = rs >> 4;
+ if (s == 0) {
+ if (rs != 0xf0)
+ break; // end block
+ k += 16;
+ } else {
+ k += r;
+ // decode into unzigzag'd location
+ zig = stbi__jpeg_dezigzag[k++];
+ data[zig] = (short)(stbi__extend_receive(j, s) * dequant[zig]);
+ }
+ }
+ } while (k < 64);
+ return 1;
+}
+
+static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg* j, short data[64], stbi__huffman* hdc, int b)
+{
+ int diff, dc;
+ int t;
+ if (j->spec_end != 0)
+ return stbi__err("can't merge dc and ac", "Corrupt JPEG");
+
+ if (j->code_bits < 16)
+ stbi__grow_buffer_unsafe(j);
+
+ if (j->succ_high == 0) {
+ // first scan for DC coefficient, must be first
+ memset(data, 0, 64 * sizeof(data[0])); // 0 all the ac values now
+ t = stbi__jpeg_huff_decode(j, hdc);
+ if (t < 0 || t > 15)
+ return stbi__err("can't merge dc and ac", "Corrupt JPEG");
+ diff = t ? stbi__extend_receive(j, t) : 0;
+
+ if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff))
+ return stbi__err("bad delta", "Corrupt JPEG");
+ dc = j->img_comp[b].dc_pred + diff;
+ j->img_comp[b].dc_pred = dc;
+ if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low))
+ return stbi__err("can't merge dc and ac", "Corrupt JPEG");
+ data[0] = (short)(dc * (1 << j->succ_low));
+ } else {
+ // refinement scan for DC coefficient
+ if (stbi__jpeg_get_bit(j))
+ data[0] += (short)(1 << j->succ_low);
+ }
+ return 1;
+}
+
+// @OPTIMIZE: store non-zigzagged during the decode passes,
+// and only de-zigzag when dequantizing
+static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg* j, short data[64], stbi__huffman* hac, stbi__int16* fac)
+{
+ int k;
+ if (j->spec_start == 0)
+ return stbi__err("can't merge dc and ac", "Corrupt JPEG");
+
+ if (j->succ_high == 0) {
+ int shift = j->succ_low;
+
+ if (j->eob_run) {
+ --j->eob_run;
+ return 1;
+ }
+
+ k = j->spec_start;
+ do {
+ unsigned int zig;
+ int c, r, s;
+ if (j->code_bits < 16)
+ stbi__grow_buffer_unsafe(j);
+ c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS) - 1);
+ r = fac[c];
+ if (r) { // fast-AC path
+ k += (r >> 4) & 15; // run
+ s = r & 15; // combined length
+ if (s > j->code_bits)
+ return stbi__err("bad huffman code", "Combined length longer than code bits available");
+ j->code_buffer <<= s;
+ j->code_bits -= s;
+ zig = stbi__jpeg_dezigzag[k++];
+ data[zig] = (short)((r >> 8) * (1 << shift));
+ } else {
+ int rs = stbi__jpeg_huff_decode(j, hac);
+ if (rs < 0)
+ return stbi__err("bad huffman code", "Corrupt JPEG");
+ s = rs & 15;
+ r = rs >> 4;
+ if (s == 0) {
+ if (r < 15) {
+ j->eob_run = (1 << r);
+ if (r)
+ j->eob_run += stbi__jpeg_get_bits(j, r);
+ --j->eob_run;
+ break;
+ }
+ k += 16;
+ } else {
+ k += r;
+ zig = stbi__jpeg_dezigzag[k++];
+ data[zig] = (short)(stbi__extend_receive(j, s) * (1 << shift));
+ }
+ }
+ } while (k <= j->spec_end);
+ } else {
+ // refinement scan for these AC coefficients
+
+ short bit = (short)(1 << j->succ_low);
+
+ if (j->eob_run) {
+ --j->eob_run;
+ for (k = j->spec_start; k <= j->spec_end; ++k) {
+ short* p = &data[stbi__jpeg_dezigzag[k]];
+ if (*p != 0)
+ if (stbi__jpeg_get_bit(j))
+ if ((*p & bit) == 0) {
+ if (*p > 0)
+ *p += bit;
+ else
+ *p -= bit;
+ }
+ }
+ } else {
+ k = j->spec_start;
+ do {
+ int r, s;
+ int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh
+ if (rs < 0)
+ return stbi__err("bad huffman code", "Corrupt JPEG");
+ s = rs & 15;
+ r = rs >> 4;
+ if (s == 0) {
+ if (r < 15) {
+ j->eob_run = (1 << r) - 1;
+ if (r)
+ j->eob_run += stbi__jpeg_get_bits(j, r);
+ r = 64; // force end of block
+ } else {
+ // r=15 s=0 should write 16 0s, so we just do
+ // a run of 15 0s and then write s (which is 0),
+ // so we don't have to do anything special here
+ }
+ } else {
+ if (s != 1)
+ return stbi__err("bad huffman code", "Corrupt JPEG");
+ // sign bit
+ if (stbi__jpeg_get_bit(j))
+ s = bit;
+ else
+ s = -bit;
+ }
+
+ // advance by r
+ while (k <= j->spec_end) {
+ short* p = &data[stbi__jpeg_dezigzag[k++]];
+ if (*p != 0) {
+ if (stbi__jpeg_get_bit(j))
+ if ((*p & bit) == 0) {
+ if (*p > 0)
+ *p += bit;
+ else
+ *p -= bit;
+ }
+ } else {
+ if (r == 0) {
+ *p = (short)s;
+ break;
+ }
+ --r;
+ }
+ }
+ } while (k <= j->spec_end);
+ }
+ }
+ return 1;
+}
+
+// take a -128..127 value and stbi__clamp it and convert to 0..255
+stbi_inline static stbi_uc stbi__clamp(int x)
+{
+ // trick to use a single test to catch both cases
+ if ((unsigned int)x > 255) {
+ if (x < 0)
+ return 0;
+ if (x > 255)
+ return 255;
+ }
+ return (stbi_uc)x;
+}
+
+# define stbi__f2f(x) ((int)(((x) * 4096 + 0.5)))
+# define stbi__fsh(x) ((x) * 4096)
+
+// derived from jidctint -- DCT_ISLOW
+# define STBI__IDCT_1D(s0, s1, s2, s3, s4, s5, s6, s7) \
+ int t0, t1, t2, t3, p1, p2, p3, p4, p5, x0, x1, x2, x3; \
+ p2 = s2; \
+ p3 = s6; \
+ p1 = (p2 + p3) * stbi__f2f(0.5411961f); \
+ t2 = p1 + p3 * stbi__f2f(-1.847759065f); \
+ t3 = p1 + p2 * stbi__f2f(0.765366865f); \
+ p2 = s0; \
+ p3 = s4; \
+ t0 = stbi__fsh(p2 + p3); \
+ t1 = stbi__fsh(p2 - p3); \
+ x0 = t0 + t3; \
+ x3 = t0 - t3; \
+ x1 = t1 + t2; \
+ x2 = t1 - t2; \
+ t0 = s7; \
+ t1 = s5; \
+ t2 = s3; \
+ t3 = s1; \
+ p3 = t0 + t2; \
+ p4 = t1 + t3; \
+ p1 = t0 + t3; \
+ p2 = t1 + t2; \
+ p5 = (p3 + p4) * stbi__f2f(1.175875602f); \
+ t0 = t0 * stbi__f2f(0.298631336f); \
+ t1 = t1 * stbi__f2f(2.053119869f); \
+ t2 = t2 * stbi__f2f(3.072711026f); \
+ t3 = t3 * stbi__f2f(1.501321110f); \
+ p1 = p5 + p1 * stbi__f2f(-0.899976223f); \
+ p2 = p5 + p2 * stbi__f2f(-2.562915447f); \
+ p3 = p3 * stbi__f2f(-1.961570560f); \
+ p4 = p4 * stbi__f2f(-0.390180644f); \
+ t3 += p1 + p4; \
+ t2 += p2 + p3; \
+ t1 += p2 + p4; \
+ t0 += p1 + p3;
+
+static void stbi__idct_block(stbi_uc* out, int out_stride, short data[64])
+{
+ int i, val[64], *v = val;
+ stbi_uc* o;
+ short* d = data;
+
+ // columns
+ for (i = 0; i < 8; ++i, ++d, ++v) {
+ // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing
+ if (d[8] == 0 && d[16] == 0 && d[24] == 0 && d[32] == 0
+ && d[40] == 0 && d[48] == 0 && d[56] == 0) {
+ // no shortcut 0 seconds
+ // (1|2|3|4|5|6|7)==0 0 seconds
+ // all separate -0.047 seconds
+ // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds
+ int dcterm = d[0] * 4;
+ v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;
+ } else {
+ STBI__IDCT_1D(d[0], d[8], d[16], d[24], d[32], d[40], d[48], d[56])
+ // constants scaled things up by 1<<12; let's bring them back
+ // down, but keep 2 extra bits of precision
+ x0 += 512;
+ x1 += 512;
+ x2 += 512;
+ x3 += 512;
+ v[0] = (x0 + t3) >> 10;
+ v[56] = (x0 - t3) >> 10;
+ v[8] = (x1 + t2) >> 10;
+ v[48] = (x1 - t2) >> 10;
+ v[16] = (x2 + t1) >> 10;
+ v[40] = (x2 - t1) >> 10;
+ v[24] = (x3 + t0) >> 10;
+ v[32] = (x3 - t0) >> 10;
+ }
+ }
+
+ for (i = 0, v = val, o = out; i < 8; ++i, v += 8, o += out_stride) {
+ // no fast case since the first 1D IDCT spread components out
+ STBI__IDCT_1D(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7])
+ // constants scaled things up by 1<<12, plus we had 1<<2 from first
+ // loop, plus horizontal and vertical each scale by sqrt(8) so together
+ // we've got an extra 1<<3, so 1<<17 total we need to remove.
+ // so we want to round that, which means adding 0.5 * 1<<17,
+ // aka 65536. Also, we'll end up with -128 to 127 that we want
+ // to encode as 0..255 by adding 128, so we'll add that before the shift
+ x0 += 65536 + (128 << 17);
+ x1 += 65536 + (128 << 17);
+ x2 += 65536 + (128 << 17);
+ x3 += 65536 + (128 << 17);
+ // tried computing the shifts into temps, or'ing the temps to see
+ // if any were out of range, but that was slower
+ o[0] = stbi__clamp((x0 + t3) >> 17);
+ o[7] = stbi__clamp((x0 - t3) >> 17);
+ o[1] = stbi__clamp((x1 + t2) >> 17);
+ o[6] = stbi__clamp((x1 - t2) >> 17);
+ o[2] = stbi__clamp((x2 + t1) >> 17);
+ o[5] = stbi__clamp((x2 - t1) >> 17);
+ o[3] = stbi__clamp((x3 + t0) >> 17);
+ o[4] = stbi__clamp((x3 - t0) >> 17);
+ }
+}
+
+# ifdef STBI_SSE2
+// sse2 integer IDCT. not the fastest possible implementation but it
+// produces bit-identical results to the generic C version so it's
+// fully "transparent".
+static void stbi__idct_simd(stbi_uc* out, int out_stride, short data[64])
+{
+ // This is constructed to match our regular (generic) integer IDCT exactly.
+ __m128i row0, row1, row2, row3, row4, row5, row6, row7;
+ __m128i tmp;
+
+// dot product constant: even elems=x, odd elems=y
+# define dct_const(x, y) _mm_setr_epi16((x), (y), (x), (y), (x), (y), (x), (y))
+
+// out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit)
+// out(1) = c1[even]*x + c1[odd]*y
+# define dct_rot(out0, out1, x, y, c0, c1) \
+ __m128i c0##lo = _mm_unpacklo_epi16((x), (y)); \
+ __m128i c0##hi = _mm_unpackhi_epi16((x), (y)); \
+ __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \
+ __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \
+ __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \
+ __m128i out1##_h = _mm_madd_epi16(c0##hi, c1)
+
+// out = in << 12 (in 16-bit, out 32-bit)
+# define dct_widen(out, in) \
+ __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \
+ __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4)
+
+// wide add
+# define dct_wadd(out, a, b) \
+ __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \
+ __m128i out##_h = _mm_add_epi32(a##_h, b##_h)
+
+// wide sub
+# define dct_wsub(out, a, b) \
+ __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \
+ __m128i out##_h = _mm_sub_epi32(a##_h, b##_h)
+
+// butterfly a/b, add bias, then shift by "s" and pack
+# define dct_bfly32o(out0, out1, a, b, bias, s) \
+ { \
+ __m128i abiased_l = _mm_add_epi32(a##_l, bias); \
+ __m128i abiased_h = _mm_add_epi32(a##_h, bias); \
+ dct_wadd(sum, abiased, b); \
+ dct_wsub(dif, abiased, b); \
+ out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \
+ out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \
+ }
+
+// 8-bit interleave step (for transposes)
+# define dct_interleave8(a, b) \
+ tmp = a; \
+ a = _mm_unpacklo_epi8(a, b); \
+ b = _mm_unpackhi_epi8(tmp, b)
+
+// 16-bit interleave step (for transposes)
+# define dct_interleave16(a, b) \
+ tmp = a; \
+ a = _mm_unpacklo_epi16(a, b); \
+ b = _mm_unpackhi_epi16(tmp, b)
+
+# define dct_pass(bias, shift) \
+ { \
+ /* even part */ \
+ dct_rot(t2e, t3e, row2, row6, rot0_0, rot0_1); \
+ __m128i sum04 = _mm_add_epi16(row0, row4); \
+ __m128i dif04 = _mm_sub_epi16(row0, row4); \
+ dct_widen(t0e, sum04); \
+ dct_widen(t1e, dif04); \
+ dct_wadd(x0, t0e, t3e); \
+ dct_wsub(x3, t0e, t3e); \
+ dct_wadd(x1, t1e, t2e); \
+ dct_wsub(x2, t1e, t2e); \
+ /* odd part */ \
+ dct_rot(y0o, y2o, row7, row3, rot2_0, rot2_1); \
+ dct_rot(y1o, y3o, row5, row1, rot3_0, rot3_1); \
+ __m128i sum17 = _mm_add_epi16(row1, row7); \
+ __m128i sum35 = _mm_add_epi16(row3, row5); \
+ dct_rot(y4o, y5o, sum17, sum35, rot1_0, rot1_1); \
+ dct_wadd(x4, y0o, y4o); \
+ dct_wadd(x5, y1o, y5o); \
+ dct_wadd(x6, y2o, y5o); \
+ dct_wadd(x7, y3o, y4o); \
+ dct_bfly32o(row0, row7, x0, x7, bias, shift); \
+ dct_bfly32o(row1, row6, x1, x6, bias, shift); \
+ dct_bfly32o(row2, row5, x2, x5, bias, shift); \
+ dct_bfly32o(row3, row4, x3, x4, bias, shift); \
+ }
+
+ __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f));
+ __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f(0.765366865f), stbi__f2f(0.5411961f));
+ __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f));
+ __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f));
+ __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f(0.298631336f), stbi__f2f(-1.961570560f));
+ __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f(3.072711026f));
+ __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f(2.053119869f), stbi__f2f(-0.390180644f));
+ __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f(1.501321110f));
+
+ // rounding biases in column/row passes, see stbi__idct_block for explanation.
+ __m128i bias_0 = _mm_set1_epi32(512);
+ __m128i bias_1 = _mm_set1_epi32(65536 + (128 << 17));
+
+ // load
+ row0 = _mm_load_si128((__m128i const*)(data + 0 * 8));
+ row1 = _mm_load_si128((__m128i const*)(data + 1 * 8));
+ row2 = _mm_load_si128((__m128i const*)(data + 2 * 8));
+ row3 = _mm_load_si128((__m128i const*)(data + 3 * 8));
+ row4 = _mm_load_si128((__m128i const*)(data + 4 * 8));
+ row5 = _mm_load_si128((__m128i const*)(data + 5 * 8));
+ row6 = _mm_load_si128((__m128i const*)(data + 6 * 8));
+ row7 = _mm_load_si128((__m128i const*)(data + 7 * 8));
+
+ // column pass
+ dct_pass(bias_0, 10);
+
+ {
+ // 16bit 8x8 transpose pass 1
+ dct_interleave16(row0, row4);
+ dct_interleave16(row1, row5);
+ dct_interleave16(row2, row6);
+ dct_interleave16(row3, row7);
+
+ // transpose pass 2
+ dct_interleave16(row0, row2);
+ dct_interleave16(row1, row3);
+ dct_interleave16(row4, row6);
+ dct_interleave16(row5, row7);
+
+ // transpose pass 3
+ dct_interleave16(row0, row1);
+ dct_interleave16(row2, row3);
+ dct_interleave16(row4, row5);
+ dct_interleave16(row6, row7);
+ }
+
+ // row pass
+ dct_pass(bias_1, 17);
+
+ {
+ // pack
+ __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7
+ __m128i p1 = _mm_packus_epi16(row2, row3);
+ __m128i p2 = _mm_packus_epi16(row4, row5);
+ __m128i p3 = _mm_packus_epi16(row6, row7);
+
+ // 8bit 8x8 transpose pass 1
+ dct_interleave8(p0, p2); // a0e0a1e1...
+ dct_interleave8(p1, p3); // c0g0c1g1...
+
+ // transpose pass 2
+ dct_interleave8(p0, p1); // a0c0e0g0...
+ dct_interleave8(p2, p3); // b0d0f0h0...
+
+ // transpose pass 3
+ dct_interleave8(p0, p2); // a0b0c0d0...
+ dct_interleave8(p1, p3); // a4b4c4d4...
+
+ // store
+ _mm_storel_epi64((__m128i*)out, p0);
+ out += out_stride;
+ _mm_storel_epi64((__m128i*)out, _mm_shuffle_epi32(p0, 0x4e));
+ out += out_stride;
+ _mm_storel_epi64((__m128i*)out, p2);
+ out += out_stride;
+ _mm_storel_epi64((__m128i*)out, _mm_shuffle_epi32(p2, 0x4e));
+ out += out_stride;
+ _mm_storel_epi64((__m128i*)out, p1);
+ out += out_stride;
+ _mm_storel_epi64((__m128i*)out, _mm_shuffle_epi32(p1, 0x4e));
+ out += out_stride;
+ _mm_storel_epi64((__m128i*)out, p3);
+ out += out_stride;
+ _mm_storel_epi64((__m128i*)out, _mm_shuffle_epi32(p3, 0x4e));
+ }
+
+# undef dct_const
+# undef dct_rot
+# undef dct_widen
+# undef dct_wadd
+# undef dct_wsub
+# undef dct_bfly32o
+# undef dct_interleave8
+# undef dct_interleave16
+# undef dct_pass
+}
+
+# endif // STBI_SSE2
+
+# ifdef STBI_NEON
+
+// NEON integer IDCT. should produce bit-identical
+// results to the generic C version.
+static void stbi__idct_simd(stbi_uc* out, int out_stride, short data[64])
+{
+ int16x8_t row0, row1, row2, row3, row4, row5, row6, row7;
+
+ int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f));
+ int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f));
+ int16x4_t rot0_2 = vdup_n_s16(stbi__f2f(0.765366865f));
+ int16x4_t rot1_0 = vdup_n_s16(stbi__f2f(1.175875602f));
+ int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f));
+ int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f));
+ int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f));
+ int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f));
+ int16x4_t rot3_0 = vdup_n_s16(stbi__f2f(0.298631336f));
+ int16x4_t rot3_1 = vdup_n_s16(stbi__f2f(2.053119869f));
+ int16x4_t rot3_2 = vdup_n_s16(stbi__f2f(3.072711026f));
+ int16x4_t rot3_3 = vdup_n_s16(stbi__f2f(1.501321110f));
+
+# define dct_long_mul(out, inq, coeff) \
+ int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \
+ int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff)
+
+# define dct_long_mac(out, acc, inq, coeff) \
+ int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \
+ int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff)
+
+# define dct_widen(out, inq) \
+ int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \
+ int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12)
+
+// wide add
+# define dct_wadd(out, a, b) \
+ int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \
+ int32x4_t out##_h = vaddq_s32(a##_h, b##_h)
+
+// wide sub
+# define dct_wsub(out, a, b) \
+ int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \
+ int32x4_t out##_h = vsubq_s32(a##_h, b##_h)
+
+// butterfly a/b, then shift using "shiftop" by "s" and pack
+# define dct_bfly32o(out0, out1, a, b, shiftop, s) \
+ { \
+ dct_wadd(sum, a, b); \
+ dct_wsub(dif, a, b); \
+ out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \
+ out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \
+ }
+
+# define dct_pass(shiftop, shift) \
+ { \
+ /* even part */ \
+ int16x8_t sum26 = vaddq_s16(row2, row6); \
+ dct_long_mul(p1e, sum26, rot0_0); \
+ dct_long_mac(t2e, p1e, row6, rot0_1); \
+ dct_long_mac(t3e, p1e, row2, rot0_2); \
+ int16x8_t sum04 = vaddq_s16(row0, row4); \
+ int16x8_t dif04 = vsubq_s16(row0, row4); \
+ dct_widen(t0e, sum04); \
+ dct_widen(t1e, dif04); \
+ dct_wadd(x0, t0e, t3e); \
+ dct_wsub(x3, t0e, t3e); \
+ dct_wadd(x1, t1e, t2e); \
+ dct_wsub(x2, t1e, t2e); \
+ /* odd part */ \
+ int16x8_t sum15 = vaddq_s16(row1, row5); \
+ int16x8_t sum17 = vaddq_s16(row1, row7); \
+ int16x8_t sum35 = vaddq_s16(row3, row5); \
+ int16x8_t sum37 = vaddq_s16(row3, row7); \
+ int16x8_t sumodd = vaddq_s16(sum17, sum35); \
+ dct_long_mul(p5o, sumodd, rot1_0); \
+ dct_long_mac(p1o, p5o, sum17, rot1_1); \
+ dct_long_mac(p2o, p5o, sum35, rot1_2); \
+ dct_long_mul(p3o, sum37, rot2_0); \
+ dct_long_mul(p4o, sum15, rot2_1); \
+ dct_wadd(sump13o, p1o, p3o); \
+ dct_wadd(sump24o, p2o, p4o); \
+ dct_wadd(sump23o, p2o, p3o); \
+ dct_wadd(sump14o, p1o, p4o); \
+ dct_long_mac(x4, sump13o, row7, rot3_0); \
+ dct_long_mac(x5, sump24o, row5, rot3_1); \
+ dct_long_mac(x6, sump23o, row3, rot3_2); \
+ dct_long_mac(x7, sump14o, row1, rot3_3); \
+ dct_bfly32o(row0, row7, x0, x7, shiftop, shift); \
+ dct_bfly32o(row1, row6, x1, x6, shiftop, shift); \
+ dct_bfly32o(row2, row5, x2, x5, shiftop, shift); \
+ dct_bfly32o(row3, row4, x3, x4, shiftop, shift); \
+ }
+
+ // load
+ row0 = vld1q_s16(data + 0 * 8);
+ row1 = vld1q_s16(data + 1 * 8);
+ row2 = vld1q_s16(data + 2 * 8);
+ row3 = vld1q_s16(data + 3 * 8);
+ row4 = vld1q_s16(data + 4 * 8);
+ row5 = vld1q_s16(data + 5 * 8);
+ row6 = vld1q_s16(data + 6 * 8);
+ row7 = vld1q_s16(data + 7 * 8);
+
+ // add DC bias
+ row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0));
+
+ // column pass
+ dct_pass(vrshrn_n_s32, 10);
+
+ // 16bit 8x8 transpose
+ {
+// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively.
+// whether compilers actually get this is another story, sadly.
+# define dct_trn16(x, y) \
+ { \
+ int16x8x2_t t = vtrnq_s16(x, y); \
+ x = t.val[0]; \
+ y = t.val[1]; \
+ }
+# define dct_trn32(x, y) \
+ { \
+ int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); \
+ x = vreinterpretq_s16_s32(t.val[0]); \
+ y = vreinterpretq_s16_s32(t.val[1]); \
+ }
+# define dct_trn64(x, y) \
+ { \
+ int16x8_t x0 = x; \
+ int16x8_t y0 = y; \
+ x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); \
+ y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); \
+ }
+
+ // pass 1
+ dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6
+ dct_trn16(row2, row3);
+ dct_trn16(row4, row5);
+ dct_trn16(row6, row7);
+
+ // pass 2
+ dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4
+ dct_trn32(row1, row3);
+ dct_trn32(row4, row6);
+ dct_trn32(row5, row7);
+
+ // pass 3
+ dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0
+ dct_trn64(row1, row5);
+ dct_trn64(row2, row6);
+ dct_trn64(row3, row7);
+
+# undef dct_trn16
+# undef dct_trn32
+# undef dct_trn64
+ }
+
+ // row pass
+ // vrshrn_n_s32 only supports shifts up to 16, we need
+ // 17. so do a non-rounding shift of 16 first then follow
+ // up with a rounding shift by 1.
+ dct_pass(vshrn_n_s32, 16);
+
+ {
+ // pack and round
+ uint8x8_t p0 = vqrshrun_n_s16(row0, 1);
+ uint8x8_t p1 = vqrshrun_n_s16(row1, 1);
+ uint8x8_t p2 = vqrshrun_n_s16(row2, 1);
+ uint8x8_t p3 = vqrshrun_n_s16(row3, 1);
+ uint8x8_t p4 = vqrshrun_n_s16(row4, 1);
+ uint8x8_t p5 = vqrshrun_n_s16(row5, 1);
+ uint8x8_t p6 = vqrshrun_n_s16(row6, 1);
+ uint8x8_t p7 = vqrshrun_n_s16(row7, 1);
+
+ // again, these can translate into one instruction, but often don't.
+# define dct_trn8_8(x, y) \
+ { \
+ uint8x8x2_t t = vtrn_u8(x, y); \
+ x = t.val[0]; \
+ y = t.val[1]; \
+ }
+# define dct_trn8_16(x, y) \
+ { \
+ uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); \
+ x = vreinterpret_u8_u16(t.val[0]); \
+ y = vreinterpret_u8_u16(t.val[1]); \
+ }
+# define dct_trn8_32(x, y) \
+ { \
+ uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); \
+ x = vreinterpret_u8_u32(t.val[0]); \
+ y = vreinterpret_u8_u32(t.val[1]); \
+ }
+
+ // sadly can't use interleaved stores here since we only write
+ // 8 bytes to each scan line!
+
+ // 8x8 8-bit transpose pass 1
+ dct_trn8_8(p0, p1);
+ dct_trn8_8(p2, p3);
+ dct_trn8_8(p4, p5);
+ dct_trn8_8(p6, p7);
+
+ // pass 2
+ dct_trn8_16(p0, p2);
+ dct_trn8_16(p1, p3);
+ dct_trn8_16(p4, p6);
+ dct_trn8_16(p5, p7);
+
+ // pass 3
+ dct_trn8_32(p0, p4);
+ dct_trn8_32(p1, p5);
+ dct_trn8_32(p2, p6);
+ dct_trn8_32(p3, p7);
+
+ // store
+ vst1_u8(out, p0);
+ out += out_stride;
+ vst1_u8(out, p1);
+ out += out_stride;
+ vst1_u8(out, p2);
+ out += out_stride;
+ vst1_u8(out, p3);
+ out += out_stride;
+ vst1_u8(out, p4);
+ out += out_stride;
+ vst1_u8(out, p5);
+ out += out_stride;
+ vst1_u8(out, p6);
+ out += out_stride;
+ vst1_u8(out, p7);
+
+# undef dct_trn8_8
+# undef dct_trn8_16
+# undef dct_trn8_32
+ }
+
+# undef dct_long_mul
+# undef dct_long_mac
+# undef dct_widen
+# undef dct_wadd
+# undef dct_wsub
+# undef dct_bfly32o
+# undef dct_pass
+}
+
+# endif // STBI_NEON
+
+# define STBI__MARKER_none 0xff
+// if there's a pending marker from the entropy stream, return that
+// otherwise, fetch from the stream and get a marker. if there's no
+// marker, return 0xff, which is never a valid marker value
+static stbi_uc stbi__get_marker(stbi__jpeg* j)
+{
+ stbi_uc x;
+ if (j->marker != STBI__MARKER_none) {
+ x = j->marker;
+ j->marker = STBI__MARKER_none;
+ return x;
+ }
+ x = stbi__get8(j->s);
+ if (x != 0xff)
+ return STBI__MARKER_none;
+ while (x == 0xff)
+ x = stbi__get8(j->s); // consume repeated 0xff fill bytes
+ return x;
+}
+
+// in each scan, we'll have scan_n components, and the order
+// of the components is specified by order[]
+# define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7)
+
+// after a restart interval, stbi__jpeg_reset the entropy decoder and
+// the dc prediction
+static void stbi__jpeg_reset(stbi__jpeg* j)
+{
+ j->code_bits = 0;
+ j->code_buffer = 0;
+ j->nomore = 0;
+ j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0;
+ j->marker = STBI__MARKER_none;
+ j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff;
+ j->eob_run = 0;
+ // no more than 1<<31 MCUs if no restart_interal? that's plenty safe,
+ // since we don't even allow 1<<30 pixels
+}
+
+static int stbi__parse_entropy_coded_data(stbi__jpeg* z)
+{
+ stbi__jpeg_reset(z);
+ if (!z->progressive) {
+ if (z->scan_n == 1) {
+ int i, j;
+ STBI_SIMD_ALIGN(short, data[64]);
+ int n = z->order[0];
+ // non-interleaved data, we just need to process one block at a time,
+ // in trivial scanline order
+ // number of blocks to do just depends on how many actual "pixels" this
+ // component has, independent of interleaved MCU blocking and such
+ int w = (z->img_comp[n].x + 7) >> 3;
+ int h = (z->img_comp[n].y + 7) >> 3;
+ for (j = 0; j < h; ++j) {
+ for (i = 0; i < w; ++i) {
+ int ha = z->img_comp[n].ha;
+ if (!stbi__jpeg_decode_block(z, data, z->huff_dc + z->img_comp[n].hd, z->huff_ac + ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq]))
+ return 0;
+ z->idct_block_kernel(z->img_comp[n].data + z->img_comp[n].w2 * j * 8 + i * 8, z->img_comp[n].w2, data);
+ // every data block is an MCU, so countdown the restart interval
+ if (--z->todo <= 0) {
+ if (z->code_bits < 24)
+ stbi__grow_buffer_unsafe(z);
+ // if it's NOT a restart, then just bail, so we get corrupt data
+ // rather than no data
+ if (!STBI__RESTART(z->marker))
+ return 1;
+ stbi__jpeg_reset(z);
+ }
+ }
+ }
+ return 1;
+ } else { // interleaved
+ int i, j, k, x, y;
+ STBI_SIMD_ALIGN(short, data[64]);
+ for (j = 0; j < z->img_mcu_y; ++j) {
+ for (i = 0; i < z->img_mcu_x; ++i) {
+ // scan an interleaved mcu... process scan_n components in order
+ for (k = 0; k < z->scan_n; ++k) {
+ int n = z->order[k];
+ // scan out an mcu's worth of this component; that's just determined
+ // by the basic H and V specified for the component
+ for (y = 0; y < z->img_comp[n].v; ++y) {
+ for (x = 0; x < z->img_comp[n].h; ++x) {
+ int x2 = (i * z->img_comp[n].h + x) * 8;
+ int y2 = (j * z->img_comp[n].v + y) * 8;
+ int ha = z->img_comp[n].ha;
+ if (!stbi__jpeg_decode_block(z, data, z->huff_dc + z->img_comp[n].hd, z->huff_ac + ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq]))
+ return 0;
+ z->idct_block_kernel(z->img_comp[n].data + z->img_comp[n].w2 * y2 + x2, z->img_comp[n].w2, data);
+ }
+ }
+ }
+ // after all interleaved components, that's an interleaved MCU,
+ // so now count down the restart interval
+ if (--z->todo <= 0) {
+ if (z->code_bits < 24)
+ stbi__grow_buffer_unsafe(z);
+ if (!STBI__RESTART(z->marker))
+ return 1;
+ stbi__jpeg_reset(z);
+ }
+ }
+ }
+ return 1;
+ }
+ } else {
+ if (z->scan_n == 1) {
+ int i, j;
+ int n = z->order[0];
+ // non-interleaved data, we just need to process one block at a time,
+ // in trivial scanline order
+ // number of blocks to do just depends on how many actual "pixels" this
+ // component has, independent of interleaved MCU blocking and such
+ int w = (z->img_comp[n].x + 7) >> 3;
+ int h = (z->img_comp[n].y + 7) >> 3;
+ for (j = 0; j < h; ++j) {
+ for (i = 0; i < w; ++i) {
+ short* data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);
+ if (z->spec_start == 0) {
+ if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))
+ return 0;
+ } else {
+ int ha = z->img_comp[n].ha;
+ if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha]))
+ return 0;
+ }
+ // every data block is an MCU, so countdown the restart interval
+ if (--z->todo <= 0) {
+ if (z->code_bits < 24)
+ stbi__grow_buffer_unsafe(z);
+ if (!STBI__RESTART(z->marker))
+ return 1;
+ stbi__jpeg_reset(z);
+ }
+ }
+ }
+ return 1;
+ } else { // interleaved
+ int i, j, k, x, y;
+ for (j = 0; j < z->img_mcu_y; ++j) {
+ for (i = 0; i < z->img_mcu_x; ++i) {
+ // scan an interleaved mcu... process scan_n components in order
+ for (k = 0; k < z->scan_n; ++k) {
+ int n = z->order[k];
+ // scan out an mcu's worth of this component; that's just determined
+ // by the basic H and V specified for the component
+ for (y = 0; y < z->img_comp[n].v; ++y) {
+ for (x = 0; x < z->img_comp[n].h; ++x) {
+ int x2 = (i * z->img_comp[n].h + x);
+ int y2 = (j * z->img_comp[n].v + y);
+ short* data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w);
+ if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))
+ return 0;
+ }
+ }
+ }
+ // after all interleaved components, that's an interleaved MCU,
+ // so now count down the restart interval
+ if (--z->todo <= 0) {
+ if (z->code_bits < 24)
+ stbi__grow_buffer_unsafe(z);
+ if (!STBI__RESTART(z->marker))
+ return 1;
+ stbi__jpeg_reset(z);
+ }
+ }
+ }
+ return 1;
+ }
+ }
+}
+
+static void stbi__jpeg_dequantize(short* data, stbi__uint16* dequant)
+{
+ int i;
+ for (i = 0; i < 64; ++i)
+ data[i] *= dequant[i];
+}
+
+static void stbi__jpeg_finish(stbi__jpeg* z)
+{
+ if (z->progressive) {
+ // dequantize and idct the data
+ int i, j, n;
+ for (n = 0; n < z->s->img_n; ++n) {
+ int w = (z->img_comp[n].x + 7) >> 3;
+ int h = (z->img_comp[n].y + 7) >> 3;
+ for (j = 0; j < h; ++j) {
+ for (i = 0; i < w; ++i) {
+ short* data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);
+ stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]);
+ z->idct_block_kernel(z->img_comp[n].data + z->img_comp[n].w2 * j * 8 + i * 8, z->img_comp[n].w2, data);
+ }
+ }
+ }
+ }
+}
+
+static int stbi__process_marker(stbi__jpeg* z, int m)
+{
+ int L;
+ switch (m) {
+ case STBI__MARKER_none: // no marker found
+ return stbi__err("expected marker", "Corrupt JPEG");
+
+ case 0xDD: // DRI - specify restart interval
+ if (stbi__get16be(z->s) != 4)
+ return stbi__err("bad DRI len", "Corrupt JPEG");
+ z->restart_interval = stbi__get16be(z->s);
+ return 1;
+
+ case 0xDB: // DQT - define quantization table
+ L = stbi__get16be(z->s) - 2;
+ while (L > 0) {
+ int q = stbi__get8(z->s);
+ int p = q >> 4, sixteen = (p != 0);
+ int t = q & 15, i;
+ if (p != 0 && p != 1)
+ return stbi__err("bad DQT type", "Corrupt JPEG");
+ if (t > 3)
+ return stbi__err("bad DQT table", "Corrupt JPEG");
+
+ for (i = 0; i < 64; ++i)
+ z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s));
+ L -= (sixteen ? 129 : 65);
+ }
+ return L == 0;
+
+ case 0xC4: // DHT - define huffman table
+ L = stbi__get16be(z->s) - 2;
+ while (L > 0) {
+ stbi_uc* v;
+ int sizes[16], i, n = 0;
+ int q = stbi__get8(z->s);
+ int tc = q >> 4;
+ int th = q & 15;
+ if (tc > 1 || th > 3)
+ return stbi__err("bad DHT header", "Corrupt JPEG");
+ for (i = 0; i < 16; ++i) {
+ sizes[i] = stbi__get8(z->s);
+ n += sizes[i];
+ }
+ if (n > 256)
+ return stbi__err("bad DHT header", "Corrupt JPEG"); // Loop over i < n would write past end of values!
+ L -= 17;
+ if (tc == 0) {
+ if (!stbi__build_huffman(z->huff_dc + th, sizes))
+ return 0;
+ v = z->huff_dc[th].values;
+ } else {
+ if (!stbi__build_huffman(z->huff_ac + th, sizes))
+ return 0;
+ v = z->huff_ac[th].values;
+ }
+ for (i = 0; i < n; ++i)
+ v[i] = stbi__get8(z->s);
+ if (tc != 0)
+ stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th);
+ L -= n;
+ }
+ return L == 0;
+ }
+
+ // check for comment block or APP blocks
+ if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) {
+ L = stbi__get16be(z->s);
+ if (L < 2) {
+ if (m == 0xFE)
+ return stbi__err("bad COM len", "Corrupt JPEG");
+ else
+ return stbi__err("bad APP len", "Corrupt JPEG");
+ }
+ L -= 2;
+
+ if (m == 0xE0 && L >= 5) { // JFIF APP0 segment
+ static unsigned char const tag[5] = { 'J', 'F', 'I', 'F', '\0' };
+ int ok = 1;
+ int i;
+ for (i = 0; i < 5; ++i)
+ if (stbi__get8(z->s) != tag[i])
+ ok = 0;
+ L -= 5;
+ if (ok)
+ z->jfif = 1;
+ } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment
+ static unsigned char const tag[6] = { 'A', 'd', 'o', 'b', 'e', '\0' };
+ int ok = 1;
+ int i;
+ for (i = 0; i < 6; ++i)
+ if (stbi__get8(z->s) != tag[i])
+ ok = 0;
+ L -= 6;
+ if (ok) {
+ stbi__get8(z->s); // version
+ stbi__get16be(z->s); // flags0
+ stbi__get16be(z->s); // flags1
+ z->app14_color_transform = stbi__get8(z->s); // color transform
+ L -= 6;
+ }
+ }
+
+ stbi__skip(z->s, L);
+ return 1;
+ }
+
+ return stbi__err("unknown marker", "Corrupt JPEG");
+}
+
+// after we see SOS
+static int stbi__process_scan_header(stbi__jpeg* z)
+{
+ int i;
+ int Ls = stbi__get16be(z->s);
+ z->scan_n = stbi__get8(z->s);
+ if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int)z->s->img_n)
+ return stbi__err("bad SOS component count", "Corrupt JPEG");
+ if (Ls != 6 + 2 * z->scan_n)
+ return stbi__err("bad SOS len", "Corrupt JPEG");
+ for (i = 0; i < z->scan_n; ++i) {
+ int id = stbi__get8(z->s), which;
+ int q = stbi__get8(z->s);
+ for (which = 0; which < z->s->img_n; ++which)
+ if (z->img_comp[which].id == id)
+ break;
+ if (which == z->s->img_n)
+ return 0; // no match
+ z->img_comp[which].hd = q >> 4;
+ if (z->img_comp[which].hd > 3)
+ return stbi__err("bad DC huff", "Corrupt JPEG");
+ z->img_comp[which].ha = q & 15;
+ if (z->img_comp[which].ha > 3)
+ return stbi__err("bad AC huff", "Corrupt JPEG");
+ z->order[i] = which;
+ }
+
+ {
+ int aa;
+ z->spec_start = stbi__get8(z->s);
+ z->spec_end = stbi__get8(z->s); // should be 63, but might be 0
+ aa = stbi__get8(z->s);
+ z->succ_high = (aa >> 4);
+ z->succ_low = (aa & 15);
+ if (z->progressive) {
+ if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13)
+ return stbi__err("bad SOS", "Corrupt JPEG");
+ } else {
+ if (z->spec_start != 0)
+ return stbi__err("bad SOS", "Corrupt JPEG");
+ if (z->succ_high != 0 || z->succ_low != 0)
+ return stbi__err("bad SOS", "Corrupt JPEG");
+ z->spec_end = 63;
+ }
+ }
+
+ return 1;
+}
+
+static int stbi__free_jpeg_components(stbi__jpeg* z, int ncomp, int why)
+{
+ int i;
+ for (i = 0; i < ncomp; ++i) {
+ if (z->img_comp[i].raw_data) {
+ STBI_FREE(z->img_comp[i].raw_data);
+ z->img_comp[i].raw_data = NULL;
+ z->img_comp[i].data = NULL;
+ }
+ if (z->img_comp[i].raw_coeff) {
+ STBI_FREE(z->img_comp[i].raw_coeff);
+ z->img_comp[i].raw_coeff = 0;
+ z->img_comp[i].coeff = 0;
+ }
+ if (z->img_comp[i].linebuf) {
+ STBI_FREE(z->img_comp[i].linebuf);
+ z->img_comp[i].linebuf = NULL;
+ }
+ }
+ return why;
+}
+
+static int stbi__process_frame_header(stbi__jpeg* z, int scan)
+{
+ stbi__context* s = z->s;
+ int Lf, p, i, q, h_max = 1, v_max = 1, c;
+ Lf = stbi__get16be(s);
+ if (Lf < 11)
+ return stbi__err("bad SOF len", "Corrupt JPEG"); // JPEG
+ p = stbi__get8(s);
+ if (p != 8)
+ return stbi__err("only 8-bit", "JPEG format not supported: 8-bit only"); // JPEG baseline
+ s->img_y = stbi__get16be(s);
+ if (s->img_y == 0)
+ return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG
+ s->img_x = stbi__get16be(s);
+ if (s->img_x == 0)
+ return stbi__err("0 width", "Corrupt JPEG"); // JPEG requires
+ if (s->img_y > STBI_MAX_DIMENSIONS)
+ return stbi__err("too large", "Very large image (corrupt?)");
+ if (s->img_x > STBI_MAX_DIMENSIONS)
+ return stbi__err("too large", "Very large image (corrupt?)");
+ c = stbi__get8(s);
+ if (c != 3 && c != 1 && c != 4)
+ return stbi__err("bad component count", "Corrupt JPEG");
+ s->img_n = c;
+ for (i = 0; i < c; ++i) {
+ z->img_comp[i].data = NULL;
+ z->img_comp[i].linebuf = NULL;
+ }
+
+ if (Lf != 8 + 3 * s->img_n)
+ return stbi__err("bad SOF len", "Corrupt JPEG");
+
+ z->rgb = 0;
+ for (i = 0; i < s->img_n; ++i) {
+ static unsigned char const rgb[3] = { 'R', 'G', 'B' };
+ z->img_comp[i].id = stbi__get8(s);
+ if (s->img_n == 3 && z->img_comp[i].id == rgb[i])
+ ++z->rgb;
+ q = stbi__get8(s);
+ z->img_comp[i].h = (q >> 4);
+ if (!z->img_comp[i].h || z->img_comp[i].h > 4)
+ return stbi__err("bad H", "Corrupt JPEG");
+ z->img_comp[i].v = q & 15;
+ if (!z->img_comp[i].v || z->img_comp[i].v > 4)
+ return stbi__err("bad V", "Corrupt JPEG");
+ z->img_comp[i].tq = stbi__get8(s);
+ if (z->img_comp[i].tq > 3)
+ return stbi__err("bad TQ", "Corrupt JPEG");
+ }
+
+ if (scan != STBI__SCAN_load)
+ return 1;
+
+ if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0))
+ return stbi__err("too large", "Image too large to decode");
+
+ for (i = 0; i < s->img_n; ++i) {
+ if (z->img_comp[i].h > h_max)
+ h_max = z->img_comp[i].h;
+ if (z->img_comp[i].v > v_max)
+ v_max = z->img_comp[i].v;
+ }
+
+ // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios
+ // and I've never seen a non-corrupted JPEG file actually use them
+ for (i = 0; i < s->img_n; ++i) {
+ if (h_max % z->img_comp[i].h != 0)
+ return stbi__err("bad H", "Corrupt JPEG");
+ if (v_max % z->img_comp[i].v != 0)
+ return stbi__err("bad V", "Corrupt JPEG");
+ }
+
+ // compute interleaved mcu info
+ z->img_h_max = h_max;
+ z->img_v_max = v_max;
+ z->img_mcu_w = h_max * 8;
+ z->img_mcu_h = v_max * 8;
+ // these sizes can't be more than 17 bits
+ z->img_mcu_x = (s->img_x + z->img_mcu_w - 1) / z->img_mcu_w;
+ z->img_mcu_y = (s->img_y + z->img_mcu_h - 1) / z->img_mcu_h;
+
+ for (i = 0; i < s->img_n; ++i) {
+ // number of effective pixels (e.g. for non-interleaved MCU)
+ z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max - 1) / h_max;
+ z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max - 1) / v_max;
+ // to simplify generation, we'll allocate enough memory to decode
+ // the bogus oversized data from using interleaved MCUs and their
+ // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't
+ // discard the extra data until colorspace conversion
+ //
+ // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier)
+ // so these muls can't overflow with 32-bit ints (which we require)
+ z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8;
+ z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8;
+ z->img_comp[i].coeff = 0;
+ z->img_comp[i].raw_coeff = 0;
+ z->img_comp[i].linebuf = NULL;
+ z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15);
+ if (z->img_comp[i].raw_data == NULL)
+ return stbi__free_jpeg_components(z, i + 1, stbi__err("outofmem", "Out of memory"));
+ // align blocks for idct using mmx/sse
+ z->img_comp[i].data = (stbi_uc*)(((size_t)z->img_comp[i].raw_data + 15) & ~15);
+ if (z->progressive) {
+ // w2, h2 are multiples of 8 (see above)
+ z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8;
+ z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8;
+ z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15);
+ if (z->img_comp[i].raw_coeff == NULL)
+ return stbi__free_jpeg_components(z, i + 1, stbi__err("outofmem", "Out of memory"));
+ z->img_comp[i].coeff = (short*)(((size_t)z->img_comp[i].raw_coeff + 15) & ~15);
+ }
+ }
+
+ return 1;
+}
+
+// use comparisons since in some cases we handle more than one case (e.g. SOF)
+# define stbi__DNL(x) ((x) == 0xdc)
+# define stbi__SOI(x) ((x) == 0xd8)
+# define stbi__EOI(x) ((x) == 0xd9)
+# define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2)
+# define stbi__SOS(x) ((x) == 0xda)
+
+# define stbi__SOF_progressive(x) ((x) == 0xc2)
+
+static int stbi__decode_jpeg_header(stbi__jpeg* z, int scan)
+{
+ int m;
+ z->jfif = 0;
+ z->app14_color_transform = -1; // valid values are 0,1,2
+ z->marker = STBI__MARKER_none; // initialize cached marker to empty
+ m = stbi__get_marker(z);
+ if (!stbi__SOI(m))
+ return stbi__err("no SOI", "Corrupt JPEG");
+ if (scan == STBI__SCAN_type)
+ return 1;
+ m = stbi__get_marker(z);
+ while (!stbi__SOF(m)) {
+ if (!stbi__process_marker(z, m))
+ return 0;
+ m = stbi__get_marker(z);
+ while (m == STBI__MARKER_none) {
+ // some files have extra padding after their blocks, so ok, we'll scan
+ if (stbi__at_eof(z->s))
+ return stbi__err("no SOF", "Corrupt JPEG");
+ m = stbi__get_marker(z);
+ }
+ }
+ z->progressive = stbi__SOF_progressive(m);
+ if (!stbi__process_frame_header(z, scan))
+ return 0;
+ return 1;
+}
+
+static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg* j)
+{
+ // some JPEGs have junk at end, skip over it but if we find what looks
+ // like a valid marker, resume there
+ while (!stbi__at_eof(j->s)) {
+ stbi_uc x = stbi__get8(j->s);
+ while (x == 0xff) { // might be a marker
+ if (stbi__at_eof(j->s))
+ return STBI__MARKER_none;
+ x = stbi__get8(j->s);
+ if (x != 0x00 && x != 0xff) {
+ // not a stuffed zero or lead-in to another marker, looks
+ // like an actual marker, return it
+ return x;
+ }
+ // stuffed zero has x=0 now which ends the loop, meaning we go
+ // back to regular scan loop.
+ // repeated 0xff keeps trying to read the next byte of the marker.
+ }
+ }
+ return STBI__MARKER_none;
+}
+
+// decode image to YCbCr format
+static int stbi__decode_jpeg_image(stbi__jpeg* j)
+{
+ int m;
+ for (m = 0; m < 4; m++) {
+ j->img_comp[m].raw_data = NULL;
+ j->img_comp[m].raw_coeff = NULL;
+ }
+ j->restart_interval = 0;
+ if (!stbi__decode_jpeg_header(j, STBI__SCAN_load))
+ return 0;
+ m = stbi__get_marker(j);
+ while (!stbi__EOI(m)) {
+ if (stbi__SOS(m)) {
+ if (!stbi__process_scan_header(j))
+ return 0;
+ if (!stbi__parse_entropy_coded_data(j))
+ return 0;
+ if (j->marker == STBI__MARKER_none) {
+ j->marker = stbi__skip_jpeg_junk_at_end(j);
+ // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0
+ }
+ m = stbi__get_marker(j);
+ if (STBI__RESTART(m))
+ m = stbi__get_marker(j);
+ } else if (stbi__DNL(m)) {
+ int Ld = stbi__get16be(j->s);
+ stbi__uint32 NL = stbi__get16be(j->s);
+ if (Ld != 4)
+ return stbi__err("bad DNL len", "Corrupt JPEG");
+ if (NL != j->s->img_y)
+ return stbi__err("bad DNL height", "Corrupt JPEG");
+ m = stbi__get_marker(j);
+ } else {
+ if (!stbi__process_marker(j, m))
+ return 1;
+ m = stbi__get_marker(j);
+ }
+ }
+ if (j->progressive)
+ stbi__jpeg_finish(j);
+ return 1;
+}
+
+// static jfif-centered resampling (across block boundaries)
+
+typedef stbi_uc* (*resample_row_func)(stbi_uc* out, stbi_uc* in0, stbi_uc* in1,
+ int w, int hs);
+
+# define stbi__div4(x) ((stbi_uc)((x) >> 2))
+
+static stbi_uc* resample_row_1(stbi_uc* out, stbi_uc* in_near, stbi_uc* in_far, int w, int hs)
+{
+ STBI_NOTUSED(out);
+ STBI_NOTUSED(in_far);
+ STBI_NOTUSED(w);
+ STBI_NOTUSED(hs);
+ return in_near;
+}
+
+static stbi_uc* stbi__resample_row_v_2(stbi_uc* out, stbi_uc* in_near, stbi_uc* in_far, int w, int hs)
+{
+ // need to generate two samples vertically for every one in input
+ int i;
+ STBI_NOTUSED(hs);
+ for (i = 0; i < w; ++i)
+ out[i] = stbi__div4(3 * in_near[i] + in_far[i] + 2);
+ return out;
+}
+
+static stbi_uc* stbi__resample_row_h_2(stbi_uc* out, stbi_uc* in_near, stbi_uc* in_far, int w, int hs)
+{
+ // need to generate two samples horizontally for every one in input
+ int i;
+ stbi_uc* input = in_near;
+
+ if (w == 1) {
+ // if only one sample, can't do any interpolation
+ out[0] = out[1] = input[0];
+ return out;
+ }
+
+ out[0] = input[0];
+ out[1] = stbi__div4(input[0] * 3 + input[1] + 2);
+ for (i = 1; i < w - 1; ++i) {
+ int n = 3 * input[i] + 2;
+ out[i * 2 + 0] = stbi__div4(n + input[i - 1]);
+ out[i * 2 + 1] = stbi__div4(n + input[i + 1]);
+ }
+ out[i * 2 + 0] = stbi__div4(input[w - 2] * 3 + input[w - 1] + 2);
+ out[i * 2 + 1] = input[w - 1];
+
+ STBI_NOTUSED(in_far);
+ STBI_NOTUSED(hs);
+
+ return out;
+}
+
+# define stbi__div16(x) ((stbi_uc)((x) >> 4))
+
+static stbi_uc* stbi__resample_row_hv_2(stbi_uc* out, stbi_uc* in_near, stbi_uc* in_far, int w, int hs)
+{
+ // need to generate 2x2 samples for every one in input
+ int i, t0, t1;
+ if (w == 1) {
+ out[0] = out[1] = stbi__div4(3 * in_near[0] + in_far[0] + 2);
+ return out;
+ }
+
+ t1 = 3 * in_near[0] + in_far[0];
+ out[0] = stbi__div4(t1 + 2);
+ for (i = 1; i < w; ++i) {
+ t0 = t1;
+ t1 = 3 * in_near[i] + in_far[i];
+ out[i * 2 - 1] = stbi__div16(3 * t0 + t1 + 8);
+ out[i * 2] = stbi__div16(3 * t1 + t0 + 8);
+ }
+ out[w * 2 - 1] = stbi__div4(t1 + 2);
+
+ STBI_NOTUSED(hs);
+
+ return out;
+}
+
+# if defined(STBI_SSE2) || defined(STBI_NEON)
+static stbi_uc* stbi__resample_row_hv_2_simd(stbi_uc* out, stbi_uc* in_near, stbi_uc* in_far, int w, int hs)
+{
+ // need to generate 2x2 samples for every one in input
+ int i = 0, t0, t1;
+
+ if (w == 1) {
+ out[0] = out[1] = stbi__div4(3 * in_near[0] + in_far[0] + 2);
+ return out;
+ }
+
+ t1 = 3 * in_near[0] + in_far[0];
+ // process groups of 8 pixels for as long as we can.
+ // note we can't handle the last pixel in a row in this loop
+ // because we need to handle the filter boundary conditions.
+ for (; i < ((w - 1) & ~7); i += 8) {
+# if defined(STBI_SSE2)
+ // load and perform the vertical filtering pass
+ // this uses 3*x + y = 4*x + (y - x)
+ __m128i zero = _mm_setzero_si128();
+ __m128i farb = _mm_loadl_epi64((__m128i*)(in_far + i));
+ __m128i nearb = _mm_loadl_epi64((__m128i*)(in_near + i));
+ __m128i farw = _mm_unpacklo_epi8(farb, zero);
+ __m128i nearw = _mm_unpacklo_epi8(nearb, zero);
+ __m128i diff = _mm_sub_epi16(farw, nearw);
+ __m128i nears = _mm_slli_epi16(nearw, 2);
+ __m128i curr = _mm_add_epi16(nears, diff); // current row
+
+ // horizontal filter works the same based on shifted vers of current
+ // row. "prev" is current row shifted right by 1 pixel; we need to
+ // insert the previous pixel value (from t1).
+ // "next" is current row shifted left by 1 pixel, with first pixel
+ // of next block of 8 pixels added in.
+ __m128i prv0 = _mm_slli_si128(curr, 2);
+ __m128i nxt0 = _mm_srli_si128(curr, 2);
+ __m128i prev = _mm_insert_epi16(prv0, t1, 0);
+ __m128i next = _mm_insert_epi16(nxt0, 3 * in_near[i + 8] + in_far[i + 8], 7);
+
+ // horizontal filter, polyphase implementation since it's convenient:
+ // even pixels = 3*cur + prev = cur*4 + (prev - cur)
+ // odd pixels = 3*cur + next = cur*4 + (next - cur)
+ // note the shared term.
+ __m128i bias = _mm_set1_epi16(8);
+ __m128i curs = _mm_slli_epi16(curr, 2);
+ __m128i prvd = _mm_sub_epi16(prev, curr);
+ __m128i nxtd = _mm_sub_epi16(next, curr);
+ __m128i curb = _mm_add_epi16(curs, bias);
+ __m128i even = _mm_add_epi16(prvd, curb);
+ __m128i odd = _mm_add_epi16(nxtd, curb);
+
+ // interleave even and odd pixels, then undo scaling.
+ __m128i int0 = _mm_unpacklo_epi16(even, odd);
+ __m128i int1 = _mm_unpackhi_epi16(even, odd);
+ __m128i de0 = _mm_srli_epi16(int0, 4);
+ __m128i de1 = _mm_srli_epi16(int1, 4);
+
+ // pack and write output
+ __m128i outv = _mm_packus_epi16(de0, de1);
+ _mm_storeu_si128((__m128i*)(out + i * 2), outv);
+# elif defined(STBI_NEON)
+ // load and perform the vertical filtering pass
+ // this uses 3*x + y = 4*x + (y - x)
+ uint8x8_t farb = vld1_u8(in_far + i);
+ uint8x8_t nearb = vld1_u8(in_near + i);
+ int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb));
+ int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2));
+ int16x8_t curr = vaddq_s16(nears, diff); // current row
+
+ // horizontal filter works the same based on shifted vers of current
+ // row. "prev" is current row shifted right by 1 pixel; we need to
+ // insert the previous pixel value (from t1).
+ // "next" is current row shifted left by 1 pixel, with first pixel
+ // of next block of 8 pixels added in.
+ int16x8_t prv0 = vextq_s16(curr, curr, 7);
+ int16x8_t nxt0 = vextq_s16(curr, curr, 1);
+ int16x8_t prev = vsetq_lane_s16(t1, prv0, 0);
+ int16x8_t next = vsetq_lane_s16(3 * in_near[i + 8] + in_far[i + 8], nxt0, 7);
+
+ // horizontal filter, polyphase implementation since it's convenient:
+ // even pixels = 3*cur + prev = cur*4 + (prev - cur)
+ // odd pixels = 3*cur + next = cur*4 + (next - cur)
+ // note the shared term.
+ int16x8_t curs = vshlq_n_s16(curr, 2);
+ int16x8_t prvd = vsubq_s16(prev, curr);
+ int16x8_t nxtd = vsubq_s16(next, curr);
+ int16x8_t even = vaddq_s16(curs, prvd);
+ int16x8_t odd = vaddq_s16(curs, nxtd);
+
+ // undo scaling and round, then store with even/odd phases interleaved
+ uint8x8x2_t o;
+ o.val[0] = vqrshrun_n_s16(even, 4);
+ o.val[1] = vqrshrun_n_s16(odd, 4);
+ vst2_u8(out + i * 2, o);
+# endif
+
+ // "previous" value for next iter
+ t1 = 3 * in_near[i + 7] + in_far[i + 7];
+ }
+
+ t0 = t1;
+ t1 = 3 * in_near[i] + in_far[i];
+ out[i * 2] = stbi__div16(3 * t1 + t0 + 8);
+
+ for (++i; i < w; ++i) {
+ t0 = t1;
+ t1 = 3 * in_near[i] + in_far[i];
+ out[i * 2 - 1] = stbi__div16(3 * t0 + t1 + 8);
+ out[i * 2] = stbi__div16(3 * t1 + t0 + 8);
+ }
+ out[w * 2 - 1] = stbi__div4(t1 + 2);
+
+ STBI_NOTUSED(hs);
+
+ return out;
+}
+# endif
+
+static stbi_uc* stbi__resample_row_generic(stbi_uc* out, stbi_uc* in_near, stbi_uc* in_far, int w, int hs)
+{
+ // resample with nearest-neighbor
+ int i, j;
+ STBI_NOTUSED(in_far);
+ for (i = 0; i < w; ++i)
+ for (j = 0; j < hs; ++j)
+ out[i * hs + j] = in_near[i];
+ return out;
+}
+
+// this is a reduced-precision calculation of YCbCr-to-RGB introduced
+// to make sure the code produces the same results in both SIMD and scalar
+# define stbi__float2fixed(x) (((int)((x) * 4096.0f + 0.5f)) << 8)
+static void stbi__YCbCr_to_RGB_row(stbi_uc* out, stbi_uc const* y, stbi_uc const* pcb, stbi_uc const* pcr, int count, int step)
+{
+ int i;
+ for (i = 0; i < count; ++i) {
+ int y_fixed = (y[i] << 20) + (1 << 19); // rounding
+ int r, g, b;
+ int cr = pcr[i] - 128;
+ int cb = pcb[i] - 128;
+ r = y_fixed + cr * stbi__float2fixed(1.40200f);
+ g = y_fixed + (cr * -stbi__float2fixed(0.71414f)) + ((cb * -stbi__float2fixed(0.34414f)) & 0xffff0000);
+ b = y_fixed + cb * stbi__float2fixed(1.77200f);
+ r >>= 20;
+ g >>= 20;
+ b >>= 20;
+ if ((unsigned)r > 255) {
+ if (r < 0)
+ r = 0;
+ else
+ r = 255;
+ }
+ if ((unsigned)g > 255) {
+ if (g < 0)
+ g = 0;
+ else
+ g = 255;
+ }
+ if ((unsigned)b > 255) {
+ if (b < 0)
+ b = 0;
+ else
+ b = 255;
+ }
+ out[0] = (stbi_uc)r;
+ out[1] = (stbi_uc)g;
+ out[2] = (stbi_uc)b;
+ out[3] = 255;
+ out += step;
+ }
+}
+
+# if defined(STBI_SSE2) || defined(STBI_NEON)
+static void stbi__YCbCr_to_RGB_simd(stbi_uc* out, stbi_uc const* y, stbi_uc const* pcb, stbi_uc const* pcr, int count, int step)
+{
+ int i = 0;
+
+# ifdef STBI_SSE2
+ // step == 3 is pretty ugly on the final interleave, and i'm not convinced
+ // it's useful in practice (you wouldn't use it for textures, for example).
+ // so just accelerate step == 4 case.
+ if (step == 4) {
+ // this is a fairly straightforward implementation and not super-optimized.
+ __m128i signflip = _mm_set1_epi8(-0x80);
+ __m128i cr_const0 = _mm_set1_epi16((short)(1.40200f * 4096.0f + 0.5f));
+ __m128i cr_const1 = _mm_set1_epi16(-(short)(0.71414f * 4096.0f + 0.5f));
+ __m128i cb_const0 = _mm_set1_epi16(-(short)(0.34414f * 4096.0f + 0.5f));
+ __m128i cb_const1 = _mm_set1_epi16((short)(1.77200f * 4096.0f + 0.5f));
+ __m128i y_bias = _mm_set1_epi8((char)(unsigned char)128);
+ __m128i xw = _mm_set1_epi16(255); // alpha channel
+
+ for (; i + 7 < count; i += 8) {
+ // load
+ __m128i y_bytes = _mm_loadl_epi64((__m128i*)(y + i));
+ __m128i cr_bytes = _mm_loadl_epi64((__m128i*)(pcr + i));
+ __m128i cb_bytes = _mm_loadl_epi64((__m128i*)(pcb + i));
+ __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128
+ __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128
+
+ // unpack to short (and left-shift cr, cb by 8)
+ __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes);
+ __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased);
+ __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased);
+
+ // color transform
+ __m128i yws = _mm_srli_epi16(yw, 4);
+ __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw);
+ __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw);
+ __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1);
+ __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1);
+ __m128i rws = _mm_add_epi16(cr0, yws);
+ __m128i gwt = _mm_add_epi16(cb0, yws);
+ __m128i bws = _mm_add_epi16(yws, cb1);
+ __m128i gws = _mm_add_epi16(gwt, cr1);
+
+ // descale
+ __m128i rw = _mm_srai_epi16(rws, 4);
+ __m128i bw = _mm_srai_epi16(bws, 4);
+ __m128i gw = _mm_srai_epi16(gws, 4);
+
+ // back to byte, set up for transpose
+ __m128i brb = _mm_packus_epi16(rw, bw);
+ __m128i gxb = _mm_packus_epi16(gw, xw);
+
+ // transpose to interleave channels
+ __m128i t0 = _mm_unpacklo_epi8(brb, gxb);
+ __m128i t1 = _mm_unpackhi_epi8(brb, gxb);
+ __m128i o0 = _mm_unpacklo_epi16(t0, t1);
+ __m128i o1 = _mm_unpackhi_epi16(t0, t1);
+
+ // store
+ _mm_storeu_si128((__m128i*)(out + 0), o0);
+ _mm_storeu_si128((__m128i*)(out + 16), o1);
+ out += 32;
+ }
+ }
+# endif
+
+# ifdef STBI_NEON
+ // in this version, step=3 support would be easy to add. but is there demand?
+ if (step == 4) {
+ // this is a fairly straightforward implementation and not super-optimized.
+ uint8x8_t signflip = vdup_n_u8(0x80);
+ int16x8_t cr_const0 = vdupq_n_s16((short)(1.40200f * 4096.0f + 0.5f));
+ int16x8_t cr_const1 = vdupq_n_s16(-(short)(0.71414f * 4096.0f + 0.5f));
+ int16x8_t cb_const0 = vdupq_n_s16(-(short)(0.34414f * 4096.0f + 0.5f));
+ int16x8_t cb_const1 = vdupq_n_s16((short)(1.77200f * 4096.0f + 0.5f));
+
+ for (; i + 7 < count; i += 8) {
+ // load
+ uint8x8_t y_bytes = vld1_u8(y + i);
+ uint8x8_t cr_bytes = vld1_u8(pcr + i);
+ uint8x8_t cb_bytes = vld1_u8(pcb + i);
+ int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip));
+ int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip));
+
+ // expand to s16
+ int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4));
+ int16x8_t crw = vshll_n_s8(cr_biased, 7);
+ int16x8_t cbw = vshll_n_s8(cb_biased, 7);
+
+ // color transform
+ int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0);
+ int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0);
+ int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1);
+ int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1);
+ int16x8_t rws = vaddq_s16(yws, cr0);
+ int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1);
+ int16x8_t bws = vaddq_s16(yws, cb1);
+
+ // undo scaling, round, convert to byte
+ uint8x8x4_t o;
+ o.val[0] = vqrshrun_n_s16(rws, 4);
+ o.val[1] = vqrshrun_n_s16(gws, 4);
+ o.val[2] = vqrshrun_n_s16(bws, 4);
+ o.val[3] = vdup_n_u8(255);
+
+ // store, interleaving r/g/b/a
+ vst4_u8(out, o);
+ out += 8 * 4;
+ }
+ }
+# endif
+
+ for (; i < count; ++i) {
+ int y_fixed = (y[i] << 20) + (1 << 19); // rounding
+ int r, g, b;
+ int cr = pcr[i] - 128;
+ int cb = pcb[i] - 128;
+ r = y_fixed + cr * stbi__float2fixed(1.40200f);
+ g = y_fixed + cr * -stbi__float2fixed(0.71414f) + ((cb * -stbi__float2fixed(0.34414f)) & 0xffff0000);
+ b = y_fixed + cb * stbi__float2fixed(1.77200f);
+ r >>= 20;
+ g >>= 20;
+ b >>= 20;
+ if ((unsigned)r > 255) {
+ if (r < 0)
+ r = 0;
+ else
+ r = 255;
+ }
+ if ((unsigned)g > 255) {
+ if (g < 0)
+ g = 0;
+ else
+ g = 255;
+ }
+ if ((unsigned)b > 255) {
+ if (b < 0)
+ b = 0;
+ else
+ b = 255;
+ }
+ out[0] = (stbi_uc)r;
+ out[1] = (stbi_uc)g;
+ out[2] = (stbi_uc)b;
+ out[3] = 255;
+ out += step;
+ }
+}
+# endif
+
+// set up the kernels
+static void stbi__setup_jpeg(stbi__jpeg* j)
+{
+ j->idct_block_kernel = stbi__idct_block;
+ j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row;
+ j->resample_row_hv_2_kernel = stbi__resample_row_hv_2;
+
+# ifdef STBI_SSE2
+ if (stbi__sse2_available()) {
+ j->idct_block_kernel = stbi__idct_simd;
+ j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;
+ j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;
+ }
+# endif
+
+# ifdef STBI_NEON
+ j->idct_block_kernel = stbi__idct_simd;
+ j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;
+ j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;
+# endif
+}
+
+// clean up the temporary component buffers
+static void stbi__cleanup_jpeg(stbi__jpeg* j)
+{
+ stbi__free_jpeg_components(j, j->s->img_n, 0);
+}
+
+typedef struct
+{
+ resample_row_func resample;
+ stbi_uc *line0, *line1;
+ int hs, vs; // expansion factor in each axis
+ int w_lores; // horizontal pixels pre-expansion
+ int ystep; // how far through vertical expansion we are
+ int ypos; // which pre-expansion row we're on
+} stbi__resample;
+
+// fast 0..255 * 0..255 => 0..255 rounded multiplication
+static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y)
+{
+ unsigned int t = x * y + 128;
+ return (stbi_uc)((t + (t >> 8)) >> 8);
+}
+
+static stbi_uc* load_jpeg_image(stbi__jpeg* z, int* out_x, int* out_y, int* comp, int req_comp)
+{
+ int n, decode_n, is_rgb;
+ z->s->img_n = 0; // make stbi__cleanup_jpeg safe
+
+ // validate req_comp
+ if (req_comp < 0 || req_comp > 4)
+ return stbi__errpuc("bad req_comp", "Internal error");
+
+ // load a jpeg image from whichever source, but leave in YCbCr format
+ if (!stbi__decode_jpeg_image(z)) {
+ stbi__cleanup_jpeg(z);
+ return NULL;
+ }
+
+ // determine actual number of components to generate
+ n = req_comp ? req_comp : z->s->img_n >= 3 ? 3
+ : 1;
+
+ is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif));
+
+ if (z->s->img_n == 3 && n < 3 && !is_rgb)
+ decode_n = 1;
+ else
+ decode_n = z->s->img_n;
+
+ // nothing to do if no components requested; check this now to avoid
+ // accessing uninitialized coutput[0] later
+ if (decode_n <= 0) {
+ stbi__cleanup_jpeg(z);
+ return NULL;
+ }
+
+ // resample and color-convert
+ {
+ int k;
+ unsigned int i, j;
+ stbi_uc* output;
+ stbi_uc* coutput[4] = { NULL, NULL, NULL, NULL };
+
+ stbi__resample res_comp[4];
+
+ for (k = 0; k < decode_n; ++k) {
+ stbi__resample* r = &res_comp[k];
+
+ // allocate line buffer big enough for upsampling off the edges
+ // with upsample factor of 4
+ z->img_comp[k].linebuf = (stbi_uc*)stbi__malloc(z->s->img_x + 3);
+ if (!z->img_comp[k].linebuf) {
+ stbi__cleanup_jpeg(z);
+ return stbi__errpuc("outofmem", "Out of memory");
+ }
+
+ r->hs = z->img_h_max / z->img_comp[k].h;
+ r->vs = z->img_v_max / z->img_comp[k].v;
+ r->ystep = r->vs >> 1;
+ r->w_lores = (z->s->img_x + r->hs - 1) / r->hs;
+ r->ypos = 0;
+ r->line0 = r->line1 = z->img_comp[k].data;
+
+ if (r->hs == 1 && r->vs == 1)
+ r->resample = resample_row_1;
+ else if (r->hs == 1 && r->vs == 2)
+ r->resample = stbi__resample_row_v_2;
+ else if (r->hs == 2 && r->vs == 1)
+ r->resample = stbi__resample_row_h_2;
+ else if (r->hs == 2 && r->vs == 2)
+ r->resample = z->resample_row_hv_2_kernel;
+ else
+ r->resample = stbi__resample_row_generic;
+ }
+
+ // can't error after this so, this is safe
+ output = (stbi_uc*)stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1);
+ if (!output) {
+ stbi__cleanup_jpeg(z);
+ return stbi__errpuc("outofmem", "Out of memory");
+ }
+
+ // now go ahead and resample
+ for (j = 0; j < z->s->img_y; ++j) {
+ stbi_uc* out = output + n * z->s->img_x * j;
+ for (k = 0; k < decode_n; ++k) {
+ stbi__resample* r = &res_comp[k];
+ int y_bot = r->ystep >= (r->vs >> 1);
+ coutput[k] = r->resample(z->img_comp[k].linebuf,
+ y_bot ? r->line1 : r->line0,
+ y_bot ? r->line0 : r->line1,
+ r->w_lores, r->hs);
+ if (++r->ystep >= r->vs) {
+ r->ystep = 0;
+ r->line0 = r->line1;
+ if (++r->ypos < z->img_comp[k].y)
+ r->line1 += z->img_comp[k].w2;
+ }
+ }
+ if (n >= 3) {
+ stbi_uc* y = coutput[0];
+ if (z->s->img_n == 3) {
+ if (is_rgb) {
+ for (i = 0; i < z->s->img_x; ++i) {
+ out[0] = y[i];
+ out[1] = coutput[1][i];
+ out[2] = coutput[2][i];
+ out[3] = 255;
+ out += n;
+ }
+ } else {
+ z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
+ }
+ } else if (z->s->img_n == 4) {
+ if (z->app14_color_transform == 0) { // CMYK
+ for (i = 0; i < z->s->img_x; ++i) {
+ stbi_uc m = coutput[3][i];
+ out[0] = stbi__blinn_8x8(coutput[0][i], m);
+ out[1] = stbi__blinn_8x8(coutput[1][i], m);
+ out[2] = stbi__blinn_8x8(coutput[2][i], m);
+ out[3] = 255;
+ out += n;
+ }
+ } else if (z->app14_color_transform == 2) { // YCCK
+ z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
+ for (i = 0; i < z->s->img_x; ++i) {
+ stbi_uc m = coutput[3][i];
+ out[0] = stbi__blinn_8x8(255 - out[0], m);
+ out[1] = stbi__blinn_8x8(255 - out[1], m);
+ out[2] = stbi__blinn_8x8(255 - out[2], m);
+ out += n;
+ }
+ } else { // YCbCr + alpha? Ignore the fourth channel for now
+ z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
+ }
+ } else
+ for (i = 0; i < z->s->img_x; ++i) {
+ out[0] = out[1] = out[2] = y[i];
+ out[3] = 255; // not used if n==3
+ out += n;
+ }
+ } else {
+ if (is_rgb) {
+ if (n == 1)
+ for (i = 0; i < z->s->img_x; ++i)
+ *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]);
+ else {
+ for (i = 0; i < z->s->img_x; ++i, out += 2) {
+ out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]);
+ out[1] = 255;
+ }
+ }
+ } else if (z->s->img_n == 4 && z->app14_color_transform == 0) {
+ for (i = 0; i < z->s->img_x; ++i) {
+ stbi_uc m = coutput[3][i];
+ stbi_uc r = stbi__blinn_8x8(coutput[0][i], m);
+ stbi_uc g = stbi__blinn_8x8(coutput[1][i], m);
+ stbi_uc b = stbi__blinn_8x8(coutput[2][i], m);
+ out[0] = stbi__compute_y(r, g, b);
+ out[1] = 255;
+ out += n;
+ }
+ } else if (z->s->img_n == 4 && z->app14_color_transform == 2) {
+ for (i = 0; i < z->s->img_x; ++i) {
+ out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]);
+ out[1] = 255;
+ out += n;
+ }
+ } else {
+ stbi_uc* y = coutput[0];
+ if (n == 1)
+ for (i = 0; i < z->s->img_x; ++i)
+ out[i] = y[i];
+ else
+ for (i = 0; i < z->s->img_x; ++i) {
+ *out++ = y[i];
+ *out++ = 255;
+ }
+ }
+ }
+ }
+ stbi__cleanup_jpeg(z);
+ *out_x = z->s->img_x;
+ *out_y = z->s->img_y;
+ if (comp)
+ *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output
+ return output;
+ }
+}
+
+static void* stbi__jpeg_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri)
+{
+ unsigned char* result;
+ stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
+ if (!j)
+ return stbi__errpuc("outofmem", "Out of memory");
+ memset(j, 0, sizeof(stbi__jpeg));
+ STBI_NOTUSED(ri);
+ j->s = s;
+ stbi__setup_jpeg(j);
+ result = load_jpeg_image(j, x, y, comp, req_comp);
+ STBI_FREE(j);
+ return result;
+}
+
+static int stbi__jpeg_test(stbi__context* s)
+{
+ int r;
+ stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
+ if (!j)
+ return stbi__err("outofmem", "Out of memory");
+ memset(j, 0, sizeof(stbi__jpeg));
+ j->s = s;
+ stbi__setup_jpeg(j);
+ r = stbi__decode_jpeg_header(j, STBI__SCAN_type);
+ stbi__rewind(s);
+ STBI_FREE(j);
+ return r;
+}
+
+static int stbi__jpeg_info_raw(stbi__jpeg* j, int* x, int* y, int* comp)
+{
+ if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) {
+ stbi__rewind(j->s);
+ return 0;
+ }
+ if (x)
+ *x = j->s->img_x;
+ if (y)
+ *y = j->s->img_y;
+ if (comp)
+ *comp = j->s->img_n >= 3 ? 3 : 1;
+ return 1;
+}
+
+static int stbi__jpeg_info(stbi__context* s, int* x, int* y, int* comp)
+{
+ int result;
+ stbi__jpeg* j = (stbi__jpeg*)(stbi__malloc(sizeof(stbi__jpeg)));
+ if (!j)
+ return stbi__err("outofmem", "Out of memory");
+ memset(j, 0, sizeof(stbi__jpeg));
+ j->s = s;
+ result = stbi__jpeg_info_raw(j, x, y, comp);
+ STBI_FREE(j);
+ return result;
+}
+# endif
+
+// public domain zlib decode v0.2 Sean Barrett 2006-11-18
+// simple implementation
+// - all input must be provided in an upfront buffer
+// - all output is written to a single output buffer (can malloc/realloc)
+// performance
+// - fast huffman
+
+# ifndef STBI_NO_ZLIB
+
+// fast-way is faster to check than jpeg huffman, but slow way is slower
+# define STBI__ZFAST_BITS 9 // accelerate all cases in default tables
+# define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1)
+# define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet
+
+// zlib-style huffman encoding
+// (jpegs packs from left, zlib from right, so can't share code)
+typedef struct
+{
+ stbi__uint16 fast[1 << STBI__ZFAST_BITS];
+ stbi__uint16 firstcode[16];
+ int maxcode[17];
+ stbi__uint16 firstsymbol[16];
+ stbi_uc size[STBI__ZNSYMS];
+ stbi__uint16 value[STBI__ZNSYMS];
+} stbi__zhuffman;
+
+stbi_inline static int stbi__bitreverse16(int n)
+{
+ n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1);
+ n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2);
+ n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4);
+ n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8);
+ return n;
+}
+
+stbi_inline static int stbi__bit_reverse(int v, int bits)
+{
+ STBI_ASSERT(bits <= 16);
+ // to bit reverse n bits, reverse 16 and shift
+ // e.g. 11 bits, bit reverse and shift away 5
+ return stbi__bitreverse16(v) >> (16 - bits);
+}
+
+static int stbi__zbuild_huffman(stbi__zhuffman* z, stbi_uc const* sizelist, int num)
+{
+ int i, k = 0;
+ int code, next_code[16], sizes[17];
+
+ // DEFLATE spec for generating codes
+ memset(sizes, 0, sizeof(sizes));
+ memset(z->fast, 0, sizeof(z->fast));
+ for (i = 0; i < num; ++i)
+ ++sizes[sizelist[i]];
+ sizes[0] = 0;
+ for (i = 1; i < 16; ++i)
+ if (sizes[i] > (1 << i))
+ return stbi__err("bad sizes", "Corrupt PNG");
+ code = 0;
+ for (i = 1; i < 16; ++i) {
+ next_code[i] = code;
+ z->firstcode[i] = (stbi__uint16)code;
+ z->firstsymbol[i] = (stbi__uint16)k;
+ code = (code + sizes[i]);
+ if (sizes[i])
+ if (code - 1 >= (1 << i))
+ return stbi__err("bad codelengths", "Corrupt PNG");
+ z->maxcode[i] = code << (16 - i); // preshift for inner loop
+ code <<= 1;
+ k += sizes[i];
+ }
+ z->maxcode[16] = 0x10000; // sentinel
+ for (i = 0; i < num; ++i) {
+ int s = sizelist[i];
+ if (s) {
+ int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];
+ stbi__uint16 fastv = (stbi__uint16)((s << 9) | i);
+ z->size[c] = (stbi_uc)s;
+ z->value[c] = (stbi__uint16)i;
+ if (s <= STBI__ZFAST_BITS) {
+ int j = stbi__bit_reverse(next_code[s], s);
+ while (j < (1 << STBI__ZFAST_BITS)) {
+ z->fast[j] = fastv;
+ j += (1 << s);
+ }
+ }
+ ++next_code[s];
+ }
+ }
+ return 1;
+}
+
+// zlib-from-memory implementation for PNG reading
+// because PNG allows splitting the zlib stream arbitrarily,
+// and it's annoying structurally to have PNG call ZLIB call PNG,
+// we require PNG read all the IDATs and combine them into a single
+// memory buffer
+
+typedef struct
+{
+ stbi_uc *zbuffer, *zbuffer_end;
+ int num_bits;
+ int hit_zeof_once;
+ stbi__uint32 code_buffer;
+
+ char* zout;
+ char* zout_start;
+ char* zout_end;
+ int z_expandable;
+
+ stbi__zhuffman z_length, z_distance;
+} stbi__zbuf;
+
+stbi_inline static int stbi__zeof(stbi__zbuf* z)
+{
+ return (z->zbuffer >= z->zbuffer_end);
+}
+
+stbi_inline static stbi_uc stbi__zget8(stbi__zbuf* z)
+{
+ return stbi__zeof(z) ? 0 : *z->zbuffer++;
+}
+
+static void stbi__fill_bits(stbi__zbuf* z)
+{
+ do {
+ if (z->code_buffer >= (1U << z->num_bits)) {
+ z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */
+ return;
+ }
+ z->code_buffer |= (unsigned int)stbi__zget8(z) << z->num_bits;
+ z->num_bits += 8;
+ } while (z->num_bits <= 24);
+}
+
+stbi_inline static unsigned int stbi__zreceive(stbi__zbuf* z, int n)
+{
+ unsigned int k;
+ if (z->num_bits < n)
+ stbi__fill_bits(z);
+ k = z->code_buffer & ((1 << n) - 1);
+ z->code_buffer >>= n;
+ z->num_bits -= n;
+ return k;
+}
+
+static int stbi__zhuffman_decode_slowpath(stbi__zbuf* a, stbi__zhuffman* z)
+{
+ int b, s, k;
+ // not resolved by fast table, so compute it the slow way
+ // use jpeg approach, which requires MSbits at top
+ k = stbi__bit_reverse(a->code_buffer, 16);
+ for (s = STBI__ZFAST_BITS + 1;; ++s)
+ if (k < z->maxcode[s])
+ break;
+ if (s >= 16)
+ return -1; // invalid code!
+ // code size is s, so:
+ b = (k >> (16 - s)) - z->firstcode[s] + z->firstsymbol[s];
+ if (b >= STBI__ZNSYMS)
+ return -1; // some data was corrupt somewhere!
+ if (z->size[b] != s)
+ return -1; // was originally an assert, but report failure instead.
+ a->code_buffer >>= s;
+ a->num_bits -= s;
+ return z->value[b];
+}
+
+stbi_inline static int stbi__zhuffman_decode(stbi__zbuf* a, stbi__zhuffman* z)
+{
+ int b, s;
+ if (a->num_bits < 16) {
+ if (stbi__zeof(a)) {
+ if (!a->hit_zeof_once) {
+ // This is the first time we hit eof, insert 16 extra padding btis
+ // to allow us to keep going; if we actually consume any of them
+ // though, that is invalid data. This is caught later.
+ a->hit_zeof_once = 1;
+ a->num_bits += 16; // add 16 implicit zero bits
+ } else {
+ // We already inserted our extra 16 padding bits and are again
+ // out, this stream is actually prematurely terminated.
+ return -1;
+ }
+ } else {
+ stbi__fill_bits(a);
+ }
+ }
+ b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
+ if (b) {
+ s = b >> 9;
+ a->code_buffer >>= s;
+ a->num_bits -= s;
+ return b & 511;
+ }
+ return stbi__zhuffman_decode_slowpath(a, z);
+}
+
+static int stbi__zexpand(stbi__zbuf* z, char* zout, int n) // need to make room for n bytes
+{
+ char* q;
+ unsigned int cur, limit, old_limit;
+ z->zout = zout;
+ if (!z->z_expandable)
+ return stbi__err("output buffer limit", "Corrupt PNG");
+ cur = (unsigned int)(z->zout - z->zout_start);
+ limit = old_limit = (unsigned)(z->zout_end - z->zout_start);
+ if (UINT_MAX - cur < (unsigned)n)
+ return stbi__err("outofmem", "Out of memory");
+ while (cur + n > limit) {
+ if (limit > UINT_MAX / 2)
+ return stbi__err("outofmem", "Out of memory");
+ limit *= 2;
+ }
+ q = (char*)STBI_REALLOC_SIZED(z->zout_start, old_limit, limit);
+ STBI_NOTUSED(old_limit);
+ if (q == NULL)
+ return stbi__err("outofmem", "Out of memory");
+ z->zout_start = q;
+ z->zout = q + cur;
+ z->zout_end = q + limit;
+ return 1;
+}
+
+static int const stbi__zlength_base[31] = {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13,
+ 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
+ 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
+};
+
+static int const stbi__zlength_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 };
+
+static int const stbi__zdist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 };
+
+static int const stbi__zdist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 };
+
+static int stbi__parse_huffman_block(stbi__zbuf* a)
+{
+ char* zout = a->zout;
+ for (;;) {
+ int z = stbi__zhuffman_decode(a, &a->z_length);
+ if (z < 256) {
+ if (z < 0)
+ return stbi__err("bad huffman code", "Corrupt PNG"); // error in huffman codes
+ if (zout >= a->zout_end) {
+ if (!stbi__zexpand(a, zout, 1))
+ return 0;
+ zout = a->zout;
+ }
+ *zout++ = (char)z;
+ } else {
+ stbi_uc* p;
+ int len, dist;
+ if (z == 256) {
+ a->zout = zout;
+ if (a->hit_zeof_once && a->num_bits < 16) {
+ // The first time we hit zeof, we inserted 16 extra zero bits into our bit
+ // buffer so the decoder can just do its speculative decoding. But if we
+ // actually consumed any of those bits (which is the case when num_bits < 16),
+ // the stream actually read past the end so it is malformed.
+ return stbi__err("unexpected end", "Corrupt PNG");
+ }
+ return 1;
+ }
+ if (z >= 286)
+ return stbi__err("bad huffman code", "Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data
+ z -= 257;
+ len = stbi__zlength_base[z];
+ if (stbi__zlength_extra[z])
+ len += stbi__zreceive(a, stbi__zlength_extra[z]);
+ z = stbi__zhuffman_decode(a, &a->z_distance);
+ if (z < 0 || z >= 30)
+ return stbi__err("bad huffman code", "Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data
+ dist = stbi__zdist_base[z];
+ if (stbi__zdist_extra[z])
+ dist += stbi__zreceive(a, stbi__zdist_extra[z]);
+ if (zout - a->zout_start < dist)
+ return stbi__err("bad dist", "Corrupt PNG");
+ if (len > a->zout_end - zout) {
+ if (!stbi__zexpand(a, zout, len))
+ return 0;
+ zout = a->zout;
+ }
+ p = (stbi_uc*)(zout - dist);
+ if (dist == 1) { // run of one byte; common in images.
+ stbi_uc v = *p;
+ if (len) {
+ do
+ *zout++ = v;
+ while (--len);
+ }
+ } else {
+ if (len) {
+ do
+ *zout++ = *p++;
+ while (--len);
+ }
+ }
+ }
+ }
+}
+
+static int stbi__compute_huffman_codes(stbi__zbuf* a)
+{
+ static stbi_uc const length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+ stbi__zhuffman z_codelength;
+ stbi_uc lencodes[286 + 32 + 137]; // padding for maximum single op
+ stbi_uc codelength_sizes[19];
+ int i, n;
+
+ int hlit = stbi__zreceive(a, 5) + 257;
+ int hdist = stbi__zreceive(a, 5) + 1;
+ int hclen = stbi__zreceive(a, 4) + 4;
+ int ntot = hlit + hdist;
+
+ memset(codelength_sizes, 0, sizeof(codelength_sizes));
+ for (i = 0; i < hclen; ++i) {
+ int s = stbi__zreceive(a, 3);
+ codelength_sizes[length_dezigzag[i]] = (stbi_uc)s;
+ }
+ if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19))
+ return 0;
+
+ n = 0;
+ while (n < ntot) {
+ int c = stbi__zhuffman_decode(a, &z_codelength);
+ if (c < 0 || c >= 19)
+ return stbi__err("bad codelengths", "Corrupt PNG");
+ if (c < 16)
+ lencodes[n++] = (stbi_uc)c;
+ else {
+ stbi_uc fill = 0;
+ if (c == 16) {
+ c = stbi__zreceive(a, 2) + 3;
+ if (n == 0)
+ return stbi__err("bad codelengths", "Corrupt PNG");
+ fill = lencodes[n - 1];
+ } else if (c == 17) {
+ c = stbi__zreceive(a, 3) + 3;
+ } else if (c == 18) {
+ c = stbi__zreceive(a, 7) + 11;
+ } else {
+ return stbi__err("bad codelengths", "Corrupt PNG");
+ }
+ if (ntot - n < c)
+ return stbi__err("bad codelengths", "Corrupt PNG");
+ memset(lencodes + n, fill, c);
+ n += c;
+ }
+ }
+ if (n != ntot)
+ return stbi__err("bad codelengths", "Corrupt PNG");
+ if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit))
+ return 0;
+ if (!stbi__zbuild_huffman(&a->z_distance, lencodes + hlit, hdist))
+ return 0;
+ return 1;
+}
+
+static int stbi__parse_uncompressed_block(stbi__zbuf* a)
+{
+ stbi_uc header[4];
+ int len, nlen, k;
+ if (a->num_bits & 7)
+ stbi__zreceive(a, a->num_bits & 7); // discard
+ // drain the bit-packed data into header
+ k = 0;
+ while (a->num_bits > 0) {
+ header[k++] = (stbi_uc)(a->code_buffer & 255); // suppress MSVC run-time check
+ a->code_buffer >>= 8;
+ a->num_bits -= 8;
+ }
+ if (a->num_bits < 0)
+ return stbi__err("zlib corrupt", "Corrupt PNG");
+ // now fill header the normal way
+ while (k < 4)
+ header[k++] = stbi__zget8(a);
+ len = header[1] * 256 + header[0];
+ nlen = header[3] * 256 + header[2];
+ if (nlen != (len ^ 0xffff))
+ return stbi__err("zlib corrupt", "Corrupt PNG");
+ if (a->zbuffer + len > a->zbuffer_end)
+ return stbi__err("read past buffer", "Corrupt PNG");
+ if (a->zout + len > a->zout_end)
+ if (!stbi__zexpand(a, a->zout, len))
+ return 0;
+ memcpy(a->zout, a->zbuffer, len);
+ a->zbuffer += len;
+ a->zout += len;
+ return 1;
+}
+
+static int stbi__parse_zlib_header(stbi__zbuf* a)
+{
+ int cmf = stbi__zget8(a);
+ int cm = cmf & 15;
+ /* int cinfo = cmf >> 4; */
+ int flg = stbi__zget8(a);
+ if (stbi__zeof(a))
+ return stbi__err("bad zlib header", "Corrupt PNG"); // zlib spec
+ if ((cmf * 256 + flg) % 31 != 0)
+ return stbi__err("bad zlib header", "Corrupt PNG"); // zlib spec
+ if (flg & 32)
+ return stbi__err("no preset dict", "Corrupt PNG"); // preset dictionary not allowed in png
+ if (cm != 8)
+ return stbi__err("bad compression", "Corrupt PNG"); // DEFLATE required for png
+ // window = 1 << (8 + cinfo)... but who cares, we fully buffer output
+ return 1;
+}
+
+static stbi_uc const stbi__zdefault_length[STBI__ZNSYMS] = {
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8
+};
+static stbi_uc const stbi__zdefault_distance[32] = {
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+};
+/*
+Init algorithm:
+{
+ int i; // use <= to match clearly with spec
+ for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8;
+ for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9;
+ for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7;
+ for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8;
+
+ for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5;
+}
+*/
+
+static int stbi__parse_zlib(stbi__zbuf* a, int parse_header)
+{
+ int final, type;
+ if (parse_header)
+ if (!stbi__parse_zlib_header(a))
+ return 0;
+ a->num_bits = 0;
+ a->code_buffer = 0;
+ a->hit_zeof_once = 0;
+ do {
+ final = stbi__zreceive(a, 1);
+ type = stbi__zreceive(a, 2);
+ if (type == 0) {
+ if (!stbi__parse_uncompressed_block(a))
+ return 0;
+ } else if (type == 3) {
+ return 0;
+ } else {
+ if (type == 1) {
+ // use fixed code lengths
+ if (!stbi__zbuild_huffman(&a->z_length, stbi__zdefault_length, STBI__ZNSYMS))
+ return 0;
+ if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32))
+ return 0;
+ } else {
+ if (!stbi__compute_huffman_codes(a))
+ return 0;
+ }
+ if (!stbi__parse_huffman_block(a))
+ return 0;
+ }
+ } while (!final);
+ return 1;
+}
+
+static int stbi__do_zlib(stbi__zbuf* a, char* obuf, int olen, int exp, int parse_header)
+{
+ a->zout_start = obuf;
+ a->zout = obuf;
+ a->zout_end = obuf + olen;
+ a->z_expandable = exp;
+
+ return stbi__parse_zlib(a, parse_header);
+}
+
+STBIDEF char* stbi_zlib_decode_malloc_guesssize(char const* buffer, int len, int initial_size, int* outlen)
+{
+ stbi__zbuf a;
+ char* p = (char*)stbi__malloc(initial_size);
+ if (p == NULL)
+ return NULL;
+ a.zbuffer = (stbi_uc*)buffer;
+ a.zbuffer_end = (stbi_uc*)buffer + len;
+ if (stbi__do_zlib(&a, p, initial_size, 1, 1)) {
+ if (outlen)
+ *outlen = (int)(a.zout - a.zout_start);
+ return a.zout_start;
+ } else {
+ STBI_FREE(a.zout_start);
+ return NULL;
+ }
+}
+
+STBIDEF char* stbi_zlib_decode_malloc(char const* buffer, int len, int* outlen)
+{
+ return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen);
+}
+
+STBIDEF char* stbi_zlib_decode_malloc_guesssize_headerflag(char const* buffer, int len, int initial_size, int* outlen, int parse_header)
+{
+ stbi__zbuf a;
+ char* p = (char*)stbi__malloc(initial_size);
+ if (p == NULL)
+ return NULL;
+ a.zbuffer = (stbi_uc*)buffer;
+ a.zbuffer_end = (stbi_uc*)buffer + len;
+ if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) {
+ if (outlen)
+ *outlen = (int)(a.zout - a.zout_start);
+ return a.zout_start;
+ } else {
+ STBI_FREE(a.zout_start);
+ return NULL;
+ }
+}
+
+STBIDEF int stbi_zlib_decode_buffer(char* obuffer, int olen, char const* ibuffer, int ilen)
+{
+ stbi__zbuf a;
+ a.zbuffer = (stbi_uc*)ibuffer;
+ a.zbuffer_end = (stbi_uc*)ibuffer + ilen;
+ if (stbi__do_zlib(&a, obuffer, olen, 0, 1))
+ return (int)(a.zout - a.zout_start);
+ else
+ return -1;
+}
+
+STBIDEF char* stbi_zlib_decode_noheader_malloc(char const* buffer, int len, int* outlen)
+{
+ stbi__zbuf a;
+ char* p = (char*)stbi__malloc(16384);
+ if (p == NULL)
+ return NULL;
+ a.zbuffer = (stbi_uc*)buffer;
+ a.zbuffer_end = (stbi_uc*)buffer + len;
+ if (stbi__do_zlib(&a, p, 16384, 1, 0)) {
+ if (outlen)
+ *outlen = (int)(a.zout - a.zout_start);
+ return a.zout_start;
+ } else {
+ STBI_FREE(a.zout_start);
+ return NULL;
+ }
+}
+
+STBIDEF int stbi_zlib_decode_noheader_buffer(char* obuffer, int olen, char const* ibuffer, int ilen)
+{
+ stbi__zbuf a;
+ a.zbuffer = (stbi_uc*)ibuffer;
+ a.zbuffer_end = (stbi_uc*)ibuffer + ilen;
+ if (stbi__do_zlib(&a, obuffer, olen, 0, 0))
+ return (int)(a.zout - a.zout_start);
+ else
+ return -1;
+}
+# endif
+
+// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18
+// simple implementation
+// - only 8-bit samples
+// - no CRC checking
+// - allocates lots of intermediate memory
+// - avoids problem of streaming data between subsystems
+// - avoids explicit window management
+// performance
+// - uses stb_zlib, a PD zlib implementation with fast huffman decoding
+
+# ifndef STBI_NO_PNG
+typedef struct
+{
+ stbi__uint32 length;
+ stbi__uint32 type;
+} stbi__pngchunk;
+
+static stbi__pngchunk stbi__get_chunk_header(stbi__context* s)
+{
+ stbi__pngchunk c;
+ c.length = stbi__get32be(s);
+ c.type = stbi__get32be(s);
+ return c;
+}
+
+static int stbi__check_png_header(stbi__context* s)
+{
+ static stbi_uc const png_sig[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
+ int i;
+ for (i = 0; i < 8; ++i)
+ if (stbi__get8(s) != png_sig[i])
+ return stbi__err("bad png sig", "Not a PNG");
+ return 1;
+}
+
+typedef struct
+{
+ stbi__context* s;
+ stbi_uc *idata, *expanded, *out;
+ int depth;
+} stbi__png;
+
+enum {
+ STBI__F_none = 0,
+ STBI__F_sub = 1,
+ STBI__F_up = 2,
+ STBI__F_avg = 3,
+ STBI__F_paeth = 4,
+ // synthetic filter used for first scanline to avoid needing a dummy row of 0s
+ STBI__F_avg_first
+};
+
+static stbi_uc first_row_filter[5] = {
+ STBI__F_none,
+ STBI__F_sub,
+ STBI__F_none,
+ STBI__F_avg_first,
+ STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub
+};
+
+static int stbi__paeth(int a, int b, int c)
+{
+ // This formulation looks very different from the reference in the PNG spec, but is
+ // actually equivalent and has favorable data dependencies and admits straightforward
+ // generation of branch-free code, which helps performance significantly.
+ int thresh = c * 3 - (a + b);
+ int lo = a < b ? a : b;
+ int hi = a < b ? b : a;
+ int t0 = (hi <= thresh) ? lo : c;
+ int t1 = (thresh <= lo) ? hi : t0;
+ return t1;
+}
+
+static stbi_uc const stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0, 0, 0, 0x01 };
+
+// adds an extra all-255 alpha channel
+// dest == src is legal
+// img_n must be 1 or 3
+static void stbi__create_png_alpha_expand8(stbi_uc* dest, stbi_uc* src, stbi__uint32 x, int img_n)
+{
+ int i;
+ // must process data backwards since we allow dest==src
+ if (img_n == 1) {
+ for (i = x - 1; i >= 0; --i) {
+ dest[i * 2 + 1] = 255;
+ dest[i * 2 + 0] = src[i];
+ }
+ } else {
+ STBI_ASSERT(img_n == 3);
+ for (i = x - 1; i >= 0; --i) {
+ dest[i * 4 + 3] = 255;
+ dest[i * 4 + 2] = src[i * 3 + 2];
+ dest[i * 4 + 1] = src[i * 3 + 1];
+ dest[i * 4 + 0] = src[i * 3 + 0];
+ }
+ }
+}
+
+// create the png data from post-deflated data
+static int stbi__create_png_image_raw(stbi__png* a, stbi_uc* raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
+{
+ int bytes = (depth == 16 ? 2 : 1);
+ stbi__context* s = a->s;
+ stbi__uint32 i, j, stride = x * out_n * bytes;
+ stbi__uint32 img_len, img_width_bytes;
+ stbi_uc* filter_buf;
+ int all_ok = 1;
+ int k;
+ int img_n = s->img_n; // copy it into a local for later
+
+ int output_bytes = out_n * bytes;
+ int filter_bytes = img_n * bytes;
+ int width = x;
+
+ STBI_ASSERT(out_n == s->img_n || out_n == s->img_n + 1);
+ a->out = (stbi_uc*)stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into
+ if (!a->out)
+ return stbi__err("outofmem", "Out of memory");
+
+ // note: error exits here don't need to clean up a->out individually,
+ // stbi__do_png always does on error.
+ if (!stbi__mad3sizes_valid(img_n, x, depth, 7))
+ return stbi__err("too large", "Corrupt PNG");
+ img_width_bytes = (((img_n * x * depth) + 7) >> 3);
+ if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes))
+ return stbi__err("too large", "Corrupt PNG");
+ img_len = (img_width_bytes + 1) * y;
+
+ // we used to check for exact match between raw_len and img_len on non-interlaced PNGs,
+ // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros),
+ // so just check for raw_len < img_len always.
+ if (raw_len < img_len)
+ return stbi__err("not enough pixels", "Corrupt PNG");
+
+ // Allocate two scan lines worth of filter workspace buffer.
+ filter_buf = (stbi_uc*)stbi__malloc_mad2(img_width_bytes, 2, 0);
+ if (!filter_buf)
+ return stbi__err("outofmem", "Out of memory");
+
+ // Filtering for low-bit-depth images
+ if (depth < 8) {
+ filter_bytes = 1;
+ width = img_width_bytes;
+ }
+
+ for (j = 0; j < y; ++j) {
+ // cur/prior filter buffers alternate
+ stbi_uc* cur = filter_buf + (j & 1) * img_width_bytes;
+ stbi_uc* prior = filter_buf + (~j & 1) * img_width_bytes;
+ stbi_uc* dest = a->out + stride * j;
+ int nk = width * filter_bytes;
+ int filter = *raw++;
+
+ // check filter type
+ if (filter > 4) {
+ all_ok = stbi__err("invalid filter", "Corrupt PNG");
+ break;
+ }
+
+ // if first row, use special filter that doesn't sample previous row
+ if (j == 0)
+ filter = first_row_filter[filter];
+
+ // perform actual filtering
+ switch (filter) {
+ case STBI__F_none:
+ memcpy(cur, raw, nk);
+ break;
+ case STBI__F_sub:
+ memcpy(cur, raw, filter_bytes);
+ for (k = filter_bytes; k < nk; ++k)
+ cur[k] = STBI__BYTECAST(raw[k] + cur[k - filter_bytes]);
+ break;
+ case STBI__F_up:
+ for (k = 0; k < nk; ++k)
+ cur[k] = STBI__BYTECAST(raw[k] + prior[k]);
+ break;
+ case STBI__F_avg:
+ for (k = 0; k < filter_bytes; ++k)
+ cur[k] = STBI__BYTECAST(raw[k] + (prior[k] >> 1));
+ for (k = filter_bytes; k < nk; ++k)
+ cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k - filter_bytes]) >> 1));
+ break;
+ case STBI__F_paeth:
+ for (k = 0; k < filter_bytes; ++k)
+ cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0)
+ for (k = filter_bytes; k < nk; ++k)
+ cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k - filter_bytes], prior[k], prior[k - filter_bytes]));
+ break;
+ case STBI__F_avg_first:
+ memcpy(cur, raw, filter_bytes);
+ for (k = filter_bytes; k < nk; ++k)
+ cur[k] = STBI__BYTECAST(raw[k] + (cur[k - filter_bytes] >> 1));
+ break;
+ }
+
+ raw += nk;
+
+ // expand decoded bits in cur to dest, also adding an extra alpha channel if desired
+ if (depth < 8) {
+ stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
+ stbi_uc* in = cur;
+ stbi_uc* out = dest;
+ stbi_uc inb = 0;
+ stbi__uint32 nsmp = x * img_n;
+
+ // expand bits to bytes first
+ if (depth == 4) {
+ for (i = 0; i < nsmp; ++i) {
+ if ((i & 1) == 0)
+ inb = *in++;
+ *out++ = scale * (inb >> 4);
+ inb <<= 4;
+ }
+ } else if (depth == 2) {
+ for (i = 0; i < nsmp; ++i) {
+ if ((i & 3) == 0)
+ inb = *in++;
+ *out++ = scale * (inb >> 6);
+ inb <<= 2;
+ }
+ } else {
+ STBI_ASSERT(depth == 1);
+ for (i = 0; i < nsmp; ++i) {
+ if ((i & 7) == 0)
+ inb = *in++;
+ *out++ = scale * (inb >> 7);
+ inb <<= 1;
+ }
+ }
+
+ // insert alpha=255 values if desired
+ if (img_n != out_n)
+ stbi__create_png_alpha_expand8(dest, dest, x, img_n);
+ } else if (depth == 8) {
+ if (img_n == out_n)
+ memcpy(dest, cur, x * img_n);
+ else
+ stbi__create_png_alpha_expand8(dest, cur, x, img_n);
+ } else if (depth == 16) {
+ // convert the image data from big-endian to platform-native
+ stbi__uint16* dest16 = (stbi__uint16*)dest;
+ stbi__uint32 nsmp = x * img_n;
+
+ if (img_n == out_n) {
+ for (i = 0; i < nsmp; ++i, ++dest16, cur += 2)
+ *dest16 = (cur[0] << 8) | cur[1];
+ } else {
+ STBI_ASSERT(img_n + 1 == out_n);
+ if (img_n == 1) {
+ for (i = 0; i < x; ++i, dest16 += 2, cur += 2) {
+ dest16[0] = (cur[0] << 8) | cur[1];
+ dest16[1] = 0xffff;
+ }
+ } else {
+ STBI_ASSERT(img_n == 3);
+ for (i = 0; i < x; ++i, dest16 += 4, cur += 6) {
+ dest16[0] = (cur[0] << 8) | cur[1];
+ dest16[1] = (cur[2] << 8) | cur[3];
+ dest16[2] = (cur[4] << 8) | cur[5];
+ dest16[3] = 0xffff;
+ }
+ }
+ }
+ }
+ }
+
+ STBI_FREE(filter_buf);
+ if (!all_ok)
+ return 0;
+
+ return 1;
+}
+
+static int stbi__create_png_image(stbi__png* a, stbi_uc* image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced)
+{
+ int bytes = (depth == 16 ? 2 : 1);
+ int out_bytes = out_n * bytes;
+ stbi_uc* final;
+ int p;
+ if (!interlaced)
+ return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color);
+
+ // de-interlacing
+ final = (stbi_uc*)stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0);
+ if (!final)
+ return stbi__err("outofmem", "Out of memory");
+ for (p = 0; p < 7; ++p) {
+ int xorig[] = { 0, 4, 0, 2, 0, 1, 0 };
+ int yorig[] = { 0, 0, 4, 0, 2, 0, 1 };
+ int xspc[] = { 8, 8, 4, 4, 2, 2, 1 };
+ int yspc[] = { 8, 8, 8, 4, 4, 2, 2 };
+ int i, j, x, y;
+ // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1
+ x = (a->s->img_x - xorig[p] + xspc[p] - 1) / xspc[p];
+ y = (a->s->img_y - yorig[p] + yspc[p] - 1) / yspc[p];
+ if (x && y) {
+ stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y;
+ if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) {
+ STBI_FREE(final);
+ return 0;
+ }
+ for (j = 0; j < y; ++j) {
+ for (i = 0; i < x; ++i) {
+ int out_y = j * yspc[p] + yorig[p];
+ int out_x = i * xspc[p] + xorig[p];
+ memcpy(final + out_y * a->s->img_x * out_bytes + out_x * out_bytes,
+ a->out + (j * x + i) * out_bytes, out_bytes);
+ }
+ }
+ STBI_FREE(a->out);
+ image_data += img_len;
+ image_data_len -= img_len;
+ }
+ }
+ a->out = final;
+
+ return 1;
+}
+
+static int stbi__compute_transparency(stbi__png* z, stbi_uc tc[3], int out_n)
+{
+ stbi__context* s = z->s;
+ stbi__uint32 i, pixel_count = s->img_x * s->img_y;
+ stbi_uc* p = z->out;
+
+ // compute color-based transparency, assuming we've
+ // already got 255 as the alpha value in the output
+ STBI_ASSERT(out_n == 2 || out_n == 4);
+
+ if (out_n == 2) {
+ for (i = 0; i < pixel_count; ++i) {
+ p[1] = (p[0] == tc[0] ? 0 : 255);
+ p += 2;
+ }
+ } else {
+ for (i = 0; i < pixel_count; ++i) {
+ if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
+ p[3] = 0;
+ p += 4;
+ }
+ }
+ return 1;
+}
+
+static int stbi__compute_transparency16(stbi__png* z, stbi__uint16 tc[3], int out_n)
+{
+ stbi__context* s = z->s;
+ stbi__uint32 i, pixel_count = s->img_x * s->img_y;
+ stbi__uint16* p = (stbi__uint16*)z->out;
+
+ // compute color-based transparency, assuming we've
+ // already got 65535 as the alpha value in the output
+ STBI_ASSERT(out_n == 2 || out_n == 4);
+
+ if (out_n == 2) {
+ for (i = 0; i < pixel_count; ++i) {
+ p[1] = (p[0] == tc[0] ? 0 : 65535);
+ p += 2;
+ }
+ } else {
+ for (i = 0; i < pixel_count; ++i) {
+ if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
+ p[3] = 0;
+ p += 4;
+ }
+ }
+ return 1;
+}
+
+static int stbi__expand_png_palette(stbi__png* a, stbi_uc* palette, int len, int pal_img_n)
+{
+ stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y;
+ stbi_uc *p, *temp_out, *orig = a->out;
+
+ p = (stbi_uc*)stbi__malloc_mad2(pixel_count, pal_img_n, 0);
+ if (p == NULL)
+ return stbi__err("outofmem", "Out of memory");
+
+ // between here and free(out) below, exitting would leak
+ temp_out = p;
+
+ if (pal_img_n == 3) {
+ for (i = 0; i < pixel_count; ++i) {
+ int n = orig[i] * 4;
+ p[0] = palette[n];
+ p[1] = palette[n + 1];
+ p[2] = palette[n + 2];
+ p += 3;
+ }
+ } else {
+ for (i = 0; i < pixel_count; ++i) {
+ int n = orig[i] * 4;
+ p[0] = palette[n];
+ p[1] = palette[n + 1];
+ p[2] = palette[n + 2];
+ p[3] = palette[n + 3];
+ p += 4;
+ }
+ }
+ STBI_FREE(a->out);
+ a->out = temp_out;
+
+ STBI_NOTUSED(len);
+
+ return 1;
+}
+
+static int stbi__unpremultiply_on_load_global = 0;
+static int stbi__de_iphone_flag_global = 0;
+
+STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply)
+{
+ stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply;
+}
+
+STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
+{
+ stbi__de_iphone_flag_global = flag_true_if_should_convert;
+}
+
+# ifndef STBI_THREAD_LOCAL
+# define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global
+# define stbi__de_iphone_flag stbi__de_iphone_flag_global
+# else
+static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set;
+static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set;
+
+STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
+{
+ stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply;
+ stbi__unpremultiply_on_load_set = 1;
+}
+
+STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert)
+{
+ stbi__de_iphone_flag_local = flag_true_if_should_convert;
+ stbi__de_iphone_flag_set = 1;
+}
+
+# define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \
+ ? stbi__unpremultiply_on_load_local \
+ : stbi__unpremultiply_on_load_global)
+# define stbi__de_iphone_flag (stbi__de_iphone_flag_set \
+ ? stbi__de_iphone_flag_local \
+ : stbi__de_iphone_flag_global)
+# endif // STBI_THREAD_LOCAL
+
+static void stbi__de_iphone(stbi__png* z)
+{
+ stbi__context* s = z->s;
+ stbi__uint32 i, pixel_count = s->img_x * s->img_y;
+ stbi_uc* p = z->out;
+
+ if (s->img_out_n == 3) { // convert bgr to rgb
+ for (i = 0; i < pixel_count; ++i) {
+ stbi_uc t = p[0];
+ p[0] = p[2];
+ p[2] = t;
+ p += 3;
+ }
+ } else {
+ STBI_ASSERT(s->img_out_n == 4);
+ if (stbi__unpremultiply_on_load) {
+ // convert bgr to rgb and unpremultiply
+ for (i = 0; i < pixel_count; ++i) {
+ stbi_uc a = p[3];
+ stbi_uc t = p[0];
+ if (a) {
+ stbi_uc half = a / 2;
+ p[0] = (p[2] * 255 + half) / a;
+ p[1] = (p[1] * 255 + half) / a;
+ p[2] = (t * 255 + half) / a;
+ } else {
+ p[0] = p[2];
+ p[2] = t;
+ }
+ p += 4;
+ }
+ } else {
+ // convert bgr to rgb
+ for (i = 0; i < pixel_count; ++i) {
+ stbi_uc t = p[0];
+ p[0] = p[2];
+ p[2] = t;
+ p += 4;
+ }
+ }
+ }
+}
+
+# define STBI__PNG_TYPE(a, b, c, d) (((unsigned)(a) << 24) + ((unsigned)(b) << 16) + ((unsigned)(c) << 8) + (unsigned)(d))
+
+static int stbi__parse_png_file(stbi__png* z, int scan, int req_comp)
+{
+ stbi_uc palette[1024], pal_img_n = 0;
+ stbi_uc has_trans = 0, tc[3] = { 0 };
+ stbi__uint16 tc16[3];
+ stbi__uint32 ioff = 0, idata_limit = 0, i, pal_len = 0;
+ int first = 1, k, interlace = 0, color = 0, is_iphone = 0;
+ stbi__context* s = z->s;
+
+ z->expanded = NULL;
+ z->idata = NULL;
+ z->out = NULL;
+
+ if (!stbi__check_png_header(s))
+ return 0;
+
+ if (scan == STBI__SCAN_type)
+ return 1;
+
+ for (;;) {
+ stbi__pngchunk c = stbi__get_chunk_header(s);
+ switch (c.type) {
+ case STBI__PNG_TYPE('C', 'g', 'B', 'I'):
+ is_iphone = 1;
+ stbi__skip(s, c.length);
+ break;
+ case STBI__PNG_TYPE('I', 'H', 'D', 'R'): {
+ int comp, filter;
+ if (!first)
+ return stbi__err("multiple IHDR", "Corrupt PNG");
+ first = 0;
+ if (c.length != 13)
+ return stbi__err("bad IHDR len", "Corrupt PNG");
+ s->img_x = stbi__get32be(s);
+ s->img_y = stbi__get32be(s);
+ if (s->img_y > STBI_MAX_DIMENSIONS)
+ return stbi__err("too large", "Very large image (corrupt?)");
+ if (s->img_x > STBI_MAX_DIMENSIONS)
+ return stbi__err("too large", "Very large image (corrupt?)");
+ z->depth = stbi__get8(s);
+ if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16)
+ return stbi__err("1/2/4/8/16-bit only", "PNG not supported: 1/2/4/8/16-bit only");
+ color = stbi__get8(s);
+ if (color > 6)
+ return stbi__err("bad ctype", "Corrupt PNG");
+ if (color == 3 && z->depth == 16)
+ return stbi__err("bad ctype", "Corrupt PNG");
+ if (color == 3)
+ pal_img_n = 3;
+ else if (color & 1)
+ return stbi__err("bad ctype", "Corrupt PNG");
+ comp = stbi__get8(s);
+ if (comp)
+ return stbi__err("bad comp method", "Corrupt PNG");
+ filter = stbi__get8(s);
+ if (filter)
+ return stbi__err("bad filter method", "Corrupt PNG");
+ interlace = stbi__get8(s);
+ if (interlace > 1)
+ return stbi__err("bad interlace method", "Corrupt PNG");
+ if (!s->img_x || !s->img_y)
+ return stbi__err("0-pixel image", "Corrupt PNG");
+ if (!pal_img_n) {
+ s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
+ if ((1 << 30) / s->img_x / s->img_n < s->img_y)
+ return stbi__err("too large", "Image too large to decode");
+ } else {
+ // if paletted, then pal_n is our final components, and
+ // img_n is # components to decompress/filter.
+ s->img_n = 1;
+ if ((1 << 30) / s->img_x / 4 < s->img_y)
+ return stbi__err("too large", "Corrupt PNG");
+ }
+ // even with SCAN_header, have to scan to see if we have a tRNS
+ break;
+ }
+
+ case STBI__PNG_TYPE('P', 'L', 'T', 'E'): {
+ if (first)
+ return stbi__err("first not IHDR", "Corrupt PNG");
+ if (c.length > 256 * 3)
+ return stbi__err("invalid PLTE", "Corrupt PNG");
+ pal_len = c.length / 3;
+ if (pal_len * 3 != c.length)
+ return stbi__err("invalid PLTE", "Corrupt PNG");
+ for (i = 0; i < pal_len; ++i) {
+ palette[i * 4 + 0] = stbi__get8(s);
+ palette[i * 4 + 1] = stbi__get8(s);
+ palette[i * 4 + 2] = stbi__get8(s);
+ palette[i * 4 + 3] = 255;
+ }
+ break;
+ }
+
+ case STBI__PNG_TYPE('t', 'R', 'N', 'S'): {
+ if (first)
+ return stbi__err("first not IHDR", "Corrupt PNG");
+ if (z->idata)
+ return stbi__err("tRNS after IDAT", "Corrupt PNG");
+ if (pal_img_n) {
+ if (scan == STBI__SCAN_header) {
+ s->img_n = 4;
+ return 1;
+ }
+ if (pal_len == 0)
+ return stbi__err("tRNS before PLTE", "Corrupt PNG");
+ if (c.length > pal_len)
+ return stbi__err("bad tRNS len", "Corrupt PNG");
+ pal_img_n = 4;
+ for (i = 0; i < c.length; ++i)
+ palette[i * 4 + 3] = stbi__get8(s);
+ } else {
+ if (!(s->img_n & 1))
+ return stbi__err("tRNS with alpha", "Corrupt PNG");
+ if (c.length != (stbi__uint32)s->img_n * 2)
+ return stbi__err("bad tRNS len", "Corrupt PNG");
+ has_trans = 1;
+ // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now.
+ if (scan == STBI__SCAN_header) {
+ ++s->img_n;
+ return 1;
+ }
+ if (z->depth == 16) {
+ for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning
+ tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
+ } else {
+ for (k = 0; k < s->img_n && k < 3; ++k)
+ tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
+ }
+ }
+ break;
+ }
+
+ case STBI__PNG_TYPE('I', 'D', 'A', 'T'): {
+ if (first)
+ return stbi__err("first not IHDR", "Corrupt PNG");
+ if (pal_img_n && !pal_len)
+ return stbi__err("no PLTE", "Corrupt PNG");
+ if (scan == STBI__SCAN_header) {
+ // header scan definitely stops at first IDAT
+ if (pal_img_n)
+ s->img_n = pal_img_n;
+ return 1;
+ }
+ if (c.length > (1u << 30))
+ return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes");
+ if ((int)(ioff + c.length) < (int)ioff)
+ return 0;
+ if (ioff + c.length > idata_limit) {
+ stbi__uint32 idata_limit_old = idata_limit;
+ stbi_uc* p;
+ if (idata_limit == 0)
+ idata_limit = c.length > 4096 ? c.length : 4096;
+ while (ioff + c.length > idata_limit)
+ idata_limit *= 2;
+ STBI_NOTUSED(idata_limit_old);
+ p = (stbi_uc*)STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit);
+ if (p == NULL)
+ return stbi__err("outofmem", "Out of memory");
+ z->idata = p;
+ }
+ if (!stbi__getn(s, z->idata + ioff, c.length))
+ return stbi__err("outofdata", "Corrupt PNG");
+ ioff += c.length;
+ break;
+ }
+
+ case STBI__PNG_TYPE('I', 'E', 'N', 'D'): {
+ stbi__uint32 raw_len, bpl;
+ if (first)
+ return stbi__err("first not IHDR", "Corrupt PNG");
+ if (scan != STBI__SCAN_load)
+ return 1;
+ if (z->idata == NULL)
+ return stbi__err("no IDAT", "Corrupt PNG");
+ // initial guess for decoded data size to avoid unnecessary reallocs
+ bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component
+ raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */;
+ z->expanded = (stbi_uc*)stbi_zlib_decode_malloc_guesssize_headerflag((char*)z->idata, ioff, raw_len, (int*)&raw_len, !is_iphone);
+ if (z->expanded == NULL)
+ return 0; // zlib should set error
+ STBI_FREE(z->idata);
+ z->idata = NULL;
+ if ((req_comp == s->img_n + 1 && req_comp != 3 && !pal_img_n) || has_trans)
+ s->img_out_n = s->img_n + 1;
+ else
+ s->img_out_n = s->img_n;
+ if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace))
+ return 0;
+ if (has_trans) {
+ if (z->depth == 16) {
+ if (!stbi__compute_transparency16(z, tc16, s->img_out_n))
+ return 0;
+ } else {
+ if (!stbi__compute_transparency(z, tc, s->img_out_n))
+ return 0;
+ }
+ }
+ if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2)
+ stbi__de_iphone(z);
+ if (pal_img_n) {
+ // pal_img_n == 3 or 4
+ s->img_n = pal_img_n; // record the actual colors we had
+ s->img_out_n = pal_img_n;
+ if (req_comp >= 3)
+ s->img_out_n = req_comp;
+ if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n))
+ return 0;
+ } else if (has_trans) {
+ // non-paletted image with tRNS -> source image has (constant) alpha
+ ++s->img_n;
+ }
+ STBI_FREE(z->expanded);
+ z->expanded = NULL;
+ // end of PNG chunk, read and skip CRC
+ stbi__get32be(s);
+ return 1;
+ }
+
+ default:
+ // if critical, fail
+ if (first)
+ return stbi__err("first not IHDR", "Corrupt PNG");
+ if ((c.type & (1 << 29)) == 0) {
+# ifndef STBI_NO_FAILURE_STRINGS
+ // not threadsafe
+ static char invalid_chunk[] = "XXXX PNG chunk not known";
+ invalid_chunk[0] = STBI__BYTECAST(c.type >> 24);
+ invalid_chunk[1] = STBI__BYTECAST(c.type >> 16);
+ invalid_chunk[2] = STBI__BYTECAST(c.type >> 8);
+ invalid_chunk[3] = STBI__BYTECAST(c.type >> 0);
+# endif
+ return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type");
+ }
+ stbi__skip(s, c.length);
+ break;
+ }
+ // end of PNG chunk, read and skip CRC
+ stbi__get32be(s);
+ }
+}
+
+static void* stbi__do_png(stbi__png* p, int* x, int* y, int* n, int req_comp, stbi__result_info* ri)
+{
+ void* result = NULL;
+ if (req_comp < 0 || req_comp > 4)
+ return stbi__errpuc("bad req_comp", "Internal error");
+ if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) {
+ if (p->depth <= 8)
+ ri->bits_per_channel = 8;
+ else if (p->depth == 16)
+ ri->bits_per_channel = 16;
+ else
+ return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth");
+ result = p->out;
+ p->out = NULL;
+ if (req_comp && req_comp != p->s->img_out_n) {
+ if (ri->bits_per_channel == 8)
+ result = stbi__convert_format((unsigned char*)result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
+ else
+ result = stbi__convert_format16((stbi__uint16*)result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
+ p->s->img_out_n = req_comp;
+ if (result == NULL)
+ return result;
+ }
+ *x = p->s->img_x;
+ *y = p->s->img_y;
+ if (n)
+ *n = p->s->img_n;
+ }
+ STBI_FREE(p->out);
+ p->out = NULL;
+ STBI_FREE(p->expanded);
+ p->expanded = NULL;
+ STBI_FREE(p->idata);
+ p->idata = NULL;
+
+ return result;
+}
+
+static void* stbi__png_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri)
+{
+ stbi__png p;
+ p.s = s;
+ return stbi__do_png(&p, x, y, comp, req_comp, ri);
+}
+
+static int stbi__png_test(stbi__context* s)
+{
+ int r;
+ r = stbi__check_png_header(s);
+ stbi__rewind(s);
+ return r;
+}
+
+static int stbi__png_info_raw(stbi__png* p, int* x, int* y, int* comp)
+{
+ if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) {
+ stbi__rewind(p->s);
+ return 0;
+ }
+ if (x)
+ *x = p->s->img_x;
+ if (y)
+ *y = p->s->img_y;
+ if (comp)
+ *comp = p->s->img_n;
+ return 1;
+}
+
+static int stbi__png_info(stbi__context* s, int* x, int* y, int* comp)
+{
+ stbi__png p;
+ p.s = s;
+ return stbi__png_info_raw(&p, x, y, comp);
+}
+
+static int stbi__png_is16(stbi__context* s)
+{
+ stbi__png p;
+ p.s = s;
+ if (!stbi__png_info_raw(&p, NULL, NULL, NULL))
+ return 0;
+ if (p.depth != 16) {
+ stbi__rewind(p.s);
+ return 0;
+ }
+ return 1;
+}
+# endif
+
+// Microsoft/Windows BMP image
+
+# ifndef STBI_NO_BMP
+static int stbi__bmp_test_raw(stbi__context* s)
+{
+ int r;
+ int sz;
+ if (stbi__get8(s) != 'B')
+ return 0;
+ if (stbi__get8(s) != 'M')
+ return 0;
+ stbi__get32le(s); // discard filesize
+ stbi__get16le(s); // discard reserved
+ stbi__get16le(s); // discard reserved
+ stbi__get32le(s); // discard data offset
+ sz = stbi__get32le(s);
+ r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124);
+ return r;
+}
+
+static int stbi__bmp_test(stbi__context* s)
+{
+ int r = stbi__bmp_test_raw(s);
+ stbi__rewind(s);
+ return r;
+}
+
+// returns 0..31 for the highest set bit
+static int stbi__high_bit(unsigned int z)
+{
+ int n = 0;
+ if (z == 0)
+ return -1;
+ if (z >= 0x10000) {
+ n += 16;
+ z >>= 16;
+ }
+ if (z >= 0x00100) {
+ n += 8;
+ z >>= 8;
+ }
+ if (z >= 0x00010) {
+ n += 4;
+ z >>= 4;
+ }
+ if (z >= 0x00004) {
+ n += 2;
+ z >>= 2;
+ }
+ if (z >= 0x00002) {
+ n += 1; /* >>= 1;*/
+ }
+ return n;
+}
+
+static int stbi__bitcount(unsigned int a)
+{
+ a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2
+ a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4
+ a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits
+ a = (a + (a >> 8)); // max 16 per 8 bits
+ a = (a + (a >> 16)); // max 32 per 8 bits
+ return a & 0xff;
+}
+
+// extract an arbitrarily-aligned N-bit value (N=bits)
+// from v, and then make it 8-bits long and fractionally
+// extend it to full full range.
+static int stbi__shiftsigned(unsigned int v, int shift, int bits)
+{
+ static unsigned int mul_table[9] = {
+ 0,
+ 0xff /*0b11111111*/,
+ 0x55 /*0b01010101*/,
+ 0x49 /*0b01001001*/,
+ 0x11 /*0b00010001*/,
+ 0x21 /*0b00100001*/,
+ 0x41 /*0b01000001*/,
+ 0x81 /*0b10000001*/,
+ 0x01 /*0b00000001*/,
+ };
+ static unsigned int shift_table[9] = {
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 2,
+ 4,
+ 6,
+ 0,
+ };
+ if (shift < 0)
+ v <<= -shift;
+ else
+ v >>= shift;
+ STBI_ASSERT(v < 256);
+ v >>= (8 - bits);
+ STBI_ASSERT(bits >= 0 && bits <= 8);
+ return (int)((unsigned)v * mul_table[bits]) >> shift_table[bits];
+}
+
+typedef struct
+{
+ int bpp, offset, hsz;
+ unsigned int mr, mg, mb, ma, all_a;
+ int extra_read;
+} stbi__bmp_data;
+
+static int stbi__bmp_set_mask_defaults(stbi__bmp_data* info, int compress)
+{
+ // BI_BITFIELDS specifies masks explicitly, don't override
+ if (compress == 3)
+ return 1;
+
+ if (compress == 0) {
+ if (info->bpp == 16) {
+ info->mr = 31u << 10;
+ info->mg = 31u << 5;
+ info->mb = 31u << 0;
+ } else if (info->bpp == 32) {
+ info->mr = 0xffu << 16;
+ info->mg = 0xffu << 8;
+ info->mb = 0xffu << 0;
+ info->ma = 0xffu << 24;
+ info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0
+ } else {
+ // otherwise, use defaults, which is all-0
+ info->mr = info->mg = info->mb = info->ma = 0;
+ }
+ return 1;
+ }
+ return 0; // error
+}
+
+static void* stbi__bmp_parse_header(stbi__context* s, stbi__bmp_data* info)
+{
+ int hsz;
+ if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M')
+ return stbi__errpuc("not BMP", "Corrupt BMP");
+ stbi__get32le(s); // discard filesize
+ stbi__get16le(s); // discard reserved
+ stbi__get16le(s); // discard reserved
+ info->offset = stbi__get32le(s);
+ info->hsz = hsz = stbi__get32le(s);
+ info->mr = info->mg = info->mb = info->ma = 0;
+ info->extra_read = 14;
+
+ if (info->offset < 0)
+ return stbi__errpuc("bad BMP", "bad BMP");
+
+ if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124)
+ return stbi__errpuc("unknown BMP", "BMP type not supported: unknown");
+ if (hsz == 12) {
+ s->img_x = stbi__get16le(s);
+ s->img_y = stbi__get16le(s);
+ } else {
+ s->img_x = stbi__get32le(s);
+ s->img_y = stbi__get32le(s);
+ }
+ if (stbi__get16le(s) != 1)
+ return stbi__errpuc("bad BMP", "bad BMP");
+ info->bpp = stbi__get16le(s);
+ if (hsz != 12) {
+ int compress = stbi__get32le(s);
+ if (compress == 1 || compress == 2)
+ return stbi__errpuc("BMP RLE", "BMP type not supported: RLE");
+ if (compress >= 4)
+ return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes
+ if (compress == 3 && info->bpp != 16 && info->bpp != 32)
+ return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel
+ stbi__get32le(s); // discard sizeof
+ stbi__get32le(s); // discard hres
+ stbi__get32le(s); // discard vres
+ stbi__get32le(s); // discard colorsused
+ stbi__get32le(s); // discard max important
+ if (hsz == 40 || hsz == 56) {
+ if (hsz == 56) {
+ stbi__get32le(s);
+ stbi__get32le(s);
+ stbi__get32le(s);
+ stbi__get32le(s);
+ }
+ if (info->bpp == 16 || info->bpp == 32) {
+ if (compress == 0) {
+ stbi__bmp_set_mask_defaults(info, compress);
+ } else if (compress == 3) {
+ info->mr = stbi__get32le(s);
+ info->mg = stbi__get32le(s);
+ info->mb = stbi__get32le(s);
+ info->extra_read += 12;
+ // not documented, but generated by photoshop and handled by mspaint
+ if (info->mr == info->mg && info->mg == info->mb) {
+ // ?!?!?
+ return stbi__errpuc("bad BMP", "bad BMP");
+ }
+ } else
+ return stbi__errpuc("bad BMP", "bad BMP");
+ }
+ } else {
+ // V4/V5 header
+ int i;
+ if (hsz != 108 && hsz != 124)
+ return stbi__errpuc("bad BMP", "bad BMP");
+ info->mr = stbi__get32le(s);
+ info->mg = stbi__get32le(s);
+ info->mb = stbi__get32le(s);
+ info->ma = stbi__get32le(s);
+ if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs
+ stbi__bmp_set_mask_defaults(info, compress);
+ stbi__get32le(s); // discard color space
+ for (i = 0; i < 12; ++i)
+ stbi__get32le(s); // discard color space parameters
+ if (hsz == 124) {
+ stbi__get32le(s); // discard rendering intent
+ stbi__get32le(s); // discard offset of profile data
+ stbi__get32le(s); // discard size of profile data
+ stbi__get32le(s); // discard reserved
+ }
+ }
+ }
+ return (void*)1;
+}
+
+static void* stbi__bmp_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri)
+{
+ stbi_uc* out;
+ unsigned int mr = 0, mg = 0, mb = 0, ma = 0, all_a;
+ stbi_uc pal[256][4];
+ int psize = 0, i, j, width;
+ int flip_vertically, pad, target;
+ stbi__bmp_data info;
+ STBI_NOTUSED(ri);
+
+ info.all_a = 255;
+ if (stbi__bmp_parse_header(s, &info) == NULL)
+ return NULL; // error code already set
+
+ flip_vertically = ((int)s->img_y) > 0;
+ s->img_y = abs((int)s->img_y);
+
+ if (s->img_y > STBI_MAX_DIMENSIONS)
+ return stbi__errpuc("too large", "Very large image (corrupt?)");
+ if (s->img_x > STBI_MAX_DIMENSIONS)
+ return stbi__errpuc("too large", "Very large image (corrupt?)");
+
+ mr = info.mr;
+ mg = info.mg;
+ mb = info.mb;
+ ma = info.ma;
+ all_a = info.all_a;
+
+ if (info.hsz == 12) {
+ if (info.bpp < 24)
+ psize = (info.offset - info.extra_read - 24) / 3;
+ } else {
+ if (info.bpp < 16)
+ psize = (info.offset - info.extra_read - info.hsz) >> 2;
+ }
+ if (psize == 0) {
+ // accept some number of extra bytes after the header, but if the offset points either to before
+ // the header ends or implies a large amount of extra data, reject the file as malformed
+ int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original);
+ int header_limit = 1024; // max we actually read is below 256 bytes currently.
+ int extra_data_limit = 256 * 4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size.
+ if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) {
+ return stbi__errpuc("bad header", "Corrupt BMP");
+ }
+ // we established that bytes_read_so_far is positive and sensible.
+ // the first half of this test rejects offsets that are either too small positives, or
+ // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn
+ // ensures the number computed in the second half of the test can't overflow.
+ if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) {
+ return stbi__errpuc("bad offset", "Corrupt BMP");
+ } else {
+ stbi__skip(s, info.offset - bytes_read_so_far);
+ }
+ }
+
+ if (info.bpp == 24 && ma == 0xff000000)
+ s->img_n = 3;
+ else
+ s->img_n = ma ? 4 : 3;
+ if (req_comp && req_comp >= 3) // we can directly decode 3 or 4
+ target = req_comp;
+ else
+ target = s->img_n; // if they want monochrome, we'll post-convert
+
+ // sanity-check size
+ if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0))
+ return stbi__errpuc("too large", "Corrupt BMP");
+
+ out = (stbi_uc*)stbi__malloc_mad3(target, s->img_x, s->img_y, 0);
+ if (!out)
+ return stbi__errpuc("outofmem", "Out of memory");
+ if (info.bpp < 16) {
+ int z = 0;
+ if (psize == 0 || psize > 256) {
+ STBI_FREE(out);
+ return stbi__errpuc("invalid", "Corrupt BMP");
+ }
+ for (i = 0; i < psize; ++i) {
+ pal[i][2] = stbi__get8(s);
+ pal[i][1] = stbi__get8(s);
+ pal[i][0] = stbi__get8(s);
+ if (info.hsz != 12)
+ stbi__get8(s);
+ pal[i][3] = 255;
+ }
+ stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4));
+ if (info.bpp == 1)
+ width = (s->img_x + 7) >> 3;
+ else if (info.bpp == 4)
+ width = (s->img_x + 1) >> 1;
+ else if (info.bpp == 8)
+ width = s->img_x;
+ else {
+ STBI_FREE(out);
+ return stbi__errpuc("bad bpp", "Corrupt BMP");
+ }
+ pad = (-width) & 3;
+ if (info.bpp == 1) {
+ for (j = 0; j < (int)s->img_y; ++j) {
+ int bit_offset = 7, v = stbi__get8(s);
+ for (i = 0; i < (int)s->img_x; ++i) {
+ int color = (v >> bit_offset) & 0x1;
+ out[z++] = pal[color][0];
+ out[z++] = pal[color][1];
+ out[z++] = pal[color][2];
+ if (target == 4)
+ out[z++] = 255;
+ if (i + 1 == (int)s->img_x)
+ break;
+ if ((--bit_offset) < 0) {
+ bit_offset = 7;
+ v = stbi__get8(s);
+ }
+ }
+ stbi__skip(s, pad);
+ }
+ } else {
+ for (j = 0; j < (int)s->img_y; ++j) {
+ for (i = 0; i < (int)s->img_x; i += 2) {
+ int v = stbi__get8(s), v2 = 0;
+ if (info.bpp == 4) {
+ v2 = v & 15;
+ v >>= 4;
+ }
+ out[z++] = pal[v][0];
+ out[z++] = pal[v][1];
+ out[z++] = pal[v][2];
+ if (target == 4)
+ out[z++] = 255;
+ if (i + 1 == (int)s->img_x)
+ break;
+ v = (info.bpp == 8) ? stbi__get8(s) : v2;
+ out[z++] = pal[v][0];
+ out[z++] = pal[v][1];
+ out[z++] = pal[v][2];
+ if (target == 4)
+ out[z++] = 255;
+ }
+ stbi__skip(s, pad);
+ }
+ }
+ } else {
+ int rshift = 0, gshift = 0, bshift = 0, ashift = 0, rcount = 0, gcount = 0, bcount = 0, acount = 0;
+ int z = 0;
+ int easy = 0;
+ stbi__skip(s, info.offset - info.extra_read - info.hsz);
+ if (info.bpp == 24)
+ width = 3 * s->img_x;
+ else if (info.bpp == 16)
+ width = 2 * s->img_x;
+ else /* bpp = 32 and pad = 0 */
+ width = 0;
+ pad = (-width) & 3;
+ if (info.bpp == 24) {
+ easy = 1;
+ } else if (info.bpp == 32) {
+ if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000)
+ easy = 2;
+ }
+ if (!easy) {
+ if (!mr || !mg || !mb) {
+ STBI_FREE(out);
+ return stbi__errpuc("bad masks", "Corrupt BMP");
+ }
+ // right shift amt to put high bit in position #7
+ rshift = stbi__high_bit(mr) - 7;
+ rcount = stbi__bitcount(mr);
+ gshift = stbi__high_bit(mg) - 7;
+ gcount = stbi__bitcount(mg);
+ bshift = stbi__high_bit(mb) - 7;
+ bcount = stbi__bitcount(mb);
+ ashift = stbi__high_bit(ma) - 7;
+ acount = stbi__bitcount(ma);
+ if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) {
+ STBI_FREE(out);
+ return stbi__errpuc("bad masks", "Corrupt BMP");
+ }
+ }
+ for (j = 0; j < (int)s->img_y; ++j) {
+ if (easy) {
+ for (i = 0; i < (int)s->img_x; ++i) {
+ unsigned char a;
+ out[z + 2] = stbi__get8(s);
+ out[z + 1] = stbi__get8(s);
+ out[z + 0] = stbi__get8(s);
+ z += 3;
+ a = (easy == 2 ? stbi__get8(s) : 255);
+ all_a |= a;
+ if (target == 4)
+ out[z++] = a;
+ }
+ } else {
+ int bpp = info.bpp;
+ for (i = 0; i < (int)s->img_x; ++i) {
+ stbi__uint32 v = (bpp == 16 ? (stbi__uint32)stbi__get16le(s) : stbi__get32le(s));
+ unsigned int a;
+ out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount));
+ out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount));
+ out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount));
+ a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255);
+ all_a |= a;
+ if (target == 4)
+ out[z++] = STBI__BYTECAST(a);
+ }
+ }
+ stbi__skip(s, pad);
+ }
+ }
+
+ // if alpha channel is all 0s, replace with all 255s
+ if (target == 4 && all_a == 0)
+ for (i = 4 * s->img_x * s->img_y - 1; i >= 0; i -= 4)
+ out[i] = 255;
+
+ if (flip_vertically) {
+ stbi_uc t;
+ for (j = 0; j < (int)s->img_y >> 1; ++j) {
+ stbi_uc* p1 = out + j * s->img_x * target;
+ stbi_uc* p2 = out + (s->img_y - 1 - j) * s->img_x * target;
+ for (i = 0; i < (int)s->img_x * target; ++i) {
+ t = p1[i];
+ p1[i] = p2[i];
+ p2[i] = t;
+ }
+ }
+ }
+
+ if (req_comp && req_comp != target) {
+ out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y);
+ if (out == NULL)
+ return out; // stbi__convert_format frees input on failure
+ }
+
+ *x = s->img_x;
+ *y = s->img_y;
+ if (comp)
+ *comp = s->img_n;
+ return out;
+}
+# endif
+
+// Targa Truevision - TGA
+// by Jonathan Dummer
+# ifndef STBI_NO_TGA
+// returns STBI_rgb or whatever, 0 on error
+static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16)
+{
+ // only RGB or RGBA (incl. 16bit) or grey allowed
+ if (is_rgb16)
+ *is_rgb16 = 0;
+ switch (bits_per_pixel) {
+ case 8:
+ return STBI_grey;
+ case 16:
+ if (is_grey)
+ return STBI_grey_alpha;
+ // fallthrough
+ case 15:
+ if (is_rgb16)
+ *is_rgb16 = 1;
+ return STBI_rgb;
+ case 24: // fallthrough
+ case 32:
+ return bits_per_pixel / 8;
+ default:
+ return 0;
+ }
+}
+
+static int stbi__tga_info(stbi__context* s, int* x, int* y, int* comp)
+{
+ int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp;
+ int sz, tga_colormap_type;
+ stbi__get8(s); // discard Offset
+ tga_colormap_type = stbi__get8(s); // colormap type
+ if (tga_colormap_type > 1) {
+ stbi__rewind(s);
+ return 0; // only RGB or indexed allowed
+ }
+ tga_image_type = stbi__get8(s); // image type
+ if (tga_colormap_type == 1) { // colormapped (paletted) image
+ if (tga_image_type != 1 && tga_image_type != 9) {
+ stbi__rewind(s);
+ return 0;
+ }
+ stbi__skip(s, 4); // skip index of first colormap entry and number of entries
+ sz = stbi__get8(s); // check bits per palette color entry
+ if ((sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32)) {
+ stbi__rewind(s);
+ return 0;
+ }
+ stbi__skip(s, 4); // skip image x and y origin
+ tga_colormap_bpp = sz;
+ } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE
+ if ((tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11)) {
+ stbi__rewind(s);
+ return 0; // only RGB or grey allowed, +/- RLE
+ }
+ stbi__skip(s, 9); // skip colormap specification and image x/y origin
+ tga_colormap_bpp = 0;
+ }
+ tga_w = stbi__get16le(s);
+ if (tga_w < 1) {
+ stbi__rewind(s);
+ return 0; // test width
+ }
+ tga_h = stbi__get16le(s);
+ if (tga_h < 1) {
+ stbi__rewind(s);
+ return 0; // test height
+ }
+ tga_bits_per_pixel = stbi__get8(s); // bits per pixel
+ stbi__get8(s); // ignore alpha bits
+ if (tga_colormap_bpp != 0) {
+ if ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) {
+ // when using a colormap, tga_bits_per_pixel is the size of the indexes
+ // I don't think anything but 8 or 16bit indexes makes sense
+ stbi__rewind(s);
+ return 0;
+ }
+ tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL);
+ } else {
+ tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL);
+ }
+ if (!tga_comp) {
+ stbi__rewind(s);
+ return 0;
+ }
+ if (x)
+ *x = tga_w;
+ if (y)
+ *y = tga_h;
+ if (comp)
+ *comp = tga_comp;
+ return 1; // seems to have passed everything
+}
+
+static int stbi__tga_test(stbi__context* s)
+{
+ int res = 0;
+ int sz, tga_color_type;
+ stbi__get8(s); // discard Offset
+ tga_color_type = stbi__get8(s); // color type
+ if (tga_color_type > 1)
+ goto errorEnd; // only RGB or indexed allowed
+ sz = stbi__get8(s); // image type
+ if (tga_color_type == 1) { // colormapped (paletted) image
+ if (sz != 1 && sz != 9)
+ goto errorEnd; // colortype 1 demands image type 1 or 9
+ stbi__skip(s, 4); // skip index of first colormap entry and number of entries
+ sz = stbi__get8(s); // check bits per palette color entry
+ if ((sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32))
+ goto errorEnd;
+ stbi__skip(s, 4); // skip image x and y origin
+ } else { // "normal" image w/o colormap
+ if ((sz != 2) && (sz != 3) && (sz != 10) && (sz != 11))
+ goto errorEnd; // only RGB or grey allowed, +/- RLE
+ stbi__skip(s, 9); // skip colormap specification and image x/y origin
+ }
+ if (stbi__get16le(s) < 1)
+ goto errorEnd; // test width
+ if (stbi__get16le(s) < 1)
+ goto errorEnd; // test height
+ sz = stbi__get8(s); // bits per pixel
+ if ((tga_color_type == 1) && (sz != 8) && (sz != 16))
+ goto errorEnd; // for colormapped images, bpp is size of an index
+ if ((sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32))
+ goto errorEnd;
+
+ res = 1; // if we got this far, everything's good and we can return 1 instead of 0
+
+errorEnd:
+ stbi__rewind(s);
+ return res;
+}
+
+// read 16bit value and convert to 24bit RGB
+static void stbi__tga_read_rgb16(stbi__context* s, stbi_uc* out)
+{
+ stbi__uint16 px = (stbi__uint16)stbi__get16le(s);
+ stbi__uint16 fiveBitMask = 31;
+ // we have 3 channels with 5bits each
+ int r = (px >> 10) & fiveBitMask;
+ int g = (px >> 5) & fiveBitMask;
+ int b = px & fiveBitMask;
+ // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later
+ out[0] = (stbi_uc)((r * 255) / 31);
+ out[1] = (stbi_uc)((g * 255) / 31);
+ out[2] = (stbi_uc)((b * 255) / 31);
+
+ // some people claim that the most significant bit might be used for alpha
+ // (possibly if an alpha-bit is set in the "image descriptor byte")
+ // but that only made 16bit test images completely translucent..
+ // so let's treat all 15 and 16bit TGAs as RGB with no alpha.
+}
+
+static void* stbi__tga_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri)
+{
+ // read in the TGA header stuff
+ int tga_offset = stbi__get8(s);
+ int tga_indexed = stbi__get8(s);
+ int tga_image_type = stbi__get8(s);
+ int tga_is_RLE = 0;
+ int tga_palette_start = stbi__get16le(s);
+ int tga_palette_len = stbi__get16le(s);
+ int tga_palette_bits = stbi__get8(s);
+ int tga_x_origin = stbi__get16le(s);
+ int tga_y_origin = stbi__get16le(s);
+ int tga_width = stbi__get16le(s);
+ int tga_height = stbi__get16le(s);
+ int tga_bits_per_pixel = stbi__get8(s);
+ int tga_comp, tga_rgb16 = 0;
+ int tga_inverted = stbi__get8(s);
+ // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?)
+ // image data
+ unsigned char* tga_data;
+ unsigned char* tga_palette = NULL;
+ int i, j;
+ unsigned char raw_data[4] = { 0 };
+ int RLE_count = 0;
+ int RLE_repeating = 0;
+ int read_next_pixel = 1;
+ STBI_NOTUSED(ri);
+ STBI_NOTUSED(tga_x_origin); // @TODO
+ STBI_NOTUSED(tga_y_origin); // @TODO
+
+ if (tga_height > STBI_MAX_DIMENSIONS)
+ return stbi__errpuc("too large", "Very large image (corrupt?)");
+ if (tga_width > STBI_MAX_DIMENSIONS)
+ return stbi__errpuc("too large", "Very large image (corrupt?)");
+
+ // do a tiny bit of precessing
+ if (tga_image_type >= 8) {
+ tga_image_type -= 8;
+ tga_is_RLE = 1;
+ }
+ tga_inverted = 1 - ((tga_inverted >> 5) & 1);
+
+ // If I'm paletted, then I'll use the number of bits from the palette
+ if (tga_indexed)
+ tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16);
+ else
+ tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16);
+
+ if (!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency
+ return stbi__errpuc("bad format", "Can't find out TGA pixelformat");
+
+ // tga info
+ *x = tga_width;
+ *y = tga_height;
+ if (comp)
+ *comp = tga_comp;
+
+ if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0))
+ return stbi__errpuc("too large", "Corrupt TGA");
+
+ tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0);
+ if (!tga_data)
+ return stbi__errpuc("outofmem", "Out of memory");
+
+ // skip to the data's starting position (offset usually = 0)
+ stbi__skip(s, tga_offset);
+
+ if (!tga_indexed && !tga_is_RLE && !tga_rgb16) {
+ for (i = 0; i < tga_height; ++i) {
+ int row = tga_inverted ? tga_height - i - 1 : i;
+ stbi_uc* tga_row = tga_data + row * tga_width * tga_comp;
+ stbi__getn(s, tga_row, tga_width * tga_comp);
+ }
+ } else {
+ // do I need to load a palette?
+ if (tga_indexed) {
+ if (tga_palette_len == 0) { /* you have to have at least one entry! */
+ STBI_FREE(tga_data);
+ return stbi__errpuc("bad palette", "Corrupt TGA");
+ }
+
+ // any data to skip? (offset usually = 0)
+ stbi__skip(s, tga_palette_start);
+ // load the palette
+ tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0);
+ if (!tga_palette) {
+ STBI_FREE(tga_data);
+ return stbi__errpuc("outofmem", "Out of memory");
+ }
+ if (tga_rgb16) {
+ stbi_uc* pal_entry = tga_palette;
+ STBI_ASSERT(tga_comp == STBI_rgb);
+ for (i = 0; i < tga_palette_len; ++i) {
+ stbi__tga_read_rgb16(s, pal_entry);
+ pal_entry += tga_comp;
+ }
+ } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) {
+ STBI_FREE(tga_data);
+ STBI_FREE(tga_palette);
+ return stbi__errpuc("bad palette", "Corrupt TGA");
+ }
+ }
+ // load the data
+ for (i = 0; i < tga_width * tga_height; ++i) {
+ // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk?
+ if (tga_is_RLE) {
+ if (RLE_count == 0) {
+ // yep, get the next byte as a RLE command
+ int RLE_cmd = stbi__get8(s);
+ RLE_count = 1 + (RLE_cmd & 127);
+ RLE_repeating = RLE_cmd >> 7;
+ read_next_pixel = 1;
+ } else if (!RLE_repeating) {
+ read_next_pixel = 1;
+ }
+ } else {
+ read_next_pixel = 1;
+ }
+ // OK, if I need to read a pixel, do it now
+ if (read_next_pixel) {
+ // load however much data we did have
+ if (tga_indexed) {
+ // read in index, then perform the lookup
+ int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s);
+ if (pal_idx >= tga_palette_len) {
+ // invalid index
+ pal_idx = 0;
+ }
+ pal_idx *= tga_comp;
+ for (j = 0; j < tga_comp; ++j) {
+ raw_data[j] = tga_palette[pal_idx + j];
+ }
+ } else if (tga_rgb16) {
+ STBI_ASSERT(tga_comp == STBI_rgb);
+ stbi__tga_read_rgb16(s, raw_data);
+ } else {
+ // read in the data raw
+ for (j = 0; j < tga_comp; ++j) {
+ raw_data[j] = stbi__get8(s);
+ }
+ }
+ // clear the reading flag for the next pixel
+ read_next_pixel = 0;
+ } // end of reading a pixel
+
+ // copy data
+ for (j = 0; j < tga_comp; ++j)
+ tga_data[i * tga_comp + j] = raw_data[j];
+
+ // in case we're in RLE mode, keep counting down
+ --RLE_count;
+ }
+ // do I need to invert the image?
+ if (tga_inverted) {
+ for (j = 0; j * 2 < tga_height; ++j) {
+ int index1 = j * tga_width * tga_comp;
+ int index2 = (tga_height - 1 - j) * tga_width * tga_comp;
+ for (i = tga_width * tga_comp; i > 0; --i) {
+ unsigned char temp = tga_data[index1];
+ tga_data[index1] = tga_data[index2];
+ tga_data[index2] = temp;
+ ++index1;
+ ++index2;
+ }
+ }
+ }
+ // clear my palette, if I had one
+ if (tga_palette != NULL) {
+ STBI_FREE(tga_palette);
+ }
+ }
+
+ // swap RGB - if the source data was RGB16, it already is in the right order
+ if (tga_comp >= 3 && !tga_rgb16) {
+ unsigned char* tga_pixel = tga_data;
+ for (i = 0; i < tga_width * tga_height; ++i) {
+ unsigned char temp = tga_pixel[0];
+ tga_pixel[0] = tga_pixel[2];
+ tga_pixel[2] = temp;
+ tga_pixel += tga_comp;
+ }
+ }
+
+ // convert to target component count
+ if (req_comp && req_comp != tga_comp)
+ tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height);
+
+ // the things I do to get rid of an error message, and yet keep
+ // Microsoft's C compilers happy... [8^(
+ tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0;
+ STBI_NOTUSED(tga_palette_start);
+ // OK, done
+ return tga_data;
+}
+# endif
+
+// *************************************************************************************************
+// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB
+
+# ifndef STBI_NO_PSD
+static int stbi__psd_test(stbi__context* s)
+{
+ int r = (stbi__get32be(s) == 0x38425053);
+ stbi__rewind(s);
+ return r;
+}
+
+static int stbi__psd_decode_rle(stbi__context* s, stbi_uc* p, int pixelCount)
+{
+ int count, nleft, len;
+
+ count = 0;
+ while ((nleft = pixelCount - count) > 0) {
+ len = stbi__get8(s);
+ if (len == 128) {
+ // No-op.
+ } else if (len < 128) {
+ // Copy next len+1 bytes literally.
+ len++;
+ if (len > nleft)
+ return 0; // corrupt data
+ count += len;
+ while (len) {
+ *p = stbi__get8(s);
+ p += 4;
+ len--;
+ }
+ } else if (len > 128) {
+ stbi_uc val;
+ // Next -len+1 bytes in the dest are replicated from next source byte.
+ // (Interpret len as a negative 8-bit int.)
+ len = 257 - len;
+ if (len > nleft)
+ return 0; // corrupt data
+ val = stbi__get8(s);
+ count += len;
+ while (len) {
+ *p = val;
+ p += 4;
+ len--;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static void* stbi__psd_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri, int bpc)
+{
+ int pixelCount;
+ int channelCount, compression;
+ int channel, i;
+ int bitdepth;
+ int w, h;
+ stbi_uc* out;
+ STBI_NOTUSED(ri);
+
+ // Check identifier
+ if (stbi__get32be(s) != 0x38425053) // "8BPS"
+ return stbi__errpuc("not PSD", "Corrupt PSD image");
+
+ // Check file type version.
+ if (stbi__get16be(s) != 1)
+ return stbi__errpuc("wrong version", "Unsupported version of PSD image");
+
+ // Skip 6 reserved bytes.
+ stbi__skip(s, 6);
+
+ // Read the number of channels (R, G, B, A, etc).
+ channelCount = stbi__get16be(s);
+ if (channelCount < 0 || channelCount > 16)
+ return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image");
+
+ // Read the rows and columns of the image.
+ h = stbi__get32be(s);
+ w = stbi__get32be(s);
+
+ if (h > STBI_MAX_DIMENSIONS)
+ return stbi__errpuc("too large", "Very large image (corrupt?)");
+ if (w > STBI_MAX_DIMENSIONS)
+ return stbi__errpuc("too large", "Very large image (corrupt?)");
+
+ // Make sure the depth is 8 bits.
+ bitdepth = stbi__get16be(s);
+ if (bitdepth != 8 && bitdepth != 16)
+ return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit");
+
+ // Make sure the color mode is RGB.
+ // Valid options are:
+ // 0: Bitmap
+ // 1: Grayscale
+ // 2: Indexed color
+ // 3: RGB color
+ // 4: CMYK color
+ // 7: Multichannel
+ // 8: Duotone
+ // 9: Lab color
+ if (stbi__get16be(s) != 3)
+ return stbi__errpuc("wrong color format", "PSD is not in RGB color format");
+
+ // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.)
+ stbi__skip(s, stbi__get32be(s));
+
+ // Skip the image resources. (resolution, pen tool paths, etc)
+ stbi__skip(s, stbi__get32be(s));
+
+ // Skip the reserved data.
+ stbi__skip(s, stbi__get32be(s));
+
+ // Find out if the data is compressed.
+ // Known values:
+ // 0: no compression
+ // 1: RLE compressed
+ compression = stbi__get16be(s);
+ if (compression > 1)
+ return stbi__errpuc("bad compression", "PSD has an unknown compression format");
+
+ // Check size
+ if (!stbi__mad3sizes_valid(4, w, h, 0))
+ return stbi__errpuc("too large", "Corrupt PSD");
+
+ // Create the destination image.
+
+ if (!compression && bitdepth == 16 && bpc == 16) {
+ out = (stbi_uc*)stbi__malloc_mad3(8, w, h, 0);
+ ri->bits_per_channel = 16;
+ } else
+ out = (stbi_uc*)stbi__malloc(4 * w * h);
+
+ if (!out)
+ return stbi__errpuc("outofmem", "Out of memory");
+ pixelCount = w * h;
+
+ // Initialize the data to zero.
+ // memset( out, 0, pixelCount * 4 );
+
+ // Finally, the image data.
+ if (compression) {
+ // RLE as used by .PSD and .TIFF
+ // Loop until you get the number of unpacked bytes you are expecting:
+ // Read the next source byte into n.
+ // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.
+ // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.
+ // Else if n is 128, noop.
+ // Endloop
+
+ // The RLE-compressed data is preceded by a 2-byte data count for each row in the data,
+ // which we're going to just skip.
+ stbi__skip(s, h * channelCount * 2);
+
+ // Read the RLE data by channel.
+ for (channel = 0; channel < 4; channel++) {
+ stbi_uc* p;
+
+ p = out + channel;
+ if (channel >= channelCount) {
+ // Fill this channel with default data.
+ for (i = 0; i < pixelCount; i++, p += 4)
+ *p = (channel == 3 ? 255 : 0);
+ } else {
+ // Read the RLE data.
+ if (!stbi__psd_decode_rle(s, p, pixelCount)) {
+ STBI_FREE(out);
+ return stbi__errpuc("corrupt", "bad RLE data");
+ }
+ }
+ }
+
+ } else {
+ // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...)
+ // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image.
+
+ // Read the data by channel.
+ for (channel = 0; channel < 4; channel++) {
+ if (channel >= channelCount) {
+ // Fill this channel with default data.
+ if (bitdepth == 16 && bpc == 16) {
+ stbi__uint16* q = ((stbi__uint16*)out) + channel;
+ stbi__uint16 val = channel == 3 ? 65535 : 0;
+ for (i = 0; i < pixelCount; i++, q += 4)
+ *q = val;
+ } else {
+ stbi_uc* p = out + channel;
+ stbi_uc val = channel == 3 ? 255 : 0;
+ for (i = 0; i < pixelCount; i++, p += 4)
+ *p = val;
+ }
+ } else {
+ if (ri->bits_per_channel == 16) { // output bpc
+ stbi__uint16* q = ((stbi__uint16*)out) + channel;
+ for (i = 0; i < pixelCount; i++, q += 4)
+ *q = (stbi__uint16)stbi__get16be(s);
+ } else {
+ stbi_uc* p = out + channel;
+ if (bitdepth == 16) { // input bpc
+ for (i = 0; i < pixelCount; i++, p += 4)
+ *p = (stbi_uc)(stbi__get16be(s) >> 8);
+ } else {
+ for (i = 0; i < pixelCount; i++, p += 4)
+ *p = stbi__get8(s);
+ }
+ }
+ }
+ }
+ }
+
+ // remove weird white matte from PSD
+ if (channelCount >= 4) {
+ if (ri->bits_per_channel == 16) {
+ for (i = 0; i < w * h; ++i) {
+ stbi__uint16* pixel = (stbi__uint16*)out + 4 * i;
+ if (pixel[3] != 0 && pixel[3] != 65535) {
+ float a = pixel[3] / 65535.0f;
+ float ra = 1.0f / a;
+ float inv_a = 65535.0f * (1 - ra);
+ pixel[0] = (stbi__uint16)(pixel[0] * ra + inv_a);
+ pixel[1] = (stbi__uint16)(pixel[1] * ra + inv_a);
+ pixel[2] = (stbi__uint16)(pixel[2] * ra + inv_a);
+ }
+ }
+ } else {
+ for (i = 0; i < w * h; ++i) {
+ unsigned char* pixel = out + 4 * i;
+ if (pixel[3] != 0 && pixel[3] != 255) {
+ float a = pixel[3] / 255.0f;
+ float ra = 1.0f / a;
+ float inv_a = 255.0f * (1 - ra);
+ pixel[0] = (unsigned char)(pixel[0] * ra + inv_a);
+ pixel[1] = (unsigned char)(pixel[1] * ra + inv_a);
+ pixel[2] = (unsigned char)(pixel[2] * ra + inv_a);
+ }
+ }
+ }
+ }
+
+ // convert to desired output format
+ if (req_comp && req_comp != 4) {
+ if (ri->bits_per_channel == 16)
+ out = (stbi_uc*)stbi__convert_format16((stbi__uint16*)out, 4, req_comp, w, h);
+ else
+ out = stbi__convert_format(out, 4, req_comp, w, h);
+ if (out == NULL)
+ return out; // stbi__convert_format frees input on failure
+ }
+
+ if (comp)
+ *comp = 4;
+ *y = h;
+ *x = w;
+
+ return out;
+}
+# endif
+
+// *************************************************************************************************
+// Softimage PIC loader
+// by Tom Seddon
+//
+// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format
+// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/
+
+# ifndef STBI_NO_PIC
+static int stbi__pic_is4(stbi__context* s, char const* str)
+{
+ int i;
+ for (i = 0; i < 4; ++i)
+ if (stbi__get8(s) != (stbi_uc)str[i])
+ return 0;
+
+ return 1;
+}
+
+static int stbi__pic_test_core(stbi__context* s)
+{
+ int i;
+
+ if (!stbi__pic_is4(s, "\x53\x80\xF6\x34"))
+ return 0;
+
+ for (i = 0; i < 84; ++i)
+ stbi__get8(s);
+
+ if (!stbi__pic_is4(s, "PICT"))
+ return 0;
+
+ return 1;
+}
+
+typedef struct
+{
+ stbi_uc size, type, channel;
+} stbi__pic_packet;
+
+static stbi_uc* stbi__readval(stbi__context* s, int channel, stbi_uc* dest)
+{
+ int mask = 0x80, i;
+
+ for (i = 0; i < 4; ++i, mask >>= 1) {
+ if (channel & mask) {
+ if (stbi__at_eof(s))
+ return stbi__errpuc("bad file", "PIC file too short");
+ dest[i] = stbi__get8(s);
+ }
+ }
+
+ return dest;
+}
+
+static void stbi__copyval(int channel, stbi_uc* dest, stbi_uc const* src)
+{
+ int mask = 0x80, i;
+
+ for (i = 0; i < 4; ++i, mask >>= 1)
+ if (channel & mask)
+ dest[i] = src[i];
+}
+
+static stbi_uc* stbi__pic_load_core(stbi__context* s, int width, int height, int* comp, stbi_uc* result)
+{
+ int act_comp = 0, num_packets = 0, y, chained;
+ stbi__pic_packet packets[10];
+
+ // this will (should...) cater for even some bizarre stuff like having data
+ // for the same channel in multiple packets.
+ do {
+ stbi__pic_packet* packet;
+
+ if (num_packets == sizeof(packets) / sizeof(packets[0]))
+ return stbi__errpuc("bad format", "too many packets");
+
+ packet = &packets[num_packets++];
+
+ chained = stbi__get8(s);
+ packet->size = stbi__get8(s);
+ packet->type = stbi__get8(s);
+ packet->channel = stbi__get8(s);
+
+ act_comp |= packet->channel;
+
+ if (stbi__at_eof(s))
+ return stbi__errpuc("bad file", "file too short (reading packets)");
+ if (packet->size != 8)
+ return stbi__errpuc("bad format", "packet isn't 8bpp");
+ } while (chained);
+
+ *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel?
+
+ for (y = 0; y < height; ++y) {
+ int packet_idx;
+
+ for (packet_idx = 0; packet_idx < num_packets; ++packet_idx) {
+ stbi__pic_packet* packet = &packets[packet_idx];
+ stbi_uc* dest = result + y * width * 4;
+
+ switch (packet->type) {
+ default:
+ return stbi__errpuc("bad format", "packet has bad compression type");
+
+ case 0: { // uncompressed
+ int x;
+
+ for (x = 0; x < width; ++x, dest += 4)
+ if (!stbi__readval(s, packet->channel, dest))
+ return 0;
+ break;
+ }
+
+ case 1: // Pure RLE
+ {
+ int left = width, i;
+
+ while (left > 0) {
+ stbi_uc count, value[4];
+
+ count = stbi__get8(s);
+ if (stbi__at_eof(s))
+ return stbi__errpuc("bad file", "file too short (pure read count)");
+
+ if (count > left)
+ count = (stbi_uc)left;
+
+ if (!stbi__readval(s, packet->channel, value))
+ return 0;
+
+ for (i = 0; i < count; ++i, dest += 4)
+ stbi__copyval(packet->channel, dest, value);
+ left -= count;
+ }
+ } break;
+
+ case 2: { // Mixed RLE
+ int left = width;
+ while (left > 0) {
+ int count = stbi__get8(s), i;
+ if (stbi__at_eof(s))
+ return stbi__errpuc("bad file", "file too short (mixed read count)");
+
+ if (count >= 128) { // Repeated
+ stbi_uc value[4];
+
+ if (count == 128)
+ count = stbi__get16be(s);
+ else
+ count -= 127;
+ if (count > left)
+ return stbi__errpuc("bad file", "scanline overrun");
+
+ if (!stbi__readval(s, packet->channel, value))
+ return 0;
+
+ for (i = 0; i < count; ++i, dest += 4)
+ stbi__copyval(packet->channel, dest, value);
+ } else { // Raw
+ ++count;
+ if (count > left)
+ return stbi__errpuc("bad file", "scanline overrun");
+
+ for (i = 0; i < count; ++i, dest += 4)
+ if (!stbi__readval(s, packet->channel, dest))
+ return 0;
+ }
+ left -= count;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+static void* stbi__pic_load(stbi__context* s, int* px, int* py, int* comp, int req_comp, stbi__result_info* ri)
+{
+ stbi_uc* result;
+ int i, x, y, internal_comp;
+ STBI_NOTUSED(ri);
+
+ if (!comp)
+ comp = &internal_comp;
+
+ for (i = 0; i < 92; ++i)
+ stbi__get8(s);
+
+ x = stbi__get16be(s);
+ y = stbi__get16be(s);
+
+ if (y > STBI_MAX_DIMENSIONS)
+ return stbi__errpuc("too large", "Very large image (corrupt?)");
+ if (x > STBI_MAX_DIMENSIONS)
+ return stbi__errpuc("too large", "Very large image (corrupt?)");
+
+ if (stbi__at_eof(s))
+ return stbi__errpuc("bad file", "file too short (pic header)");
+ if (!stbi__mad3sizes_valid(x, y, 4, 0))
+ return stbi__errpuc("too large", "PIC image too large to decode");
+
+ stbi__get32be(s); // skip `ratio'
+ stbi__get16be(s); // skip `fields'
+ stbi__get16be(s); // skip `pad'
+
+ // intermediate buffer is RGBA
+ result = (stbi_uc*)stbi__malloc_mad3(x, y, 4, 0);
+ if (!result)
+ return stbi__errpuc("outofmem", "Out of memory");
+ memset(result, 0xff, x * y * 4);
+
+ if (!stbi__pic_load_core(s, x, y, comp, result)) {
+ STBI_FREE(result);
+ result = 0;
+ }
+ *px = x;
+ *py = y;
+ if (req_comp == 0)
+ req_comp = *comp;
+ result = stbi__convert_format(result, 4, req_comp, x, y);
+
+ return result;
+}
+
+static int stbi__pic_test(stbi__context* s)
+{
+ int r = stbi__pic_test_core(s);
+ stbi__rewind(s);
+ return r;
+}
+# endif
+
+// *************************************************************************************************
+// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb
+
+# ifndef STBI_NO_GIF
+typedef struct
+{
+ stbi__int16 prefix;
+ stbi_uc first;
+ stbi_uc suffix;
+} stbi__gif_lzw;
+
+typedef struct
+{
+ int w, h;
+ stbi_uc* out; // output buffer (always 4 components)
+ stbi_uc* background; // The current "background" as far as a gif is concerned
+ stbi_uc* history;
+ int flags, bgindex, ratio, transparent, eflags;
+ stbi_uc pal[256][4];
+ stbi_uc lpal[256][4];
+ stbi__gif_lzw codes[8192];
+ stbi_uc* color_table;
+ int parse, step;
+ int lflags;
+ int start_x, start_y;
+ int max_x, max_y;
+ int cur_x, cur_y;
+ int line_size;
+ int delay;
+} stbi__gif;
+
+static int stbi__gif_test_raw(stbi__context* s)
+{
+ int sz;
+ if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8')
+ return 0;
+ sz = stbi__get8(s);
+ if (sz != '9' && sz != '7')
+ return 0;
+ if (stbi__get8(s) != 'a')
+ return 0;
+ return 1;
+}
+
+static int stbi__gif_test(stbi__context* s)
+{
+ int r = stbi__gif_test_raw(s);
+ stbi__rewind(s);
+ return r;
+}
+
+static void stbi__gif_parse_colortable(stbi__context* s, stbi_uc pal[256][4], int num_entries, int transp)
+{
+ int i;
+ for (i = 0; i < num_entries; ++i) {
+ pal[i][2] = stbi__get8(s);
+ pal[i][1] = stbi__get8(s);
+ pal[i][0] = stbi__get8(s);
+ pal[i][3] = transp == i ? 0 : 255;
+ }
+}
+
+static int stbi__gif_header(stbi__context* s, stbi__gif* g, int* comp, int is_info)
+{
+ stbi_uc version;
+ if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8')
+ return stbi__err("not GIF", "Corrupt GIF");
+
+ version = stbi__get8(s);
+ if (version != '7' && version != '9')
+ return stbi__err("not GIF", "Corrupt GIF");
+ if (stbi__get8(s) != 'a')
+ return stbi__err("not GIF", "Corrupt GIF");
+
+ stbi__g_failure_reason = "";
+ g->w = stbi__get16le(s);
+ g->h = stbi__get16le(s);
+ g->flags = stbi__get8(s);
+ g->bgindex = stbi__get8(s);
+ g->ratio = stbi__get8(s);
+ g->transparent = -1;
+
+ if (g->w > STBI_MAX_DIMENSIONS)
+ return stbi__err("too large", "Very large image (corrupt?)");
+ if (g->h > STBI_MAX_DIMENSIONS)
+ return stbi__err("too large", "Very large image (corrupt?)");
+
+ if (comp != 0)
+ *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments
+
+ if (is_info)
+ return 1;
+
+ if (g->flags & 0x80)
+ stbi__gif_parse_colortable(s, g->pal, 2 << (g->flags & 7), -1);
+
+ return 1;
+}
+
+static int stbi__gif_info_raw(stbi__context* s, int* x, int* y, int* comp)
+{
+ stbi__gif* g = (stbi__gif*)stbi__malloc(sizeof(stbi__gif));
+ if (!g)
+ return stbi__err("outofmem", "Out of memory");
+ if (!stbi__gif_header(s, g, comp, 1)) {
+ STBI_FREE(g);
+ stbi__rewind(s);
+ return 0;
+ }
+ if (x)
+ *x = g->w;
+ if (y)
+ *y = g->h;
+ STBI_FREE(g);
+ return 1;
+}
+
+static void stbi__out_gif_code(stbi__gif* g, stbi__uint16 code)
+{
+ stbi_uc *p, *c;
+ int idx;
+
+ // recurse to decode the prefixes, since the linked-list is backwards,
+ // and working backwards through an interleaved image would be nasty
+ if (g->codes[code].prefix >= 0)
+ stbi__out_gif_code(g, g->codes[code].prefix);
+
+ if (g->cur_y >= g->max_y)
+ return;
+
+ idx = g->cur_x + g->cur_y;
+ p = &g->out[idx];
+ g->history[idx / 4] = 1;
+
+ c = &g->color_table[g->codes[code].suffix * 4];
+ if (c[3] > 128) { // don't render transparent pixels;
+ p[0] = c[2];
+ p[1] = c[1];
+ p[2] = c[0];
+ p[3] = c[3];
+ }
+ g->cur_x += 4;
+
+ if (g->cur_x >= g->max_x) {
+ g->cur_x = g->start_x;
+ g->cur_y += g->step;
+
+ while (g->cur_y >= g->max_y && g->parse > 0) {
+ g->step = (1 << g->parse) * g->line_size;
+ g->cur_y = g->start_y + (g->step >> 1);
+ --g->parse;
+ }
+ }
+}
+
+static stbi_uc* stbi__process_gif_raster(stbi__context* s, stbi__gif* g)
+{
+ stbi_uc lzw_cs;
+ stbi__int32 len, init_code;
+ stbi__uint32 first;
+ stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear;
+ stbi__gif_lzw* p;
+
+ lzw_cs = stbi__get8(s);
+ if (lzw_cs > 12)
+ return NULL;
+ clear = 1 << lzw_cs;
+ first = 1;
+ codesize = lzw_cs + 1;
+ codemask = (1 << codesize) - 1;
+ bits = 0;
+ valid_bits = 0;
+ for (init_code = 0; init_code < clear; init_code++) {
+ g->codes[init_code].prefix = -1;
+ g->codes[init_code].first = (stbi_uc)init_code;
+ g->codes[init_code].suffix = (stbi_uc)init_code;
+ }
+
+ // support no starting clear code
+ avail = clear + 2;
+ oldcode = -1;
+
+ len = 0;
+ for (;;) {
+ if (valid_bits < codesize) {
+ if (len == 0) {
+ len = stbi__get8(s); // start new block
+ if (len == 0)
+ return g->out;
+ }
+ --len;
+ bits |= (stbi__int32)stbi__get8(s) << valid_bits;
+ valid_bits += 8;
+ } else {
+ stbi__int32 code = bits & codemask;
+ bits >>= codesize;
+ valid_bits -= codesize;
+ // @OPTIMIZE: is there some way we can accelerate the non-clear path?
+ if (code == clear) { // clear code
+ codesize = lzw_cs + 1;
+ codemask = (1 << codesize) - 1;
+ avail = clear + 2;
+ oldcode = -1;
+ first = 0;
+ } else if (code == clear + 1) { // end of stream code
+ stbi__skip(s, len);
+ while ((len = stbi__get8(s)) > 0)
+ stbi__skip(s, len);
+ return g->out;
+ } else if (code <= avail) {
+ if (first) {
+ return stbi__errpuc("no clear code", "Corrupt GIF");
+ }
+
+ if (oldcode >= 0) {
+ p = &g->codes[avail++];
+ if (avail > 8192) {
+ return stbi__errpuc("too many codes", "Corrupt GIF");
+ }
+
+ p->prefix = (stbi__int16)oldcode;
+ p->first = g->codes[oldcode].first;
+ p->suffix = (code == avail) ? p->first : g->codes[code].first;
+ } else if (code == avail)
+ return stbi__errpuc("illegal code in raster", "Corrupt GIF");
+
+ stbi__out_gif_code(g, (stbi__uint16)code);
+
+ if ((avail & codemask) == 0 && avail <= 0x0FFF) {
+ codesize++;
+ codemask = (1 << codesize) - 1;
+ }
+
+ oldcode = code;
+ } else {
+ return stbi__errpuc("illegal code in raster", "Corrupt GIF");
+ }
+ }
+ }
+}
+
+// this function is designed to support animated gifs, although stb_image doesn't support it
+// two back is the image from two frames ago, used for a very specific disposal format
+static stbi_uc* stbi__gif_load_next(stbi__context* s, stbi__gif* g, int* comp, int req_comp, stbi_uc* two_back)
+{
+ int dispose;
+ int first_frame;
+ int pi;
+ int pcount;
+ STBI_NOTUSED(req_comp);
+
+ // on first frame, any non-written pixels get the background colour (non-transparent)
+ first_frame = 0;
+ if (g->out == 0) {
+ if (!stbi__gif_header(s, g, comp, 0))
+ return 0; // stbi__g_failure_reason set by stbi__gif_header
+ if (!stbi__mad3sizes_valid(4, g->w, g->h, 0))
+ return stbi__errpuc("too large", "GIF image is too large");
+ pcount = g->w * g->h;
+ g->out = (stbi_uc*)stbi__malloc(4 * pcount);
+ g->background = (stbi_uc*)stbi__malloc(4 * pcount);
+ g->history = (stbi_uc*)stbi__malloc(pcount);
+ if (!g->out || !g->background || !g->history)
+ return stbi__errpuc("outofmem", "Out of memory");
+
+ // image is treated as "transparent" at the start - ie, nothing overwrites the current background;
+ // background colour is only used for pixels that are not rendered first frame, after that "background"
+ // color refers to the color that was there the previous frame.
+ memset(g->out, 0x00, 4 * pcount);
+ memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent)
+ memset(g->history, 0x00, pcount); // pixels that were affected previous frame
+ first_frame = 1;
+ } else {
+ // second frame - how do we dispose of the previous one?
+ dispose = (g->eflags & 0x1C) >> 2;
+ pcount = g->w * g->h;
+
+ if ((dispose == 3) && (two_back == 0)) {
+ dispose = 2; // if I don't have an image to revert back to, default to the old background
+ }
+
+ if (dispose == 3) { // use previous graphic
+ for (pi = 0; pi < pcount; ++pi) {
+ if (g->history[pi]) {
+ memcpy(&g->out[pi * 4], &two_back[pi * 4], 4);
+ }
+ }
+ } else if (dispose == 2) {
+ // restore what was changed last frame to background before that frame;
+ for (pi = 0; pi < pcount; ++pi) {
+ if (g->history[pi]) {
+ memcpy(&g->out[pi * 4], &g->background[pi * 4], 4);
+ }
+ }
+ } else {
+ // This is a non-disposal case eithe way, so just
+ // leave the pixels as is, and they will become the new background
+ // 1: do not dispose
+ // 0: not specified.
+ }
+
+ // background is what out is after the undoing of the previou frame;
+ memcpy(g->background, g->out, 4 * g->w * g->h);
+ }
+
+ // clear my history;
+ memset(g->history, 0x00, g->w * g->h); // pixels that were affected previous frame
+
+ for (;;) {
+ int tag = stbi__get8(s);
+ switch (tag) {
+ case 0x2C: /* Image Descriptor */
+ {
+ stbi__int32 x, y, w, h;
+ stbi_uc* o;
+
+ x = stbi__get16le(s);
+ y = stbi__get16le(s);
+ w = stbi__get16le(s);
+ h = stbi__get16le(s);
+ if (((x + w) > (g->w)) || ((y + h) > (g->h)))
+ return stbi__errpuc("bad Image Descriptor", "Corrupt GIF");
+
+ g->line_size = g->w * 4;
+ g->start_x = x * 4;
+ g->start_y = y * g->line_size;
+ g->max_x = g->start_x + w * 4;
+ g->max_y = g->start_y + h * g->line_size;
+ g->cur_x = g->start_x;
+ g->cur_y = g->start_y;
+
+ // if the width of the specified rectangle is 0, that means
+ // we may not see *any* pixels or the image is malformed;
+ // to make sure this is caught, move the current y down to
+ // max_y (which is what out_gif_code checks).
+ if (w == 0)
+ g->cur_y = g->max_y;
+
+ g->lflags = stbi__get8(s);
+
+ if (g->lflags & 0x40) {
+ g->step = 8 * g->line_size; // first interlaced spacing
+ g->parse = 3;
+ } else {
+ g->step = g->line_size;
+ g->parse = 0;
+ }
+
+ if (g->lflags & 0x80) {
+ stbi__gif_parse_colortable(s, g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1);
+ g->color_table = (stbi_uc*)g->lpal;
+ } else if (g->flags & 0x80) {
+ g->color_table = (stbi_uc*)g->pal;
+ } else
+ return stbi__errpuc("missing color table", "Corrupt GIF");
+
+ o = stbi__process_gif_raster(s, g);
+ if (!o)
+ return NULL;
+
+ // if this was the first frame,
+ pcount = g->w * g->h;
+ if (first_frame && (g->bgindex > 0)) {
+ // if first frame, any pixel not drawn to gets the background color
+ for (pi = 0; pi < pcount; ++pi) {
+ if (g->history[pi] == 0) {
+ g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be;
+ memcpy(&g->out[pi * 4], &g->pal[g->bgindex], 4);
+ }
+ }
+ }
+
+ return o;
+ }
+
+ case 0x21: // Comment Extension.
+ {
+ int len;
+ int ext = stbi__get8(s);
+ if (ext == 0xF9) { // Graphic Control Extension.
+ len = stbi__get8(s);
+ if (len == 4) {
+ g->eflags = stbi__get8(s);
+ g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths.
+
+ // unset old transparent
+ if (g->transparent >= 0) {
+ g->pal[g->transparent][3] = 255;
+ }
+ if (g->eflags & 0x01) {
+ g->transparent = stbi__get8(s);
+ if (g->transparent >= 0) {
+ g->pal[g->transparent][3] = 0;
+ }
+ } else {
+ // don't need transparent
+ stbi__skip(s, 1);
+ g->transparent = -1;
+ }
+ } else {
+ stbi__skip(s, len);
+ break;
+ }
+ }
+ while ((len = stbi__get8(s)) != 0) {
+ stbi__skip(s, len);
+ }
+ break;
+ }
+
+ case 0x3B: // gif stream termination code
+ return (stbi_uc*)s; // using '1' causes warning on some compilers
+
+ default:
+ return stbi__errpuc("unknown code", "Corrupt GIF");
+ }
+ }
+}
+
+static void* stbi__load_gif_main_outofmem(stbi__gif* g, stbi_uc* out, int** delays)
+{
+ STBI_FREE(g->out);
+ STBI_FREE(g->history);
+ STBI_FREE(g->background);
+
+ if (out)
+ STBI_FREE(out);
+ if (delays && *delays)
+ STBI_FREE(*delays);
+ return stbi__errpuc("outofmem", "Out of memory");
+}
+
+static void* stbi__load_gif_main(stbi__context* s, int** delays, int* x, int* y, int* z, int* comp, int req_comp)
+{
+ if (stbi__gif_test(s)) {
+ int layers = 0;
+ stbi_uc* u = 0;
+ stbi_uc* out = 0;
+ stbi_uc* two_back = 0;
+ stbi__gif g;
+ int stride;
+ int out_size = 0;
+ int delays_size = 0;
+
+ STBI_NOTUSED(out_size);
+ STBI_NOTUSED(delays_size);
+
+ memset(&g, 0, sizeof(g));
+ if (delays) {
+ *delays = 0;
+ }
+
+ do {
+ u = stbi__gif_load_next(s, &g, comp, req_comp, two_back);
+ if (u == (stbi_uc*)s)
+ u = 0; // end of animated gif marker
+
+ if (u) {
+ *x = g.w;
+ *y = g.h;
+ ++layers;
+ stride = g.w * g.h * 4;
+
+ if (out) {
+ void* tmp = (stbi_uc*)STBI_REALLOC_SIZED(out, out_size, layers * stride);
+ if (!tmp)
+ return stbi__load_gif_main_outofmem(&g, out, delays);
+ else {
+ out = (stbi_uc*)tmp;
+ out_size = layers * stride;
+ }
+
+ if (delays) {
+ int* new_delays = (int*)STBI_REALLOC_SIZED(*delays, delays_size, sizeof(int) * layers);
+ if (!new_delays)
+ return stbi__load_gif_main_outofmem(&g, out, delays);
+ *delays = new_delays;
+ delays_size = layers * sizeof(int);
+ }
+ } else {
+ out = (stbi_uc*)stbi__malloc(layers * stride);
+ if (!out)
+ return stbi__load_gif_main_outofmem(&g, out, delays);
+ out_size = layers * stride;
+ if (delays) {
+ *delays = (int*)stbi__malloc(layers * sizeof(int));
+ if (!*delays)
+ return stbi__load_gif_main_outofmem(&g, out, delays);
+ delays_size = layers * sizeof(int);
+ }
+ }
+ memcpy(out + ((layers - 1) * stride), u, stride);
+ if (layers >= 2) {
+ two_back = out - 2 * stride;
+ }
+
+ if (delays) {
+ (*delays)[layers - 1U] = g.delay;
+ }
+ }
+ } while (u != 0);
+
+ // free temp buffer;
+ STBI_FREE(g.out);
+ STBI_FREE(g.history);
+ STBI_FREE(g.background);
+
+ // do the final conversion after loading everything;
+ if (req_comp && req_comp != 4)
+ out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h);
+
+ *z = layers;
+ return out;
+ } else {
+ return stbi__errpuc("not GIF", "Image was not as a gif type.");
+ }
+}
+
+static void* stbi__gif_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri)
+{
+ stbi_uc* u = 0;
+ stbi__gif g;
+ memset(&g, 0, sizeof(g));
+ STBI_NOTUSED(ri);
+
+ u = stbi__gif_load_next(s, &g, comp, req_comp, 0);
+ if (u == (stbi_uc*)s)
+ u = 0; // end of animated gif marker
+ if (u) {
+ *x = g.w;
+ *y = g.h;
+
+ // moved conversion to after successful load so that the same
+ // can be done for multiple frames.
+ if (req_comp && req_comp != 4)
+ u = stbi__convert_format(u, 4, req_comp, g.w, g.h);
+ } else if (g.out) {
+ // if there was an error and we allocated an image buffer, free it!
+ STBI_FREE(g.out);
+ }
+
+ // free buffers needed for multiple frame loading;
+ STBI_FREE(g.history);
+ STBI_FREE(g.background);
+
+ return u;
+}
+
+static int stbi__gif_info(stbi__context* s, int* x, int* y, int* comp)
+{
+ return stbi__gif_info_raw(s, x, y, comp);
+}
+# endif
+
+// *************************************************************************************************
+// Radiance RGBE HDR loader
+// originally by Nicolas Schulz
+# ifndef STBI_NO_HDR
+static int stbi__hdr_test_core(stbi__context* s, char const* signature)
+{
+ int i;
+ for (i = 0; signature[i]; ++i)
+ if (stbi__get8(s) != signature[i])
+ return 0;
+ stbi__rewind(s);
+ return 1;
+}
+
+static int stbi__hdr_test(stbi__context* s)
+{
+ int r = stbi__hdr_test_core(s, "#?RADIANCE\n");
+ stbi__rewind(s);
+ if (!r) {
+ r = stbi__hdr_test_core(s, "#?RGBE\n");
+ stbi__rewind(s);
+ }
+ return r;
+}
+
+# define STBI__HDR_BUFLEN 1024
+static char* stbi__hdr_gettoken(stbi__context* z, char* buffer)
+{
+ int len = 0;
+ char c = '\0';
+
+ c = (char)stbi__get8(z);
+
+ while (!stbi__at_eof(z) && c != '\n') {
+ buffer[len++] = c;
+ if (len == STBI__HDR_BUFLEN - 1) {
+ // flush to end of line
+ while (!stbi__at_eof(z) && stbi__get8(z) != '\n')
+ ;
+ break;
+ }
+ c = (char)stbi__get8(z);
+ }
+
+ buffer[len] = 0;
+ return buffer;
+}
+
+static void stbi__hdr_convert(float* output, stbi_uc* input, int req_comp)
+{
+ if (input[3] != 0) {
+ float f1;
+ // Exponent
+ f1 = (float)ldexp(1.0f, input[3] - (int)(128 + 8));
+ if (req_comp <= 2)
+ output[0] = (input[0] + input[1] + input[2]) * f1 / 3;
+ else {
+ output[0] = input[0] * f1;
+ output[1] = input[1] * f1;
+ output[2] = input[2] * f1;
+ }
+ if (req_comp == 2)
+ output[1] = 1;
+ if (req_comp == 4)
+ output[3] = 1;
+ } else {
+ switch (req_comp) {
+ case 4:
+ output[3] = 1; /* fallthrough */
+ case 3:
+ output[0] = output[1] = output[2] = 0;
+ break;
+ case 2:
+ output[1] = 1; /* fallthrough */
+ case 1:
+ output[0] = 0;
+ break;
+ }
+ }
+}
+
+static float* stbi__hdr_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri)
+{
+ char buffer[STBI__HDR_BUFLEN];
+ char* token;
+ int valid = 0;
+ int width, height;
+ stbi_uc* scanline;
+ float* hdr_data;
+ int len;
+ unsigned char count, value;
+ int i, j, k, c1, c2, z;
+ char const* headerToken;
+ STBI_NOTUSED(ri);
+
+ // Check identifier
+ headerToken = stbi__hdr_gettoken(s, buffer);
+ if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0)
+ return stbi__errpf("not HDR", "Corrupt HDR image");
+
+ // Parse header
+ for (;;) {
+ token = stbi__hdr_gettoken(s, buffer);
+ if (token[0] == 0)
+ break;
+ if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0)
+ valid = 1;
+ }
+
+ if (!valid)
+ return stbi__errpf("unsupported format", "Unsupported HDR format");
+
+ // Parse width and height
+ // can't use sscanf() if we're not using stdio!
+ token = stbi__hdr_gettoken(s, buffer);
+ if (strncmp(token, "-Y ", 3))
+ return stbi__errpf("unsupported data layout", "Unsupported HDR format");
+ token += 3;
+ height = (int)strtol(token, &token, 10);
+ while (*token == ' ')
+ ++token;
+ if (strncmp(token, "+X ", 3))
+ return stbi__errpf("unsupported data layout", "Unsupported HDR format");
+ token += 3;
+ width = (int)strtol(token, NULL, 10);
+
+ if (height > STBI_MAX_DIMENSIONS)
+ return stbi__errpf("too large", "Very large image (corrupt?)");
+ if (width > STBI_MAX_DIMENSIONS)
+ return stbi__errpf("too large", "Very large image (corrupt?)");
+
+ *x = width;
+ *y = height;
+
+ if (comp)
+ *comp = 3;
+ if (req_comp == 0)
+ req_comp = 3;
+
+ if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0))
+ return stbi__errpf("too large", "HDR image is too large");
+
+ // Read data
+ hdr_data = (float*)stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0);
+ if (!hdr_data)
+ return stbi__errpf("outofmem", "Out of memory");
+
+ // Load image data
+ // image data is stored as some number of sca
+ if (width < 8 || width >= 32768) {
+ // Read flat data
+ for (j = 0; j < height; ++j) {
+ for (i = 0; i < width; ++i) {
+ stbi_uc rgbe[4];
+ main_decode_loop:
+ stbi__getn(s, rgbe, 4);
+ stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp);
+ }
+ }
+ } else {
+ // Read RLE-encoded data
+ scanline = NULL;
+
+ for (j = 0; j < height; ++j) {
+ c1 = stbi__get8(s);
+ c2 = stbi__get8(s);
+ len = stbi__get8(s);
+ if (c1 != 2 || c2 != 2 || (len & 0x80)) {
+ // not run-length encoded, so we have to actually use THIS data as a decoded
+ // pixel (note this can't be a valid pixel--one of RGB must be >= 128)
+ stbi_uc rgbe[4];
+ rgbe[0] = (stbi_uc)c1;
+ rgbe[1] = (stbi_uc)c2;
+ rgbe[2] = (stbi_uc)len;
+ rgbe[3] = (stbi_uc)stbi__get8(s);
+ stbi__hdr_convert(hdr_data, rgbe, req_comp);
+ i = 1;
+ j = 0;
+ STBI_FREE(scanline);
+ goto main_decode_loop; // yes, this makes no sense
+ }
+ len <<= 8;
+ len |= stbi__get8(s);
+ if (len != width) {
+ STBI_FREE(hdr_data);
+ STBI_FREE(scanline);
+ return stbi__errpf("invalid decoded scanline length", "corrupt HDR");
+ }
+ if (scanline == NULL) {
+ scanline = (stbi_uc*)stbi__malloc_mad2(width, 4, 0);
+ if (!scanline) {
+ STBI_FREE(hdr_data);
+ return stbi__errpf("outofmem", "Out of memory");
+ }
+ }
+
+ for (k = 0; k < 4; ++k) {
+ int nleft;
+ i = 0;
+ while ((nleft = width - i) > 0) {
+ count = stbi__get8(s);
+ if (count > 128) {
+ // Run
+ value = stbi__get8(s);
+ count -= 128;
+ if ((count == 0) || (count > nleft)) {
+ STBI_FREE(hdr_data);
+ STBI_FREE(scanline);
+ return stbi__errpf("corrupt", "bad RLE data in HDR");
+ }
+ for (z = 0; z < count; ++z)
+ scanline[i++ * 4 + k] = value;
+ } else {
+ // Dump
+ if ((count == 0) || (count > nleft)) {
+ STBI_FREE(hdr_data);
+ STBI_FREE(scanline);
+ return stbi__errpf("corrupt", "bad RLE data in HDR");
+ }
+ for (z = 0; z < count; ++z)
+ scanline[i++ * 4 + k] = stbi__get8(s);
+ }
+ }
+ }
+ for (i = 0; i < width; ++i)
+ stbi__hdr_convert(hdr_data + (j * width + i) * req_comp, scanline + i * 4, req_comp);
+ }
+ if (scanline)
+ STBI_FREE(scanline);
+ }
+
+ return hdr_data;
+}
+
+static int stbi__hdr_info(stbi__context* s, int* x, int* y, int* comp)
+{
+ char buffer[STBI__HDR_BUFLEN];
+ char* token;
+ int valid = 0;
+ int dummy;
+
+ if (!x)
+ x = &dummy;
+ if (!y)
+ y = &dummy;
+ if (!comp)
+ comp = &dummy;
+
+ if (stbi__hdr_test(s) == 0) {
+ stbi__rewind(s);
+ return 0;
+ }
+
+ for (;;) {
+ token = stbi__hdr_gettoken(s, buffer);
+ if (token[0] == 0)
+ break;
+ if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0)
+ valid = 1;
+ }
+
+ if (!valid) {
+ stbi__rewind(s);
+ return 0;
+ }
+ token = stbi__hdr_gettoken(s, buffer);
+ if (strncmp(token, "-Y ", 3)) {
+ stbi__rewind(s);
+ return 0;
+ }
+ token += 3;
+ *y = (int)strtol(token, &token, 10);
+ while (*token == ' ')
+ ++token;
+ if (strncmp(token, "+X ", 3)) {
+ stbi__rewind(s);
+ return 0;
+ }
+ token += 3;
+ *x = (int)strtol(token, NULL, 10);
+ *comp = 3;
+ return 1;
+}
+# endif // STBI_NO_HDR
+
+# ifndef STBI_NO_BMP
+static int stbi__bmp_info(stbi__context* s, int* x, int* y, int* comp)
+{
+ void* p;
+ stbi__bmp_data info;
+
+ info.all_a = 255;
+ p = stbi__bmp_parse_header(s, &info);
+ if (p == NULL) {
+ stbi__rewind(s);
+ return 0;
+ }
+ if (x)
+ *x = s->img_x;
+ if (y)
+ *y = s->img_y;
+ if (comp) {
+ if (info.bpp == 24 && info.ma == 0xff000000)
+ *comp = 3;
+ else
+ *comp = info.ma ? 4 : 3;
+ }
+ return 1;
+}
+# endif
+
+# ifndef STBI_NO_PSD
+static int stbi__psd_info(stbi__context* s, int* x, int* y, int* comp)
+{
+ int channelCount, dummy, depth;
+ if (!x)
+ x = &dummy;
+ if (!y)
+ y = &dummy;
+ if (!comp)
+ comp = &dummy;
+ if (stbi__get32be(s) != 0x38425053) {
+ stbi__rewind(s);
+ return 0;
+ }
+ if (stbi__get16be(s) != 1) {
+ stbi__rewind(s);
+ return 0;
+ }
+ stbi__skip(s, 6);
+ channelCount = stbi__get16be(s);
+ if (channelCount < 0 || channelCount > 16) {
+ stbi__rewind(s);
+ return 0;
+ }
+ *y = stbi__get32be(s);
+ *x = stbi__get32be(s);
+ depth = stbi__get16be(s);
+ if (depth != 8 && depth != 16) {
+ stbi__rewind(s);
+ return 0;
+ }
+ if (stbi__get16be(s) != 3) {
+ stbi__rewind(s);
+ return 0;
+ }
+ *comp = 4;
+ return 1;
+}
+
+static int stbi__psd_is16(stbi__context* s)
+{
+ int channelCount, depth;
+ if (stbi__get32be(s) != 0x38425053) {
+ stbi__rewind(s);
+ return 0;
+ }
+ if (stbi__get16be(s) != 1) {
+ stbi__rewind(s);
+ return 0;
+ }
+ stbi__skip(s, 6);
+ channelCount = stbi__get16be(s);
+ if (channelCount < 0 || channelCount > 16) {
+ stbi__rewind(s);
+ return 0;
+ }
+ STBI_NOTUSED(stbi__get32be(s));
+ STBI_NOTUSED(stbi__get32be(s));
+ depth = stbi__get16be(s);
+ if (depth != 16) {
+ stbi__rewind(s);
+ return 0;
+ }
+ return 1;
+}
+# endif
+
+# ifndef STBI_NO_PIC
+static int stbi__pic_info(stbi__context* s, int* x, int* y, int* comp)
+{
+ int act_comp = 0, num_packets = 0, chained, dummy;
+ stbi__pic_packet packets[10];
+
+ if (!x)
+ x = &dummy;
+ if (!y)
+ y = &dummy;
+ if (!comp)
+ comp = &dummy;
+
+ if (!stbi__pic_is4(s, "\x53\x80\xF6\x34")) {
+ stbi__rewind(s);
+ return 0;
+ }
+
+ stbi__skip(s, 88);
+
+ *x = stbi__get16be(s);
+ *y = stbi__get16be(s);
+ if (stbi__at_eof(s)) {
+ stbi__rewind(s);
+ return 0;
+ }
+ if ((*x) != 0 && (1 << 28) / (*x) < (*y)) {
+ stbi__rewind(s);
+ return 0;
+ }
+
+ stbi__skip(s, 8);
+
+ do {
+ stbi__pic_packet* packet;
+
+ if (num_packets == sizeof(packets) / sizeof(packets[0]))
+ return 0;
+
+ packet = &packets[num_packets++];
+ chained = stbi__get8(s);
+ packet->size = stbi__get8(s);
+ packet->type = stbi__get8(s);
+ packet->channel = stbi__get8(s);
+ act_comp |= packet->channel;
+
+ if (stbi__at_eof(s)) {
+ stbi__rewind(s);
+ return 0;
+ }
+ if (packet->size != 8) {
+ stbi__rewind(s);
+ return 0;
+ }
+ } while (chained);
+
+ *comp = (act_comp & 0x10 ? 4 : 3);
+
+ return 1;
+}
+# endif
+
+// *************************************************************************************************
+// Portable Gray Map and Portable Pixel Map loader
+// by Ken Miller
+//
+// PGM: http://netpbm.sourceforge.net/doc/pgm.html
+// PPM: http://netpbm.sourceforge.net/doc/ppm.html
+//
+// Known limitations:
+// Does not support comments in the header section
+// Does not support ASCII image data (formats P2 and P3)
+
+# ifndef STBI_NO_PNM
+
+static int stbi__pnm_test(stbi__context* s)
+{
+ char p, t;
+ p = (char)stbi__get8(s);
+ t = (char)stbi__get8(s);
+ if (p != 'P' || (t != '5' && t != '6')) {
+ stbi__rewind(s);
+ return 0;
+ }
+ return 1;
+}
+
+static void* stbi__pnm_load(stbi__context* s, int* x, int* y, int* comp, int req_comp, stbi__result_info* ri)
+{
+ stbi_uc* out;
+ STBI_NOTUSED(ri);
+
+ ri->bits_per_channel = stbi__pnm_info(s, (int*)&s->img_x, (int*)&s->img_y, (int*)&s->img_n);
+ if (ri->bits_per_channel == 0)
+ return 0;
+
+ if (s->img_y > STBI_MAX_DIMENSIONS)
+ return stbi__errpuc("too large", "Very large image (corrupt?)");
+ if (s->img_x > STBI_MAX_DIMENSIONS)
+ return stbi__errpuc("too large", "Very large image (corrupt?)");
+
+ *x = s->img_x;
+ *y = s->img_y;
+ if (comp)
+ *comp = s->img_n;
+
+ if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0))
+ return stbi__errpuc("too large", "PNM too large");
+
+ out = (stbi_uc*)stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0);
+ if (!out)
+ return stbi__errpuc("outofmem", "Out of memory");
+ if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) {
+ STBI_FREE(out);
+ return stbi__errpuc("bad PNM", "PNM file truncated");
+ }
+
+ if (req_comp && req_comp != s->img_n) {
+ if (ri->bits_per_channel == 16) {
+ out = (stbi_uc*)stbi__convert_format16((stbi__uint16*)out, s->img_n, req_comp, s->img_x, s->img_y);
+ } else {
+ out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
+ }
+ if (out == NULL)
+ return out; // stbi__convert_format frees input on failure
+ }
+ return out;
+}
+
+static int stbi__pnm_isspace(char c)
+{
+ return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
+}
+
+static void stbi__pnm_skip_whitespace(stbi__context* s, char* c)
+{
+ for (;;) {
+ while (!stbi__at_eof(s) && stbi__pnm_isspace(*c))
+ *c = (char)stbi__get8(s);
+
+ if (stbi__at_eof(s) || *c != '#')
+ break;
+
+ while (!stbi__at_eof(s) && *c != '\n' && *c != '\r')
+ *c = (char)stbi__get8(s);
+ }
+}
+
+static int stbi__pnm_isdigit(char c)
+{
+ return c >= '0' && c <= '9';
+}
+
+static int stbi__pnm_getinteger(stbi__context* s, char* c)
+{
+ int value = 0;
+
+ while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {
+ value = value * 10 + (*c - '0');
+ *c = (char)stbi__get8(s);
+ if ((value > 214748364) || (value == 214748364 && *c > '7'))
+ return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int");
+ }
+
+ return value;
+}
+
+static int stbi__pnm_info(stbi__context* s, int* x, int* y, int* comp)
+{
+ int maxv, dummy;
+ char c, p, t;
+
+ if (!x)
+ x = &dummy;
+ if (!y)
+ y = &dummy;
+ if (!comp)
+ comp = &dummy;
+
+ stbi__rewind(s);
+
+ // Get identifier
+ p = (char)stbi__get8(s);
+ t = (char)stbi__get8(s);
+ if (p != 'P' || (t != '5' && t != '6')) {
+ stbi__rewind(s);
+ return 0;
+ }
+
+ *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm
+
+ c = (char)stbi__get8(s);
+ stbi__pnm_skip_whitespace(s, &c);
+
+ *x = stbi__pnm_getinteger(s, &c); // read width
+ if (*x == 0)
+ return stbi__err("invalid width", "PPM image header had zero or overflowing width");
+ stbi__pnm_skip_whitespace(s, &c);
+
+ *y = stbi__pnm_getinteger(s, &c); // read height
+ if (*y == 0)
+ return stbi__err("invalid width", "PPM image header had zero or overflowing width");
+ stbi__pnm_skip_whitespace(s, &c);
+
+ maxv = stbi__pnm_getinteger(s, &c); // read max value
+ if (maxv > 65535)
+ return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images");
+ else if (maxv > 255)
+ return 16;
+ else
+ return 8;
+}
+
+static int stbi__pnm_is16(stbi__context* s)
+{
+ if (stbi__pnm_info(s, NULL, NULL, NULL) == 16)
+ return 1;
+ return 0;
+}
+# endif
+
+static int stbi__info_main(stbi__context* s, int* x, int* y, int* comp)
+{
+# ifndef STBI_NO_JPEG
+ if (stbi__jpeg_info(s, x, y, comp))
+ return 1;
+# endif
+
+# ifndef STBI_NO_PNG
+ if (stbi__png_info(s, x, y, comp))
+ return 1;
+# endif
+
+# ifndef STBI_NO_GIF
+ if (stbi__gif_info(s, x, y, comp))
+ return 1;
+# endif
+
+# ifndef STBI_NO_BMP
+ if (stbi__bmp_info(s, x, y, comp))
+ return 1;
+# endif
+
+# ifndef STBI_NO_PSD
+ if (stbi__psd_info(s, x, y, comp))
+ return 1;
+# endif
+
+# ifndef STBI_NO_PIC
+ if (stbi__pic_info(s, x, y, comp))
+ return 1;
+# endif
+
+# ifndef STBI_NO_PNM
+ if (stbi__pnm_info(s, x, y, comp))
+ return 1;
+# endif
+
+# ifndef STBI_NO_HDR
+ if (stbi__hdr_info(s, x, y, comp))
+ return 1;
+# endif
+
+// test tga last because it's a crappy test!
+# ifndef STBI_NO_TGA
+ if (stbi__tga_info(s, x, y, comp))
+ return 1;
+# endif
+ return stbi__err("unknown image type", "Image not of any known type, or corrupt");
+}
+
+static int stbi__is_16_main(stbi__context* s)
+{
+# ifndef STBI_NO_PNG
+ if (stbi__png_is16(s))
+ return 1;
+# endif
+
+# ifndef STBI_NO_PSD
+ if (stbi__psd_is16(s))
+ return 1;
+# endif
+
+# ifndef STBI_NO_PNM
+ if (stbi__pnm_is16(s))
+ return 1;
+# endif
+ return 0;
+}
+
+# ifndef STBI_NO_STDIO
+STBIDEF int stbi_info(char const* filename, int* x, int* y, int* comp)
+{
+ FILE* f = stbi__fopen(filename, "rb");
+ int result;
+ if (!f)
+ return stbi__err("can't fopen", "Unable to open file");
+ result = stbi_info_from_file(f, x, y, comp);
+ fclose(f);
+ return result;
+}
+
+STBIDEF int stbi_info_from_file(FILE* f, int* x, int* y, int* comp)
+{
+ int r;
+ stbi__context s;
+ long pos = ftell(f);
+ stbi__start_file(&s, f);
+ r = stbi__info_main(&s, x, y, comp);
+ fseek(f, pos, SEEK_SET);
+ return r;
+}
+
+STBIDEF int stbi_is_16_bit(char const* filename)
+{
+ FILE* f = stbi__fopen(filename, "rb");
+ int result;
+ if (!f)
+ return stbi__err("can't fopen", "Unable to open file");
+ result = stbi_is_16_bit_from_file(f);
+ fclose(f);
+ return result;
+}
+
+STBIDEF int stbi_is_16_bit_from_file(FILE* f)
+{
+ int r;
+ stbi__context s;
+ long pos = ftell(f);
+ stbi__start_file(&s, f);
+ r = stbi__is_16_main(&s);
+ fseek(f, pos, SEEK_SET);
+ return r;
+}
+# endif // !STBI_NO_STDIO
+
+STBIDEF int stbi_info_from_memory(stbi_uc const* buffer, int len, int* x, int* y, int* comp)
+{
+ stbi__context s;
+ stbi__start_mem(&s, buffer, len);
+ return stbi__info_main(&s, x, y, comp);
+}
+
+STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const* c, void* user, int* x, int* y, int* comp)
+{
+ stbi__context s;
+ stbi__start_callbacks(&s, (stbi_io_callbacks*)c, user);
+ return stbi__info_main(&s, x, y, comp);
+}
+
+STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const* buffer, int len)
+{
+ stbi__context s;
+ stbi__start_mem(&s, buffer, len);
+ return stbi__is_16_main(&s);
+}
+
+STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const* c, void* user)
+{
+ stbi__context s;
+ stbi__start_callbacks(&s, (stbi_io_callbacks*)c, user);
+ return stbi__is_16_main(&s);
+}
+
+#endif // STB_IMAGE_IMPLEMENTATION
+
+/*
+ revision history:
+ 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs
+ 2.19 (2018-02-11) fix warning
+ 2.18 (2018-01-30) fix warnings
+ 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug
+ 1-bit BMP
+ *_is_16_bit api
+ avoid warnings
+ 2.16 (2017-07-23) all functions have 16-bit variants;
+ STBI_NO_STDIO works again;
+ compilation fixes;
+ fix rounding in unpremultiply;
+ optimize vertical flip;
+ disable raw_len validation;
+ documentation fixes
+ 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode;
+ warning fixes; disable run-time SSE detection on gcc;
+ uniform handling of optional "return" values;
+ thread-safe initialization of zlib tables
+ 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs
+ 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now
+ 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes
+ 2.11 (2016-04-02) allocate large structures on the stack
+ remove white matting for transparent PSD
+ fix reported channel count for PNG & BMP
+ re-enable SSE2 in non-gcc 64-bit
+ support RGB-formatted JPEG
+ read 16-bit PNGs (only as 8-bit)
+ 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED
+ 2.09 (2016-01-16) allow comments in PNM files
+ 16-bit-per-pixel TGA (not bit-per-component)
+ info() for TGA could break due to .hdr handling
+ info() for BMP to shares code instead of sloppy parse
+ can use STBI_REALLOC_SIZED if allocator doesn't support realloc
+ code cleanup
+ 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA
+ 2.07 (2015-09-13) fix compiler warnings
+ partial animated GIF support
+ limited 16-bpc PSD support
+ #ifdef unused functions
+ bug with < 92 byte PIC,PNM,HDR,TGA
+ 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value
+ 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning
+ 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit
+ 2.03 (2015-04-12) extra corruption checking (mmozeiko)
+ stbi_set_flip_vertically_on_load (nguillemot)
+ fix NEON support; fix mingw support
+ 2.02 (2015-01-19) fix incorrect assert, fix warning
+ 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2
+ 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG
+ 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg)
+ progressive JPEG (stb)
+ PGM/PPM support (Ken Miller)
+ STBI_MALLOC,STBI_REALLOC,STBI_FREE
+ GIF bugfix -- seemingly never worked
+ STBI_NO_*, STBI_ONLY_*
+ 1.48 (2014-12-14) fix incorrectly-named assert()
+ 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb)
+ optimize PNG (ryg)
+ fix bug in interlaced PNG with user-specified channel count (stb)
+ 1.46 (2014-08-26)
+ fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG
+ 1.45 (2014-08-16)
+ fix MSVC-ARM internal compiler error by wrapping malloc
+ 1.44 (2014-08-07)
+ various warning fixes from Ronny Chevalier
+ 1.43 (2014-07-15)
+ fix MSVC-only compiler problem in code changed in 1.42
+ 1.42 (2014-07-09)
+ don't define _CRT_SECURE_NO_WARNINGS (affects user code)
+ fixes to stbi__cleanup_jpeg path
+ added STBI_ASSERT to avoid requiring assert.h
+ 1.41 (2014-06-25)
+ fix search&replace from 1.36 that messed up comments/error messages
+ 1.40 (2014-06-22)
+ fix gcc struct-initialization warning
+ 1.39 (2014-06-15)
+ fix to TGA optimization when req_comp != number of components in TGA;
+ fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite)
+ add support for BMP version 5 (more ignored fields)
+ 1.38 (2014-06-06)
+ suppress MSVC warnings on integer casts truncating values
+ fix accidental rename of 'skip' field of I/O
+ 1.37 (2014-06-04)
+ remove duplicate typedef
+ 1.36 (2014-06-03)
+ convert to header file single-file library
+ if de-iphone isn't set, load iphone images color-swapped instead of returning NULL
+ 1.35 (2014-05-27)
+ various warnings
+ fix broken STBI_SIMD path
+ fix bug where stbi_load_from_file no longer left file pointer in correct place
+ fix broken non-easy path for 32-bit BMP (possibly never used)
+ TGA optimization by Arseny Kapoulkine
+ 1.34 (unknown)
+ use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case
+ 1.33 (2011-07-14)
+ make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements
+ 1.32 (2011-07-13)
+ support for "info" function for all supported filetypes (SpartanJ)
+ 1.31 (2011-06-20)
+ a few more leak fixes, bug in PNG handling (SpartanJ)
+ 1.30 (2011-06-11)
+ added ability to load files via callbacks to accomidate custom input streams (Ben Wenger)
+ removed deprecated format-specific test/load functions
+ removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway
+ error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha)
+ fix inefficiency in decoding 32-bit BMP (David Woo)
+ 1.29 (2010-08-16)
+ various warning fixes from Aurelien Pocheville
+ 1.28 (2010-08-01)
+ fix bug in GIF palette transparency (SpartanJ)
+ 1.27 (2010-08-01)
+ cast-to-stbi_uc to fix warnings
+ 1.26 (2010-07-24)
+ fix bug in file buffering for PNG reported by SpartanJ
+ 1.25 (2010-07-17)
+ refix trans_data warning (Won Chun)
+ 1.24 (2010-07-12)
+ perf improvements reading from files on platforms with lock-heavy fgetc()
+ minor perf improvements for jpeg
+ deprecated type-specific functions so we'll get feedback if they're needed
+ attempt to fix trans_data warning (Won Chun)
+ 1.23 fixed bug in iPhone support
+ 1.22 (2010-07-10)
+ removed image *writing* support
+ stbi_info support from Jetro Lauha
+ GIF support from Jean-Marc Lienher
+ iPhone PNG-extensions from James Brown
+ warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva)
+ 1.21 fix use of 'stbi_uc' in header (reported by jon blow)
+ 1.20 added support for Softimage PIC, by Tom Seddon
+ 1.19 bug in interlaced PNG corruption check (found by ryg)
+ 1.18 (2008-08-02)
+ fix a threading bug (local mutable static)
+ 1.17 support interlaced PNG
+ 1.16 major bugfix - stbi__convert_format converted one too many pixels
+ 1.15 initialize some fields for thread safety
+ 1.14 fix threadsafe conversion bug
+ header-file-only version (#define STBI_HEADER_FILE_ONLY before including)
+ 1.13 threadsafe
+ 1.12 const qualifiers in the API
+ 1.11 Support installable IDCT, colorspace conversion routines
+ 1.10 Fixes for 64-bit (don't use "unsigned long")
+ optimized upsampling by Fabian "ryg" Giesen
+ 1.09 Fix format-conversion for PSD code (bad global variables!)
+ 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz
+ 1.07 attempt to fix C++ warning/errors again
+ 1.06 attempt to fix C++ warning/errors again
+ 1.05 fix TGA loading to return correct *comp and use good luminance calc
+ 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free
+ 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR
+ 1.02 support for (subset of) HDR files, float interface for preferred access to them
+ 1.01 fix bug: possible bug in handling right-side up bmps... not sure
+ fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all
+ 1.00 interface to zlib that skips zlib header
+ 0.99 correct handling of alpha in palette
+ 0.98 TGA loader by lonesock; dynamically add loaders (untested)
+ 0.97 jpeg errors on too large a file; also catch another malloc failure
+ 0.96 fix detection of invalid v value - particleman@mollyrocket forum
+ 0.95 during header scan, seek to markers in case of padding
+ 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same
+ 0.93 handle jpegtran output; verbose errors
+ 0.92 read 4,8,16,24,32-bit BMP files of several formats
+ 0.91 output 24-bit Windows 3.0 BMP files
+ 0.90 fix a few more warnings; bump version number to approach 1.0
+ 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd
+ 0.60 fix compiling as c++
+ 0.59 fix warnings: merge Dave Moore's -Wall fixes
+ 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian
+ 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available
+ 0.56 fix bug: zlib uncompressed mode len vs. nlen
+ 0.55 fix bug: restart_interval not initialized to 0
+ 0.54 allow NULL for 'int *comp'
+ 0.53 fix bug in png 3->4; speedup png decoding
+ 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments
+ 0.51 obey req_comp requests, 1-component jpegs return as 1-component,
+ on 'test' only check type, not whether we support this variant
+ 0.50 (2006-11-19)
+ first released version
+*/
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/
diff --git a/src/image/stb_image_write.h b/src/image/stb_image_write.h
new file mode 100644
index 0000000..6ed3dd8
--- /dev/null
+++ b/src/image/stb_image_write.h
@@ -0,0 +1,1807 @@
+/* stb_image_write - v1.16 - public domain - http://nothings.org/stb
+ writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015
+ no warranty implied; use at your own risk
+
+ Before #including,
+
+ #define STB_IMAGE_WRITE_IMPLEMENTATION
+
+ in the file that you want to have the implementation.
+
+ Will probably not work correctly with strict-aliasing optimizations.
+
+ABOUT:
+
+ This header file is a library for writing images to C stdio or a callback.
+
+ The PNG output is not optimal; it is 20-50% larger than the file
+ written by a decent optimizing implementation; though providing a custom
+ zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that.
+ This library is designed for source code compactness and simplicity,
+ not optimal image file size or run-time performance.
+
+BUILDING:
+
+ You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h.
+ You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace
+ malloc,realloc,free.
+ You can #define STBIW_MEMMOVE() to replace memmove()
+ You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function
+ for PNG compression (instead of the builtin one), it must have the following signature:
+ unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality);
+ The returned data will be freed with STBIW_FREE() (free() by default),
+ so it must be heap allocated with STBIW_MALLOC() (malloc() by default),
+
+UNICODE:
+
+ If compiling for Windows and you wish to use Unicode filenames, compile
+ with
+ #define STBIW_WINDOWS_UTF8
+ and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert
+ Windows wchar_t filenames to utf8.
+
+USAGE:
+
+ There are five functions, one for each image file format:
+
+ int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
+ int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
+ int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
+ int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality);
+ int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
+
+ void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically
+
+ There are also five equivalent functions that use an arbitrary write function. You are
+ expected to open/close your file-equivalent before and after calling these:
+
+ int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes);
+ int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
+ int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
+ int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);
+ int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality);
+
+ where the callback is:
+ void stbi_write_func(void *context, void *data, int size);
+
+ You can configure it with these global variables:
+ int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE
+ int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression
+ int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode
+
+
+ You can define STBI_WRITE_NO_STDIO to disable the file variant of these
+ functions, so the library will not use stdio.h at all. However, this will
+ also disable HDR writing, because it requires stdio for formatted output.
+
+ Each function returns 0 on failure and non-0 on success.
+
+ The functions create an image file defined by the parameters. The image
+ is a rectangle of pixels stored from left-to-right, top-to-bottom.
+ Each pixel contains 'comp' channels of data stored interleaved with 8-bits
+ per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is
+ monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall.
+ The *data pointer points to the first byte of the top-left-most pixel.
+ For PNG, "stride_in_bytes" is the distance in bytes from the first byte of
+ a row of pixels to the first byte of the next row of pixels.
+
+ PNG creates output files with the same number of components as the input.
+ The BMP format expands Y to RGB in the file format and does not
+ output alpha.
+
+ PNG supports writing rectangles of data even when the bytes storing rows of
+ data are not consecutive in memory (e.g. sub-rectangles of a larger image),
+ by supplying the stride between the beginning of adjacent rows. The other
+ formats do not. (Thus you cannot write a native-format BMP through the BMP
+ writer, both because it is in BGR order and because it may have padding
+ at the end of the line.)
+
+ PNG allows you to set the deflate compression level by setting the global
+ variable 'stbi_write_png_compression_level' (it defaults to 8).
+
+ HDR expects linear float data. Since the format is always 32-bit rgb(e)
+ data, alpha (if provided) is discarded, and for monochrome data it is
+ replicated across all three channels.
+
+ TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed
+ data, set the global variable 'stbi_write_tga_with_rle' to 0.
+
+ JPEG does ignore alpha channels in input data; quality is between 1 and 100.
+ Higher quality looks better but results in a bigger image.
+ JPEG baseline (no JPEG progressive).
+
+CREDITS:
+
+
+ Sean Barrett - PNG/BMP/TGA
+ Baldur Karlsson - HDR
+ Jean-Sebastien Guay - TGA monochrome
+ Tim Kelsey - misc enhancements
+ Alan Hickman - TGA RLE
+ Emmanuel Julien - initial file IO callback implementation
+ Jon Olick - original jo_jpeg.cpp code
+ Daniel Gibson - integrate JPEG, allow external zlib
+ Aarni Koskela - allow choosing PNG filter
+
+ bugfixes:
+ github:Chribba
+ Guillaume Chereau
+ github:jry2
+ github:romigrou
+ Sergio Gonzalez
+ Jonas Karlsson
+ Filip Wasil
+ Thatcher Ulrich
+ github:poppolopoppo
+ Patrick Boettcher
+ github:xeekworx
+ Cap Petschulat
+ Simon Rodriguez
+ Ivan Tikhonov
+ github:ignotion
+ Adam Schackart
+ Andrew Kensler
+
+LICENSE
+
+ See end of file for license information.
+
+*/
+
+#ifndef INCLUDE_STB_IMAGE_WRITE_H
+# define INCLUDE_STB_IMAGE_WRITE_H
+
+# include
+
+// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline'
+# ifndef STBIWDEF
+# ifdef STB_IMAGE_WRITE_STATIC
+# define STBIWDEF static
+# else
+# ifdef __cplusplus
+# define STBIWDEF extern "C"
+# else
+# define STBIWDEF extern
+# endif
+# endif
+# endif
+
+# ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations
+STBIWDEF int stbi_write_tga_with_rle;
+STBIWDEF int stbi_write_png_compression_level;
+STBIWDEF int stbi_write_force_png_filter;
+# endif
+
+# ifndef STBI_WRITE_NO_STDIO
+STBIWDEF int stbi_write_png(char const* filename, int w, int h, int comp, void const* data, int stride_in_bytes);
+STBIWDEF int stbi_write_bmp(char const* filename, int w, int h, int comp, void const* data);
+STBIWDEF int stbi_write_tga(char const* filename, int w, int h, int comp, void const* data);
+STBIWDEF int stbi_write_hdr(char const* filename, int w, int h, int comp, float const* data);
+STBIWDEF int stbi_write_jpg(char const* filename, int x, int y, int comp, void const* data, int quality);
+
+# ifdef STBIW_WINDOWS_UTF8
+STBIWDEF int stbiw_convert_wchar_to_utf8(char* buffer, size_t bufferlen, wchar_t const* input);
+# endif
+# endif
+
+typedef void stbi_write_func(void* context, void* data, int size);
+
+STBIWDEF int stbi_write_png_to_func(stbi_write_func* func, void* context, int w, int h, int comp, void const* data, int stride_in_bytes);
+STBIWDEF int stbi_write_bmp_to_func(stbi_write_func* func, void* context, int w, int h, int comp, void const* data);
+STBIWDEF int stbi_write_tga_to_func(stbi_write_func* func, void* context, int w, int h, int comp, void const* data);
+STBIWDEF int stbi_write_hdr_to_func(stbi_write_func* func, void* context, int w, int h, int comp, float const* data);
+STBIWDEF int stbi_write_jpg_to_func(stbi_write_func* func, void* context, int x, int y, int comp, void const* data, int quality);
+
+STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean);
+
+#endif // INCLUDE_STB_IMAGE_WRITE_H
+
+#ifdef STB_IMAGE_WRITE_IMPLEMENTATION
+
+# ifdef _WIN32
+# ifndef _CRT_SECURE_NO_WARNINGS
+# define _CRT_SECURE_NO_WARNINGS
+# endif
+# ifndef _CRT_NONSTDC_NO_DEPRECATE
+# define _CRT_NONSTDC_NO_DEPRECATE
+# endif
+# endif
+
+# ifndef STBI_WRITE_NO_STDIO
+# include
+# endif // STBI_WRITE_NO_STDIO
+
+# include
+# include
+# include
+# include
+
+# if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED))
+// ok
+# elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED)
+// ok
+# else
+# error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)."
+# endif
+
+# ifndef STBIW_MALLOC
+# define STBIW_MALLOC(sz) malloc(sz)
+# define STBIW_REALLOC(p, newsz) realloc(p, newsz)
+# define STBIW_FREE(p) free(p)
+# endif
+
+# ifndef STBIW_REALLOC_SIZED
+# define STBIW_REALLOC_SIZED(p, oldsz, newsz) STBIW_REALLOC(p, newsz)
+# endif
+
+# ifndef STBIW_MEMMOVE
+# define STBIW_MEMMOVE(a, b, sz) memmove(a, b, sz)
+# endif
+
+# ifndef STBIW_ASSERT
+# include
+# define STBIW_ASSERT(x) assert(x)
+# endif
+
+# define STBIW_UCHAR(x) (unsigned char)((x) & 0xff)
+
+# ifdef STB_IMAGE_WRITE_STATIC
+static int stbi_write_png_compression_level = 8;
+static int stbi_write_tga_with_rle = 1;
+static int stbi_write_force_png_filter = -1;
+# else
+int stbi_write_png_compression_level = 8;
+int stbi_write_tga_with_rle = 1;
+int stbi_write_force_png_filter = -1;
+# endif
+
+static int stbi__flip_vertically_on_write = 0;
+
+STBIWDEF void stbi_flip_vertically_on_write(int flag)
+{
+ stbi__flip_vertically_on_write = flag;
+}
+
+typedef struct
+{
+ stbi_write_func* func;
+ void* context;
+ unsigned char buffer[64];
+ int buf_used;
+} stbi__write_context;
+
+// initialize a callback-based context
+static void stbi__start_write_callbacks(stbi__write_context* s, stbi_write_func* c, void* context)
+{
+ s->func = c;
+ s->context = context;
+}
+
+# ifndef STBI_WRITE_NO_STDIO
+
+static void stbi__stdio_write(void* context, void* data, int size)
+{
+ fwrite(data, 1, size, (FILE*)context);
+}
+
+# if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8)
+# ifdef __cplusplus
+# define STBIW_EXTERN extern "C"
+# else
+# define STBIW_EXTERN extern
+# endif
+STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, char const* str, int cbmb, wchar_t* widestr, int cchwide);
+STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, wchar_t const* widestr, int cchwide, char* str, int cbmb, char const* defchar, int* used_default);
+
+STBIWDEF int stbiw_convert_wchar_to_utf8(char* buffer, size_t bufferlen, wchar_t const* input)
+{
+ return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int)bufferlen, NULL, NULL);
+}
+# endif
+
+static FILE* stbiw__fopen(char const* filename, char const* mode)
+{
+ FILE* f;
+# if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8)
+ wchar_t wMode[64];
+ wchar_t wFilename[1024];
+ if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename) / sizeof(*wFilename)))
+ return 0;
+
+ if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode) / sizeof(*wMode)))
+ return 0;
+
+# if defined(_MSC_VER) && _MSC_VER >= 1400
+ if (0 != _wfopen_s(&f, wFilename, wMode))
+ f = 0;
+# else
+ f = _wfopen(wFilename, wMode);
+# endif
+
+# elif defined(_MSC_VER) && _MSC_VER >= 1400
+ if (0 != fopen_s(&f, filename, mode))
+ f = 0;
+# else
+ f = fopen(filename, mode);
+# endif
+ return f;
+}
+
+static int stbi__start_write_file(stbi__write_context* s, char const* filename)
+{
+ FILE* f = stbiw__fopen(filename, "wb");
+ stbi__start_write_callbacks(s, stbi__stdio_write, (void*)f);
+ return f != NULL;
+}
+
+static void stbi__end_write_file(stbi__write_context* s)
+{
+ fclose((FILE*)s->context);
+}
+
+# endif // !STBI_WRITE_NO_STDIO
+
+typedef unsigned int stbiw_uint32;
+typedef int stb_image_write_test[sizeof(stbiw_uint32) == 4 ? 1 : -1];
+
+static void stbiw__writefv(stbi__write_context* s, char const* fmt, va_list v)
+{
+ while (*fmt) {
+ switch (*fmt++) {
+ case ' ':
+ break;
+ case '1': {
+ unsigned char x = STBIW_UCHAR(va_arg(v, int));
+ s->func(s->context, &x, 1);
+ break;
+ }
+ case '2': {
+ int x = va_arg(v, int);
+ unsigned char b[2];
+ b[0] = STBIW_UCHAR(x);
+ b[1] = STBIW_UCHAR(x >> 8);
+ s->func(s->context, b, 2);
+ break;
+ }
+ case '4': {
+ stbiw_uint32 x = va_arg(v, int);
+ unsigned char b[4];
+ b[0] = STBIW_UCHAR(x);
+ b[1] = STBIW_UCHAR(x >> 8);
+ b[2] = STBIW_UCHAR(x >> 16);
+ b[3] = STBIW_UCHAR(x >> 24);
+ s->func(s->context, b, 4);
+ break;
+ }
+ default:
+ STBIW_ASSERT(0);
+ return;
+ }
+ }
+}
+
+static void stbiw__writef(stbi__write_context* s, char const* fmt, ...)
+{
+ va_list v;
+ va_start(v, fmt);
+ stbiw__writefv(s, fmt, v);
+ va_end(v);
+}
+
+static void stbiw__write_flush(stbi__write_context* s)
+{
+ if (s->buf_used) {
+ s->func(s->context, &s->buffer, s->buf_used);
+ s->buf_used = 0;
+ }
+}
+
+static void stbiw__putc(stbi__write_context* s, unsigned char c)
+{
+ s->func(s->context, &c, 1);
+}
+
+static void stbiw__write1(stbi__write_context* s, unsigned char a)
+{
+ if ((size_t)s->buf_used + 1 > sizeof(s->buffer))
+ stbiw__write_flush(s);
+ s->buffer[s->buf_used++] = a;
+}
+
+static void stbiw__write3(stbi__write_context* s, unsigned char a, unsigned char b, unsigned char c)
+{
+ int n;
+ if ((size_t)s->buf_used + 3 > sizeof(s->buffer))
+ stbiw__write_flush(s);
+ n = s->buf_used;
+ s->buf_used = n + 3;
+ s->buffer[n + 0] = a;
+ s->buffer[n + 1] = b;
+ s->buffer[n + 2] = c;
+}
+
+static void stbiw__write_pixel(stbi__write_context* s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char* d)
+{
+ unsigned char bg[3] = { 255, 0, 255 }, px[3];
+ int k;
+
+ if (write_alpha < 0)
+ stbiw__write1(s, d[comp - 1]);
+
+ switch (comp) {
+ case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case
+ case 1:
+ if (expand_mono)
+ stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp
+ else
+ stbiw__write1(s, d[0]); // monochrome TGA
+ break;
+ case 4:
+ if (!write_alpha) {
+ // composite against pink background
+ for (k = 0; k < 3; ++k)
+ px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255;
+ stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]);
+ break;
+ }
+ /* FALLTHROUGH */
+ case 3:
+ stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]);
+ break;
+ }
+ if (write_alpha > 0)
+ stbiw__write1(s, d[comp - 1]);
+}
+
+static void stbiw__write_pixels(stbi__write_context* s, int rgb_dir, int vdir, int x, int y, int comp, void* data, int write_alpha, int scanline_pad, int expand_mono)
+{
+ stbiw_uint32 zero = 0;
+ int i, j, j_end;
+
+ if (y <= 0)
+ return;
+
+ if (stbi__flip_vertically_on_write)
+ vdir *= -1;
+
+ if (vdir < 0) {
+ j_end = -1;
+ j = y - 1;
+ } else {
+ j_end = y;
+ j = 0;
+ }
+
+ for (; j != j_end; j += vdir) {
+ for (i = 0; i < x; ++i) {
+ unsigned char* d = (unsigned char*)data + (j * x + i) * comp;
+ stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d);
+ }
+ stbiw__write_flush(s);
+ s->func(s->context, &zero, scanline_pad);
+ }
+}
+
+static int stbiw__outfile(stbi__write_context* s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void* data, int alpha, int pad, char const* fmt, ...)
+{
+ if (y < 0 || x < 0) {
+ return 0;
+ } else {
+ va_list v;
+ va_start(v, fmt);
+ stbiw__writefv(s, fmt, v);
+ va_end(v);
+ stbiw__write_pixels(s, rgb_dir, vdir, x, y, comp, data, alpha, pad, expand_mono);
+ return 1;
+ }
+}
+
+static int stbi_write_bmp_core(stbi__write_context* s, int x, int y, int comp, void const* data)
+{
+ if (comp != 4) {
+ // write RGB bitmap
+ int pad = (-x * 3) & 3;
+ return stbiw__outfile(s, -1, -1, x, y, comp, 1, (void*)data, 0, pad,
+ "11 4 22 4"
+ "4 44 22 444444",
+ 'B', 'M', 14 + 40 + (x * 3 + pad) * y, 0, 0, 14 + 40, // file header
+ 40, x, y, 1, 24, 0, 0, 0, 0, 0, 0); // bitmap header
+ } else {
+ // RGBA bitmaps need a v4 header
+ // use BI_BITFIELDS mode with 32bpp and alpha mask
+ // (straight BI_RGB with alpha mask doesn't work in most readers)
+ return stbiw__outfile(s, -1, -1, x, y, comp, 1, (void*)data, 1, 0,
+ "11 4 22 4"
+ "4 44 22 444444 4444 4 444 444 444 444",
+ 'B', 'M', 14 + 108 + x * y * 4, 0, 0, 14 + 108, // file header
+ 108, x, y, 1, 32, 3, 0, 0, 0, 0, 0, 0xff0000, 0xff00, 0xff, 0xff000000u, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); // bitmap V4 header
+ }
+}
+
+STBIWDEF int stbi_write_bmp_to_func(stbi_write_func* func, void* context, int x, int y, int comp, void const* data)
+{
+ stbi__write_context s = { 0 };
+ stbi__start_write_callbacks(&s, func, context);
+ return stbi_write_bmp_core(&s, x, y, comp, data);
+}
+
+# ifndef STBI_WRITE_NO_STDIO
+STBIWDEF int stbi_write_bmp(char const* filename, int x, int y, int comp, void const* data)
+{
+ stbi__write_context s = { 0 };
+ if (stbi__start_write_file(&s, filename)) {
+ int r = stbi_write_bmp_core(&s, x, y, comp, data);
+ stbi__end_write_file(&s);
+ return r;
+ } else
+ return 0;
+}
+# endif //! STBI_WRITE_NO_STDIO
+
+static int stbi_write_tga_core(stbi__write_context* s, int x, int y, int comp, void* data)
+{
+ int has_alpha = (comp == 2 || comp == 4);
+ int colorbytes = has_alpha ? comp - 1 : comp;
+ int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3
+
+ if (y < 0 || x < 0)
+ return 0;
+
+ if (!stbi_write_tga_with_rle) {
+ return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void*)data, has_alpha, 0,
+ "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8);
+ } else {
+ int i, j, k;
+ int jend, jdir;
+
+ stbiw__writef(s, "111 221 2222 11", 0, 0, format + 8, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8);
+
+ if (stbi__flip_vertically_on_write) {
+ j = 0;
+ jend = y;
+ jdir = 1;
+ } else {
+ j = y - 1;
+ jend = -1;
+ jdir = -1;
+ }
+ for (; j != jend; j += jdir) {
+ unsigned char* row = (unsigned char*)data + j * x * comp;
+ int len;
+
+ for (i = 0; i < x; i += len) {
+ unsigned char* begin = row + i * comp;
+ int diff = 1;
+ len = 1;
+
+ if (i < x - 1) {
+ ++len;
+ diff = memcmp(begin, row + (i + 1) * comp, comp);
+ if (diff) {
+ unsigned char const* prev = begin;
+ for (k = i + 2; k < x && len < 128; ++k) {
+ if (memcmp(prev, row + k * comp, comp)) {
+ prev += comp;
+ ++len;
+ } else {
+ --len;
+ break;
+ }
+ }
+ } else {
+ for (k = i + 2; k < x && len < 128; ++k) {
+ if (!memcmp(begin, row + k * comp, comp)) {
+ ++len;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ if (diff) {
+ unsigned char header = STBIW_UCHAR(len - 1);
+ stbiw__write1(s, header);
+ for (k = 0; k < len; ++k) {
+ stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp);
+ }
+ } else {
+ unsigned char header = STBIW_UCHAR(len - 129);
+ stbiw__write1(s, header);
+ stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin);
+ }
+ }
+ }
+ stbiw__write_flush(s);
+ }
+ return 1;
+}
+
+STBIWDEF int stbi_write_tga_to_func(stbi_write_func* func, void* context, int x, int y, int comp, void const* data)
+{
+ stbi__write_context s = { 0 };
+ stbi__start_write_callbacks(&s, func, context);
+ return stbi_write_tga_core(&s, x, y, comp, (void*)data);
+}
+
+# ifndef STBI_WRITE_NO_STDIO
+STBIWDEF int stbi_write_tga(char const* filename, int x, int y, int comp, void const* data)
+{
+ stbi__write_context s = { 0 };
+ if (stbi__start_write_file(&s, filename)) {
+ int r = stbi_write_tga_core(&s, x, y, comp, (void*)data);
+ stbi__end_write_file(&s);
+ return r;
+ } else
+ return 0;
+}
+# endif
+
+// *************************************************************************************************
+// Radiance RGBE HDR writer
+// by Baldur Karlsson
+
+# define stbiw__max(a, b) ((a) > (b) ? (a) : (b))
+
+# ifndef STBI_WRITE_NO_STDIO
+
+static void stbiw__linear_to_rgbe(unsigned char* rgbe, float* linear)
+{
+ int exponent;
+ float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2]));
+
+ if (maxcomp < 1e-32f) {
+ rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
+ } else {
+ float normalize = (float)frexp(maxcomp, &exponent) * 256.0f / maxcomp;
+
+ rgbe[0] = (unsigned char)(linear[0] * normalize);
+ rgbe[1] = (unsigned char)(linear[1] * normalize);
+ rgbe[2] = (unsigned char)(linear[2] * normalize);
+ rgbe[3] = (unsigned char)(exponent + 128);
+ }
+}
+
+static void stbiw__write_run_data(stbi__write_context* s, int length, unsigned char databyte)
+{
+ unsigned char lengthbyte = STBIW_UCHAR(length + 128);
+ STBIW_ASSERT(length + 128 <= 255);
+ s->func(s->context, &lengthbyte, 1);
+ s->func(s->context, &databyte, 1);
+}
+
+static void stbiw__write_dump_data(stbi__write_context* s, int length, unsigned char* data)
+{
+ unsigned char lengthbyte = STBIW_UCHAR(length);
+ STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code
+ s->func(s->context, &lengthbyte, 1);
+ s->func(s->context, data, length);
+}
+
+static void stbiw__write_hdr_scanline(stbi__write_context* s, int width, int ncomp, unsigned char* scratch, float* scanline)
+{
+ unsigned char scanlineheader[4] = { 2, 2, 0, 0 };
+ unsigned char rgbe[4];
+ float linear[3];
+ int x;
+
+ scanlineheader[2] = (width & 0xff00) >> 8;
+ scanlineheader[3] = (width & 0x00ff);
+
+ /* skip RLE for images too small or large */
+ if (width < 8 || width >= 32768) {
+ for (x = 0; x < width; x++) {
+ switch (ncomp) {
+ case 4: /* fallthrough */
+ case 3:
+ linear[2] = scanline[x * ncomp + 2];
+ linear[1] = scanline[x * ncomp + 1];
+ linear[0] = scanline[x * ncomp + 0];
+ break;
+ default:
+ linear[0] = linear[1] = linear[2] = scanline[x * ncomp + 0];
+ break;
+ }
+ stbiw__linear_to_rgbe(rgbe, linear);
+ s->func(s->context, rgbe, 4);
+ }
+ } else {
+ int c, r;
+ /* encode into scratch buffer */
+ for (x = 0; x < width; x++) {
+ switch (ncomp) {
+ case 4: /* fallthrough */
+ case 3:
+ linear[2] = scanline[x * ncomp + 2];
+ linear[1] = scanline[x * ncomp + 1];
+ linear[0] = scanline[x * ncomp + 0];
+ break;
+ default:
+ linear[0] = linear[1] = linear[2] = scanline[x * ncomp + 0];
+ break;
+ }
+ stbiw__linear_to_rgbe(rgbe, linear);
+ scratch[x + width * 0] = rgbe[0];
+ scratch[x + width * 1] = rgbe[1];
+ scratch[x + width * 2] = rgbe[2];
+ scratch[x + width * 3] = rgbe[3];
+ }
+
+ s->func(s->context, scanlineheader, 4);
+
+ /* RLE each component separately */
+ for (c = 0; c < 4; c++) {
+ unsigned char* comp = &scratch[width * c];
+
+ x = 0;
+ while (x < width) {
+ // find first run
+ r = x;
+ while (r + 2 < width) {
+ if (comp[r] == comp[r + 1] && comp[r] == comp[r + 2])
+ break;
+ ++r;
+ }
+ if (r + 2 >= width)
+ r = width;
+ // dump up to first run
+ while (x < r) {
+ int len = r - x;
+ if (len > 128)
+ len = 128;
+ stbiw__write_dump_data(s, len, &comp[x]);
+ x += len;
+ }
+ // if there's a run, output it
+ if (r + 2 < width) { // same test as what we break out of in search loop, so only true if we break'd
+ // find next byte after run
+ while (r < width && comp[r] == comp[x])
+ ++r;
+ // output run up to r
+ while (x < r) {
+ int len = r - x;
+ if (len > 127)
+ len = 127;
+ stbiw__write_run_data(s, len, comp[x]);
+ x += len;
+ }
+ }
+ }
+ }
+ }
+}
+
+static int stbi_write_hdr_core(stbi__write_context* s, int x, int y, int comp, float* data)
+{
+ if (y <= 0 || x <= 0 || data == NULL)
+ return 0;
+ else {
+ // Each component is stored separately. Allocate scratch space for full output scanline.
+ unsigned char* scratch = (unsigned char*)STBIW_MALLOC(x * 4);
+ int i, len;
+ char buffer[128];
+ char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n";
+ s->func(s->context, header, sizeof(header) - 1);
+
+# ifdef __STDC_LIB_EXT1__
+ len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
+# else
+ len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
+# endif
+ s->func(s->context, buffer, len);
+
+ for (i = 0; i < y; i++)
+ stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp * x * (stbi__flip_vertically_on_write ? y - 1 - i : i));
+ STBIW_FREE(scratch);
+ return 1;
+ }
+}
+
+STBIWDEF int stbi_write_hdr_to_func(stbi_write_func* func, void* context, int x, int y, int comp, float const* data)
+{
+ stbi__write_context s = { 0 };
+ stbi__start_write_callbacks(&s, func, context);
+ return stbi_write_hdr_core(&s, x, y, comp, (float*)data);
+}
+
+STBIWDEF int stbi_write_hdr(char const* filename, int x, int y, int comp, float const* data)
+{
+ stbi__write_context s = { 0 };
+ if (stbi__start_write_file(&s, filename)) {
+ int r = stbi_write_hdr_core(&s, x, y, comp, (float*)data);
+ stbi__end_write_file(&s);
+ return r;
+ } else
+ return 0;
+}
+# endif // STBI_WRITE_NO_STDIO
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// PNG writer
+//
+
+# ifndef STBIW_ZLIB_COMPRESS
+// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size()
+# define stbiw__sbraw(a) ((int*)(void*)(a) - 2)
+# define stbiw__sbm(a) stbiw__sbraw(a)[0]
+# define stbiw__sbn(a) stbiw__sbraw(a)[1]
+
+# define stbiw__sbneedgrow(a, n) ((a) == 0 || stbiw__sbn(a) + n >= stbiw__sbm(a))
+# define stbiw__sbmaybegrow(a, n) (stbiw__sbneedgrow(a, (n)) ? stbiw__sbgrow(a, n) : 0)
+# define stbiw__sbgrow(a, n) stbiw__sbgrowf((void**)&(a), (n), sizeof(*(a)))
+
+# define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a, 1), (a)[stbiw__sbn(a)++] = (v))
+# define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0)
+# define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)), 0 : 0)
+
+static void* stbiw__sbgrowf(void** arr, int increment, int itemsize)
+{
+ int m = *arr ? 2 * stbiw__sbm(*arr) + increment : increment + 1;
+ void* p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr) * itemsize + sizeof(int) * 2) : 0, itemsize * m + sizeof(int) * 2);
+ STBIW_ASSERT(p);
+ if (p) {
+ if (!*arr)
+ ((int*)p)[1] = 0;
+ *arr = (void*)((int*)p + 2);
+ stbiw__sbm(*arr) = m;
+ }
+ return *arr;
+}
+
+static unsigned char* stbiw__zlib_flushf(unsigned char* data, unsigned int* bitbuffer, int* bitcount)
+{
+ while (*bitcount >= 8) {
+ stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer));
+ *bitbuffer >>= 8;
+ *bitcount -= 8;
+ }
+ return data;
+}
+
+static int stbiw__zlib_bitrev(int code, int codebits)
+{
+ int res = 0;
+ while (codebits--) {
+ res = (res << 1) | (code & 1);
+ code >>= 1;
+ }
+ return res;
+}
+
+static unsigned int stbiw__zlib_countm(unsigned char* a, unsigned char* b, int limit)
+{
+ int i;
+ for (i = 0; i < limit && i < 258; ++i)
+ if (a[i] != b[i])
+ break;
+ return i;
+}
+
+static unsigned int stbiw__zhash(unsigned char* data)
+{
+ stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);
+ hash ^= hash << 3;
+ hash += hash >> 5;
+ hash ^= hash << 4;
+ hash += hash >> 17;
+ hash ^= hash << 25;
+ hash += hash >> 6;
+ return hash;
+}
+
+# define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount))
+# define stbiw__zlib_add(code, codebits) \
+ (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush())
+# define stbiw__zlib_huffa(b, c) stbiw__zlib_add(stbiw__zlib_bitrev(b, c), c)
+// default huffman tables
+# define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8)
+# define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n) - 144, 9)
+# define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n) - 256, 7)
+# define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n) - 280, 8)
+# define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) \
+ : (n) <= 279 ? stbiw__zlib_huff3(n) \
+ : stbiw__zlib_huff4(n))
+# define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n))
+
+# define stbiw__ZHASH 16384
+
+# endif // STBIW_ZLIB_COMPRESS
+
+STBIWDEF unsigned char* stbi_zlib_compress(unsigned char* data, int data_len, int* out_len, int quality)
+{
+# ifdef STBIW_ZLIB_COMPRESS
+ // user provided a zlib compress implementation, use that
+ return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality);
+# else // use builtin
+ static unsigned short lengthc[] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 259 };
+ static unsigned char lengtheb[] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 };
+ static unsigned short distc[] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 32768 };
+ static unsigned char disteb[] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 };
+ unsigned int bitbuf = 0;
+ int i, j, bitcount = 0;
+ unsigned char* out = NULL;
+ unsigned char*** hash_table = (unsigned char***)STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**));
+ if (hash_table == NULL)
+ return NULL;
+ if (quality < 5)
+ quality = 5;
+
+ stbiw__sbpush(out, 0x78); // DEFLATE 32K window
+ stbiw__sbpush(out, 0x5e); // FLEVEL = 1
+ stbiw__zlib_add(1, 1); // BFINAL = 1
+ stbiw__zlib_add(1, 2); // BTYPE = 1 -- fixed huffman
+
+ for (i = 0; i < stbiw__ZHASH; ++i)
+ hash_table[i] = NULL;
+
+ i = 0;
+ while (i < data_len - 3) {
+ // hash next 3 bytes of data to be compressed
+ int h = stbiw__zhash(data + i) & (stbiw__ZHASH - 1), best = 3;
+ unsigned char* bestloc = 0;
+ unsigned char** hlist = hash_table[h];
+ int n = stbiw__sbcount(hlist);
+ for (j = 0; j < n; ++j) {
+ if (hlist[j] - data > i - 32768) { // if entry lies within window
+ int d = stbiw__zlib_countm(hlist[j], data + i, data_len - i);
+ if (d >= best) {
+ best = d;
+ bestloc = hlist[j];
+ }
+ }
+ }
+ // when hash table entry is too long, delete half the entries
+ if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2 * quality) {
+ STBIW_MEMMOVE(hash_table[h], hash_table[h] + quality, sizeof(hash_table[h][0]) * quality);
+ stbiw__sbn(hash_table[h]) = quality;
+ }
+ stbiw__sbpush(hash_table[h], data + i);
+
+ if (bestloc) {
+ // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal
+ h = stbiw__zhash(data + i + 1) & (stbiw__ZHASH - 1);
+ hlist = hash_table[h];
+ n = stbiw__sbcount(hlist);
+ for (j = 0; j < n; ++j) {
+ if (hlist[j] - data > i - 32767) {
+ int e = stbiw__zlib_countm(hlist[j], data + i + 1, data_len - i - 1);
+ if (e > best) { // if next match is better, bail on current match
+ bestloc = NULL;
+ break;
+ }
+ }
+ }
+ }
+
+ if (bestloc) {
+ int d = (int)(data + i - bestloc); // distance back
+ STBIW_ASSERT(d <= 32767 && best <= 258);
+ for (j = 0; best > lengthc[j + 1] - 1; ++j)
+ ;
+ stbiw__zlib_huff(j + 257);
+ if (lengtheb[j])
+ stbiw__zlib_add(best - lengthc[j], lengtheb[j]);
+ for (j = 0; d > distc[j + 1] - 1; ++j)
+ ;
+ stbiw__zlib_add(stbiw__zlib_bitrev(j, 5), 5);
+ if (disteb[j])
+ stbiw__zlib_add(d - distc[j], disteb[j]);
+ i += best;
+ } else {
+ stbiw__zlib_huffb(data[i]);
+ ++i;
+ }
+ }
+ // write out final bytes
+ for (; i < data_len; ++i)
+ stbiw__zlib_huffb(data[i]);
+ stbiw__zlib_huff(256); // end of block
+ // pad with 0 bits to byte boundary
+ while (bitcount)
+ stbiw__zlib_add(0, 1);
+
+ for (i = 0; i < stbiw__ZHASH; ++i)
+ (void)stbiw__sbfree(hash_table[i]);
+ STBIW_FREE(hash_table);
+
+ // store uncompressed instead if compression was worse
+ if (stbiw__sbn(out) > data_len + 2 + ((data_len + 32766) / 32767) * 5) {
+ stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1
+ for (j = 0; j < data_len;) {
+ int blocklen = data_len - j;
+ if (blocklen > 32767)
+ blocklen = 32767;
+ stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression
+ stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN
+ stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8));
+ stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN
+ stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8));
+ memcpy(out + stbiw__sbn(out), data + j, blocklen);
+ stbiw__sbn(out) += blocklen;
+ j += blocklen;
+ }
+ }
+
+ {
+ // compute adler32 on input
+ unsigned int s1 = 1, s2 = 0;
+ int blocklen = (int)(data_len % 5552);
+ j = 0;
+ while (j < data_len) {
+ for (i = 0; i < blocklen; ++i) {
+ s1 += data[j + i];
+ s2 += s1;
+ }
+ s1 %= 65521;
+ s2 %= 65521;
+ j += blocklen;
+ blocklen = 5552;
+ }
+ stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8));
+ stbiw__sbpush(out, STBIW_UCHAR(s2));
+ stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8));
+ stbiw__sbpush(out, STBIW_UCHAR(s1));
+ }
+ *out_len = stbiw__sbn(out);
+ // make returned pointer freeable
+ STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len);
+ return (unsigned char*)stbiw__sbraw(out);
+# endif // STBIW_ZLIB_COMPRESS
+}
+
+static unsigned int stbiw__crc32(unsigned char* buffer, int len)
+{
+# ifdef STBIW_CRC32
+ return STBIW_CRC32(buffer, len);
+# else
+ static unsigned int crc_table[256] = {
+ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
+ 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
+ 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
+ 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
+ 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+ 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
+ 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
+ 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
+ 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
+ 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
+ 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
+ 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
+ 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
+ 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
+ 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
+ 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
+ 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
+ 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
+ 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
+ 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
+ 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
+ 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+ };
+
+ unsigned int crc = ~0u;
+ int i;
+ for (i = 0; i < len; ++i)
+ crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
+ return ~crc;
+# endif
+}
+
+# define stbiw__wpng4(o, a, b, c, d) ((o)[0] = STBIW_UCHAR(a), (o)[1] = STBIW_UCHAR(b), (o)[2] = STBIW_UCHAR(c), (o)[3] = STBIW_UCHAR(d), (o) += 4)
+# define stbiw__wp32(data, v) stbiw__wpng4(data, (v) >> 24, (v) >> 16, (v) >> 8, (v));
+# define stbiw__wptag(data, s) stbiw__wpng4(data, s[0], s[1], s[2], s[3])
+
+static void stbiw__wpcrc(unsigned char** data, int len)
+{
+ unsigned int crc = stbiw__crc32(*data - len - 4, len + 4);
+ stbiw__wp32(*data, crc);
+}
+
+static unsigned char stbiw__paeth(int a, int b, int c)
+{
+ int p = a + b - c, pa = abs(p - a), pb = abs(p - b), pc = abs(p - c);
+ if (pa <= pb && pa <= pc)
+ return STBIW_UCHAR(a);
+ if (pb <= pc)
+ return STBIW_UCHAR(b);
+ return STBIW_UCHAR(c);
+}
+
+// @OPTIMIZE: provide an option that always forces left-predict or paeth predict
+static void stbiw__encode_png_line(unsigned char* pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char* line_buffer)
+{
+ static int mapping[] = { 0, 1, 2, 3, 4 };
+ static int firstmap[] = { 0, 1, 0, 5, 6 };
+ int* mymap = (y != 0) ? mapping : firstmap;
+ int i;
+ int type = mymap[filter_type];
+ unsigned char* z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height - 1 - y : y);
+ int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes;
+
+ if (type == 0) {
+ memcpy(line_buffer, z, width * n);
+ return;
+ }
+
+ // first loop isn't optimized since it's just one pixel
+ for (i = 0; i < n; ++i) {
+ switch (type) {
+ case 1:
+ line_buffer[i] = z[i];
+ break;
+ case 2:
+ line_buffer[i] = z[i] - z[i - signed_stride];
+ break;
+ case 3:
+ line_buffer[i] = z[i] - (z[i - signed_stride] >> 1);
+ break;
+ case 4:
+ line_buffer[i] = (signed char)(z[i] - stbiw__paeth(0, z[i - signed_stride], 0));
+ break;
+ case 5:
+ line_buffer[i] = z[i];
+ break;
+ case 6:
+ line_buffer[i] = z[i];
+ break;
+ }
+ }
+ switch (type) {
+ case 1:
+ for (i = n; i < width * n; ++i)
+ line_buffer[i] = z[i] - z[i - n];
+ break;
+ case 2:
+ for (i = n; i < width * n; ++i)
+ line_buffer[i] = z[i] - z[i - signed_stride];
+ break;
+ case 3:
+ for (i = n; i < width * n; ++i)
+ line_buffer[i] = z[i] - ((z[i - n] + z[i - signed_stride]) >> 1);
+ break;
+ case 4:
+ for (i = n; i < width * n; ++i)
+ line_buffer[i] = z[i] - stbiw__paeth(z[i - n], z[i - signed_stride], z[i - signed_stride - n]);
+ break;
+ case 5:
+ for (i = n; i < width * n; ++i)
+ line_buffer[i] = z[i] - (z[i - n] >> 1);
+ break;
+ case 6:
+ for (i = n; i < width * n; ++i)
+ line_buffer[i] = z[i] - stbiw__paeth(z[i - n], 0, 0);
+ break;
+ }
+}
+
+STBIWDEF unsigned char* stbi_write_png_to_mem(unsigned char const* pixels, int stride_bytes, int x, int y, int n, int* out_len)
+{
+ int force_filter = stbi_write_force_png_filter;
+ int ctype[5] = { -1, 0, 4, 2, 6 };
+ unsigned char sig[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
+ unsigned char *out, *o, *filt, *zlib;
+ signed char* line_buffer;
+ int j, zlen;
+
+ if (stride_bytes == 0)
+ stride_bytes = x * n;
+
+ if (force_filter >= 5) {
+ force_filter = -1;
+ }
+
+ filt = (unsigned char*)STBIW_MALLOC((x * n + 1) * y);
+ if (!filt)
+ return 0;
+ line_buffer = (signed char*)STBIW_MALLOC(x * n);
+ if (!line_buffer) {
+ STBIW_FREE(filt);
+ return 0;
+ }
+ for (j = 0; j < y; ++j) {
+ int filter_type;
+ if (force_filter > -1) {
+ filter_type = force_filter;
+ stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer);
+ } else { // Estimate the best filter by running through all of them:
+ int best_filter = 0, best_filter_val = 0x7fffffff, est, i;
+ for (filter_type = 0; filter_type < 5; filter_type++) {
+ stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer);
+
+ // Estimate the entropy of the line using this filter; the less, the better.
+ est = 0;
+ for (i = 0; i < x * n; ++i) {
+ est += abs((signed char)line_buffer[i]);
+ }
+ if (est < best_filter_val) {
+ best_filter_val = est;
+ best_filter = filter_type;
+ }
+ }
+ if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it
+ stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer);
+ filter_type = best_filter;
+ }
+ }
+ // when we get here, filter_type contains the filter type, and line_buffer contains the data
+ filt[j * (x * n + 1)] = (unsigned char)filter_type;
+ STBIW_MEMMOVE(filt + j * (x * n + 1) + 1, line_buffer, x * n);
+ }
+ STBIW_FREE(line_buffer);
+ zlib = stbi_zlib_compress(filt, y * (x * n + 1), &zlen, stbi_write_png_compression_level);
+ STBIW_FREE(filt);
+ if (!zlib)
+ return 0;
+
+ // each tag requires 12 bytes of overhead
+ out = (unsigned char*)STBIW_MALLOC(8 + 12 + 13 + 12 + zlen + 12);
+ if (!out)
+ return 0;
+ *out_len = 8 + 12 + 13 + 12 + zlen + 12;
+
+ o = out;
+ STBIW_MEMMOVE(o, sig, 8);
+ o += 8;
+ stbiw__wp32(o, 13); // header length
+ stbiw__wptag(o, "IHDR");
+ stbiw__wp32(o, x);
+ stbiw__wp32(o, y);
+ *o++ = 8;
+ *o++ = STBIW_UCHAR(ctype[n]);
+ *o++ = 0;
+ *o++ = 0;
+ *o++ = 0;
+ stbiw__wpcrc(&o, 13);
+
+ stbiw__wp32(o, zlen);
+ stbiw__wptag(o, "IDAT");
+ STBIW_MEMMOVE(o, zlib, zlen);
+ o += zlen;
+ STBIW_FREE(zlib);
+ stbiw__wpcrc(&o, zlen);
+
+ stbiw__wp32(o, 0);
+ stbiw__wptag(o, "IEND");
+ stbiw__wpcrc(&o, 0);
+
+ STBIW_ASSERT(o == out + *out_len);
+
+ return out;
+}
+
+# ifndef STBI_WRITE_NO_STDIO
+STBIWDEF int stbi_write_png(char const* filename, int x, int y, int comp, void const* data, int stride_bytes)
+{
+ FILE* f;
+ int len;
+ unsigned char* png = stbi_write_png_to_mem((unsigned char const*)data, stride_bytes, x, y, comp, &len);
+ if (png == NULL)
+ return 0;
+
+ f = stbiw__fopen(filename, "wb");
+ if (!f) {
+ STBIW_FREE(png);
+ return 0;
+ }
+ fwrite(png, 1, len, f);
+ fclose(f);
+ STBIW_FREE(png);
+ return 1;
+}
+# endif
+
+STBIWDEF int stbi_write_png_to_func(stbi_write_func* func, void* context, int x, int y, int comp, void const* data, int stride_bytes)
+{
+ int len;
+ unsigned char* png = stbi_write_png_to_mem((unsigned char const*)data, stride_bytes, x, y, comp, &len);
+ if (png == NULL)
+ return 0;
+ func(context, png, len);
+ STBIW_FREE(png);
+ return 1;
+}
+
+/* ***************************************************************************
+ *
+ * JPEG writer
+ *
+ * This is based on Jon Olick's jo_jpeg.cpp:
+ * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html
+ */
+
+static unsigned char const stbiw__jpg_ZigZag[] = { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18,
+ 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63 };
+
+static void stbiw__jpg_writeBits(stbi__write_context* s, int* bitBufP, int* bitCntP, unsigned short const* bs)
+{
+ int bitBuf = *bitBufP, bitCnt = *bitCntP;
+ bitCnt += bs[1];
+ bitBuf |= bs[0] << (24 - bitCnt);
+ while (bitCnt >= 8) {
+ unsigned char c = (bitBuf >> 16) & 255;
+ stbiw__putc(s, c);
+ if (c == 255) {
+ stbiw__putc(s, 0);
+ }
+ bitBuf <<= 8;
+ bitCnt -= 8;
+ }
+ *bitBufP = bitBuf;
+ *bitCntP = bitCnt;
+}
+
+static void stbiw__jpg_DCT(float* d0p, float* d1p, float* d2p, float* d3p, float* d4p, float* d5p, float* d6p, float* d7p)
+{
+ float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p;
+ float z1, z2, z3, z4, z5, z11, z13;
+
+ float tmp0 = d0 + d7;
+ float tmp7 = d0 - d7;
+ float tmp1 = d1 + d6;
+ float tmp6 = d1 - d6;
+ float tmp2 = d2 + d5;
+ float tmp5 = d2 - d5;
+ float tmp3 = d3 + d4;
+ float tmp4 = d3 - d4;
+
+ // Even part
+ float tmp10 = tmp0 + tmp3; // phase 2
+ float tmp13 = tmp0 - tmp3;
+ float tmp11 = tmp1 + tmp2;
+ float tmp12 = tmp1 - tmp2;
+
+ d0 = tmp10 + tmp11; // phase 3
+ d4 = tmp10 - tmp11;
+
+ z1 = (tmp12 + tmp13) * 0.707106781f; // c4
+ d2 = tmp13 + z1; // phase 5
+ d6 = tmp13 - z1;
+
+ // Odd part
+ tmp10 = tmp4 + tmp5; // phase 2
+ tmp11 = tmp5 + tmp6;
+ tmp12 = tmp6 + tmp7;
+
+ // The rotator is modified from fig 4-8 to avoid extra negations.
+ z5 = (tmp10 - tmp12) * 0.382683433f; // c6
+ z2 = tmp10 * 0.541196100f + z5; // c2-c6
+ z4 = tmp12 * 1.306562965f + z5; // c2+c6
+ z3 = tmp11 * 0.707106781f; // c4
+
+ z11 = tmp7 + z3; // phase 5
+ z13 = tmp7 - z3;
+
+ *d5p = z13 + z2; // phase 6
+ *d3p = z13 - z2;
+ *d1p = z11 + z4;
+ *d7p = z11 - z4;
+
+ *d0p = d0;
+ *d2p = d2;
+ *d4p = d4;
+ *d6p = d6;
+}
+
+static void stbiw__jpg_calcBits(int val, unsigned short bits[2])
+{
+ int tmp1 = val < 0 ? -val : val;
+ val = val < 0 ? val - 1 : val;
+ bits[1] = 1;
+ while (tmp1 >>= 1) {
+ ++bits[1];
+ }
+ bits[0] = val & ((1 << bits[1]) - 1);
+}
+
+static int stbiw__jpg_processDU(stbi__write_context* s, int* bitBuf, int* bitCnt, float* CDU, int du_stride, float* fdtbl, int DC, unsigned short const HTDC[256][2], unsigned short const HTAC[256][2])
+{
+ unsigned short const EOB[2] = { HTAC[0x00][0], HTAC[0x00][1] };
+ unsigned short const M16zeroes[2] = { HTAC[0xF0][0], HTAC[0xF0][1] };
+ int dataOff, i, j, n, diff, end0pos, x, y;
+ int DU[64];
+
+ // DCT rows
+ for (dataOff = 0, n = du_stride * 8; dataOff < n; dataOff += du_stride) {
+ stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff + 1], &CDU[dataOff + 2], &CDU[dataOff + 3], &CDU[dataOff + 4], &CDU[dataOff + 5], &CDU[dataOff + 6], &CDU[dataOff + 7]);
+ }
+ // DCT columns
+ for (dataOff = 0; dataOff < 8; ++dataOff) {
+ stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff + du_stride], &CDU[dataOff + du_stride * 2], &CDU[dataOff + du_stride * 3], &CDU[dataOff + du_stride * 4],
+ &CDU[dataOff + du_stride * 5], &CDU[dataOff + du_stride * 6], &CDU[dataOff + du_stride * 7]);
+ }
+ // Quantize/descale/zigzag the coefficients
+ for (y = 0, j = 0; y < 8; ++y) {
+ for (x = 0; x < 8; ++x, ++j) {
+ float v;
+ i = y * du_stride + x;
+ v = CDU[i] * fdtbl[j];
+ // DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));
+ // ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway?
+ DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? v - 0.5f : v + 0.5f);
+ }
+ }
+
+ // Encode DC
+ diff = DU[0] - DC;
+ if (diff == 0) {
+ stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[0]);
+ } else {
+ unsigned short bits[2];
+ stbiw__jpg_calcBits(diff, bits);
+ stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[bits[1]]);
+ stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);
+ }
+ // Encode ACs
+ end0pos = 63;
+ for (; (end0pos > 0) && (DU[end0pos] == 0); --end0pos) {
+ }
+ // end0pos = first element in reverse order !=0
+ if (end0pos == 0) {
+ stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);
+ return DU[0];
+ }
+ for (i = 1; i <= end0pos; ++i) {
+ int startpos = i;
+ int nrzeroes;
+ unsigned short bits[2];
+ for (; DU[i] == 0 && i <= end0pos; ++i) {
+ }
+ nrzeroes = i - startpos;
+ if (nrzeroes >= 16) {
+ int lng = nrzeroes >> 4;
+ int nrmarker;
+ for (nrmarker = 1; nrmarker <= lng; ++nrmarker)
+ stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes);
+ nrzeroes &= 15;
+ }
+ stbiw__jpg_calcBits(DU[i], bits);
+ stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes << 4) + bits[1]]);
+ stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);
+ }
+ if (end0pos != 63) {
+ stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);
+ }
+ return DU[0];
+}
+
+static int stbi_write_jpg_core(stbi__write_context* s, int width, int height, int comp, void const* data, int quality)
+{
+ // Constants that don't pollute global namespace
+ static unsigned char const std_dc_luminance_nrcodes[] = { 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 };
+ static unsigned char const std_dc_luminance_values[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
+ static unsigned char const std_ac_luminance_nrcodes[] = { 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d };
+ static unsigned char const std_ac_luminance_values[] = {
+ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
+ 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+ 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+ 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+ 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa
+ };
+ static unsigned char const std_dc_chrominance_nrcodes[] = { 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 };
+ static unsigned char const std_dc_chrominance_values[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
+ static unsigned char const std_ac_chrominance_nrcodes[] = { 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 };
+ static unsigned char const std_ac_chrominance_values[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+ 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
+ 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
+ 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
+ 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa
+ };
+ // Huffman tables
+ static unsigned short const YDC_HT[256][2] = { { 0, 2 }, { 2, 3 }, { 3, 3 }, { 4, 3 }, { 5, 3 }, { 6, 3 }, { 14, 4 }, { 30, 5 }, { 62, 6 }, { 126, 7 }, { 254, 8 }, { 510, 9 } };
+ static unsigned short const UVDC_HT[256][2] = { { 0, 2 }, { 1, 2 }, { 2, 2 }, { 6, 3 }, { 14, 4 }, { 30, 5 }, { 62, 6 }, { 126, 7 }, { 254, 8 }, { 510, 9 }, { 1022, 10 }, { 2046, 11 } };
+ static unsigned short const YAC_HT[256][2] = {
+ { 10, 4 }, { 0, 2 }, { 1, 2 }, { 4, 3 }, { 11, 4 }, { 26, 5 }, { 120, 7 }, { 248, 8 }, { 1014, 10 }, { 65410, 16 }, { 65411, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 12, 4 }, { 27, 5 }, { 121, 7 }, { 502, 9 }, { 2038, 11 }, { 65412, 16 }, { 65413, 16 }, { 65414, 16 }, { 65415, 16 }, { 65416, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 28, 5 }, { 249, 8 }, { 1015, 10 }, { 4084, 12 }, { 65417, 16 }, { 65418, 16 }, { 65419, 16 }, { 65420, 16 }, { 65421, 16 }, { 65422, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 58, 6 }, { 503, 9 }, { 4085, 12 }, { 65423, 16 }, { 65424, 16 }, { 65425, 16 }, { 65426, 16 }, { 65427, 16 }, { 65428, 16 }, { 65429, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 59, 6 }, { 1016, 10 }, { 65430, 16 }, { 65431, 16 }, { 65432, 16 }, { 65433, 16 }, { 65434, 16 }, { 65435, 16 }, { 65436, 16 }, { 65437, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 122, 7 }, { 2039, 11 }, { 65438, 16 }, { 65439, 16 }, { 65440, 16 }, { 65441, 16 }, { 65442, 16 }, { 65443, 16 }, { 65444, 16 }, { 65445, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 123, 7 }, { 4086, 12 }, { 65446, 16 }, { 65447, 16 }, { 65448, 16 }, { 65449, 16 }, { 65450, 16 }, { 65451, 16 }, { 65452, 16 }, { 65453, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 250, 8 }, { 4087, 12 }, { 65454, 16 }, { 65455, 16 }, { 65456, 16 }, { 65457, 16 }, { 65458, 16 }, { 65459, 16 }, { 65460, 16 }, { 65461, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 504, 9 }, { 32704, 15 }, { 65462, 16 }, { 65463, 16 }, { 65464, 16 }, { 65465, 16 }, { 65466, 16 }, { 65467, 16 }, { 65468, 16 }, { 65469, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 505, 9 }, { 65470, 16 }, { 65471, 16 }, { 65472, 16 }, { 65473, 16 }, { 65474, 16 }, { 65475, 16 }, { 65476, 16 }, { 65477, 16 }, { 65478, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 506, 9 }, { 65479, 16 }, { 65480, 16 }, { 65481, 16 }, { 65482, 16 }, { 65483, 16 }, { 65484, 16 }, { 65485, 16 }, { 65486, 16 }, { 65487, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 1017, 10 }, { 65488, 16 }, { 65489, 16 }, { 65490, 16 }, { 65491, 16 }, { 65492, 16 }, { 65493, 16 }, { 65494, 16 }, { 65495, 16 }, { 65496, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 1018, 10 }, { 65497, 16 }, { 65498, 16 }, { 65499, 16 }, { 65500, 16 }, { 65501, 16 }, { 65502, 16 }, { 65503, 16 }, { 65504, 16 }, { 65505, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 2040, 11 }, { 65506, 16 }, { 65507, 16 }, { 65508, 16 }, { 65509, 16 }, { 65510, 16 }, { 65511, 16 }, { 65512, 16 }, { 65513, 16 }, { 65514, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 65515, 16 }, { 65516, 16 }, { 65517, 16 }, { 65518, 16 }, { 65519, 16 }, { 65520, 16 }, { 65521, 16 }, { 65522, 16 }, { 65523, 16 }, { 65524, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 2041, 11 }, { 65525, 16 }, { 65526, 16 }, { 65527, 16 }, { 65528, 16 }, { 65529, 16 }, { 65530, 16 }, { 65531, 16 }, { 65532, 16 }, { 65533, 16 }, { 65534, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }
+ };
+ static unsigned short const UVAC_HT[256][2] = {
+ { 0, 2 }, { 1, 2 }, { 4, 3 }, { 10, 4 }, { 24, 5 }, { 25, 5 }, { 56, 6 }, { 120, 7 }, { 500, 9 }, { 1014, 10 }, { 4084, 12 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 11, 4 }, { 57, 6 }, { 246, 8 }, { 501, 9 }, { 2038, 11 }, { 4085, 12 }, { 65416, 16 }, { 65417, 16 }, { 65418, 16 }, { 65419, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 26, 5 }, { 247, 8 }, { 1015, 10 }, { 4086, 12 }, { 32706, 15 }, { 65420, 16 }, { 65421, 16 }, { 65422, 16 }, { 65423, 16 }, { 65424, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 27, 5 }, { 248, 8 }, { 1016, 10 }, { 4087, 12 }, { 65425, 16 }, { 65426, 16 }, { 65427, 16 }, { 65428, 16 }, { 65429, 16 }, { 65430, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 58, 6 }, { 502, 9 }, { 65431, 16 }, { 65432, 16 }, { 65433, 16 }, { 65434, 16 }, { 65435, 16 }, { 65436, 16 }, { 65437, 16 }, { 65438, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 59, 6 }, { 1017, 10 }, { 65439, 16 }, { 65440, 16 }, { 65441, 16 }, { 65442, 16 }, { 65443, 16 }, { 65444, 16 }, { 65445, 16 }, { 65446, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 121, 7 }, { 2039, 11 }, { 65447, 16 }, { 65448, 16 }, { 65449, 16 }, { 65450, 16 }, { 65451, 16 }, { 65452, 16 }, { 65453, 16 }, { 65454, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 122, 7 }, { 2040, 11 }, { 65455, 16 }, { 65456, 16 }, { 65457, 16 }, { 65458, 16 }, { 65459, 16 }, { 65460, 16 }, { 65461, 16 }, { 65462, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 249, 8 }, { 65463, 16 }, { 65464, 16 }, { 65465, 16 }, { 65466, 16 }, { 65467, 16 }, { 65468, 16 }, { 65469, 16 }, { 65470, 16 }, { 65471, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 503, 9 }, { 65472, 16 }, { 65473, 16 }, { 65474, 16 }, { 65475, 16 }, { 65476, 16 }, { 65477, 16 }, { 65478, 16 }, { 65479, 16 }, { 65480, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 504, 9 }, { 65481, 16 }, { 65482, 16 }, { 65483, 16 }, { 65484, 16 }, { 65485, 16 }, { 65486, 16 }, { 65487, 16 }, { 65488, 16 }, { 65489, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 505, 9 }, { 65490, 16 }, { 65491, 16 }, { 65492, 16 }, { 65493, 16 }, { 65494, 16 }, { 65495, 16 }, { 65496, 16 }, { 65497, 16 }, { 65498, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 506, 9 }, { 65499, 16 }, { 65500, 16 }, { 65501, 16 }, { 65502, 16 }, { 65503, 16 }, { 65504, 16 }, { 65505, 16 }, { 65506, 16 }, { 65507, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 2041, 11 }, { 65508, 16 }, { 65509, 16 }, { 65510, 16 }, { 65511, 16 }, { 65512, 16 }, { 65513, 16 }, { 65514, 16 }, { 65515, 16 }, { 65516, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 16352, 14 }, { 65517, 16 }, { 65518, 16 }, { 65519, 16 }, { 65520, 16 }, { 65521, 16 }, { 65522, 16 }, { 65523, 16 }, { 65524, 16 }, { 65525, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 1018, 10 }, { 32707, 15 }, { 65526, 16 }, { 65527, 16 }, { 65528, 16 }, { 65529, 16 }, { 65530, 16 }, { 65531, 16 }, { 65532, 16 }, { 65533, 16 }, { 65534, 16 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }
+ };
+ static int const YQT[] = { 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22,
+ 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99 };
+ static int const UVQT[] = { 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 };
+ static float const aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f,
+ 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f };
+
+ int row, col, i, k, subsample;
+ float fdtbl_Y[64], fdtbl_UV[64];
+ unsigned char YTable[64], UVTable[64];
+
+ if (!data || !width || !height || comp > 4 || comp < 1) {
+ return 0;
+ }
+
+ quality = quality ? quality : 90;
+ subsample = quality <= 90 ? 1 : 0;
+ quality = quality < 1 ? 1 : quality > 100 ? 100
+ : quality;
+ quality = quality < 50 ? 5000 / quality : 200 - quality * 2;
+
+ for (i = 0; i < 64; ++i) {
+ int uvti, yti = (YQT[i] * quality + 50) / 100;
+ YTable[stbiw__jpg_ZigZag[i]] = (unsigned char)(yti < 1 ? 1 : yti > 255 ? 255
+ : yti);
+ uvti = (UVQT[i] * quality + 50) / 100;
+ UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char)(uvti < 1 ? 1 : uvti > 255 ? 255
+ : uvti);
+ }
+
+ for (row = 0, k = 0; row < 8; ++row) {
+ for (col = 0; col < 8; ++col, ++k) {
+ fdtbl_Y[k] = 1 / (YTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);
+ fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);
+ }
+ }
+
+ // Write Headers
+ {
+ static unsigned char const head0[] = { 0xFF, 0xD8, 0xFF, 0xE0, 0, 0x10, 'J', 'F', 'I', 'F', 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0xFF, 0xDB, 0, 0x84, 0 };
+ static unsigned char const head2[] = { 0xFF, 0xDA, 0, 0xC, 3, 1, 0, 2, 0x11, 3, 0x11, 0, 0x3F, 0 };
+ unsigned char const head1[] = { 0xFF, 0xC0, 0, 0x11, 8, (unsigned char)(height >> 8), STBIW_UCHAR(height), (unsigned char)(width >> 8), STBIW_UCHAR(width),
+ 3, 1, (unsigned char)(subsample ? 0x22 : 0x11), 0, 2, 0x11, 1, 3, 0x11, 1, 0xFF, 0xC4, 0x01, 0xA2, 0 };
+ s->func(s->context, (void*)head0, sizeof(head0));
+ s->func(s->context, (void*)YTable, sizeof(YTable));
+ stbiw__putc(s, 1);
+ s->func(s->context, UVTable, sizeof(UVTable));
+ s->func(s->context, (void*)head1, sizeof(head1));
+ s->func(s->context, (void*)(std_dc_luminance_nrcodes + 1), sizeof(std_dc_luminance_nrcodes) - 1);
+ s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values));
+ stbiw__putc(s, 0x10); // HTYACinfo
+ s->func(s->context, (void*)(std_ac_luminance_nrcodes + 1), sizeof(std_ac_luminance_nrcodes) - 1);
+ s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values));
+ stbiw__putc(s, 1); // HTUDCinfo
+ s->func(s->context, (void*)(std_dc_chrominance_nrcodes + 1), sizeof(std_dc_chrominance_nrcodes) - 1);
+ s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values));
+ stbiw__putc(s, 0x11); // HTUACinfo
+ s->func(s->context, (void*)(std_ac_chrominance_nrcodes + 1), sizeof(std_ac_chrominance_nrcodes) - 1);
+ s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values));
+ s->func(s->context, (void*)head2, sizeof(head2));
+ }
+
+ // Encode 8x8 macroblocks
+ {
+ static unsigned short const fillBits[] = { 0x7F, 7 };
+ int DCY = 0, DCU = 0, DCV = 0;
+ int bitBuf = 0, bitCnt = 0;
+ // comp == 2 is grey+alpha (alpha is ignored)
+ int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0;
+ unsigned char const* dataR = (unsigned char const*)data;
+ unsigned char const* dataG = dataR + ofsG;
+ unsigned char const* dataB = dataR + ofsB;
+ int x, y, pos;
+ if (subsample) {
+ for (y = 0; y < height; y += 16) {
+ for (x = 0; x < width; x += 16) {
+ float Y[256], U[256], V[256];
+ for (row = y, pos = 0; row < y + 16; ++row) {
+ // row >= height => use last input row
+ int clamped_row = (row < height) ? row : height - 1;
+ int base_p = (stbi__flip_vertically_on_write ? (height - 1 - clamped_row) : clamped_row) * width * comp;
+ for (col = x; col < x + 16; ++col, ++pos) {
+ // if col >= width => use pixel from last input column
+ int p = base_p + ((col < width) ? col : (width - 1)) * comp;
+ float r = dataR[p], g = dataG[p], b = dataB[p];
+ Y[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128;
+ U[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b;
+ V[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b;
+ }
+ }
+ DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
+ DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
+ DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
+ DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
+
+ // subsample U,V
+ {
+ float subU[64], subV[64];
+ int yy, xx;
+ for (yy = 0, pos = 0; yy < 8; ++yy) {
+ for (xx = 0; xx < 8; ++xx, ++pos) {
+ int j = yy * 32 + xx * 2;
+ subU[pos] = (U[j + 0] + U[j + 1] + U[j + 16] + U[j + 17]) * 0.25f;
+ subV[pos] = (V[j + 0] + V[j + 1] + V[j + 16] + V[j + 17]) * 0.25f;
+ }
+ }
+ DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
+ DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
+ }
+ }
+ }
+ } else {
+ for (y = 0; y < height; y += 8) {
+ for (x = 0; x < width; x += 8) {
+ float Y[64], U[64], V[64];
+ for (row = y, pos = 0; row < y + 8; ++row) {
+ // row >= height => use last input row
+ int clamped_row = (row < height) ? row : height - 1;
+ int base_p = (stbi__flip_vertically_on_write ? (height - 1 - clamped_row) : clamped_row) * width * comp;
+ for (col = x; col < x + 8; ++col, ++pos) {
+ // if col >= width => use pixel from last input column
+ int p = base_p + ((col < width) ? col : (width - 1)) * comp;
+ float r = dataR[p], g = dataG[p], b = dataB[p];
+ Y[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128;
+ U[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b;
+ V[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b;
+ }
+ }
+
+ DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT);
+ DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
+ DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
+ }
+ }
+ }
+
+ // Do the bit alignment of the EOI marker
+ stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits);
+ }
+
+ // EOI
+ stbiw__putc(s, 0xFF);
+ stbiw__putc(s, 0xD9);
+
+ return 1;
+}
+
+STBIWDEF int stbi_write_jpg_to_func(stbi_write_func* func, void* context, int x, int y, int comp, void const* data, int quality)
+{
+ stbi__write_context s = { 0 };
+ stbi__start_write_callbacks(&s, func, context);
+ return stbi_write_jpg_core(&s, x, y, comp, (void*)data, quality);
+}
+
+# ifndef STBI_WRITE_NO_STDIO
+STBIWDEF int stbi_write_jpg(char const* filename, int x, int y, int comp, void const* data, int quality)
+{
+ stbi__write_context s = { 0 };
+ if (stbi__start_write_file(&s, filename)) {
+ int r = stbi_write_jpg_core(&s, x, y, comp, data, quality);
+ stbi__end_write_file(&s);
+ return r;
+ } else
+ return 0;
+}
+# endif
+
+#endif // STB_IMAGE_WRITE_IMPLEMENTATION
+
+/* Revision history
+ 1.16 (2021-07-11)
+ make Deflate code emit uncompressed blocks when it would otherwise expand
+ support writing BMPs with alpha channel
+ 1.15 (2020-07-13) unknown
+ 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels
+ 1.13
+ 1.12
+ 1.11 (2019-08-11)
+
+ 1.10 (2019-02-07)
+ support utf8 filenames in Windows; fix warnings and platform ifdefs
+ 1.09 (2018-02-11)
+ fix typo in zlib quality API, improve STB_I_W_STATIC in C++
+ 1.08 (2018-01-29)
+ add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter
+ 1.07 (2017-07-24)
+ doc fix
+ 1.06 (2017-07-23)
+ writing JPEG (using Jon Olick's code)
+ 1.05 ???
+ 1.04 (2017-03-03)
+ monochrome BMP expansion
+ 1.03 ???
+ 1.02 (2016-04-02)
+ avoid allocating large structures on the stack
+ 1.01 (2016-01-16)
+ STBIW_REALLOC_SIZED: support allocators with no realloc support
+ avoid race-condition in crc initialization
+ minor compile issues
+ 1.00 (2015-09-14)
+ installable file IO function
+ 0.99 (2015-09-13)
+ warning fixes; TGA rle support
+ 0.98 (2015-04-08)
+ added STBIW_MALLOC, STBIW_ASSERT etc
+ 0.97 (2015-01-18)
+ fixed HDR asserts, rewrote HDR rle logic
+ 0.96 (2015-01-17)
+ add HDR output
+ fix monochrome BMP
+ 0.95 (2014-08-17)
+ add monochrome TGA output
+ 0.94 (2014-05-31)
+ rename private functions to avoid conflicts with stb_image.h
+ 0.93 (2014-05-27)
+ warning fixes
+ 0.92 (2010-08-01)
+ casts to unsigned char to fix warnings
+ 0.91 (2010-07-17)
+ first public release
+ 0.90 first internal release
+*/
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/
diff --git a/src/libtemple/ioport.h b/src/libtemple/ioport.h
new file mode 100644
index 0000000..69e69e3
--- /dev/null
+++ b/src/libtemple/ioport.h
@@ -0,0 +1,6 @@
+u8 ioport_read_u8(u16 address);
+u16 ioport_read_u16(u16 address);
+u32 ioport_read_u32(u16 address);
+void ioport_write_u8(u16 address, u8 value);
+void ioport_write_u16(u16 address, u16 value);
+void ioport_write_u32(u16 address, u32 value);
diff --git a/src/libtemple/libtemple.cpp b/src/libtemple/libtemple.cpp
new file mode 100644
index 0000000..82ce415
--- /dev/null
+++ b/src/libtemple/libtemple.cpp
@@ -0,0 +1,77 @@
+unsigned long ioport_read_u8(unsigned short address) { return 0; }
+
+unsigned long ioport_read_u16(unsigned short address) { return 0; }
+
+unsigned long ioport_read_u32(unsigned short address) { return 0; }
+
+void ioport_write_u8(unsigned short address, unsigned char value) { }
+
+void ioport_write_u16(unsigned short address, unsigned short value) { }
+
+void ioport_write_u32(unsigned short address, unsigned int value) { }
+
+bool os_blink(char const* frequency_as_string) { return 0; }
+
+unsigned long os_call(unsigned long function_name, unsigned long arg) { return 0; }
+
+unsigned int os_device_calloc(unsigned int size) { return 0; }
+
+void os_exit() { }
+
+char const* os_file_picker(char const* path, char const* glob) { return 0; }
+
+char const* os_files_list(char const* path) { return 0; }
+
+bool os_is_vm() { return 0; }
+
+bool os_path_exists(char const* path) { return 0; }
+
+void os_pc_speaker(char const* frequency_as_string) { }
+
+unsigned long os_random() { return 0; }
+
+unsigned long os_read_entire_file(char const* filename, long* size)
+{
+ return 0;
+}
+
+void os_screenshot() { }
+
+char const* os_to_uppercase(char const* input_string) { return 0; }
+
+void os_write_entire_file(char const* filename, unsigned char* buffer,
+ long size) { }
+
+long pci_find(long class_code) { return 0; }
+
+unsigned long pci_read_u8(long bus, long device, long fun, long offset)
+{
+ return 0;
+}
+
+unsigned long pci_read_u16(long bus, long device, long fun, long offset)
+{
+ return 0;
+}
+
+unsigned long pci_read_u32(long bus, long device, long fun, long offset)
+{
+ return 0;
+}
+
+void pci_write_u8(long bus, long device, long fun, long offset,
+ unsigned char value) { }
+
+void pci_write_u16(long bus, long device, long fun, long offset,
+ unsigned short value) { }
+
+void pci_write_u32(long bus, long device, long fun, long offset,
+ unsigned int value) { }
+
+void time_busy(long duration) { }
+
+long time_jiffies() { return 0; }
+
+long time_now() { return 0; }
+
+void time_sleep(long duration) { }
\ No newline at end of file
diff --git a/src/libtemple/os.h b/src/libtemple/os.h
new file mode 100644
index 0000000..24e489e
--- /dev/null
+++ b/src/libtemple/os.h
@@ -0,0 +1,15 @@
+bool os_blink(char const* frequency_as_string);
+unsigned long os_call(unsigned long function_name, unsigned long arg);
+unsigned int os_device_calloc(unsigned int size);
+void os_exit();
+char const* os_file_picker(char const* path, char const* glob);
+char const* os_files_list(char const* path);
+bool os_is_vm();
+bool os_path_exists(char const* path);
+void os_pc_speaker(char const* frequency_as_string);
+unsigned long os_random();
+u8* os_read_entire_file(char const* filename, i64* size);
+void os_screenshot();
+char const* os_to_uppercase(char const* input_string);
+void os_write_entire_file(char const* filename, unsigned char* buffer,
+ i64 size);
\ No newline at end of file
diff --git a/src/libtemple/pci.h b/src/libtemple/pci.h
new file mode 100644
index 0000000..33d7fea
--- /dev/null
+++ b/src/libtemple/pci.h
@@ -0,0 +1,10 @@
+long pci_find(long class_code);
+unsigned long pci_read_u8(long bus, long device, long fun, long offset);
+unsigned long pci_read_u16(long bus, long device, long fun, long offset);
+unsigned long pci_read_u32(long bus, long device, long fun, long offset);
+void pci_write_u8(long bus, long device, long fun, long offset,
+ unsigned char value);
+void pci_write_u16(long bus, long device, long fun, long offset,
+ unsigned short value);
+void pci_write_u32(long bus, long device, long fun, long offset,
+ unsigned int value);
\ No newline at end of file
diff --git a/src/libtemple/time.h b/src/libtemple/time.h
new file mode 100644
index 0000000..e2fe7c4
--- /dev/null
+++ b/src/libtemple/time.h
@@ -0,0 +1,4 @@
+void time_busy(i64 duration);
+i64 time_jiffies();
+i64 time_now();
+void time_sleep(i64 duration);
\ No newline at end of file
diff --git a/src/net/devices/virtio.h b/src/net/devices/virtio.h
new file mode 100644
index 0000000..989e1d0
--- /dev/null
+++ b/src/net/devices/virtio.h
@@ -0,0 +1,28 @@
+struct virtio_queue_buf {
+ u64 address;
+ u32 length;
+ u16 flags;
+ u16 next;
+};
+struct virtio_avail {
+ u16 flags;
+ u16 index;
+ u16 ring[256];
+ u16 int_index;
+};
+struct virtio_used_item {
+ u32 index;
+ u32 length;
+};
+struct virtio_used {
+ u16 flags;
+ u16 index;
+ virtio_used_item ring[256];
+ u16 int_index;
+};
+struct virtio_queue {
+ virtio_queue_buf buffers[256];
+ virtio_avail available;
+ u8 padding[3578];
+ virtio_used used;
+};
\ No newline at end of file
diff --git a/src/net/devices/virtio.jakt b/src/net/devices/virtio.jakt
new file mode 100644
index 0000000..647af28
--- /dev/null
+++ b/src/net/devices/virtio.jakt
@@ -0,0 +1,143 @@
+import relative parent::os::os { OS }
+import relative parent::os::pci { PCI, PCIDevice }
+
+enum VirtIOConfig: u8 {
+ acknowledge = 1
+ driver = 2
+ driver_ok = 4
+}
+
+enum VirtIOReg: u16 {
+ host_features = 0
+ guest_features = 4
+ queue_page_frame_number = 8
+ queue_size = 12
+ queue_select = 14
+ queue_notify = 16
+ status = 18
+ isr = 19
+ config = 20
+}
+
+class VirtIO {
+ public pci_device: PCIDevice
+ public rq_index: i64
+ public rq_size: u16
+ public rq: u32
+ public tq_size: u16
+ public tq: u32
+ public fn rx_frame(mut this) throws -> [u8] {
+ mut frame: [u8] = []
+ mut queue_notify: bool = false
+ unsafe {
+ cpp {
+"
+#include <../../src/net/devices/virtio.h>
+virtio_queue *rq = (virtio_queue*)this->rq;
+i64 i = this->rq_index;
+i64 used_index = rq->used.index;
+if (used_index < i)
+ used_index += 0x10000;
+if (used_index && i != used_index) {
+ virtio_used_item* item = rq->used.ring;
+ u8* buffer = (u8*)rq->buffers[item[i % 256].index + 1].address;
+ i64 length = item[i % 256].length - 10;
+ for (i64 j = 0; j < length; j++)
+ frame.push(buffer[j]);
+ this->rq_index = used_index % 0x10000;
+ rq->available.index++;
+ queue_notify = true;
+}
+"
+ }
+ }
+ if queue_notify {
+ .pci_device.io_write_u16(offset: VirtIOReg::queue_notify as! u16, value: 0)
+ }
+ return frame
+ }
+ public fn tx_frame(mut this, anon mut data: [u8]) throws {
+ mut size = data.size()
+ unsafe {
+ cpp {
+"
+#include <../../src/net/devices/virtio.h>
+virtio_queue *tq = (virtio_queue*)this->tq;
+int tq_idx = tq->available.index % 256;
+int tq_idx2 = tq_idx % 128;
+memset((u8*)tq->buffers[tq_idx2 * 2].address, 0, 10);
+u8 *buffer = (u8*)tq->buffers[(tq_idx2 * 2) + 1].address;
+ for (int i = 0; i < size; i++)
+ buffer[i] = data[i];
+tq->buffers[tq_idx2 * 2].length = 10;
+tq->buffers[tq_idx2 * 2].flags = 1;
+tq->buffers[tq_idx2 * 2].next = (tq_idx2 * 2) + 1;
+tq->buffers[(tq_idx2 * 2) + 1].length = size;
+tq->buffers[(tq_idx2 * 2) + 1].flags = 0;
+tq->buffers[(tq_idx2 * 2) + 1].next = 0;
+tq->available.ring[tq_idx] = tq_idx2 * 2;
+tq->available.index++;
+"
+ }
+ }
+ .pci_device.io_write_u16(offset: VirtIOReg::queue_notify as! u16, value: 1)
+ }
+ fn reset_device(this) {
+ .pci_device.io_write_u8(offset: VirtIOReg::status as! u16, value: 0)
+ }
+ fn found_driver(this) throws {
+ .pci_device.io_write_u8(offset: VirtIOReg::status as! u16,
+ value: .pci_device.io_read_u8(VirtIOReg::status as! u16) | VirtIOConfig::acknowledge as! u8 | VirtIOConfig::driver as! u8)
+ }
+ fn setup_rx_queue(mut this) throws {
+ .pci_device.io_write_u16(offset: VirtIOReg::queue_select as! u16, value: 0)
+ .rq_size = .pci_device.io_read_u16(VirtIOReg::queue_size as! u16)
+ .rq = OS::device_calloc(16384)
+ .pci_device.io_write_u32(offset: VirtIOReg::queue_page_frame_number as! u16, value: .rq / 4096)
+ }
+ fn setup_tx_queue(mut this) throws {
+ .pci_device.io_write_u16(offset: VirtIOReg::queue_select as! u16, value: 1)
+ .tq_size = .pci_device.io_read_u16(VirtIOReg::queue_size as! u16)
+ .tq = OS::device_calloc(16384)
+ .pci_device.io_write_u32(offset: VirtIOReg::queue_page_frame_number as! u16, value: .tq / 4096)
+ }
+ fn init_queue_buffers(this) {
+ unsafe {
+ cpp {
+"
+#include <../../src/net/devices/virtio.h>
+virtio_queue *rq = (virtio_queue*)this->rq;
+virtio_queue *tq = (virtio_queue*)this->tq;
+for (int i = 0; i < 128; i++) {
+ rq->buffers[i * 2].address = (u64)calloc(1, 16);
+ rq->buffers[i * 2].length = 10;
+ rq->buffers[i * 2].flags = 3;
+ rq->buffers[i * 2].next = (i * 2) + 1;
+ rq->buffers[(i * 2) + 1].address = (u64)calloc(1, 2048);
+ rq->buffers[(i * 2) + 1].length = 2048;
+ rq->buffers[(i * 2) + 1].flags = 2;
+ rq->buffers[(i * 2) + 1].next = 0;
+ rq->available.ring[i] = i * 2;
+ rq->available.ring[i + 128] = i * 2;
+ tq->buffers[i * 2].address = (u64)calloc(1, 16);
+ tq->buffers[(i * 2) + 1].address = (u64)calloc(1, 2048);
+}
+rq->available.index = 1;
+"
+ }
+ }
+ }
+ fn init_ok(this) throws {
+ .pci_device.io_write_u8(offset: VirtIOReg::status as! u16,
+ value: .pci_device.io_read_u8(VirtIOReg::status as! u16) | VirtIOConfig::driver_ok as! u8)
+ .pci_device.io_write_u16(offset: VirtIOReg::queue_notify as! u16, value: 0)
+ }
+ public fn init(mut this) throws {
+ .reset_device()
+ .found_driver()
+ .setup_rx_queue()
+ .setup_tx_queue()
+ .init_queue_buffers()
+ .init_ok()
+ }
+}
\ No newline at end of file
diff --git a/src/net/lib/json.jakt b/src/net/lib/json.jakt
new file mode 100644
index 0000000..5cdd177
--- /dev/null
+++ b/src/net/lib/json.jakt
@@ -0,0 +1,350 @@
+/// Expect:
+/// - output: "JsonValue::JsonArray([JsonValue::Object([\"id\": JsonValue::Number(0.5), \"displayName\": JsonValue::JsonString(\"Air\"), \"name\": JsonValue::JsonString(\"air\"), \"hardness\": JsonValue::Number(3.9), \"resistance\": JsonValue::Number(0), \"minStateId\": JsonValue::Number(0), \"maxStateId\": JsonValue::Number(0), \"states\": JsonValue::JsonArray([])])])\n"
+
+enum JsonValue {
+ Null
+ Bool(bool)
+ Number(f64)
+ // FIXME: This variant should be called String
+ JsonString(String)
+ // FIXME: This variant should be called Array
+ JsonArray([JsonValue])
+ Object([String:JsonValue])
+}
+
+fn is_whitespace(anon c: u8) -> bool {
+ return match c {
+ b'\t' | b'\n' | b'\r' | b' ' => true
+ else => false
+ }
+}
+
+class JsonParser {
+ input: String
+ index: usize
+
+ public fn construct(input: String) throws -> JsonParser {
+ return JsonParser(input, index: 0)
+ }
+
+ fn eof(this) -> bool {
+ return .index >= .input.length()
+ }
+
+ public fn parse(mut this) throws -> JsonValue {
+ // FIXME: Jakt::JsonParser ignores trailing whitespace for some reason.
+ let value = .parse_helper()
+ if not .eof() {
+ // FIXME: "Didn't consume all input"
+ throw Error::from_errno(9000)
+ }
+ return value
+ }
+
+ fn skip_whitespace(mut this) {
+ while not .eof() {
+ if not is_whitespace(.input.byte_at(.index)) {
+ break
+ }
+ .index++
+ }
+ }
+
+ fn consume_and_unescape_string(mut this) throws -> String {
+ if not .consume_specific(b'"') {
+ // FIXME: "Expected '"'
+ throw Error::from_errno(9007)
+ }
+
+ mut builder = StringBuilder::create()
+
+ loop {
+ mut ch = 0u8
+ mut peek_index = .index
+ while peek_index < .input.length() {
+ ch = .input.byte_at(peek_index)
+ if ch == b'"' or ch == b'\\' {
+ break
+ }
+ // FIXME: This is is_ascii_c0_control()
+ if ch < 0x20 {
+ // FIXME: "Error while parsing string"
+ throw Error::from_errno(9008)
+ }
+ peek_index++
+ }
+
+ while peek_index != .index {
+ builder.append(.input.byte_at(.index))
+ .index++
+ }
+
+ if .eof() {
+ break
+ }
+
+ if ch == b'"' {
+ break
+ }
+
+ if ch != b'\\' {
+ builder.append(.consume())
+ continue
+ }
+
+ .ignore()
+
+ match .peek() {
+ b'"' | b'/' | b'\\' | b'n' | b'r' | b't' | b'b' | b'f' => {
+ let ch = .consume()
+ builder.append(match ch {
+ b'n' => b'\n'
+ b'r' => b'\r'
+ b't' => b'\t'
+ b'b' => b'\b'
+ b'f' => b'\f'
+ else => ch
+ })
+ }
+ b'u' => {
+ eprintln("FIXME: Implement unicode literals")
+ abort()
+ }
+ else => {
+ // FIXME: "Error while parsing string"
+ throw Error::from_errno(9009)
+ }
+ }
+ }
+
+ if not .consume_specific(b'"') {
+ // FIXME: "Expected '"'"
+ throw Error::from_errno(9010)
+ }
+
+ return builder.to_string()
+ }
+
+ fn ignore(mut this) {
+ .index++
+ }
+
+ fn peek(this) -> u8 {
+ if .eof() {
+ return 0
+ }
+ return .input.byte_at(.index)
+ }
+
+ fn consume(mut this) -> u8 {
+ let ch = .peek()
+ .index++
+ return ch
+ }
+
+ fn consume_specific(mut this, anon expected: u8) -> bool {
+ if .peek() != expected {
+ return false
+ }
+ .index++
+ return true
+ }
+
+ fn parse_helper(mut this) throws -> JsonValue {
+ .skip_whitespace()
+ return match .peek() {
+ b'{' => .parse_object()
+ b'[' => .parse_array()
+ b'"' => .parse_string()
+ b'-' => .parse_number()
+ b'0' | b'1' | b'2' | b'3' | b'4' | b'5' | b'6' | b'7' | b'8' | b'9' => .parse_number()
+ b'f' => .parse_false()
+ b't' => .parse_true()
+ b'n' => .parse_null()
+ else => .parse_failure(error_message: "Unexpected character")
+ }
+ }
+
+ fn parse_failure(this, error_message: String) throws -> JsonValue {
+ throw Error::from_errno(9001)
+ }
+
+ fn parse_array(mut this) throws -> JsonValue {
+ mut array: [JsonValue] = []
+ if (not .consume_specific(b'[')) {
+ // Expected '['
+ throw Error::from_errno(9014)
+ }
+ loop {
+ .skip_whitespace()
+ if .peek() == b']' {
+ break
+ }
+ array.push(.parse_helper())
+ .skip_whitespace()
+ if .peek() == b']' {
+ break
+ }
+ if not .consume_specific(b',') {
+ // Expected ','
+ throw Error::from_errno(9014)
+ }
+ .skip_whitespace()
+ if .peek() == b']' {
+ // Unexpected ']'
+ throw Error::from_errno(9014)
+ }
+ }
+ if not .consume_specific(b']') {
+ // Expected ']'
+ throw Error::from_errno(9015)
+ }
+ return JsonValue::JsonArray(array)
+ }
+
+ fn parse_object(mut this) throws -> JsonValue {
+ if not .consume_specific(b'{') {
+ // FIXME: "Expected '{'"
+ throw Error::from_errno(9002)
+ }
+
+ mut values: [String:JsonValue] = [:]
+
+ loop {
+ .skip_whitespace()
+ if .peek() == b'}' {
+ break
+ }
+ .skip_whitespace()
+ let key = .consume_and_unescape_string()
+ .skip_whitespace()
+ if not .consume_specific(b':') {
+ // FIXME: "Expected ':'"
+ throw Error::from_errno(9003)
+ }
+ .skip_whitespace()
+ let value = .parse_helper()
+ // FIXME: This should say `values[key] = value`, but the compiler doesn't wrap it in TRY()
+ values.set(key, value)
+ .skip_whitespace()
+ if .peek() == b'}' {
+ break
+ }
+ if not .consume_specific(b',') {
+ // FIXME: "Expected ','"
+ throw Error::from_errno(9004)
+ }
+ .skip_whitespace()
+ if .peek() == b'}' {
+ // FIXME: "Unexpected '}'"
+ throw Error::from_errno(9005)
+ }
+ }
+ if not .consume_specific(b'}') {
+ // FIXME: "Expected '}'"
+ throw Error::from_errno(9006)
+ }
+ return JsonValue::Object(values)
+ }
+
+ fn char_to_f64(anon num: u8) throws -> f64 {
+ // FIXME 1: Shouldn't need this function at all
+ // FIXME 2: Shouldn't need return in else branch
+ return match num {
+ 0u8 => 0.0
+ 1u8 => 1.0
+ 2u8 => 2.0
+ 3u8 => 3.0
+ 4u8 => 4.0
+ 5u8 => 5.0
+ 6u8 => 6.0
+ 7u8 => 7.0
+ 8u8 => 8.0
+ 9u8 => 9.0
+ else => {
+ // FIXME: "Unexpected number"
+ throw Error::from_errno(9017)
+ }
+ }
+ }
+
+ fn parse_number(mut this) throws -> JsonValue {
+ // FIXME: This implementation doesn't match JsonParser.cpp
+ let is_negative = .consume_specific(b'-')
+ mut decimal_start_index: usize? = None
+
+ mut value = 0.0
+
+ while not .eof() {
+ let ch = .peek()
+ if ch == b'.' {
+ if decimal_start_index.has_value() {
+ // FIXME: "Unexpected '.'"
+ throw Error::from_errno(9016)
+ }
+ decimal_start_index = .index++
+ continue
+ } else if not (ch >= b'0' and ch <= b'9') {
+ break
+ }
+
+ if not decimal_start_index.has_value() {
+ value *= 10.0
+ value += char_to_f64(ch - b'0')
+ } else {
+ mut num = char_to_f64(ch - b'0')
+ // FIXME: This should really be: `value += pow(10, -decimal_place)*num`, but: there's no pow function and you can't multiply float by usize
+ let decimal_place = .index - decimal_start_index.value()
+ for i in 0..decimal_place {
+ num /= 10.0
+ }
+ value += num
+ }
+ .index++
+ }
+
+ if is_negative {
+ value *= -1.0
+ }
+
+ return JsonValue::Number(value)
+ }
+
+ fn parse_string(mut this) throws -> JsonValue {
+ return JsonValue::JsonString(.consume_and_unescape_string())
+ }
+
+ fn parse_false(mut this) throws -> JsonValue {
+ if (.consume() != b'f' or .consume() != b'a' or .consume() != b'l' or .consume() != b's' or .consume() != b'e') {
+ // FIXME: "Expected 'false'"
+ throw Error::from_errno(9011)
+ }
+ return JsonValue::Bool(false)
+ }
+
+ fn parse_true(mut this) throws -> JsonValue {
+ if (.consume() != b't' or .consume() != b'r' or .consume() != b'u' or .consume() != b'e') {
+ // FIXME: "Expected 'true'"
+ throw Error::from_errno(9012)
+ }
+ return JsonValue::Bool(true)
+ }
+
+ fn parse_null(mut this) throws -> JsonValue {
+ if (.consume() != b'n' or .consume() != b'u' or .consume() != b'l' or .consume() != b'l') {
+ // FIXME: "Expected 'null'"
+ throw Error::from_errno(9013)
+ }
+ return JsonValue::Null
+ }
+}
+
+// fn parse_json(input: String) throws -> JsonValue {
+// mut parser = JsonParser::construct(input)
+// return parser.parse()
+// }
+//
+// fn main() {
+// let value = parse_json(input: "[{\"id\":0.5,\"displayName\":\"Air\",\"name\":\"air\",\"hardness\":3.9,\"resistance\":0,\"minStateId\":0,\"maxStateId\":0,\"states\":[]}]")
+// println("{}", value)
+// }
\ No newline at end of file
diff --git a/src/net/lib/util.jakt b/src/net/lib/util.jakt
new file mode 100644
index 0000000..434158d
--- /dev/null
+++ b/src/net/lib/util.jakt
@@ -0,0 +1,147 @@
+import relative parent::os::os { OS }
+
+struct Util {
+ fn get_address_u32_from_ipv4_u8_array(anon array: [u8]) -> u32 {
+ if array.size() != 4 {
+ return 0
+ }
+ mut address: u32 = (array[3] as! u32 & 0xff) as! u32
+ address += ((array[2] as! u32 & 0xff) << 8) as! u32
+ address += ((array[1] as! u32 & 0xff) << 16) as! u32
+ address += ((array[0] as! u32 & 0xff) << 24) as! u32
+ return address
+ }
+ fn get_hexadecimal_string_from_ipv4_u8_array(anon array: [u8]) throws -> String {
+ mut s = StringBuilder::create()
+ unsafe {
+ cpp {
+ "char *chars = (char*)calloc(32, 1);
+ sprintf(chars, \"%02x%02x%02x%02x\", array[0], array[1], array[2], array[3]);
+ s.append_c_string(chars);
+ delete(chars);"
+ }
+ }
+ return s.to_string()
+ }
+ fn get_md5_string_from_string(anon s: String) throws -> String {
+ mut sb = StringBuilder::create()
+ unsafe {
+ cpp {
+ "
+ char* md5 = (char*)os_call((u64)\"@saubari_get_md5_string_from_string\", (u64)s.characters());
+ sb.append_c_string(md5);
+ delete(md5);
+ "
+ }
+ }
+ return sb.to_string()
+ }
+ fn get_ipv4_u8_array_from_address_string(anon s: String) throws -> [u8] {
+ mut address: [u8] = []
+ let octet_strings = s.split(c'.')
+ for octet_string in octet_strings {
+ unsafe {
+ cpp {
+ "auto value = octet_string.to_number();
+ if (value.has_value()) {
+ auto result = value.release_value();
+ address.push(result & 0xff);
+ }"
+ }
+ }
+ }
+ return address
+ }
+ fn get_ipv4_u8_array_from_address_u32(anon addr: u32) throws -> [u8] {
+ mut address: [u8] = []
+ // let source_address: [u8] = [ipv4_packet[12], ipv4_packet[13], ipv4_packet[14], ipv4_packet[15]]
+ address.push(((addr >> 24) & 0xff) as! u8)
+ address.push(((addr >> 16) & 0xff) as! u8)
+ address.push(((addr >> 8) & 0xff) as! u8)
+ address.push((addr & 0xff) as! u8)
+ return address
+ }
+ fn get_string_from_u8_array(anon array: [u8]) throws -> String {
+ mut s = StringBuilder::create()
+ unsafe {
+ cpp {
+ "for (int i = 0; i < array.size(); i++) {
+ s.append(array[i]);
+ }"
+ }
+ }
+ return s.to_string()
+ }
+ fn get_u16_from_u8_array(anon array: [u8], anon offset: i64) -> u16{
+ return (array[offset] as! u16 << 8) + array[offset + 1] as! u16
+ }
+ fn get_u16_from_u8_arrayslice(anon array: ArraySlice, anon offset: i64) -> u16{
+ return (array[offset] as! u16 << 8) + array[offset + 1] as! u16
+ }
+ fn push_string_to_u8_array(anon mut array: [u8], anon s: String) throws {
+ for i in 0..s.length() {
+ unsafe {
+ cpp {
+ "array.push(s.characters()[i]);"
+ }
+ }
+ }
+ }
+ fn push_u16_to_u8_array(anon mut array: [u8], anon value: u16) throws {
+ array.push((value >> 8) as! u8)
+ array.push((value & 0xff) as! u8)
+ }
+ fn push_u32_to_u8_array(anon mut array: [u8], anon value: u32) throws {
+ mut val_u32_to_u8: u32 = 0
+ val_u32_to_u8 = (value >> 24) & 0xff
+ array.push(val_u32_to_u8 as! u8)
+ val_u32_to_u8 = (value >> 16) & 0xff
+ array.push(val_u32_to_u8 as! u8)
+ val_u32_to_u8 = (value >> 8) & 0xff
+ array.push(val_u32_to_u8 as! u8)
+ array.push((value & 0xff) as! u8)
+ }
+ fn get_dictionary_from_json_file(anon json_file: String) throws -> [String:String] {
+ mut dictionary: [String:String] = Dictionary()
+ let json_bytes = OS::read_entire_file(json_file)
+ let json_string = get_string_from_u8_array(json_bytes)
+ unsafe {
+ cpp {
+ "auto json = JsonValue::from_string(json_string).value();
+ auto const& object = json.as_object();
+ object.for_each_member([&]([[maybe_unused]] auto& property_name, [[maybe_unused]] const JsonValue& property_value) {
+ dictionary.set(property_name, property_value.deprecated_to_byte_string());
+ });"
+ }
+ }
+ return dictionary
+ }
+ fn get_dictionary_from_string(anon s: String) throws -> [String:String] {
+ mut dictionary: [String:String] = Dictionary()
+ unsafe {
+ cpp {
+ "auto json = JsonValue::from_string(s).value();
+ auto const& object = json.as_object();
+ object.for_each_member([&]([[maybe_unused]] auto& property_name, [[maybe_unused]] const JsonValue& property_value) {
+ dictionary.set(property_name, property_value.deprecated_to_byte_string());
+ });"
+ }
+ }
+ return dictionary
+ }
+ fn string_from_file(anon filepath: String) throws -> String {
+ if filepath.is_empty() or not OS::path_exists(filepath) {
+ return ""
+ }
+ let array = OS::read_entire_file(filepath)
+ mut s = StringBuilder::create()
+ unsafe {
+ cpp {
+ "for (int i = 0; i < array.size(); i++) {
+ s.append(array[i]);
+ }"
+ }
+ }
+ return s.to_string()
+ }
+}
\ No newline at end of file
diff --git a/src/net/net.jakt b/src/net/net.jakt
new file mode 100644
index 0000000..b7bdec9
--- /dev/null
+++ b/src/net/net.jakt
@@ -0,0 +1,173 @@
+import devices::virtio { VirtIO, VirtIOReg }
+
+import lib::util { Util }
+
+import os::os { OS }
+import os::pci { PCI, PCIDevice }
+import os::time { Time }
+
+import tcpip { TCPIP }
+
+class NetDevices {
+ public virtio: VirtIO
+ public fn create(pci_device: PCIDevice) throws -> NetDevices {
+ return NetDevices(
+ virtio: VirtIO(pci_device, rq_index: 0, rq_size: 0, rq: 0, tq_size: 0, tq: 0)
+ )
+ }
+}
+
+class Net {
+ public device: NetDevices
+ public mac_address: [u8]
+ public tcpip: TCPIP
+ public pci_device: PCIDevice
+ public fn init(config: [String:String]) throws -> Net {
+ let pci_device = PCI::find_device_by_class_code(0x020000)
+ mut net = Net(
+ device: NetDevices::create(pci_device)
+ mac_address: []
+ tcpip: TCPIP(
+ ipv4_address: Util::get_ipv4_u8_array_from_address_string(config["tcpip.ipv4_address"])
+ ipv4_netmask: Util::get_ipv4_u8_array_from_address_string(config["tcpip.ipv4_netmask"])
+ ipv4_network: Util::get_ipv4_u8_array_from_address_string(config["tcpip.ipv4_network"])
+ ipv4_gateway: Util::get_ipv4_u8_array_from_address_string(config["tcpip.ipv4_gateway"])
+ dns_server_address: Util::get_ipv4_u8_array_from_address_string(config["tcpip.ipv4_dns_server_address"])
+ dns_server_port: config["tcpip.ipv4_dns_server_port"].to_number().value() as! u16
+ mss_size: config["tcpip.mss_size"].to_number().value() as! u16
+ tx_queue: []
+ ttl: 64
+ arp_cache: Dictionary()
+ bound_sockets: Dictionary()
+ dns_cache: Dictionary()
+ tcp_sessions: []
+ pending_dns_lookups: Dictionary()
+ pending_dns_cached_entries: Dictionary()
+ pending_icmp_requests: Dictionary()
+ timestamp_last_arp_request: 0
+ rx_bytes: 0
+ rx_frames: 0
+ tx_bytes: 0
+ tx_frames: 0
+ )
+ pci_device
+ )
+ if net.pci_device.vendor_id() == 0x1af4 and net.pci_device.device_id() == 0x1000 {
+ println("[net] Found device: virtio-net, QEMU")
+ for i in 0u16..6u16 {
+ net.mac_address.push(net.pci_device.io_read_u8(VirtIOReg::config as! u16 + i))
+ }
+ net.device.virtio.init()
+ return net
+ }
+ println("[net] No supported vendor ids found")
+ OS::exit()
+ return net
+ }
+ fn process_ethernet_frame(mut this, anon frame: [u8]) throws {
+ let ethertype: u16 = (frame[12] as! u16 * 256) + frame[13] as! u16
+ match ethertype {
+ 0x0806 => {
+ //println("ARP")
+ .tcpip.process_arp_packet(.mac_address, frame)
+ }
+ 0x0800 => {
+ //println("IPv4")
+ .tcpip.process_ipv4_packet(.mac_address, frame)
+ }
+ 0x86dd => {
+ //.tcpip.process_ipv6_packet(frame)
+ }
+ 0x8035 => {
+ //.tcpip.process_rarp_packet(frame)
+ }
+ else => {
+ // unsupported
+ }
+ }
+ }
+ public fn process_events(mut this) throws {
+ mut received_frame = .rx_frame()
+ if received_frame.size() > 0 {
+ .tcpip.rx_bytes += received_frame.size() as! u64
+ .tcpip.rx_frames++
+ .process_ethernet_frame(received_frame)
+ }
+ .tcpip.tcp_transmit_pending_data_for_existing_sessions()
+ for frame in .tcpip.tx_queue {
+ .tx_frame(frame)
+ }
+ if .tcpip.tx_queue.size() > 0 {
+ .tcpip.tx_queue.shrink(0)
+ }
+ .tcpip.tcp_process_bind_request()
+ .tcpip.tcp_process_client_socket_request(.mac_address)
+ .tcpip.tcp_process_client_received_data()
+ .tcpip.tcp_process_client_send_requests(.mac_address)
+ .tcpip.dns_process_client_request(.mac_address)
+ .tcpip.icmp_process_client_request(.mac_address)
+ .tcpip.netinfo_process_client_request(.mac_address)
+ }
+ fn rx_frame(mut this) throws -> [u8] {
+ mut frame: [u8] = []
+ if .pci_device.vendor_id() == 0x1af4 and .pci_device.device_id() == 0x1000 {
+ frame = .device.virtio.rx_frame()
+ }
+ return frame
+ }
+ fn tx_frame(mut this, anon mut data: [u8]) throws {
+ if data.size() < 1 {
+ return
+ }
+ while data.size() < 60 {
+ data.push(0u8)
+ }
+ .tcpip.tx_bytes += data.size() as! u64
+ .tcpip.tx_frames++
+ if .pci_device.vendor_id() == 0x1af4 and .pci_device.device_id() == 0x1000 {
+ .device.virtio.tx_frame(data)
+ }
+ }
+}
+
+fn main() {
+ println("$WW,1$")
+ mut config = Util::get_dictionary_from_json_file("M:/System/Config/Net.json")
+ mut net = Net::init(config)
+
+ println("[net] PCI device is {}", net.pci_device)
+ print("[net] MAC address is ")
+ for i in 0u16..5u16 {
+ print("{:0>2x}:", net.mac_address[i])
+ }
+ println("{:0>2x}", net.mac_address[5])
+ print("[net] IPv4 address is ")
+ for i in 0u16..3u16 {
+ print("{:d}.", net.tcpip.ipv4_address[i])
+ }
+ println("{:d}", net.tcpip.ipv4_address[3])
+ println(" ")
+
+ // Update the ARP cache entry for IPv4 gateway address
+ net.tcpip.send_arp_request(net.mac_address, net.tcpip.ipv4_gateway)
+
+ mut prev_rx_frames = net.tcpip.rx_frames
+ mut prev_tx_frames = net.tcpip.tx_frames
+ mut prev_jiffies = Time::jiffies()
+
+ while true {
+ net.process_events()
+ if (prev_rx_frames != net.tcpip.rx_frames) or (prev_tx_frames != net.tcpip.tx_frames) {
+ prev_rx_frames = net.tcpip.rx_frames
+ prev_tx_frames = net.tcpip.tx_frames
+ prev_jiffies = Time::jiffies()
+ }
+ if Time::jiffies() < prev_jiffies + 250 {
+ Time::sleep(0)
+ } else {
+ Time::sleep(1)
+ }
+ }
+
+ OS::exit()
+}
\ No newline at end of file
diff --git a/src/net/os/ioport.jakt b/src/net/os/ioport.jakt
new file mode 100644
index 0000000..a01eaa2
--- /dev/null
+++ b/src/net/os/ioport.jakt
@@ -0,0 +1,29 @@
+import extern c "ioport.h" {
+ extern fn ioport_read_u8(address: u16) -> u8
+ extern fn ioport_read_u16(address: u16) -> u16
+ extern fn ioport_read_u32(address: u16) -> u32
+ extern fn ioport_write_u8(address: u16, value: u8)
+ extern fn ioport_write_u16(address: u16, value: u16)
+ extern fn ioport_write_u32(address: u16, value: u32)
+}
+
+struct IOPort {
+ fn read_u8(anon address: u16) throws -> u8 {
+ return ioport_read_u8(address)
+ }
+ fn read_u16(anon address: u16) throws -> u16 {
+ return ioport_read_u16(address)
+ }
+ fn read_u32(anon address: u16) throws -> u32 {
+ return ioport_read_u32(address)
+ }
+ fn write_u8(address: u16, value: u8) {
+ return ioport_write_u8(address, value)
+ }
+ fn write_u16(address: u16, value: u16) {
+ return ioport_write_u16(address, value)
+ }
+ fn write_u32(address: u16, value: u32) {
+ return ioport_write_u32(address, value)
+ }
+}
diff --git a/src/net/os/os.jakt b/src/net/os/os.jakt
new file mode 100644
index 0000000..358ac9f
--- /dev/null
+++ b/src/net/os/os.jakt
@@ -0,0 +1,154 @@
+import extern c "os.h" {
+ extern fn os_blink(frequency: raw c_char) -> bool
+ extern fn os_call(function_name: u64, arg: u64) -> u64
+ extern fn os_device_calloc(size: u32) -> u32
+ extern fn os_exit()
+ extern fn os_file_picker(path: raw c_char, glob: raw c_char)
+ extern fn os_files_list(path: raw c_char)
+ extern fn os_is_vm() -> bool
+ extern fn os_path_exists(anon path: raw c_char) -> bool
+ extern fn os_pc_speaker(frequency: raw c_char)
+ extern fn os_random() -> u64
+ extern fn os_screenshot()
+ extern fn os_to_uppercase(anon input_string: raw c_char) -> raw c_char
+}
+
+struct OS {
+ fn blink(frequency: f64 = 2.5) throws -> bool {
+ let frequency_as_string = format("{}", frequency)
+ return os_blink(frequency: frequency_as_string.c_string())
+ }
+ fn call(anon function_name: String, anon arg: String) throws -> u64 {
+ mut res: u64 = 0
+ unsafe {
+ cpp {
+ "
+ res = os_call((u64)function_name.characters(), (u64)arg.characters());
+ "
+ }
+ }
+ return res
+ }
+ fn device_calloc(anon size: u32) throws -> u32 {
+ return os_device_calloc(size)
+ }
+ fn device_copy_buffer(anon buffer: [u8]) -> u32 {
+ mut address: u32 = 0
+ mut size = buffer.size()
+ unsafe {
+ cpp {
+ "u8 *data = (u8*)os_device_calloc(size);
+ for (int i = 0; i < size; i++)
+ data[i] = buffer[i];
+ address = (uintptr_t)data;"
+ }
+ }
+ return address
+ }
+ fn exit() {
+ os_exit()
+ }
+ fn file_picker(path: String, glob: String) throws -> String {
+ mut s = StringBuilder::create()
+ unsafe {
+ cpp {
+ "char const *chars = os_file_picker(path.characters(), glob.characters());
+ s.append_c_string(chars);
+ delete(chars);"
+ }
+ }
+ return s.to_string()
+ }
+ fn files_list(path: String) throws -> [String] {
+ mut s = StringBuilder::create()
+ unsafe {
+ cpp {
+ "char const *chars = os_files_list(path.characters());
+ if (chars) {
+ s.append_c_string(chars);
+ delete(chars);
+ }"
+ }
+ }
+ return s.to_string().split(c'|')
+ }
+ fn path_exists(anon path: String) -> bool {
+ return os_path_exists(path.c_string())
+ }
+ fn is_vm() -> bool {
+ return os_is_vm()
+ }
+ fn pc_speaker(frequency: f64) throws {
+ let frequency_as_string = format("{}", frequency)
+ os_pc_speaker(frequency: frequency_as_string.c_string())
+ }
+ fn put_char(ch: u8) {
+ unsafe {
+ cpp {
+ "putchar(ch);"
+ }
+ }
+ }
+ fn random() -> u64 {
+ return os_random()
+ }
+ fn read_entire_file(anon filename: String) throws -> [u8] {
+ mut size = 0
+ mut buffer: [u8] = []
+ unsafe {
+ cpp {
+ "u8 *data = os_read_entire_file(filename.characters(), &size);
+ for (int i = 0; i < size; i++)
+ buffer.push(data[i]);
+ free(data);"
+ }
+ }
+ return buffer
+ }
+ fn read_device_memory(address: u32, size: i64) throws -> [u8] {
+ mut buffer: [u8] = [];
+ unsafe {
+ cpp {
+ "u8 *device_memory = (u8*)address;
+ for (int i = 0; i < size; i++)
+ buffer.push(device_memory[i]);"
+ }
+ }
+ return buffer
+ }
+ fn read_u16_from_device_memory(anon address: u32) throws -> u16 {
+ mut value: u16 = 0
+ unsafe {
+ cpp {
+ "value = *(u16*)address;"
+ }
+ }
+ return value
+ }
+ fn screenshot() {
+ os_screenshot()
+ }
+ fn to_uppercase(anon input_string: String) throws -> String {
+ mut s = StringBuilder::create()
+ unsafe {
+ cpp {
+ "char const *chars = os_to_uppercase(input_string.characters());
+ s.append_c_string(chars);
+ delete(chars);"
+ }
+ }
+ return s.to_string()
+ }
+ fn write_entire_file(filename: String, buffer: [u8]) {
+ mut size = buffer.size()
+ unsafe {
+ cpp {
+ "unsigned char *data = (unsigned char *)malloc(size);
+ for (int i = 0; i < size; i++)
+ data[i] = buffer[i];
+ os_write_entire_file(filename.characters(), data, size);
+ free(data);"
+ }
+ }
+ }
+}
diff --git a/src/net/os/pci.jakt b/src/net/os/pci.jakt
new file mode 100644
index 0000000..6d15132
--- /dev/null
+++ b/src/net/os/pci.jakt
@@ -0,0 +1,103 @@
+import ioport { IOPort }
+
+import extern c "pci.h" {
+ extern fn pci_find(anon class_code: i64) -> i64
+ extern fn pci_read_u8(anon bus: i64, anon device: i64, anon fun: i64, anon offset: i64) -> u8
+ extern fn pci_read_u16(anon bus: i64, anon device: i64, anon fun: i64, anon offset: i64) -> u16
+ extern fn pci_read_u32(anon bus: i64, anon device: i64, anon fun: i64, anon offset: i64) -> u32
+ extern fn pci_write_u8(anon bus: i64, anon device: i64, anon fun: i64, anon offset: i64, anon value: u8)
+ extern fn pci_write_u16(anon bus: i64, anon device: i64, anon fun: i64, anon offset: i64, anon value: u16)
+ extern fn pci_write_u32(anon bus: i64, anon device: i64, anon fun: i64, anon offset: i64, anon value: u32)
+}
+
+struct PCIDevice {
+ bus: i64
+ device: i64
+ fun: i64
+ pci_vendor_id: u16
+ pci_device_id: u16
+ bar: [u32]
+ public fn enable_bus_master(mut this) {
+ .set_command(.command() | 0x4)
+ }
+ public fn read_u8(this, anon offset: i64) -> u8 {
+ return pci_read_u8(.bus, .device, .fun, offset)
+ }
+ public fn read_u16(this, anon offset: i64) -> u16 {
+ return pci_read_u16(.bus, .device, .fun, offset)
+ }
+ public fn read_u32(this, anon offset: i64) -> u32 {
+ return pci_read_u32(.bus, .device, .fun, offset)
+ }
+ public fn write_u8(this, offset: i64, value: u8) {
+ pci_write_u8(.bus, .device, .fun, offset, value)
+ }
+ public fn write_u16(this, offset: i64, value: u16) {
+ pci_write_u16(.bus, .device, .fun, offset, value)
+ }
+ public fn write_u32(this, offset: i64, value: u32) {
+ pci_write_u32(.bus, .device, .fun, offset, value)
+ }
+ public fn io_read_u8(this, anon offset: u16) throws -> u8 {
+ return IOPort::read_u8(.bar[0] as! u16 + offset)
+ }
+ public fn io_read_u16(this, anon offset: u16) throws -> u16 {
+ return IOPort::read_u16(.bar[0] as! u16 + offset)
+ }
+ public fn io_read_u32(this, anon offset: u16) throws -> u32 {
+ return IOPort::read_u32(.bar[0] as! u16 + offset)
+ }
+ public fn io_write_u8(this, offset: u16, value: u8) {
+ IOPort::write_u8(address: .bar[0] as! u16 + offset, value)
+ }
+ public fn io_write_u16(this, offset: u16, value: u16) {
+ IOPort::write_u16(address: .bar[0] as! u16 + offset, value)
+ }
+ public fn io_write_u32(this, offset: u16, value: u32) {
+ IOPort::write_u32(address: .bar[0] as! u16 + offset, value)
+ }
+ public fn vendor_id(this) -> u16 {
+ return .pci_vendor_id
+ }
+ public fn device_id(this) -> u16 {
+ return .pci_device_id
+ }
+ public fn command(this) -> u16 {
+ return pci_read_u16(.bus, .device, .fun, 0x4)
+ }
+ public fn set_command(this, anon value: u16) {
+ pci_write_u16(.bus, .device, .fun, offset: 0x4, value)
+ }
+ public fn status(this) -> u16 {
+ return pci_read_u16(.bus, .device, .fun, 0x6)
+ }
+}
+
+fn lookup_bar(bus: i64, device: i64, fun: i64, anon index: i64) -> u32 {
+ if index < 0 or index > 5 {
+ return 0xFFFFFFFF
+ }
+ return pci_read_u32(bus, device, fun, 0x10 + (index * 4)) & 0xFFFFFFFC
+}
+
+struct PCI {
+ public fn find_device_by_class_code(anon class_code: i64) throws -> PCIDevice {
+ let result = pci_find(class_code)
+
+ if result < 0 {
+ eprintln("error: device not found")
+ throw Error::from_errno(1)
+ }
+
+ let bus = (result >> 16) & 0xff
+ let device = (result >> 8) & 0xff
+ let fun = result & 0xff
+ let pci_vendor_id = pci_read_u16(bus, device, fun, 0x0)
+ let pci_device_id = pci_read_u16(bus, device, fun, 0x2)
+ mut bar: [u32] = []
+ for i in 0..5 {
+ bar.push(lookup_bar(bus, device, fun, i))
+ }
+ return PCIDevice(bus, device, fun, pci_vendor_id, pci_device_id, bar)
+ }
+}
diff --git a/src/net/os/time.jakt b/src/net/os/time.jakt
new file mode 100644
index 0000000..c84daaa
--- /dev/null
+++ b/src/net/os/time.jakt
@@ -0,0 +1,94 @@
+import extern c "time.h" {
+ extern fn time_busy(anon duration: i64)
+ extern fn time_jiffies() -> i64
+ extern fn time_now() -> i64
+ extern fn time_sleep(anon duration: i64)
+}
+
+struct Time {
+ fn busy(anon duration: i64) {
+ time_busy(duration)
+ }
+ fn jiffies() throws -> i64 {
+ return time_jiffies()
+ }
+ fn now() throws -> i64 {
+ return time_now()
+ }
+ fn cdate_to_unix(anon cdate: i64) -> i64 {
+ // (cdate - Str2Date("1/1/1970") / CDATE_FREQ + NIST_TIME_OFFSET
+ return (cdate - 3090344933588992) / 49710 + 8575
+ }
+ fn unix_to_cdate(anon unix: i64) -> i64 {
+ // (unix - NIST_TIME_OFFSET) * CDATE_FREQ + Str2Date("1/1/1970")
+ return (unix - 8575) * 49710 + 3090344933588992
+ }
+ fn sleep(anon duration: i64) {
+ time_sleep(duration)
+ }
+ fn timestamp_from_unix(anon timestamp: i64) -> String {
+
+ let SECS_PER_DAY = 86400
+ let DAYS_PER_YEAR = 365
+ let DAYS_PER_LYEAR = 366
+ let DAYS_PER_LYEAR_PERIOD = 146097
+ let YEARS_PER_LYEAR_PERIOD = 400
+
+ mut days = timestamp / SECS_PER_DAY
+ mut remainder = timestamp - (days * SECS_PER_DAY)
+ if timestamp < 0 and remainder == 0 {
+ days++
+ remainder -= SECS_PER_DAY
+ }
+
+ mut cur_year = 0
+ mut months: [i64] = []
+ mut tmp_days = 0
+
+ let month_tab = [ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 ]
+ let month_tab_leap = [ -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 ]
+
+ tmp_days = days;
+ if tmp_days >= DAYS_PER_LYEAR_PERIOD or tmp_days <= -DAYS_PER_LYEAR_PERIOD {
+ cur_year += YEARS_PER_LYEAR_PERIOD * (tmp_days / DAYS_PER_LYEAR_PERIOD);
+ tmp_days -= DAYS_PER_LYEAR_PERIOD * (tmp_days / DAYS_PER_LYEAR_PERIOD);
+ }
+ while tmp_days >= DAYS_PER_LYEAR {
+ cur_year++;
+ if cur_year % 4 == 0 {
+ tmp_days -= DAYS_PER_LYEAR;
+ } else {
+ tmp_days -= DAYS_PER_YEAR;
+ }
+ }
+ if cur_year % 4 == 0 {
+ months = month_tab_leap
+ } else {
+ months = month_tab
+ }
+
+ mut i = 11
+ while i > 0 {
+ if tmp_days > months[i] {
+ break;
+ }
+ i--
+ }
+
+ let year = 1970 + cur_year
+ let month = i + 1
+ let day = tmp_days - months[i]
+
+ let hours = remainder / 3600
+ let minutes = (remainder - hours * 3600) / 60
+ let seconds = remainder % 60
+
+ mut sb = StringBuilder::create()
+ sb.clear()
+ sb.appendff("{:0>4d}-{:0>2d}-{:0>2d}T{:0>2d}:{:0>2d}:{:0>2d}.000Z", year, month, day, hours, minutes, seconds)
+ return sb.to_string()
+ }
+ fn timestamp_from_cdate(anon cdate: i64) -> String {
+ return timestamp_from_unix(cdate_to_unix(cdate))
+ }
+}
\ No newline at end of file
diff --git a/src/net/tcpip.jakt b/src/net/tcpip.jakt
new file mode 100644
index 0000000..7bed452
--- /dev/null
+++ b/src/net/tcpip.jakt
@@ -0,0 +1,1671 @@
+import lib::util { Util }
+
+import os::os { OS }
+import os::time { Time }
+
+struct IcmpRequest {
+ host: u64
+ identifier: u64
+ sequence_number: u64
+ response_type_pointer: u64
+}
+
+enum TcpSessionState: i64 {
+ Idle = 0
+ Established = 1
+ Closed = 2
+ Connecting = 4
+}
+
+struct TcpSession {
+ local_mac: [u8]
+ remote_mac: [u8]
+ local_address: [u8]
+ remote_address: [u8]
+ local_port: u16
+ remote_port: u16
+ local_sequence_number: u32
+ acknowledgement_number: u32
+ timestamp_last_echo_reply: u32
+ timestamp_last_jiffies: i64
+ timestamp_origin: i64
+ last_window_size: u16
+ last_tx_sequence_number: u32
+ last_tx_was_acked: bool
+ tx_chunk_counter: i64
+ state: TcpSessionState
+ pending_data_to_transmit: [u8]
+ received_data: [u8]
+ received_frames: [[u8]]
+ socket: u64
+}
+
+enum TcpBindError: u64 {
+ BadRequest = 1
+ SocketIsAlreadyBound = 2
+}
+
+class TCPIP {
+ public ipv4_address: [u8]
+ public ipv4_netmask: [u8]
+ public ipv4_network: [u8]
+ public ipv4_gateway: [u8]
+ public dns_server_address: [u8]
+ public dns_server_port: u16
+ public mss_size: u16
+ public tx_queue: [[u8]]
+ public ttl: u8
+ public arp_cache: [String:[u8]]
+ public bound_sockets: [u16:u64]
+ public dns_cache: [String:[u8]]
+ public tcp_sessions: [TcpSession]
+ public pending_dns_lookups: [u16:u64]
+ public pending_dns_cached_entries: [u16:String]
+ public pending_icmp_requests: [u16:u64]
+ public timestamp_last_arp_request: i64
+ public rx_bytes: u64
+ public rx_frames: u64
+ public tx_bytes: u64
+ public tx_frames: u64
+ fn calculate_header_checksum(this, anon mut packet: [u8], offset: i64, count: i64) -> u16 {
+ mut sum: i64 = 0
+ for i in 0..count {
+ if (i & 1) == 1 {
+ sum += packet[offset + i] as! i64
+ } else {
+ sum += (packet[offset + i] as! i64 * 256)
+ }
+ }
+ mut calculated_checksum: u16 = 0
+ let overflowable_checksum: i64 = (sum & 0xffff) + (sum >> 16)
+ if overflowable_checksum < 65536 {
+ calculated_checksum = ~((sum & 0xffff) as! u16 + (sum >> 16) as! u16)
+ } else {
+ calculated_checksum = ~(unchecked_add ((sum & 0xffff) as! u16, (sum >> 16) as! u16)) - 1
+ }
+ return calculated_checksum
+ }
+ fn push_ethernet_header(this, mut packet: [u8], destination_mac: [u8], source_mac: [u8], ethertype: u16) throws {
+ for i in 0..6 {
+ packet.push(destination_mac[i])
+ }
+ for i in 0..6 {
+ packet.push(source_mac[i])
+ }
+ Util::push_u16_to_u8_array(packet, ethertype)
+ }
+ fn push_arp_packet(this, mut packet: [u8], opcode: u16, sender_mac: [u8], sender_ipv4: [u8], target_mac: [u8], target_ipv4: [u8]) throws {
+ let hardware_protocol_header_data: [u8] = [0x00, 0x01, 0x08, 0x00, 0x06, 0x04]
+ for i in 0..6 {
+ packet.push(hardware_protocol_header_data[i])
+ }
+ Util::push_u16_to_u8_array(packet, opcode)
+ for i in 0..6 {
+ packet.push(sender_mac[i])
+ }
+ for i in 0..4 {
+ packet.push(sender_ipv4[i])
+ }
+ for i in 0..6 {
+ packet.push(target_mac[i])
+ }
+ for i in 0..4 {
+ packet.push(target_ipv4[i])
+ }
+ }
+ fn push_ipv4_header(this, mut packet: [u8], total_length: u16, flags: u8, protocol: u8, source_address: [u8], destination_address: [u8]) throws {
+ packet.push(0x45u8) // version & header length
+ packet.push(0x00u8) // differentiated services field
+ Util::push_u16_to_u8_array(packet, total_length) // total length of entire packet
+ let identification: u64 = OS::random() & 0xffff
+ Util::push_u16_to_u8_array(packet, identification as! u16)
+ packet.push(flags)
+ packet.push(0x00u8) // fragment size (unused)
+ packet.push(.ttl) // time-to-live
+ packet.push(protocol) // protocol number
+ Util::push_u16_to_u8_array(packet, 0 as! u16) // Checksum placeholder
+ for i in 0..4 {
+ packet.push(source_address[i])
+ }
+ for i in 0..4 {
+ packet.push(destination_address[i])
+ }
+ let checksum = .calculate_header_checksum(packet, offset: 14, count: 20)
+ packet[24] = (checksum >> 8) as! u8
+ packet[25] = (checksum & 0xff) as! u8
+ }
+ fn push_udp_header(this, mut packet: [u8], source_port: u16, destination_port: u16, length: i64) throws {
+ Util::push_u16_to_u8_array(packet, source_port)
+ Util::push_u16_to_u8_array(packet, destination_port)
+ Util::push_u16_to_u8_array(packet, length as! u16)
+ Util::push_u16_to_u8_array(packet, 0 as! u16) // Checksum placeholder
+ }
+ fn send_arp_reply(mut this, anon mac_address: [u8], anon sender_mac_address: [u8], anon sender_ipv4_address: [u8]) throws {
+ mut packet: [u8] = []
+ .push_ethernet_header(packet, destination_mac: sender_mac_address, source_mac: mac_address, ethertype: 0x0806)
+ .push_arp_packet(
+ packet
+ opcode: 0x0002
+ sender_mac: mac_address
+ sender_ipv4: .ipv4_address
+ target_mac: sender_mac_address
+ target_ipv4: sender_ipv4_address
+ )
+ .tx_queue.push(packet)
+ }
+ public fn send_arp_request(mut this, anon mac_address: [u8], anon target_ipv4_address: [u8]) throws {
+ if .timestamp_last_arp_request > 0 and (.timestamp_last_arp_request + 5000) > Time::jiffies() {
+ return
+ }
+ mut packet: [u8] = []
+ .push_ethernet_header(packet, destination_mac: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff], source_mac: mac_address, ethertype: 0x0806)
+ .push_arp_packet(
+ packet
+ opcode: 0x0001
+ sender_mac: mac_address
+ sender_ipv4: .ipv4_address
+ target_mac: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
+ target_ipv4: target_ipv4_address
+ )
+ .tx_queue.push(packet)
+ .timestamp_last_arp_request = Time::jiffies()
+ }
+ fn insert_arp_cache_entry(mut this, ipv4_address: [u8], mac_address: [u8]) throws {
+ .arp_cache[Util::get_hexadecimal_string_from_ipv4_u8_array(ipv4_address)] = mac_address
+ }
+ public fn process_arp_packet(mut this, anon mac_address: [u8], anon frame: [u8]) throws {
+ let arp_packet = frame[14..]
+
+ let hardware_type = Util::get_u16_from_u8_arrayslice(arp_packet, 0)
+ let protocol_type = Util::get_u16_from_u8_arrayslice(arp_packet, 2)
+ let hardware_size = arp_packet[4]
+ let protocol_size = arp_packet[5]
+ let opcode = Util::get_u16_from_u8_arrayslice(arp_packet, 6)
+
+ let sender_mac_address: [u8] = [arp_packet[8], arp_packet[9], arp_packet[10],
+ arp_packet[11], arp_packet[12], arp_packet[13]]
+ let sender_ipv4_address: [u8] = [arp_packet[14], arp_packet[15], arp_packet[16], arp_packet[17]]
+
+ let target_mac_address: [u8] = [arp_packet[18], arp_packet[19], arp_packet[20],
+ arp_packet[21], arp_packet[22], arp_packet[23]]
+ let target_ipv4_address: [u8] = [arp_packet[24], arp_packet[25], arp_packet[26], arp_packet[27]]
+
+ // If any of these mismatch, the packet is invalid
+ if hardware_type != 0x0001 or protocol_type != 0x0800 or
+ hardware_size != 6 or protocol_size != 4 {
+ return
+ }
+
+ // Add the sender to ARP cache
+ .insert_arp_cache_entry(ipv4_address: sender_ipv4_address, mac_address: sender_mac_address)
+ //print("Updated ARP cache entry for {}: ", sender_ipv4_address)
+ //for i in 0..6 {
+ // print("{:0>2x}", sender_mac_address[i])
+ // if (i < 5) {
+ // print(":")
+ // }
+ //}
+ //println(" ")
+
+ match opcode {
+ 1 => {
+ // ARP request, check if our ipv4 address matches
+ for i in 0..4 {
+ if .ipv4_address[i] != target_ipv4_address[i] {
+ // doesn't match, break
+ break
+ }
+ }
+ // matches, send ARP reply
+ .send_arp_reply(mac_address, sender_mac_address, sender_ipv4_address)
+ }
+ else => {
+ // unsupported opcode, ignore
+ }
+ }
+ }
+ public fn send_dns_query(mut this, anon mac_address: [u8], anon query: String, anon pointer_to_u32: u64) throws {
+
+ mut cached_result: u32 = 0
+ if .dns_cache.contains(query) {
+ cached_result += (.dns_cache[query][0] as! u32 * (256 * 256 * 256) as! u32)
+ cached_result += (.dns_cache[query][1] as! u32 * (256 * 256) as! u32)
+ cached_result += (.dns_cache[query][2] as! u32 * 256 as! u32)
+ cached_result += .dns_cache[query][3] as! u32
+ unsafe {
+ cpp {
+ "u32 *r = (u32*)pointer_to_u32;
+ r[0] = cached_result;"
+ }
+ }
+ return
+ }
+
+ mut packet: [u8] = []
+ mut dns_packet: [u8] = []
+
+ let transaction_id: u64 = OS::random() & 0xffff
+ Util::push_u16_to_u8_array(dns_packet, transaction_id as! u16)
+
+ .pending_dns_lookups.set(transaction_id as! u16, pointer_to_u32)
+ .pending_dns_cached_entries.set(transaction_id as! u16, query)
+
+ let dns_header_data: [u8] = [0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
+ dns_packet.push_values(&dns_header_data)
+
+ let segments = query.split(c'.')
+ for segment in segments {
+ dns_packet.push(segment.length() as! u8)
+ //println("segment_length: {} bytes", segment.length())
+ unsafe {
+ cpp {
+ "const char *raw_string = segment.characters();
+ for (int i = 0; i < segment.length(); i++)
+ dns_packet.push(raw_string[i]);"
+ }
+ }
+ }
+ dns_packet.push(0x00u8)
+
+ let dns_footer_data: [u8] = [0x00, 0x01, 0x00, 0x01]
+ dns_packet.push_values(&dns_footer_data)
+
+ let source_port: u16 = (OS::random() & 0xffff) as! u16
+ let destination_port: u16 = .dns_server_port
+
+ let destination_mac = .arp_cache[Util::get_hexadecimal_string_from_ipv4_u8_array(.ipv4_gateway)]
+ .push_ethernet_header(packet, destination_mac, source_mac: mac_address, ethertype: 0x0800)
+ .push_ipv4_header(packet, total_length: 28 + dns_packet.size() as! u16, flags: 0x00, protocol: 0x11, source_address: .ipv4_address, destination_address: .dns_server_address)
+ .push_udp_header(packet, source_port, destination_port, length: 8 + dns_packet.size() as! i64)
+
+ packet.push_values(&dns_packet)
+
+ // Calculate UDP checksum
+
+ mut checksum_packet: [u8] = []
+ // Source and destination IP
+ for i in 26..34 {
+ checksum_packet.push(packet[i])
+ }
+ // Protocol
+ checksum_packet.push(0x00u8)
+ checksum_packet.push(0x11u8)
+ // UDP Packet length
+ let udp_packet_length: u16 = 8 + dns_packet.size() as! u16
+ Util::push_u16_to_u8_array(checksum_packet, udp_packet_length)
+ // Source Port
+ Util::push_u16_to_u8_array(checksum_packet, source_port)
+ // Destination port
+ Util::push_u16_to_u8_array(checksum_packet, destination_port)
+ // UDP Packet length (again)
+ Util::push_u16_to_u8_array(checksum_packet, udp_packet_length)
+ // Data
+ checksum_packet.push_values(&dns_packet)
+
+ let checksum = .calculate_header_checksum(checksum_packet, offset: 0, count: checksum_packet.size() as! i64)
+ packet[40] = (checksum >> 8) as! u8
+ packet[41] = (checksum & 0xff) as! u8
+
+ .tx_queue.push(packet)
+
+ }
+ public fn netinfo_process_client_request(mut this, anon mac_address: [u8]) throws {
+ unsafe {
+ cpp {
+ "u64 *request = (u64*)0x300030;
+ if (*request) {
+ int i = 0;
+ u64 *r = (u64*)*request;
+
+ for (i = 0; i < 6; i++) {
+ r[0] = (r[0] << 8) | mac_address[i];
+ if (i < 4) {
+ r[1] = (r[1] << 8) | this->ipv4_address[i];
+ r[2] = (r[2] << 8) | this->ipv4_netmask[i];
+ r[3] = (r[3] << 8) | this->ipv4_network[i];
+ r[4] = (r[4] << 8) | this->ipv4_gateway[i];
+ r[5] = (r[5] << 8) | this->dns_server_address[i];
+ }
+ }
+
+ r[6] = this->dns_server_port;
+
+ r[7] = this->rx_bytes;
+ r[8] = this->rx_frames;
+ r[9] = this->tx_bytes;
+ r[10] = this->tx_frames;
+
+ u32 *p = (u32*)r[11];
+ p[0] = 1; // pointer_to_u32
+
+ *request = 0;
+ }"
+ }
+ }
+ }
+
+ public fn icmp_process_client_request(mut this, anon mac_address: [u8]) throws {
+ mut did_receive_request = false
+ mut destination_mac: [u8] = []
+ mut addr: u32 = 0
+ mut iden: u64 = 0
+ mut seq: u64 = 0
+ mut pointer_to_u32: u64 = 0
+ unsafe {
+ cpp {
+ "u64 *ls_request = (u64*)0x300020;
+ if (*ls_request) {
+ u64 *ls_r = (u64*)*ls_request;
+ addr = ls_r[0];
+ }"
+ }
+ }
+ // FIXME: This will crash if we 1) Ping a host on the local subnet that does not exist, then 2) subsequently ping the gateway.
+ if (addr > 0) {
+ mut remote_address_hex_string = Util::get_hexadecimal_string_from_ipv4_u8_array(.ipv4_gateway)
+ if addr != Util::get_address_u32_from_ipv4_u8_array(.ipv4_gateway) {
+ if .is_local_ipv4_address(Util::get_ipv4_u8_array_from_address_u32(addr)) {
+ remote_address_hex_string = Util::get_hexadecimal_string_from_ipv4_u8_array(Util::get_ipv4_u8_array_from_address_u32(addr))
+ if not .arp_cache.contains(remote_address_hex_string) {
+ // send arp request
+ //println("send arp request for: {}", remote_address_hex_string)
+ .send_arp_request(mac_address, Util::get_ipv4_u8_array_from_address_u32(addr))
+ return
+ }
+ }
+ }
+ destination_mac = .arp_cache[remote_address_hex_string]
+ } else {
+ return
+ }
+ unsafe {
+ cpp {
+ "u64 *request = (u64*)0x300020;
+ if (*request) {
+ did_receive_request = true;
+ u64 *r = (u64*)*request;
+ addr = r[0];
+ iden = r[1];
+ seq = r[2];
+ pointer_to_u32 = r[3];
+ *request = 0;
+ }"
+ }
+ }
+ if (did_receive_request) {
+ .send_icmp_request(mac_address, destination_mac, addr, iden, seq, pointer_to_u32)
+ did_receive_request = false
+ }
+ }
+
+ public fn dns_process_client_request(mut this, anon mac_address: [u8]) throws {
+ mut did_receive_request = false
+ mut request_is_ipv4_address = true
+ mut s = StringBuilder::create()
+ mut pointer_to_u32: u64 = 0
+ unsafe {
+ cpp {
+ "u64 *request = (u64*)0x300010;
+ if (*request) {
+ did_receive_request = true;
+ u64 *r = (u64*)*request;
+
+ char const *chars = (char const*)r[0];
+ s.append_c_string(chars);
+ for (int i = 0; i < s.to_string().length(); i++)
+ if ((chars[i] < '0' || chars[i] > '9') && chars[i] != '.')
+ request_is_ipv4_address = false;
+ delete(chars);
+
+ pointer_to_u32 = r[1];
+
+ *request = 0;
+ }"
+ }
+ }
+ if (did_receive_request) {
+ if (request_is_ipv4_address) {
+ let octets = s.to_string().split(c'.')
+ mut u32_address: u32 = 0
+ u32_address += octets[3].to_number().value()
+ u32_address += octets[2].to_number().value() << 8
+ u32_address += octets[1].to_number().value() << 16
+ u32_address += octets[0].to_number().value() << 24
+ unsafe {
+ cpp {
+ "u32 *r = (u32*)pointer_to_u32;
+ r[0] = u32_address;"
+ }
+ }
+ } else {
+ .send_dns_query(mac_address, s.to_string(), pointer_to_u32)
+ }
+ did_receive_request = false
+ }
+ }
+ fn send_icmp_request(mut this, anon mac_address: [u8], anon destination_mac: [u8], anon addr: u32, anon iden: u64, anon seq: u64, anon pointer_to_u32: u64) throws {
+ mut packet: [u8] = []
+
+ mut destination_address: [u8] = []
+ Util::push_u32_to_u8_array(destination_address, addr)
+
+ .pending_icmp_requests.set(iden as! u16, pointer_to_u32)
+
+ let total_length: u16 = 84
+
+ .push_ethernet_header(packet, destination_mac, source_mac: mac_address, ethertype: 0x0800)
+ .push_ipv4_header(packet, total_length, flags: 0x40, protocol: 0x01, source_address: .ipv4_address, destination_address)
+
+ packet.push(0x08u8) // type
+ for i in 0..3 {
+ packet.push(0x00u8) // code, checksum placeholder
+ }
+
+ Util::push_u16_to_u8_array(packet, iden as! u16)
+ Util::push_u16_to_u8_array(packet, seq as! u16)
+
+ for i in 0..8 {
+ packet.push(0x00u8) // FIXME: timestamp
+ }
+
+ mut ch: u8 = 0x20
+ for i in 0..48 {
+ packet.push(ch) // data
+ ch++
+ }
+
+ let checksum = .calculate_header_checksum(packet, offset: 34, count: packet.size() as! i64 - 34)
+ packet[36] = (checksum >> 8) as! u8
+ packet[37] = (checksum & 0xff) as! u8
+
+ .tx_queue.push(packet)
+ }
+ fn process_icmp_reply(mut this, anon frame: [u8]) throws {
+ let iden = Util::get_u16_from_u8_array(frame, 38)
+ let ttl: u16 = frame[22] as! u16
+ let payload_size: u16 = Util::get_u16_from_u8_array(frame, 16) - 20
+ mut result: u32 = 0
+ result = (payload_size as! u32) << 16
+ result += ttl as! u32
+ mut pointer_to_u32: u64 = 0
+ if .pending_icmp_requests.contains(iden) {
+ pointer_to_u32 = .pending_icmp_requests[iden]
+ }
+ if pointer_to_u32 > 0 {
+ unsafe {
+ cpp {
+ "u32 *r = (u32*)pointer_to_u32;
+ r[0] = result;"
+ }
+ }
+ }
+ }
+ fn send_icmp_reply(mut this, anon mac_address: [u8], anon frame: [u8]) throws {
+ mut packet: [u8] = []
+ let ipv4_packet = frame[14..]
+ let icmp_packet = frame[34..]
+
+ mut destination_mac: [u8] = []
+ for octet in frame[6..12] {
+ destination_mac.push(octet)
+ }
+ mut destination_address: [u8] = []
+ for octet in ipv4_packet[12..16] {
+ destination_address.push(octet)
+ }
+
+ .push_ethernet_header(packet, destination_mac, source_mac: mac_address, ethertype: 0x0800)
+ .push_ipv4_header(packet, total_length: frame.size() as! u16 - 14, flags: 0x40, protocol: 0x01, source_address: .ipv4_address, destination_address)
+
+ for i in 0..4 {
+ packet.push(0x00u8) // type, code, checksum placeholder
+ }
+ for i in 4..8 {
+ packet.push(icmp_packet[i]) // identifier & sequence number
+ }
+ for i in 8..icmp_packet.size() {
+ packet.push(icmp_packet[i]) // data
+ }
+ let checksum = .calculate_header_checksum(packet, offset: 34, count: packet.size() as! i64 - 34)
+ packet[36] = (checksum >> 8) as! u8
+ packet[37] = (checksum & 0xff) as! u8
+
+ .tx_queue.push(packet)
+ }
+ public fn process_icmp_packet(mut this, anon mac_address: [u8], anon frame: [u8]) throws {
+ let icmp_packet = frame[34..]
+
+ let type = icmp_packet[0]
+ let code = icmp_packet[1]
+ let checksum: u16 = Util::get_u16_from_u8_arrayslice(icmp_packet, 2)
+ let identifier: u16 = Util::get_u16_from_u8_arrayslice(icmp_packet, 4)
+ let sequence_number: u16 = Util::get_u16_from_u8_arrayslice(icmp_packet, 6)
+
+ match type {
+ 0 => {
+ // ICMP reply
+ .process_icmp_reply(frame)
+ }
+ 8 => {
+ // ICMP request, send ICMP reply
+ .send_icmp_reply(mac_address, frame)
+ }
+ else => {
+ // unsupported
+ }
+ }
+ }
+ fn tcp_is_fin(this, flags: u16) -> bool {
+ if (flags & 1) == 1 {
+ return true
+ }
+ return false
+ }
+ fn tcp_is_syn(this, flags: u16) -> bool {
+ if (flags & 2) == 2 {
+ return true
+ }
+ return false
+ }
+ fn tcp_is_reset(this, flags: u16) -> bool {
+ if (flags & 4) == 4 {
+ return true
+ }
+ return false
+ }
+ fn tcp_is_push(this, flags: u16) -> bool {
+ if (flags & 8) == 8 {
+ return true
+ }
+ return false
+ }
+ fn tcp_is_ack(this, flags: u16) -> bool {
+ if (flags & 16) == 16 {
+ return true
+ }
+ return false
+ }
+ fn tcp_is_urgent(this, flags: u16) -> bool {
+ if (flags & 32) == 32 {
+ return true
+ }
+ return false
+ }
+ fn tcp_syn_packet(mut this, anon mut session: TcpSession) throws {
+ mut packet: [u8] = []
+
+ let header_length: u16 = 40
+ let flags: u16 = 0xa002 // SYN
+
+ mut ipv4_total_length: u16 = 0
+ ipv4_total_length += 20; // IPv4
+ ipv4_total_length += header_length as! u16 // TCP
+
+ .push_ethernet_header(packet, destination_mac: session.remote_mac, source_mac: session.local_mac, ethertype: 0x0800)
+ .push_ipv4_header(packet, total_length: ipv4_total_length, flags: 0x40, protocol: 0x06, source_address: session.local_address, destination_address: session.remote_address)
+
+ // FIXME: put this into .push_tcp_header
+
+ Util::push_u16_to_u8_array(packet, session.local_port)
+ Util::push_u16_to_u8_array(packet, session.remote_port)
+ Util::push_u32_to_u8_array(packet, session.local_sequence_number)
+ Util::push_u32_to_u8_array(packet, 0 as! u32) // Acknowledgement number
+ Util::push_u16_to_u8_array(packet, flags)
+ Util::push_u16_to_u8_array(packet, session.last_window_size)
+ Util::push_u16_to_u8_array(packet, 0 as! u16) // Checksum placeholder
+ Util::push_u16_to_u8_array(packet, 0 as! u16) // Urgent pointer
+
+ mut tcp_options: [u8] = [0x02, 0x04]
+ Util::push_u16_to_u8_array(tcp_options, .mss_size)
+ Util::push_u32_to_u8_array(tcp_options, 0x0101080a)
+ packet.push_values(&tcp_options)
+ let timestamp: u32 = (session.timestamp_origin + (Time::jiffies() - session.timestamp_origin)) as! u32
+ Util::push_u32_to_u8_array(packet, timestamp)
+ Util::push_u32_to_u8_array(packet, session.timestamp_last_echo_reply)
+
+ packet.push(0x01u8)
+ packet.push(0x03u8)
+ packet.push(0x03u8)
+ packet.push(0x07u8)
+
+ // Calculate TCP checksum
+
+ mut checksum_packet: [u8] = []
+ // Source and destination IP
+ for i in 26..34 {
+ checksum_packet.push(packet[i])
+ }
+ // Protocol
+ checksum_packet.push(0x00u8)
+ checksum_packet.push(0x06u8)
+ // TCP Packet length
+ let tcp_packet_length: u16 = header_length as! u16
+ Util::push_u16_to_u8_array(checksum_packet, tcp_packet_length)
+
+ for i in 34..packet.size() {
+ checksum_packet.push(packet[i])
+ }
+
+ mut checksum = .calculate_header_checksum(checksum_packet, offset: 0, count: checksum_packet.size() as! i64)
+ packet[50] = (checksum >> 8) as! u8
+ packet[51] = (checksum & 0xff) as! u8
+
+ .tx_queue.push(packet)
+
+ }
+ fn tcp_psh_packet(mut this, anon mut session: TcpSession) throws {
+ mut packet: [u8] = []
+
+ let header_length: u16 = 32
+ let flags: u16 = 0x8018 // PSH, ACK
+
+ let maximum_payload_size_to_transmit: i64 = 1024
+ mut payload_size: i64 = maximum_payload_size_to_transmit
+ let payload_offset: i64 = session.tx_chunk_counter * maximum_payload_size_to_transmit
+ if session.pending_data_to_transmit.size() as! i64 - payload_offset < maximum_payload_size_to_transmit {
+ payload_size = session.pending_data_to_transmit.size() as! i64 - payload_offset
+ }
+
+ mut ipv4_total_length: u16 = 0
+ ipv4_total_length += 20; // IPv4
+ ipv4_total_length += header_length + payload_size as! u16 // TCP
+
+ .push_ethernet_header(packet, destination_mac: session.remote_mac, source_mac: session.local_mac, ethertype: 0x0800)
+ .push_ipv4_header(packet, total_length: ipv4_total_length, flags: 0x40, protocol: 0x06, source_address: session.local_address, destination_address: session.remote_address)
+
+
+ // FIXME: put this into .push_tcp_header
+
+ Util::push_u16_to_u8_array(packet, session.local_port)
+ Util::push_u16_to_u8_array(packet, session.remote_port)
+ Util::push_u32_to_u8_array(packet, session.local_sequence_number)
+ Util::push_u32_to_u8_array(packet, session.acknowledgement_number)
+ Util::push_u16_to_u8_array(packet, flags)
+ Util::push_u16_to_u8_array(packet, session.last_window_size)
+ Util::push_u16_to_u8_array(packet, 0 as! u16) // Checksum placeholder
+ Util::push_u16_to_u8_array(packet, 0 as! u16) // Urgent pointer
+
+ mut tcp_options: [u8] = [0x01, 0x01, 0x08, 0x0a]
+ packet.push_values(&tcp_options)
+
+ let timestamp: u32 = (session.timestamp_origin + (Time::jiffies() - session.timestamp_origin)) as! u32
+ Util::push_u32_to_u8_array(packet, timestamp)
+ Util::push_u32_to_u8_array(packet, session.timestamp_last_echo_reply)
+
+ for i in payload_offset..(payload_offset + payload_size) {
+ packet.push(session.pending_data_to_transmit[i])
+ }
+
+ // Calculate TCP checksum
+
+ mut checksum_packet: [u8] = []
+ // Source and destination IP
+ for i in 26..34 {
+ checksum_packet.push(packet[i])
+ }
+ // Protocol
+ checksum_packet.push(0x00u8)
+ checksum_packet.push(0x06u8)
+ // TCP Packet length
+ let tcp_packet_length: u16 = header_length + payload_size as! u16
+ Util::push_u16_to_u8_array(checksum_packet, tcp_packet_length)
+
+ for i in 34..packet.size() {
+ checksum_packet.push(packet[i])
+ }
+
+ mut checksum = .calculate_header_checksum(checksum_packet, offset: 0, count: checksum_packet.size() as! i64)
+ packet[50] = (checksum >> 8) as! u8
+ packet[51] = (checksum & 0xff) as! u8
+
+ .tx_queue.push(packet)
+
+ }
+ public fn tcp_transmit_pending_data_for_existing_sessions(mut this) throws {
+ for i in 0..this.tcp_sessions.size() {
+ if .tcp_sessions[i].pending_data_to_transmit.size() > 0 {
+ if (.tcp_sessions[i].tx_chunk_counter == 0) or (.tcp_sessions[i].tx_chunk_counter > 0 and .tcp_sessions[i].last_tx_was_acked) {
+ let maximum_payload_size_to_transmit: i64 = 1024
+ let payload_offset: i64 = .tcp_sessions[i].tx_chunk_counter * maximum_payload_size_to_transmit
+ mut truncated_packet: [u8] = []
+ .tcp_psh_packet(.tcp_sessions[i])
+ if (.tcp_sessions[i].pending_data_to_transmit.size() as! i64 - payload_offset >= maximum_payload_size_to_transmit) {
+ .tcp_sessions[i].last_tx_sequence_number = .tcp_sessions[i].local_sequence_number + maximum_payload_size_to_transmit as! u32
+ .tcp_sessions[i].last_tx_was_acked = false
+ .tcp_sessions[i].tx_chunk_counter++
+ } else {
+ // We have transmitted all of the pending data.
+ .tcp_sessions[i].pending_data_to_transmit.shrink(0)
+ .tcp_sessions[i].last_tx_sequence_number = 0
+ .tcp_sessions[i].last_tx_was_acked = false
+ .tcp_sessions[i].tx_chunk_counter = 0
+ }
+ }
+ }
+ }
+ }
+ fn tcp_ack_packet(mut this, anon mut session: TcpSession, anon frame: [u8], mut flags: u16) throws -> u32 {
+ mut packet: [u8] = []
+
+ let syn_ipv4_packet = frame[14..]
+ let syn_tcp_packet = frame[34..]
+
+ // Swap source/destination ports
+ mut destination_port: u16 = Util::get_u16_from_u8_arrayslice(syn_tcp_packet, 0)
+ mut source_port: u16 = Util::get_u16_from_u8_arrayslice(syn_tcp_packet, 2)
+
+ let header_length: u16 = ((syn_tcp_packet[12] as! u16) >> 4) * 4
+
+ flags += ((header_length / 4) << 12)
+ flags |= 16 // ACK
+
+ mut sequence_number: u32 = session.local_sequence_number
+ mut acknowledgement_number: u32 = 0
+ for i in 0..4 {
+ acknowledgement_number += (syn_tcp_packet[7 - i] as! u32) * match i {
+ 1 => 256 as! u32
+ 2 => (256 * 256) as! u32
+ 3 => (256 * 256 * 256) as! u32
+ else => 1 as! u32
+ }
+ }
+
+ if (.tcp_is_syn(flags) or .tcp_is_fin(flags)) {
+ acknowledgement_number += 1
+ }
+ if (.tcp_is_push(flags)) {
+ // advance ACK by number of bytes
+ flags -= 8
+ }
+ acknowledgement_number += (syn_tcp_packet.size() as! u32 - header_length as! u32)
+
+ let window_size: u16 = 0xffff
+ let urgent_pointer: u16 = Util::get_u16_from_u8_arrayslice(syn_tcp_packet, 18)
+
+ mut destination_mac: [u8] = []
+ for octet in frame[6..12] {
+ destination_mac.push(octet)
+ }
+ mut destination_address: [u8] = []
+ for octet in syn_ipv4_packet[12..16] {
+ destination_address.push(octet)
+ }
+
+ mut ipv4_total_length: u16 = 0
+ ipv4_total_length += 20; // IPv4
+ ipv4_total_length += header_length // TCP
+
+ .push_ethernet_header(packet, destination_mac, source_mac: session.local_mac, ethertype: 0x0800)
+ .push_ipv4_header(packet, total_length: ipv4_total_length, flags: 0x40, protocol: 0x06, source_address: .ipv4_address, destination_address)
+
+ // FIXME: put this into .push_tcp_header
+
+ Util::push_u16_to_u8_array(packet, source_port)
+ Util::push_u16_to_u8_array(packet, destination_port)
+ Util::push_u32_to_u8_array(packet, sequence_number)
+ Util::push_u32_to_u8_array(packet, acknowledgement_number)
+ Util::push_u16_to_u8_array(packet, flags)
+ Util::push_u16_to_u8_array(packet, window_size)
+ Util::push_u16_to_u8_array(packet, 0 as! u16) // Checksum placeholder
+ Util::push_u16_to_u8_array(packet, urgent_pointer)
+
+ mut tcp_options_offset = 20
+ mut tcp_option_length = 0
+ while tcp_options_offset < header_length as! i64 {
+ match syn_tcp_packet[tcp_options_offset] {
+ 0u8 => {
+ packet.push(0u8)
+ tcp_options_offset++
+ }
+ 1u8 => {
+ packet.push(1u8)
+ tcp_options_offset++
+ }
+ 8u8 => {
+ packet.push(8u8)
+ packet.push(10u8)
+ let timestamp: u32 = (session.timestamp_origin + (Time::jiffies() - session.timestamp_origin)) as! u32
+ Util::push_u32_to_u8_array(packet, timestamp)
+ Util::push_u32_to_u8_array(packet, session.timestamp_last_echo_reply)
+ tcp_options_offset += 10
+ }
+ else => {
+ packet.push(syn_tcp_packet[tcp_options_offset])
+ packet.push(syn_tcp_packet[tcp_options_offset + 1])
+ tcp_option_length = syn_tcp_packet[tcp_options_offset + 1] as! i64
+ for i in 2..tcp_option_length {
+ packet.push(syn_tcp_packet[tcp_options_offset + i])
+ }
+ tcp_options_offset += tcp_option_length
+ }
+ }
+ }
+
+ // Calculate TCP checksum
+
+ mut checksum_packet: [u8] = []
+ // Source and destination IP
+ for i in 26..34 {
+ checksum_packet.push(packet[i])
+ }
+ // Protocol
+ checksum_packet.push(0x00u8)
+ checksum_packet.push(0x06u8)
+ // TCP Packet length
+ let tcp_packet_length: u16 = 40 as! u16
+ Util::push_u16_to_u8_array(checksum_packet, tcp_packet_length)
+
+ for i in 34..packet.size() {
+ checksum_packet.push(packet[i])
+ }
+
+ mut checksum = .calculate_header_checksum(checksum_packet, offset: 0, count: checksum_packet.size() as! i64)
+ if (not .tcp_is_syn(flags)) {
+ checksum += 8
+ }
+ packet[50] = (checksum >> 8) as! u8
+ packet[51] = (checksum & 0xff) as! u8
+
+ .tx_queue.push(packet)
+ return acknowledgement_number
+
+ }
+ fn tcp_synack_ack_packet(mut this, anon mut session: TcpSession, anon frame: [u8], mut flags: u16) throws -> u32 {
+ mut packet: [u8] = []
+
+ let syn_ipv4_packet = frame[14..]
+ let syn_tcp_packet = frame[34..]
+
+ // Swap source/destination ports
+ mut destination_port: u16 = Util::get_u16_from_u8_arrayslice(syn_tcp_packet, 0)
+ mut source_port: u16 = Util::get_u16_from_u8_arrayslice(syn_tcp_packet, 2)
+
+ let header_length: u16 = (20 + 12) as! u16
+
+ flags += ((header_length / 4) << 12)
+ flags |= 16 // ACK
+
+ mut sequence_number: u32 = session.local_sequence_number
+ mut acknowledgement_number: u32 = 0
+ for i in 0..4 {
+ acknowledgement_number += (syn_tcp_packet[7 - i] as! u32) * match i {
+ 1 => 256 as! u32
+ 2 => (256 * 256) as! u32
+ 3 => (256 * 256 * 256) as! u32
+ else => 1 as! u32
+ }
+ }
+
+ acknowledgement_number += 1
+
+ let window_size: u16 = 0xffff
+ let urgent_pointer: u16 = Util::get_u16_from_u8_arrayslice(syn_tcp_packet, 18)
+
+ mut destination_mac: [u8] = []
+ for octet in frame[6..12] {
+ destination_mac.push(octet)
+ }
+ mut destination_address: [u8] = []
+ for octet in syn_ipv4_packet[12..16] {
+ destination_address.push(octet)
+ }
+
+ mut ipv4_total_length: u16 = 0
+ ipv4_total_length += 20; // IPv4
+ ipv4_total_length += header_length // TCP
+
+ .push_ethernet_header(packet, destination_mac, source_mac: session.local_mac, ethertype: 0x0800)
+ .push_ipv4_header(packet, total_length: ipv4_total_length, flags: 0x40, protocol: 0x06, source_address: .ipv4_address, destination_address)
+
+ // FIXME: put this into .push_tcp_header
+
+ Util::push_u16_to_u8_array(packet, source_port)
+ Util::push_u16_to_u8_array(packet, destination_port)
+ Util::push_u32_to_u8_array(packet, sequence_number)
+ Util::push_u32_to_u8_array(packet, acknowledgement_number)
+ Util::push_u16_to_u8_array(packet, flags)
+ Util::push_u16_to_u8_array(packet, window_size)
+ Util::push_u16_to_u8_array(packet, 0 as! u16) // Checksum placeholder
+ Util::push_u16_to_u8_array(packet, urgent_pointer)
+
+ // Options: NOP, NOP, Timestamp
+ packet.push(0x01u8)
+ packet.push(0x01u8)
+ Util::push_u16_to_u8_array(packet, 0x080a)
+ let timestamp: u32 = (session.timestamp_origin + (Time::jiffies() - session.timestamp_origin)) as! u32
+ Util::push_u32_to_u8_array(packet, timestamp)
+ Util::push_u32_to_u8_array(packet, session.timestamp_last_echo_reply)
+
+ // Calculate TCP checksum
+
+ mut checksum_packet: [u8] = []
+ // Source and destination IP
+ for i in 26..34 {
+ checksum_packet.push(packet[i])
+ }
+ // Protocol
+ checksum_packet.push(0x00u8)
+ checksum_packet.push(0x06u8)
+ // TCP Packet length
+ let tcp_packet_length: u16 = 40 as! u16
+ Util::push_u16_to_u8_array(checksum_packet, tcp_packet_length)
+
+ for i in 34..packet.size() {
+ checksum_packet.push(packet[i])
+ }
+
+ mut checksum = .calculate_header_checksum(checksum_packet, offset: 0, count: checksum_packet.size() as! i64)
+ checksum += 8
+ packet[50] = (checksum >> 8) as! u8
+ packet[51] = (checksum & 0xff) as! u8
+
+ .tx_queue.push(packet)
+ return acknowledgement_number
+
+ }
+ fn tcp_session_matches_current_session(mut this, anon session: TcpSession, anon local_address: [u8], anon remote_address: [u8], anon local_port: u16, anon remote_port: u16) -> bool {
+ if session.state as! i64 == TcpSessionState::Closed as! i64 {
+ return false
+ }
+ if (session.local_port != local_port) or (session.remote_port != remote_port) {
+ return false
+ }
+ for i in 0..4 {
+ if (session.local_address[i] != local_address[i]) {
+ return false
+ }
+ if (session.remote_address[i] != remote_address[i]) {
+ return false
+ }
+ }
+ return true
+ }
+ fn tcp_send(mut this, anon mut session: TcpSession, anon data: [u8]) throws {
+ session.pending_data_to_transmit.push_values(&data)
+ }
+ public fn tcp_process_client_send_requests(mut this, anon mac_address: [u8]) throws {
+ for i in 0..this.tcp_sessions.size() {
+ let socket = .tcp_sessions[i].socket
+ if socket > 0 {
+ mut data: [u8] = []
+ unsafe {
+ cpp {
+ "u64 *s = (u64*)socket;
+ if (s[10] == 1) {
+ u8 *buffer = (u8*)s[7];
+ for (int i=0; i < s[9]; i++) {
+ data.push(buffer[i]);
+ }
+ s[10] = 0;
+ }"
+ }
+ }
+ if data.size() > 0 {
+ .tcp_send(.tcp_sessions[i], data)
+ }
+ }
+ }
+ }
+ public fn tcp_process_client_received_data(mut this) throws {
+ for i in 0..this.tcp_sessions.size() {
+ mut session = .tcp_sessions[i]
+ let socket = session.socket
+ mut length = session.received_data.size()
+ mut max_length: usize = 0
+ // s[4] = 0; // receive_buffer_size
+ if (socket > 0) {
+ unsafe {
+ cpp {
+ "u64 *s = (u64*)socket;
+ max_length = s[4];"
+ }
+ }
+ }
+ // FIXME: Should the client be responsible for malloc()ing the receive buffer?
+ if length > 65536 {
+ length = 65536
+ }
+ if (length > max_length) {
+ length = max_length
+ }
+ if (socket > 0) and (length == 0) and (.tcp_sessions[i].state as! i64 == TcpSessionState::Closed as! i64) {
+ .tcp_update_socket_session_state(.tcp_sessions[i].state, .tcp_sessions[i].socket)
+ mut client_is_not_ready_to_receive_data = 0
+ unsafe {
+ cpp {
+ "u64 *s = (u64*)socket;
+ client_is_not_ready_to_receive_data = s[6];"
+ }
+ }
+ if (client_is_not_ready_to_receive_data == 0) {
+ // really close the connection
+ unsafe {
+ cpp {
+ "u64 *s = (u64*)socket;
+ s[5] = 0;
+ s[6] = 1;
+ if (s[3] > 0)
+ free((u8*)s[3]);
+ if (s[7] > 0)
+ free((u8*)s[7]);
+ "
+ }
+ }
+ .tcp_sessions[i].socket = 0
+ }
+ }
+ if socket > 0 and length > 0 {
+ mut client_is_not_ready_to_receive_data = 0
+ unsafe {
+ cpp {
+ "u64 *s = (u64*)socket;
+ client_is_not_ready_to_receive_data = s[6];"
+ }
+ }
+ if (client_is_not_ready_to_receive_data == 0) {
+ unsafe {
+ cpp {
+ "u64 *s = (u64*)socket;
+ u8 *buffer = (u8*)s[3];
+ for (int i=0; i < length; i++) {
+ buffer[i] = session.received_data[i];
+ }
+ s[5] = length;
+ s[6] = 1;"
+ }
+ }
+ if (length == session.received_data.size() as! usize) {
+ session.received_data.shrink(0)
+ } else {
+ mut remaining_received_data: [u8] = []
+ for j in length..session.received_data.size() {
+ remaining_received_data.push(session.received_data[j])
+ }
+ session.received_data.shrink(0)
+ for j in 0..remaining_received_data.size() {
+ session.received_data.push(remaining_received_data[j])
+ }
+ }
+ }
+ }
+ }
+ }
+ fn tcp_is_next_frame(mut this, index: i64, frame: [u8]) throws -> bool {
+ let tcp_packet = frame[34..]
+ mut sequence_number: u32 = 0
+ for o in 0..4 {
+ sequence_number += (tcp_packet[7 - o] as! u32) * match o {
+ 1 => 256 as! u32
+ 2 => (256 * 256) as! u32
+ 3 => (256 * 256 * 256) as! u32
+ else => 1 as! u32
+ }
+ }
+ if (.tcp_sessions[index].acknowledgement_number == 0) {
+ // session not established yet, we're (hopefully) in a SYN/ACK
+ return true
+ }
+ if (sequence_number == .tcp_sessions[index].acknowledgement_number) {
+ return true
+ }
+ return false
+ }
+ fn tcp_handle_received_frame(mut this, anon i: i64, anon frame: [u8]) throws {
+ let ipv4_packet = frame[14..]
+ let tcp_packet = frame[34..]
+
+ let total_length = Util::get_u16_from_u8_arrayslice(ipv4_packet, 2) + 14
+
+ let source_address: [u8] = [ipv4_packet[12], ipv4_packet[13], ipv4_packet[14], ipv4_packet[15]]
+ let destination_address: [u8] = [ipv4_packet[16], ipv4_packet[17], ipv4_packet[18], ipv4_packet[19]]
+
+ mut source_port: u16 = Util::get_u16_from_u8_arrayslice(tcp_packet, 0)
+ mut destination_port: u16 = Util::get_u16_from_u8_arrayslice(tcp_packet, 2)
+
+ let header_length: u16 = ((tcp_packet[12] as! u16) >> 4) * 4
+ let flags: u16 = Util::get_u16_from_u8_arrayslice(tcp_packet, 12) & 0xfff
+
+ mut echo_reply: u32 = 0
+ mut tcp_options_offset = 54
+
+ while tcp_options_offset < frame.size() as! i64 {
+ match frame[tcp_options_offset] {
+ 0u8 => { tcp_options_offset++ }
+ 1u8 => { tcp_options_offset++ }
+ 8u8 => {
+ tcp_options_offset++
+ echo_reply += (frame[tcp_options_offset + 1] as! u32 * (256 * 256 * 256) as! u32)
+ echo_reply += (frame[tcp_options_offset + 2] as! u32 * (256 * 256) as! u32)
+ echo_reply += (frame[tcp_options_offset + 3] as! u32 * 256 as! u32)
+ echo_reply += frame[tcp_options_offset + 4] as! u32
+ break
+ }
+ else => { tcp_options_offset += frame[tcp_options_offset + 1] as! i64 }
+ }
+ }
+
+ if (flags & 0xff) != 0x10 {
+ // Received a packet with flags other than just ACK
+ for i in 0..this.tcp_sessions.size() {
+ if (.tcp_session_matches_current_session(.tcp_sessions[i], destination_address, source_address, destination_port, source_port)) {
+ mut received_data: [u8] = []
+ for j in frame[(34u16 + header_length)..total_length] {
+ received_data.push(j)
+ }
+ for j in 0..received_data.size() {
+ .tcp_sessions[i].received_data.push(received_data[j])
+ }
+ if (.tcp_is_fin(flags)) {
+ // Received request to close connection
+ //println("Closing connection to IPv4 address: {}", source_address)
+ if (.tcp_is_push(flags)) {
+ .tcp_sessions[i].acknowledgement_number = .tcp_ack_packet(.tcp_sessions[i], frame, flags: 9u16)
+ } else {
+ .tcp_sessions[i].acknowledgement_number = .tcp_ack_packet(.tcp_sessions[i], frame, flags: 1u16)
+ }
+ .tcp_sessions[i].state = TcpSessionState::Closed
+ } else if (.tcp_is_syn(flags)) and (.tcp_is_ack(flags)) {
+ // Received SYN, ACK
+ .tcp_sessions[i].timestamp_last_echo_reply = echo_reply
+ .tcp_sessions[i].timestamp_last_jiffies = Time::jiffies()
+ .tcp_sessions[i].local_sequence_number++
+ .tcp_sessions[i].acknowledgement_number = .tcp_synack_ack_packet(.tcp_sessions[i], frame, flags: 0u16)
+ .tcp_sessions[i].state = TcpSessionState::Established
+ .tcp_update_socket_session_state(.tcp_sessions[i].state, .tcp_sessions[i].socket)
+ } else {
+ .tcp_sessions[i].timestamp_last_echo_reply = echo_reply
+ .tcp_sessions[i].timestamp_last_jiffies = Time::jiffies()
+ mut acknowledgement_number: u32 = 0
+ for o in 0..4 {
+ acknowledgement_number += (tcp_packet[11 - o] as! u32) * match o {
+ 1 => 256 as! u32
+ 2 => (256 * 256) as! u32
+ 3 => (256 * 256 * 256) as! u32
+ else => 1 as! u32
+ }
+ }
+ .tcp_sessions[i].local_sequence_number = acknowledgement_number
+ if (.tcp_is_push(flags)) {
+ .tcp_sessions[i].acknowledgement_number = .tcp_ack_packet(.tcp_sessions[i], frame, flags: 8u16)
+ } else {
+ .tcp_sessions[i].acknowledgement_number = .tcp_ack_packet(.tcp_sessions[i], frame, flags: 0u16)
+ }
+ }
+ }
+ }
+ } else if (flags & 0xff) == 0x10 {
+ // Received a packet with flags == just ACK
+ for i in 0..this.tcp_sessions.size() {
+ if (.tcp_session_matches_current_session(.tcp_sessions[i], destination_address, source_address, destination_port, source_port)) {
+ mut received_data: [u8] = []
+ for j in frame[(34u16 + header_length)..total_length] {
+ received_data.push(j)
+ }
+ for j in 0..received_data.size() {
+ .tcp_sessions[i].received_data.push(received_data[j])
+ }
+ mut sequence_number: u32 = 0
+ for o in 0..4 {
+ sequence_number += (tcp_packet[7 - o] as! u32) * match o {
+ 1 => 256 as! u32
+ 2 => (256 * 256) as! u32
+ 3 => (256 * 256 * 256) as! u32
+ else => 1 as! u32
+ }
+ }
+ mut acknowledgement_number: u32 = 0
+ for o in 0..4 {
+ acknowledgement_number += (tcp_packet[11 - o] as! u32) * match o {
+ 1 => 256 as! u32
+ 2 => (256 * 256) as! u32
+ 3 => (256 * 256 * 256) as! u32
+ else => 1 as! u32
+ }
+ }
+ let window_size: u16 = 0xffff
+ if acknowledgement_number == .tcp_sessions[i].last_tx_sequence_number {
+ .tcp_sessions[i].last_tx_was_acked = true
+ }
+ .tcp_sessions[i].local_sequence_number = acknowledgement_number
+ if received_data.size() > 0 {
+ .tcp_sessions[i].acknowledgement_number = .tcp_ack_packet(.tcp_sessions[i], frame, flags: 0u16)
+ } else {
+ .tcp_sessions[i].acknowledgement_number = sequence_number + (received_data.size() as! u32)
+ }
+ .tcp_sessions[i].last_window_size = window_size
+ .tcp_sessions[i].timestamp_last_echo_reply = echo_reply
+ .tcp_sessions[i].timestamp_last_jiffies = Time::jiffies()
+ }
+ }
+ }
+ }
+ fn tcp_handle_received_frames(mut this, anon index: i64) throws {
+ mut unhandled_frames: [[u8]] = []
+ for frame in .tcp_sessions[index].received_frames {
+ if .tcp_is_next_frame(index, frame) {
+ .tcp_handle_received_frame(index, frame)
+ } else {
+ unhandled_frames.push(frame)
+ }
+ }
+ .tcp_sessions[index].received_frames.shrink(0)
+ for frame in unhandled_frames {
+ .tcp_sessions[index].received_frames.push(frame)
+ }
+ }
+ fn process_udp_packet(mut this, anon mac_address: [u8], anon frame: [u8]) throws {
+ let ipv4_packet = frame[14..]
+ let udp_packet = frame[34..]
+
+ let source_address: [u8] = [ipv4_packet[12], ipv4_packet[13], ipv4_packet[14], ipv4_packet[15]]
+ let destination_address: [u8] = [ipv4_packet[16], ipv4_packet[17], ipv4_packet[18], ipv4_packet[19]]
+
+ mut source_port: u16 = Util::get_u16_from_u8_arrayslice(udp_packet, 0)
+ mut destination_port: u16 = Util::get_u16_from_u8_arrayslice(udp_packet, 2)
+
+ if (Util::get_hexadecimal_string_from_ipv4_u8_array(source_address) == Util::get_hexadecimal_string_from_ipv4_u8_array(.dns_server_address)) and (source_port == .dns_server_port) {
+ let transaction_id: u16 = Util::get_u16_from_u8_arrayslice(udp_packet, 8)
+ let flags: u16 = Util::get_u16_from_u8_arrayslice(udp_packet, 10)
+ // questions : 12-13
+ // answer_rrs: 14-15
+ // authority_rrs: 16-17
+ // additional_rrs: 18-19
+
+ if .pending_dns_lookups.contains(transaction_id) {
+ mut result: u32 = 0
+ mut result_type: u16 = 0
+ // skip over query data to get answer
+ mut response_pos = 20
+ while (udp_packet[response_pos] > 0) {
+ response_pos += 1 + udp_packet[response_pos] as! i64
+ }
+ response_pos += 5 // skip over null byte, type, class
+ response_pos += 2 // skip over answer: name
+
+ result_type = 256 * udp_packet[response_pos] as! u16
+ result_type += udp_packet[response_pos + 1] as! u16
+
+ mut pointer_to_u32 = .pending_dns_lookups[transaction_id]
+ mut cached_result: [u8] = []
+
+ match result_type {
+ 1 => {
+ // A record
+ response_pos += 10
+ result += (udp_packet[response_pos] as! u32 * (256 * 256 * 256) as! u32)
+ result += (udp_packet[response_pos + 1] as! u32 * (256 * 256) as! u32)
+ result += (udp_packet[response_pos + 2] as! u32 * 256 as! u32)
+ result += udp_packet[response_pos + 3] as! u32
+ cached_result.push(udp_packet[response_pos])
+ cached_result.push(udp_packet[response_pos + 1])
+ cached_result.push(udp_packet[response_pos + 2])
+ cached_result.push(udp_packet[response_pos + 3])
+ .dns_cache[.pending_dns_cached_entries[transaction_id]] = cached_result
+ }
+ 5 => {
+ // CNAME record
+ response_pos += 8 // skip over answer: type, class, ttl
+ let data_length = (256 * udp_packet[response_pos] as! i64) + udp_packet[response_pos + 1] as! i64
+ response_pos += 2
+ let data_pos = response_pos
+ mut pointer_pos = 0
+
+ mut s = StringBuilder::create()
+ mut length = 0
+
+ while (response_pos < data_pos + data_length) {
+ match udp_packet[response_pos] {
+ 0xc0 => {
+ response_pos++
+ pointer_pos = 8 + udp_packet[response_pos++] as! i64
+ while (udp_packet[pointer_pos] > 0) {
+ length = udp_packet[pointer_pos] as! i64
+ pointer_pos++
+ for i in 0..length {
+ s.append(udp_packet[pointer_pos++])
+ }
+ if udp_packet[pointer_pos] > 0 {
+ s.append('.')
+ }
+ }
+ }
+ else => {
+ length = udp_packet[response_pos++] as! i64
+ for i in 0..length {
+ s.append(udp_packet[response_pos++])
+ }
+ if udp_packet[response_pos] > 0 {
+ s.append('.')
+ }
+ }
+ }
+ }
+ .send_dns_query(mac_address, s.to_string(), pointer_to_u32)
+ pointer_to_u32 = 0
+ }
+ else => {
+ // Unsupported record
+ response_pos += 10 // skip over answer: type, class, ttl, data length
+ result = 0xFFFFFFFF
+ }
+ }
+ if pointer_to_u32 > 0 {
+ unsafe {
+ cpp {
+ "u32 *r = (u32*)pointer_to_u32;
+ r[0] = result;"
+ }
+ }
+ }
+ .pending_dns_lookups.remove(transaction_id)
+ }
+ }
+
+ }
+ fn is_listening_port(this, anon port: u16) -> bool {
+ return .bound_sockets.contains(port)
+ }
+ fn process_tcp_packet(mut this, anon mac_address: [u8], anon frame: [u8]) throws {
+ let ipv4_packet = frame[14..]
+ let tcp_packet = frame[34..]
+
+ let source_address: [u8] = [ipv4_packet[12], ipv4_packet[13], ipv4_packet[14], ipv4_packet[15]]
+ let destination_address: [u8] = [ipv4_packet[16], ipv4_packet[17], ipv4_packet[18], ipv4_packet[19]]
+
+ mut source_port: u16 = Util::get_u16_from_u8_arrayslice(tcp_packet, 0)
+ mut destination_port: u16 = Util::get_u16_from_u8_arrayslice(tcp_packet, 2)
+
+ let header_length: u16 = ((tcp_packet[12] as! u16) >> 4) * 4
+ let flags: u16 = Util::get_u16_from_u8_arrayslice(tcp_packet, 12) & 0xfff
+
+ mut echo_reply: u32 = 0
+ mut tcp_options_offset = 54
+
+ while tcp_options_offset < frame.size() as! i64 {
+ match frame[tcp_options_offset] {
+ 0u8 => { tcp_options_offset++ }
+ 1u8 => { tcp_options_offset++ }
+ 8u8 => {
+ tcp_options_offset++
+ echo_reply += (frame[tcp_options_offset + 1] as! u32 * (256 * 256 * 256) as! u32)
+ echo_reply += (frame[tcp_options_offset + 2] as! u32 * (256 * 256) as! u32)
+ echo_reply += (frame[tcp_options_offset + 3] as! u32 * 256 as! u32)
+ echo_reply += frame[tcp_options_offset + 4] as! u32
+ break
+ }
+ else => { tcp_options_offset += frame[tcp_options_offset + 1] as! i64 }
+ }
+ }
+
+ if .tcp_is_syn(flags) and not (.tcp_is_ack(flags)) and (.is_listening_port(destination_port)) {
+ // Received SYN, begin a new connection
+ mut remote_mac: [u8] = []
+ for i in 6..12 {
+ remote_mac.push(frame[i])
+ }
+ if .is_local_ipv4_address(source_address) {
+ if not .arp_cache.contains(Util::get_hexadecimal_string_from_ipv4_u8_array(source_address)) {
+ // send arp request
+ //println("send arp request for: {}", Util::get_hexadecimal_string_from_ipv4_u8_array(source_address))
+ .send_arp_request(mac_address, source_address)
+ }
+ }
+ mut socket: u64 = 0
+ let function: u64 = .bound_sockets[destination_port]
+ let source_address_u32 = Util::get_address_u32_from_ipv4_u8_array(source_address)
+ unsafe {
+ cpp {
+ "
+ u64* s = (u64*)calloc(256, 1);
+
+ s[0] = source_address_u32;
+ s[1] = source_port;
+ s[2] = 1; // TCP_SOCKET_STATE_ESTABLISHED
+ s[3] = (u64)calloc(65536, 1); // receive_buffer_ptr
+ s[4] = 0; // receive_buffer_size
+ s[5] = 0; // receive_buffer_filled
+ s[6] = 0; // receive_buffer_kick
+ s[7] = (u64)calloc(65536, 1); // send_buffer_ptr
+ s[8] = 65536; // send_buffer_size
+ s[9] = 0; // send_buffer_filled
+ s[10] = 0; // send_buffer_kick
+
+ socket = (u64)s;
+ if (function && socket) {
+ os_call(function, socket);
+ }
+ "
+ }
+ }
+ mut session = TcpSession(
+ local_mac: mac_address
+ remote_mac: remote_mac
+ local_address: destination_address
+ remote_address: source_address
+ local_port: destination_port
+ remote_port: source_port
+ local_sequence_number: (OS::random() & 0xffffffff) as! u32
+ acknowledgement_number: 0
+ timestamp_last_echo_reply: 0
+ timestamp_last_jiffies: 0
+ timestamp_origin: Time::jiffies()
+ last_window_size: 0
+ last_tx_sequence_number: 0
+ last_tx_was_acked: false
+ tx_chunk_counter: 0
+ state: TcpSessionState::Established
+ pending_data_to_transmit: []
+ received_data: []
+ received_frames: []
+ socket)
+ //println("Accepting connection from IPv4 address: {}", source_address)
+ session.timestamp_last_echo_reply = echo_reply
+ session.timestamp_last_jiffies = Time::jiffies()
+ session.acknowledgement_number = .tcp_ack_packet(session, frame, flags)
+ .tcp_sessions.push(session)
+ } else {
+ // Add packet to list to be processed
+ for i in 0..this.tcp_sessions.size() {
+ if (.tcp_session_matches_current_session(.tcp_sessions[i], destination_address, source_address, destination_port, source_port)) {
+ .tcp_sessions[i].received_frames.push(frame)
+ .tcp_handle_received_frames(i as! i64)
+ }
+ }
+ }
+ }
+ public fn process_ipv4_packet(mut this, anon mac_address: [u8], anon frame: [u8]) throws {
+ let ipv4_packet = frame[14..]
+
+ let version = ipv4_packet[0] >> 4
+ let header_length = (ipv4_packet[0] as! i64 & 0xf) * 4
+ let dsf = ipv4_packet[1]
+ let total_length = Util::get_u16_from_u8_arrayslice(ipv4_packet, 2)
+ let identification = Util::get_u16_from_u8_arrayslice(ipv4_packet, 4)
+ let flags = ipv4_packet[6]
+ let ttl = ipv4_packet[8]
+ let protocol = ipv4_packet[9]
+ let checksum = Util::get_u16_from_u8_arrayslice(ipv4_packet, 10)
+ let source_address: [u8] = [ipv4_packet[12], ipv4_packet[13], ipv4_packet[14], ipv4_packet[15]]
+ let destination_address: [u8] = [ipv4_packet[16], ipv4_packet[17], ipv4_packet[18], ipv4_packet[19]]
+
+ match protocol {
+ 1 => {
+ .process_icmp_packet(mac_address, frame)
+ }
+ 6 => {
+ .process_tcp_packet(mac_address, frame)
+ }
+ 17 => {
+ .process_udp_packet(mac_address, frame)
+ }
+ else => {
+ // unsupported
+ }
+ }
+ }
+ public fn set_ipv4_address(mut this, anon ipv4_address: [i64]) {
+ for i in 0..4 {
+ .ipv4_address[i] = ipv4_address[i] as! u8
+ }
+ }
+ public fn set_ipv4_netmask(mut this, anon ipv4_netmask: [i64]) {
+ for i in 0..4 {
+ .ipv4_netmask[i] = ipv4_netmask[i] as! u8
+ }
+ }
+ public fn set_ipv4_network(mut this, anon ipv4_network: [i64]) {
+ for i in 0..4 {
+ .ipv4_network[i] = ipv4_network[i] as! u8
+ }
+ }
+ public fn set_ipv4_gateway(mut this, anon ipv4_gateway: [i64]) {
+ for i in 0..4 {
+ .ipv4_gateway[i] = ipv4_gateway[i] as! u8
+ }
+ }
+ fn is_local_ipv4_address(this, anon check_ipv4_address: [u8]) -> bool {
+ let check_ipv4_address_u32 = Util::get_address_u32_from_ipv4_u8_array(check_ipv4_address)
+ let local_network_address_u32 = Util::get_address_u32_from_ipv4_u8_array(.ipv4_network)
+ let local_netmask_address_u32 = Util::get_address_u32_from_ipv4_u8_array(.ipv4_netmask)
+ // NOTE: The line below was added and the subsequent line was modified after jakt PR #1596 to suppress a warning from codegen output
+ // tcpip.cpp:2599:90: warning: & has lower precedence than ==; == will be evaluated first [-Wparentheses]
+ // 2599 | return (local_network_address_u32 & local_netmask_address_u32) == check_ipv4_address_u32 & local_netmask_address_u32;
+ // | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
+ // tcpip.cpp:2599:90: note: place parentheses around the '==' expression to silence this warning
+ let check_ipv4_address_u32_and_with_netmask = check_ipv4_address_u32 & local_netmask_address_u32
+ return (local_network_address_u32 & local_netmask_address_u32) == check_ipv4_address_u32_and_with_netmask
+ }
+ fn tcp_update_socket_session_state(mut this, anon state: TcpSessionState, anon socket: u64) throws {
+ unsafe {
+ cpp {
+ "u64 *s = (u64*)socket;
+ s[2] = (u64)state;
+ "
+ }
+ }
+ }
+ public fn tcp_process_bind_request(mut this) {
+ mut did_receive_request = false
+ mut port: u16 = 0
+ mut function: u64 = 0
+ mut response_code: u64 = 0
+ unsafe {
+ cpp {
+ "
+ u64 *request = (u64*)0x300040;
+ if (*request) {
+ did_receive_request = true;
+ u64 *b = (u64*)*request;
+ port = b[0];
+ function = b[1];
+ }
+ "
+ }
+ }
+ if did_receive_request {
+ if .bound_sockets.contains(port) {
+ response_code = TcpBindError::SocketIsAlreadyBound as! u64
+ } else if port < 1 or function < 1 {
+ response_code = TcpBindError::BadRequest as! u64
+ } else {
+ .bound_sockets[port] = function
+ }
+ unsafe {
+ cpp {
+ "
+ u64 *request = (u64*)0x300040;
+ u64 *b = (u64*)*request;
+ b[2] = response_code;
+ *request = 0;
+ "
+ }
+ }
+ }
+ }
+ public fn tcp_process_client_socket_request(mut this, anon mac_address: [u8]) throws {
+ mut did_receive_request = false
+ mut remote_address: u32 = 0
+ mut remote_port: u16 = 0
+ mut session_remote_mac: [u8] = []
+ mut socket: u64 = 0
+ unsafe {
+ cpp {
+ "u64 *ls_request = (u64*)0x300000;
+ if (*ls_request) {
+ u64 *ls_s = (u64*)*ls_request;
+ remote_address = ls_s[0];
+ }"
+ }
+ }
+ if (remote_address > 0) {
+ mut remote_address_hex_string = Util::get_hexadecimal_string_from_ipv4_u8_array(.ipv4_gateway)
+ if .is_local_ipv4_address(Util::get_ipv4_u8_array_from_address_u32(remote_address)) {
+ remote_address_hex_string = Util::get_hexadecimal_string_from_ipv4_u8_array(Util::get_ipv4_u8_array_from_address_u32(remote_address))
+ if not .arp_cache.contains(remote_address_hex_string) {
+ // send arp request
+ //println("send arp request for: {}", remote_address_hex_string)
+ .send_arp_request(mac_address, Util::get_ipv4_u8_array_from_address_u32(remote_address))
+ return
+ }
+ }
+ session_remote_mac = .arp_cache[remote_address_hex_string]
+ } else {
+ return
+ }
+ unsafe {
+ cpp {
+ "u64 *request = (u64*)0x300000;
+ if (*request) {
+ did_receive_request = true;
+ u64 *s = (u64*)*request;
+
+ s[2] = 5; // TCP_SOCKET_STATE_IDLE
+
+ s[3] = (u64)calloc(65536, 1); // receive_buffer_ptr
+ s[4] = 0; // receive_buffer_size
+ s[5] = 0; // receive_buffer_filled
+ s[6] = 1; // receive_buffer_kick
+
+ s[7] = (u64)calloc(65536, 1); // send_buffer_ptr
+ s[8] = 65536; // send_buffer_size
+ s[9] = 0; // send_buffer_filled
+ s[10] = 0; // send_buffer_kick
+
+ remote_address = s[0];
+ remote_port = s[1];
+ socket = (u64)s;
+ *request = 0;
+ }"
+ }
+ }
+ if (did_receive_request) {
+ let local_sequence_number: u32 = (OS::random() & 0xffffffff) as! u32
+
+ mut session = TcpSession(
+ local_mac: mac_address
+ remote_mac: session_remote_mac
+ local_address: .ipv4_address
+ remote_address: Util::get_ipv4_u8_array_from_address_u32(remote_address)
+ local_port: (OS::random() & 0xffff) as! u16 // FIXME
+ remote_port
+ local_sequence_number
+ acknowledgement_number: 0
+ timestamp_last_echo_reply: 0
+ timestamp_last_jiffies: Time::jiffies()
+ timestamp_origin: Time::jiffies()
+ last_window_size: 0xffff
+ last_tx_sequence_number: 0
+ last_tx_was_acked: false
+ tx_chunk_counter: 0
+ state: TcpSessionState::Connecting
+ pending_data_to_transmit: []
+ received_data: []
+ received_frames: []
+ socket)
+ .tcp_syn_packet(session)
+ .tcp_update_socket_session_state(session.state, session.socket)
+ .tcp_sessions.push(session)
+ did_receive_request = false
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/tlse/LICENSE b/src/tlse/LICENSE
new file mode 100644
index 0000000..6494b3d
--- /dev/null
+++ b/src/tlse/LICENSE
@@ -0,0 +1,60 @@
+This software is available under your choice of one of the following licenses.
+Choose whichever you prefer.
+
+===============================================================================
+License Choice 1 - The 2-Clause BSD License
+===============================================================================
+
+Copyright (c) 2016 - 2024, Eduard Suica
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+===============================================================================
+License Choice 2 - Public Domain (Unlicense)
+===============================================================================
+
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+
diff --git a/src/tlse/libtomcrypt.c b/src/tlse/libtomcrypt.c
new file mode 100644
index 0000000..b6e9c33
--- /dev/null
+++ b/src/tlse/libtomcrypt.c
@@ -0,0 +1,34766 @@
+#define CRYPT 0x0117
+#define LTC_NO_ROLC
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://math.libtomcrypt.com
+ */
+#ifndef BN_H_
+#define BN_H_
+
+#include
+#include
+#include
+#include
+
+#if !(defined(LTM1) && defined(LTM2) && defined(LTM3))
+ #if defined(LTM2)
+ #define LTM3
+ #endif
+ #if defined(LTM1)
+ #define LTM2
+ #endif
+ #define LTM1
+
+ #if defined(LTM_ALL)
+ #define BN_ERROR_C
+ #define BN_FAST_MP_INVMOD_C
+ #define BN_FAST_MP_MONTGOMERY_REDUCE_C
+ #define BN_FAST_S_MP_MUL_DIGS_C
+ #define BN_FAST_S_MP_MUL_HIGH_DIGS_C
+ #define BN_FAST_S_MP_SQR_C
+ #define BN_MP_2EXPT_C
+ #define BN_MP_ABS_C
+ #define BN_MP_ADD_C
+ #define BN_MP_ADD_D_C
+ #define BN_MP_ADDMOD_C
+ #define BN_MP_AND_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_CLEAR_MULTI_C
+ #define BN_MP_CMP_C
+ #define BN_MP_CMP_D_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_MP_CNT_LSB_C
+ #define BN_MP_COPY_C
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_DIV_C
+ #define BN_MP_DIV_2_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_DIV_3_C
+ #define BN_MP_DIV_D_C
+ #define BN_MP_DR_IS_MODULUS_C
+ #define BN_MP_DR_REDUCE_C
+ #define BN_MP_DR_SETUP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_EXPORT_C
+ #define BN_MP_EXPT_D_C
+ #define BN_MP_EXPT_D_EX_C
+ #define BN_MP_EXPTMOD_C
+ #define BN_MP_EXPTMOD_FAST_C
+ #define BN_MP_EXTEUCLID_C
+ #define BN_MP_FREAD_C
+ #define BN_MP_FWRITE_C
+ #define BN_MP_GCD_C
+ #define BN_MP_GET_INT_C
+ #define BN_MP_GET_LONG_C
+ #define BN_MP_GET_LONG_LONG_C
+ #define BN_MP_GROW_C
+ #define BN_MP_IMPORT_C
+ #define BN_MP_INIT_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_INIT_SET_C
+ #define BN_MP_INIT_SET_INT_C
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_INVMOD_C
+ #define BN_MP_INVMOD_SLOW_C
+ #define BN_MP_IS_SQUARE_C
+ #define BN_MP_JACOBI_C
+ #define BN_MP_KARATSUBA_MUL_C
+ #define BN_MP_KARATSUBA_SQR_C
+ #define BN_MP_LCM_C
+ #define BN_MP_LSHD_C
+ #define BN_MP_MOD_C
+ #define BN_MP_MOD_2D_C
+ #define BN_MP_MOD_D_C
+ #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+ #define BN_MP_MONTGOMERY_REDUCE_C
+ #define BN_MP_MONTGOMERY_SETUP_C
+ #define BN_MP_MUL_C
+ #define BN_MP_MUL_2_C
+ #define BN_MP_MUL_2D_C
+ #define BN_MP_MUL_D_C
+ #define BN_MP_MULMOD_C
+ #define BN_MP_N_ROOT_C
+ #define BN_MP_N_ROOT_EX_C
+ #define BN_MP_NEG_C
+ #define BN_MP_OR_C
+ #define BN_MP_PRIME_FERMAT_C
+ #define BN_MP_PRIME_IS_DIVISIBLE_C
+ #define BN_MP_PRIME_IS_PRIME_C
+ #define BN_MP_PRIME_MILLER_RABIN_C
+ #define BN_MP_PRIME_NEXT_PRIME_C
+ #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C
+ #define BN_MP_PRIME_RANDOM_EX_C
+ #define BN_MP_RADIX_SIZE_C
+ #define BN_MP_RADIX_SMAP_C
+ #define BN_MP_RAND_C
+ #define BN_MP_READ_RADIX_C
+ #define BN_MP_READ_SIGNED_BIN_C
+ #define BN_MP_READ_UNSIGNED_BIN_C
+ #define BN_MP_REDUCE_C
+ #define BN_MP_REDUCE_2K_C
+ #define BN_MP_REDUCE_2K_L_C
+ #define BN_MP_REDUCE_2K_SETUP_C
+ #define BN_MP_REDUCE_2K_SETUP_L_C
+ #define BN_MP_REDUCE_IS_2K_C
+ #define BN_MP_REDUCE_IS_2K_L_C
+ #define BN_MP_REDUCE_SETUP_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_SET_C
+ #define BN_MP_SET_INT_C
+ #define BN_MP_SET_LONG_C
+ #define BN_MP_SET_LONG_LONG_C
+ #define BN_MP_SHRINK_C
+ #define BN_MP_SIGNED_BIN_SIZE_C
+ #define BN_MP_SQR_C
+ #define BN_MP_SQRMOD_C
+ #define BN_MP_SQRT_C
+ #define BN_MP_SQRTMOD_PRIME_C
+ #define BN_MP_SUB_C
+ #define BN_MP_SUB_D_C
+ #define BN_MP_SUBMOD_C
+ #define BN_MP_TO_SIGNED_BIN_C
+ #define BN_MP_TO_SIGNED_BIN_N_C
+ #define BN_MP_TO_UNSIGNED_BIN_C
+ #define BN_MP_TO_UNSIGNED_BIN_N_C
+ #define BN_MP_TOOM_MUL_C
+ #define BN_MP_TOOM_SQR_C
+ #define BN_MP_TORADIX_C
+ #define BN_MP_TORADIX_N_C
+ #define BN_MP_UNSIGNED_BIN_SIZE_C
+ #define BN_MP_XOR_C
+ #define BN_MP_ZERO_C
+ #define BN_PRIME_TAB_C
+ #define BN_REVERSE_C
+ #define BN_S_MP_ADD_C
+ #define BN_S_MP_EXPTMOD_C
+ #define BN_S_MP_MUL_DIGS_C
+ #define BN_S_MP_MUL_HIGH_DIGS_C
+ #define BN_S_MP_SQR_C
+ #define BN_S_MP_SUB_C
+ #define BNCORE_C
+ #endif
+
+ #if defined(BN_ERROR_C)
+ #define BN_MP_ERROR_TO_STRING_C
+ #endif
+
+ #if defined(BN_FAST_MP_INVMOD_C)
+ #define BN_MP_ISEVEN_C
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_COPY_C
+ #define BN_MP_MOD_C
+ #define BN_MP_SET_C
+ #define BN_MP_DIV_2_C
+ #define BN_MP_ISODD_C
+ #define BN_MP_SUB_C
+ #define BN_MP_CMP_C
+ #define BN_MP_ISZERO_C
+ #define BN_MP_CMP_D_C
+ #define BN_MP_ADD_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_MULTI_C
+ #endif
+
+ #if defined(BN_FAST_MP_MONTGOMERY_REDUCE_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_S_MP_SUB_C
+ #endif
+
+ #if defined(BN_FAST_S_MP_MUL_DIGS_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_FAST_S_MP_SQR_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_2EXPT_C)
+ #define BN_MP_ZERO_C
+ #define BN_MP_GROW_C
+ #endif
+
+ #if defined(BN_MP_ABS_C)
+ #define BN_MP_COPY_C
+ #endif
+
+ #if defined(BN_MP_ADD_C)
+ #define BN_S_MP_ADD_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_S_MP_SUB_C
+ #endif
+
+ #if defined(BN_MP_ADD_D_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_SUB_D_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_ADDMOD_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_ADD_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_MOD_C
+ #endif
+
+ #if defined(BN_MP_AND_C)
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_CLAMP_C)
+ #endif
+
+ #if defined(BN_MP_CLEAR_C)
+ #endif
+
+ #if defined(BN_MP_CLEAR_MULTI_C)
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_CMP_C)
+ #define BN_MP_CMP_MAG_C
+ #endif
+
+ #if defined(BN_MP_CMP_D_C)
+ #endif
+
+ #if defined(BN_MP_CMP_MAG_C)
+ #endif
+
+ #if defined(BN_MP_CNT_LSB_C)
+ #define BN_MP_ISZERO_C
+ #endif
+
+ #if defined(BN_MP_COPY_C)
+ #define BN_MP_GROW_C
+ #endif
+
+ #if defined(BN_MP_COUNT_BITS_C)
+ #endif
+
+ #if defined(BN_MP_DIV_C)
+ #define BN_MP_ISZERO_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_MP_COPY_C
+ #define BN_MP_ZERO_C
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_SET_C
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_ABS_C
+ #define BN_MP_MUL_2D_C
+ #define BN_MP_CMP_C
+ #define BN_MP_SUB_C
+ #define BN_MP_ADD_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_MULTI_C
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_INIT_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_LSHD_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_MUL_D_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_DIV_2_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_DIV_2D_C)
+ #define BN_MP_COPY_C
+ #define BN_MP_ZERO_C
+ #define BN_MP_INIT_C
+ #define BN_MP_MOD_2D_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #endif
+
+ #if defined(BN_MP_DIV_3_C)
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_DIV_D_C)
+ #define BN_MP_ISZERO_C
+ #define BN_MP_COPY_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_DIV_3_C
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_DR_IS_MODULUS_C)
+ #endif
+
+ #if defined(BN_MP_DR_REDUCE_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_S_MP_SUB_C
+ #endif
+
+ #if defined(BN_MP_DR_SETUP_C)
+ #endif
+
+ #if defined(BN_MP_EXCH_C)
+ #endif
+
+ #if defined(BN_MP_EXPORT_C)
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_EXPT_D_C)
+ #define BN_MP_EXPT_D_EX_C
+ #endif
+
+ #if defined(BN_MP_EXPT_D_EX_C)
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_SET_C
+ #define BN_MP_MUL_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_SQR_C
+ #endif
+
+ #if defined(BN_MP_EXPTMOD_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_INVMOD_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_ABS_C
+ #define BN_MP_CLEAR_MULTI_C
+ #define BN_MP_REDUCE_IS_2K_L_C
+ #define BN_S_MP_EXPTMOD_C
+ #define BN_MP_DR_IS_MODULUS_C
+ #define BN_MP_REDUCE_IS_2K_C
+ #define BN_MP_ISODD_C
+ #define BN_MP_EXPTMOD_FAST_C
+ #endif
+
+ #if defined(BN_MP_EXPTMOD_FAST_C)
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_INIT_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_MONTGOMERY_SETUP_C
+ #define BN_FAST_MP_MONTGOMERY_REDUCE_C
+ #define BN_MP_MONTGOMERY_REDUCE_C
+ #define BN_MP_DR_SETUP_C
+ #define BN_MP_DR_REDUCE_C
+ #define BN_MP_REDUCE_2K_SETUP_C
+ #define BN_MP_REDUCE_2K_C
+ #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+ #define BN_MP_MULMOD_C
+ #define BN_MP_SET_C
+ #define BN_MP_MOD_C
+ #define BN_MP_COPY_C
+ #define BN_MP_SQR_C
+ #define BN_MP_MUL_C
+ #define BN_MP_EXCH_C
+ #endif
+
+ #if defined(BN_MP_EXTEUCLID_C)
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_SET_C
+ #define BN_MP_COPY_C
+ #define BN_MP_ISZERO_C
+ #define BN_MP_DIV_C
+ #define BN_MP_MUL_C
+ #define BN_MP_SUB_C
+ #define BN_MP_NEG_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_MULTI_C
+ #endif
+
+ #if defined(BN_MP_FREAD_C)
+ #define BN_MP_ZERO_C
+ #define BN_MP_S_RMAP_C
+ #define BN_MP_MUL_D_C
+ #define BN_MP_ADD_D_C
+ #define BN_MP_CMP_D_C
+ #endif
+
+ #if defined(BN_MP_FWRITE_C)
+ #define BN_MP_RADIX_SIZE_C
+ #define BN_MP_TORADIX_C
+ #endif
+
+ #if defined(BN_MP_GCD_C)
+ #define BN_MP_ISZERO_C
+ #define BN_MP_ABS_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_CNT_LSB_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_MP_EXCH_C
+ #define BN_S_MP_SUB_C
+ #define BN_MP_MUL_2D_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_GET_INT_C)
+ #endif
+
+ #if defined(BN_MP_GET_LONG_C)
+ #endif
+
+ #if defined(BN_MP_GET_LONG_LONG_C)
+ #endif
+
+ #if defined(BN_MP_GROW_C)
+ #endif
+
+ #if defined(BN_MP_IMPORT_C)
+ #define BN_MP_ZERO_C
+ #define BN_MP_MUL_2D_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_INIT_C)
+ #endif
+
+ #if defined(BN_MP_INIT_COPY_C)
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_COPY_C
+ #endif
+
+ #if defined(BN_MP_INIT_MULTI_C)
+ #define BN_MP_ERR_C
+ #define BN_MP_INIT_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_INIT_SET_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_SET_C
+ #endif
+
+ #if defined(BN_MP_INIT_SET_INT_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_SET_INT_C
+ #endif
+
+ #if defined(BN_MP_INIT_SIZE_C)
+ #define BN_MP_INIT_C
+ #endif
+
+ #if defined(BN_MP_INVMOD_C)
+ #define BN_MP_ISZERO_C
+ #define BN_MP_ISODD_C
+ #define BN_FAST_MP_INVMOD_C
+ #define BN_MP_INVMOD_SLOW_C
+ #endif
+
+ #if defined(BN_MP_INVMOD_SLOW_C)
+ #define BN_MP_ISZERO_C
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_MOD_C
+ #define BN_MP_COPY_C
+ #define BN_MP_ISEVEN_C
+ #define BN_MP_SET_C
+ #define BN_MP_DIV_2_C
+ #define BN_MP_ISODD_C
+ #define BN_MP_ADD_C
+ #define BN_MP_SUB_C
+ #define BN_MP_CMP_C
+ #define BN_MP_CMP_D_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_MULTI_C
+ #endif
+
+ #if defined(BN_MP_IS_SQUARE_C)
+ #define BN_MP_MOD_D_C
+ #define BN_MP_INIT_SET_INT_C
+ #define BN_MP_MOD_C
+ #define BN_MP_GET_INT_C
+ #define BN_MP_SQRT_C
+ #define BN_MP_SQR_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_JACOBI_C)
+ #define BN_MP_CMP_D_C
+ #define BN_MP_ISZERO_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_CNT_LSB_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_MOD_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_KARATSUBA_MUL_C)
+ #define BN_MP_MUL_C
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_CLAMP_C
+ #define BN_S_MP_ADD_C
+ #define BN_MP_ADD_C
+ #define BN_S_MP_SUB_C
+ #define BN_MP_LSHD_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_KARATSUBA_SQR_C)
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_SQR_C
+ #define BN_S_MP_ADD_C
+ #define BN_S_MP_SUB_C
+ #define BN_MP_LSHD_C
+ #define BN_MP_ADD_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_LCM_C)
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_GCD_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_MP_DIV_C
+ #define BN_MP_MUL_C
+ #define BN_MP_CLEAR_MULTI_C
+ #endif
+
+ #if defined(BN_MP_LSHD_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_RSHD_C
+ #endif
+
+ #if defined(BN_MP_MOD_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_DIV_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_ISZERO_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_ADD_C
+ #endif
+
+ #if defined(BN_MP_MOD_2D_C)
+ #define BN_MP_ZERO_C
+ #define BN_MP_COPY_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_MOD_D_C)
+ #define BN_MP_DIV_D_C
+ #endif
+
+ #if defined(BN_MP_MONTGOMERY_CALC_NORMALIZATION_C)
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_2EXPT_C
+ #define BN_MP_SET_C
+ #define BN_MP_MUL_2_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_S_MP_SUB_C
+ #endif
+
+ #if defined(BN_MP_MONTGOMERY_REDUCE_C)
+ #define BN_FAST_MP_MONTGOMERY_REDUCE_C
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_S_MP_SUB_C
+ #endif
+
+ #if defined(BN_MP_MONTGOMERY_SETUP_C)
+ #endif
+
+ #if defined(BN_MP_MUL_C)
+ #define BN_MP_TOOM_MUL_C
+ #define BN_MP_KARATSUBA_MUL_C
+ #define BN_FAST_S_MP_MUL_DIGS_C
+ #define BN_S_MP_MUL_C
+ #define BN_S_MP_MUL_DIGS_C
+ #endif
+
+ #if defined(BN_MP_MUL_2_C)
+ #define BN_MP_GROW_C
+ #endif
+
+ #if defined(BN_MP_MUL_2D_C)
+ #define BN_MP_COPY_C
+ #define BN_MP_GROW_C
+ #define BN_MP_LSHD_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_MUL_D_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_MULMOD_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_MUL_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_MOD_C
+ #endif
+
+ #if defined(BN_MP_N_ROOT_C)
+ #define BN_MP_N_ROOT_EX_C
+ #endif
+
+ #if defined(BN_MP_N_ROOT_EX_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_SET_C
+ #define BN_MP_COPY_C
+ #define BN_MP_EXPT_D_EX_C
+ #define BN_MP_MUL_C
+ #define BN_MP_SUB_C
+ #define BN_MP_MUL_D_C
+ #define BN_MP_DIV_C
+ #define BN_MP_CMP_C
+ #define BN_MP_SUB_D_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_NEG_C)
+ #define BN_MP_COPY_C
+ #define BN_MP_ISZERO_C
+ #endif
+
+ #if defined(BN_MP_OR_C)
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_PRIME_FERMAT_C)
+ #define BN_MP_CMP_D_C
+ #define BN_MP_INIT_C
+ #define BN_MP_EXPTMOD_C
+ #define BN_MP_CMP_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_PRIME_IS_DIVISIBLE_C)
+ #define BN_MP_MOD_D_C
+ #endif
+
+ #if defined(BN_MP_PRIME_IS_PRIME_C)
+ #define BN_MP_CMP_D_C
+ #define BN_MP_PRIME_IS_DIVISIBLE_C
+ #define BN_MP_INIT_C
+ #define BN_MP_SET_C
+ #define BN_MP_PRIME_MILLER_RABIN_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_PRIME_MILLER_RABIN_C)
+ #define BN_MP_CMP_D_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_SUB_D_C
+ #define BN_MP_CNT_LSB_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_EXPTMOD_C
+ #define BN_MP_CMP_C
+ #define BN_MP_SQRMOD_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_PRIME_NEXT_PRIME_C)
+ #define BN_MP_CMP_D_C
+ #define BN_MP_SET_C
+ #define BN_MP_SUB_D_C
+ #define BN_MP_ISEVEN_C
+ #define BN_MP_MOD_D_C
+ #define BN_MP_INIT_C
+ #define BN_MP_ADD_D_C
+ #define BN_MP_PRIME_MILLER_RABIN_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_PRIME_RABIN_MILLER_TRIALS_C)
+ #endif
+
+ #if defined(BN_MP_PRIME_RANDOM_EX_C)
+ #define BN_MP_READ_UNSIGNED_BIN_C
+ #define BN_MP_PRIME_IS_PRIME_C
+ #define BN_MP_SUB_D_C
+ #define BN_MP_DIV_2_C
+ #define BN_MP_MUL_2_C
+ #define BN_MP_ADD_D_C
+ #endif
+
+ #if defined(BN_MP_RADIX_SIZE_C)
+ #define BN_MP_ISZERO_C
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_DIV_D_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_RADIX_SMAP_C)
+ #define BN_MP_S_RMAP_C
+ #endif
+
+ #if defined(BN_MP_RAND_C)
+ #define BN_MP_ZERO_C
+ #define BN_MP_ADD_D_C
+ #define BN_MP_LSHD_C
+ #endif
+
+ #if defined(BN_MP_READ_RADIX_C)
+ #define BN_MP_ZERO_C
+ #define BN_MP_S_RMAP_C
+ #define BN_MP_MUL_D_C
+ #define BN_MP_ADD_D_C
+ #define BN_MP_ISZERO_C
+ #endif
+
+ #if defined(BN_MP_READ_SIGNED_BIN_C)
+ #define BN_MP_READ_UNSIGNED_BIN_C
+ #endif
+
+ #if defined(BN_MP_READ_UNSIGNED_BIN_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_ZERO_C
+ #define BN_MP_MUL_2D_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_REDUCE_C)
+ #define BN_MP_REDUCE_SETUP_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_MUL_C
+ #define BN_S_MP_MUL_HIGH_DIGS_C
+ #define BN_FAST_S_MP_MUL_HIGH_DIGS_C
+ #define BN_MP_MOD_2D_C
+ #define BN_S_MP_MUL_DIGS_C
+ #define BN_MP_SUB_C
+ #define BN_MP_CMP_D_C
+ #define BN_MP_SET_C
+ #define BN_MP_LSHD_C
+ #define BN_MP_ADD_C
+ #define BN_MP_CMP_C
+ #define BN_S_MP_SUB_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_REDUCE_2K_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_MUL_D_C
+ #define BN_S_MP_ADD_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_S_MP_SUB_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_REDUCE_2K_L_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_MUL_C
+ #define BN_S_MP_ADD_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_S_MP_SUB_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_REDUCE_2K_SETUP_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_2EXPT_C
+ #define BN_MP_CLEAR_C
+ #define BN_S_MP_SUB_C
+ #endif
+
+ #if defined(BN_MP_REDUCE_2K_SETUP_L_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_2EXPT_C
+ #define BN_MP_COUNT_BITS_C
+ #define BN_S_MP_SUB_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_REDUCE_IS_2K_C)
+ #define BN_MP_REDUCE_2K_C
+ #define BN_MP_COUNT_BITS_C
+ #endif
+
+ #if defined(BN_MP_REDUCE_IS_2K_L_C)
+ #endif
+
+ #if defined(BN_MP_REDUCE_SETUP_C)
+ #define BN_MP_2EXPT_C
+ #define BN_MP_DIV_C
+ #endif
+
+ #if defined(BN_MP_RSHD_C)
+ #define BN_MP_ZERO_C
+ #endif
+
+ #if defined(BN_MP_SET_C)
+ #define BN_MP_ZERO_C
+ #endif
+
+ #if defined(BN_MP_SET_INT_C)
+ #define BN_MP_ZERO_C
+ #define BN_MP_MUL_2D_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_SET_LONG_C)
+ #endif
+
+ #if defined(BN_MP_SET_LONG_LONG_C)
+ #endif
+
+ #if defined(BN_MP_SHRINK_C)
+ #endif
+
+ #if defined(BN_MP_SIGNED_BIN_SIZE_C)
+ #define BN_MP_UNSIGNED_BIN_SIZE_C
+ #endif
+
+ #if defined(BN_MP_SQR_C)
+ #define BN_MP_TOOM_SQR_C
+ #define BN_MP_KARATSUBA_SQR_C
+ #define BN_FAST_S_MP_SQR_C
+ #define BN_S_MP_SQR_C
+ #endif
+
+ #if defined(BN_MP_SQRMOD_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_SQR_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_MOD_C
+ #endif
+
+ #if defined(BN_MP_SQRT_C)
+ #define BN_MP_N_ROOT_C
+ #define BN_MP_ISZERO_C
+ #define BN_MP_ZERO_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_DIV_C
+ #define BN_MP_ADD_C
+ #define BN_MP_DIV_2_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_SQRTMOD_PRIME_C)
+ #define BN_MP_CMP_D_C
+ #define BN_MP_ZERO_C
+ #define BN_MP_JACOBI_C
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_MOD_D_C
+ #define BN_MP_ADD_D_C
+ #define BN_MP_DIV_2_C
+ #define BN_MP_EXPTMOD_C
+ #define BN_MP_COPY_C
+ #define BN_MP_SUB_D_C
+ #define BN_MP_ISEVEN_C
+ #define BN_MP_SET_INT_C
+ #define BN_MP_SQRMOD_C
+ #define BN_MP_MULMOD_C
+ #define BN_MP_SET_C
+ #define BN_MP_CLEAR_MULTI_C
+ #endif
+
+ #if defined(BN_MP_SUB_C)
+ #define BN_S_MP_ADD_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_S_MP_SUB_C
+ #endif
+
+ #if defined(BN_MP_SUB_D_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_ADD_D_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_SUBMOD_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_SUB_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_MOD_C
+ #endif
+
+ #if defined(BN_MP_TO_SIGNED_BIN_C)
+ #define BN_MP_TO_UNSIGNED_BIN_C
+ #endif
+
+ #if defined(BN_MP_TO_SIGNED_BIN_N_C)
+ #define BN_MP_SIGNED_BIN_SIZE_C
+ #define BN_MP_TO_SIGNED_BIN_C
+ #endif
+
+ #if defined(BN_MP_TO_UNSIGNED_BIN_C)
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_ISZERO_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_TO_UNSIGNED_BIN_N_C)
+ #define BN_MP_UNSIGNED_BIN_SIZE_C
+ #define BN_MP_TO_UNSIGNED_BIN_C
+ #endif
+
+ #if defined(BN_MP_TOOM_MUL_C)
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_MOD_2D_C
+ #define BN_MP_COPY_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_MUL_C
+ #define BN_MP_MUL_2_C
+ #define BN_MP_ADD_C
+ #define BN_MP_SUB_C
+ #define BN_MP_DIV_2_C
+ #define BN_MP_MUL_2D_C
+ #define BN_MP_MUL_D_C
+ #define BN_MP_DIV_3_C
+ #define BN_MP_LSHD_C
+ #define BN_MP_CLEAR_MULTI_C
+ #endif
+
+ #if defined(BN_MP_TOOM_SQR_C)
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_MOD_2D_C
+ #define BN_MP_COPY_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_SQR_C
+ #define BN_MP_MUL_2_C
+ #define BN_MP_ADD_C
+ #define BN_MP_SUB_C
+ #define BN_MP_DIV_2_C
+ #define BN_MP_MUL_2D_C
+ #define BN_MP_MUL_D_C
+ #define BN_MP_DIV_3_C
+ #define BN_MP_LSHD_C
+ #define BN_MP_CLEAR_MULTI_C
+ #endif
+
+ #if defined(BN_MP_TORADIX_C)
+ #define BN_MP_ISZERO_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_DIV_D_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_S_RMAP_C
+ #endif
+
+ #if defined(BN_MP_TORADIX_N_C)
+ #define BN_MP_ISZERO_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_DIV_D_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_S_RMAP_C
+ #endif
+
+ #if defined(BN_MP_UNSIGNED_BIN_SIZE_C)
+ #define BN_MP_COUNT_BITS_C
+ #endif
+
+ #if defined(BN_MP_XOR_C)
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_ZERO_C)
+ #endif
+
+ #if defined(BN_PRIME_TAB_C)
+ #endif
+
+ #if defined(BN_REVERSE_C)
+ #endif
+
+ #if defined(BN_S_MP_ADD_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_S_MP_EXPTMOD_C)
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_INIT_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_REDUCE_SETUP_C
+ #define BN_MP_REDUCE_C
+ #define BN_MP_REDUCE_2K_SETUP_L_C
+ #define BN_MP_REDUCE_2K_L_C
+ #define BN_MP_MOD_C
+ #define BN_MP_COPY_C
+ #define BN_MP_SQR_C
+ #define BN_MP_MUL_C
+ #define BN_MP_SET_C
+ #define BN_MP_EXCH_C
+ #endif
+
+ #if defined(BN_S_MP_MUL_DIGS_C)
+ #define BN_FAST_S_MP_MUL_DIGS_C
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_S_MP_MUL_HIGH_DIGS_C)
+ #define BN_FAST_S_MP_MUL_HIGH_DIGS_C
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_S_MP_SQR_C)
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_S_MP_SUB_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BNCORE_C)
+ #endif
+
+ #ifdef LTM3
+ #define LTM_LAST
+ #endif
+/* super class file for PK algos */
+
+/* default ... include all MPI */
+#define LTM_ALL
+
+/* RSA only (does not support DH/DSA/ECC) */
+/* #define SC_RSA_1 */
+
+/* For reference.... On an Athlon64 optimizing for speed...
+
+ LTM's mpi.o with all functions [striped] is 142KiB in size.
+
+ */
+
+/* Works for RSA only, mpi.o is 68KiB */
+#ifdef SC_RSA_1
+ #define BN_MP_SHRINK_C
+ #define BN_MP_LCM_C
+ #define BN_MP_PRIME_RANDOM_EX_C
+ #define BN_MP_INVMOD_C
+ #define BN_MP_GCD_C
+ #define BN_MP_MOD_C
+ #define BN_MP_MULMOD_C
+ #define BN_MP_ADDMOD_C
+ #define BN_MP_EXPTMOD_C
+ #define BN_MP_SET_INT_C
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_CLEAR_MULTI_C
+ #define BN_MP_UNSIGNED_BIN_SIZE_C
+ #define BN_MP_TO_UNSIGNED_BIN_C
+ #define BN_MP_MOD_D_C
+ #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C
+ #define BN_REVERSE_C
+ #define BN_PRIME_TAB_C
+
+/* other modifiers */
+ #define BN_MP_DIV_SMALL /* Slower division, not critical */
+
+/* here we are on the last pass so we turn things off. The functions classes are still there
+ * but we remove them specifically from the build. This also invokes tweaks in functions
+ * like removing support for even moduli, etc...
+ */
+ #ifdef LTM_LAST
+ #undef BN_MP_TOOM_MUL_C
+ #undef BN_MP_TOOM_SQR_C
+ #undef BN_MP_KARATSUBA_MUL_C
+ #undef BN_MP_KARATSUBA_SQR_C
+ #undef BN_MP_REDUCE_C
+ #undef BN_MP_REDUCE_SETUP_C
+ #undef BN_MP_DR_IS_MODULUS_C
+ #undef BN_MP_DR_SETUP_C
+ #undef BN_MP_DR_REDUCE_C
+ #undef BN_MP_REDUCE_IS_2K_C
+ #undef BN_MP_REDUCE_2K_SETUP_C
+ #undef BN_MP_REDUCE_2K_C
+ #undef BN_S_MP_EXPTMOD_C
+ #undef BN_MP_DIV_3_C
+ #undef BN_S_MP_MUL_HIGH_DIGS_C
+ #undef BN_FAST_S_MP_MUL_HIGH_DIGS_C
+ #undef BN_FAST_MP_INVMOD_C
+
+/* To safely undefine these you have to make sure your RSA key won't exceed the Comba threshold
+ * which is roughly 255 digits [7140 bits for 32-bit machines, 15300 bits for 64-bit machines]
+ * which means roughly speaking you can handle upto 2536-bit RSA keys with these defined without
+ * trouble.
+ */
+ #undef BN_S_MP_MUL_DIGS_C
+ #undef BN_S_MP_SQR_C
+ #undef BN_MP_MONTGOMERY_REDUCE_C
+ #endif
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+#if !(defined(LTM1) && defined(LTM2) && defined(LTM3))
+ #if defined(LTM2)
+ #define LTM3
+ #endif
+ #if defined(LTM1)
+ #define LTM2
+ #endif
+ #define LTM1
+
+ #if defined(LTM_ALL)
+ #define BN_ERROR_C
+ #define BN_FAST_MP_INVMOD_C
+ #define BN_FAST_MP_MONTGOMERY_REDUCE_C
+ #define BN_FAST_S_MP_MUL_DIGS_C
+ #define BN_FAST_S_MP_MUL_HIGH_DIGS_C
+ #define BN_FAST_S_MP_SQR_C
+ #define BN_MP_2EXPT_C
+ #define BN_MP_ABS_C
+ #define BN_MP_ADD_C
+ #define BN_MP_ADD_D_C
+ #define BN_MP_ADDMOD_C
+ #define BN_MP_AND_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_CLEAR_MULTI_C
+ #define BN_MP_CMP_C
+ #define BN_MP_CMP_D_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_MP_CNT_LSB_C
+ #define BN_MP_COPY_C
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_DIV_C
+ #define BN_MP_DIV_2_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_DIV_3_C
+ #define BN_MP_DIV_D_C
+ #define BN_MP_DR_IS_MODULUS_C
+ #define BN_MP_DR_REDUCE_C
+ #define BN_MP_DR_SETUP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_EXPORT_C
+ #define BN_MP_EXPT_D_C
+ #define BN_MP_EXPT_D_EX_C
+ #define BN_MP_EXPTMOD_C
+ #define BN_MP_EXPTMOD_FAST_C
+ #define BN_MP_EXTEUCLID_C
+ #define BN_MP_FREAD_C
+ #define BN_MP_FWRITE_C
+ #define BN_MP_GCD_C
+ #define BN_MP_GET_INT_C
+ #define BN_MP_GET_LONG_C
+ #define BN_MP_GET_LONG_LONG_C
+ #define BN_MP_GROW_C
+ #define BN_MP_IMPORT_C
+ #define BN_MP_INIT_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_INIT_SET_C
+ #define BN_MP_INIT_SET_INT_C
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_INVMOD_C
+ #define BN_MP_INVMOD_SLOW_C
+ #define BN_MP_IS_SQUARE_C
+ #define BN_MP_JACOBI_C
+ #define BN_MP_KARATSUBA_MUL_C
+ #define BN_MP_KARATSUBA_SQR_C
+ #define BN_MP_LCM_C
+ #define BN_MP_LSHD_C
+ #define BN_MP_MOD_C
+ #define BN_MP_MOD_2D_C
+ #define BN_MP_MOD_D_C
+ #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+ #define BN_MP_MONTGOMERY_REDUCE_C
+ #define BN_MP_MONTGOMERY_SETUP_C
+ #define BN_MP_MUL_C
+ #define BN_MP_MUL_2_C
+ #define BN_MP_MUL_2D_C
+ #define BN_MP_MUL_D_C
+ #define BN_MP_MULMOD_C
+ #define BN_MP_N_ROOT_C
+ #define BN_MP_N_ROOT_EX_C
+ #define BN_MP_NEG_C
+ #define BN_MP_OR_C
+ #define BN_MP_PRIME_FERMAT_C
+ #define BN_MP_PRIME_IS_DIVISIBLE_C
+ #define BN_MP_PRIME_IS_PRIME_C
+ #define BN_MP_PRIME_MILLER_RABIN_C
+ #define BN_MP_PRIME_NEXT_PRIME_C
+ #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C
+ #define BN_MP_PRIME_RANDOM_EX_C
+ #define BN_MP_RADIX_SIZE_C
+ #define BN_MP_RADIX_SMAP_C
+ #define BN_MP_RAND_C
+ #define BN_MP_READ_RADIX_C
+ #define BN_MP_READ_SIGNED_BIN_C
+ #define BN_MP_READ_UNSIGNED_BIN_C
+ #define BN_MP_REDUCE_C
+ #define BN_MP_REDUCE_2K_C
+ #define BN_MP_REDUCE_2K_L_C
+ #define BN_MP_REDUCE_2K_SETUP_C
+ #define BN_MP_REDUCE_2K_SETUP_L_C
+ #define BN_MP_REDUCE_IS_2K_C
+ #define BN_MP_REDUCE_IS_2K_L_C
+ #define BN_MP_REDUCE_SETUP_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_SET_C
+ #define BN_MP_SET_INT_C
+ #define BN_MP_SET_LONG_C
+ #define BN_MP_SET_LONG_LONG_C
+ #define BN_MP_SHRINK_C
+ #define BN_MP_SIGNED_BIN_SIZE_C
+ #define BN_MP_SQR_C
+ #define BN_MP_SQRMOD_C
+ #define BN_MP_SQRT_C
+ #define BN_MP_SQRTMOD_PRIME_C
+ #define BN_MP_SUB_C
+ #define BN_MP_SUB_D_C
+ #define BN_MP_SUBMOD_C
+ #define BN_MP_TO_SIGNED_BIN_C
+ #define BN_MP_TO_SIGNED_BIN_N_C
+ #define BN_MP_TO_UNSIGNED_BIN_C
+ #define BN_MP_TO_UNSIGNED_BIN_N_C
+ #define BN_MP_TOOM_MUL_C
+ #define BN_MP_TOOM_SQR_C
+ #define BN_MP_TORADIX_C
+ #define BN_MP_TORADIX_N_C
+ #define BN_MP_UNSIGNED_BIN_SIZE_C
+ #define BN_MP_XOR_C
+ #define BN_MP_ZERO_C
+ #define BN_PRIME_TAB_C
+ #define BN_REVERSE_C
+ #define BN_S_MP_ADD_C
+ #define BN_S_MP_EXPTMOD_C
+ #define BN_S_MP_MUL_DIGS_C
+ #define BN_S_MP_MUL_HIGH_DIGS_C
+ #define BN_S_MP_SQR_C
+ #define BN_S_MP_SUB_C
+ #define BNCORE_C
+ #endif
+
+ #if defined(BN_ERROR_C)
+ #define BN_MP_ERROR_TO_STRING_C
+ #endif
+
+ #if defined(BN_FAST_MP_INVMOD_C)
+ #define BN_MP_ISEVEN_C
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_COPY_C
+ #define BN_MP_MOD_C
+ #define BN_MP_SET_C
+ #define BN_MP_DIV_2_C
+ #define BN_MP_ISODD_C
+ #define BN_MP_SUB_C
+ #define BN_MP_CMP_C
+ #define BN_MP_ISZERO_C
+ #define BN_MP_CMP_D_C
+ #define BN_MP_ADD_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_MULTI_C
+ #endif
+
+ #if defined(BN_FAST_MP_MONTGOMERY_REDUCE_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_S_MP_SUB_C
+ #endif
+
+ #if defined(BN_FAST_S_MP_MUL_DIGS_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_FAST_S_MP_SQR_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_2EXPT_C)
+ #define BN_MP_ZERO_C
+ #define BN_MP_GROW_C
+ #endif
+
+ #if defined(BN_MP_ABS_C)
+ #define BN_MP_COPY_C
+ #endif
+
+ #if defined(BN_MP_ADD_C)
+ #define BN_S_MP_ADD_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_S_MP_SUB_C
+ #endif
+
+ #if defined(BN_MP_ADD_D_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_SUB_D_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_ADDMOD_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_ADD_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_MOD_C
+ #endif
+
+ #if defined(BN_MP_AND_C)
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_CLAMP_C)
+ #endif
+
+ #if defined(BN_MP_CLEAR_C)
+ #endif
+
+ #if defined(BN_MP_CLEAR_MULTI_C)
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_CMP_C)
+ #define BN_MP_CMP_MAG_C
+ #endif
+
+ #if defined(BN_MP_CMP_D_C)
+ #endif
+
+ #if defined(BN_MP_CMP_MAG_C)
+ #endif
+
+ #if defined(BN_MP_CNT_LSB_C)
+ #define BN_MP_ISZERO_C
+ #endif
+
+ #if defined(BN_MP_COPY_C)
+ #define BN_MP_GROW_C
+ #endif
+
+ #if defined(BN_MP_COUNT_BITS_C)
+ #endif
+
+ #if defined(BN_MP_DIV_C)
+ #define BN_MP_ISZERO_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_MP_COPY_C
+ #define BN_MP_ZERO_C
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_SET_C
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_ABS_C
+ #define BN_MP_MUL_2D_C
+ #define BN_MP_CMP_C
+ #define BN_MP_SUB_C
+ #define BN_MP_ADD_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_MULTI_C
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_INIT_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_LSHD_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_MUL_D_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_DIV_2_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_DIV_2D_C)
+ #define BN_MP_COPY_C
+ #define BN_MP_ZERO_C
+ #define BN_MP_INIT_C
+ #define BN_MP_MOD_2D_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #endif
+
+ #if defined(BN_MP_DIV_3_C)
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_DIV_D_C)
+ #define BN_MP_ISZERO_C
+ #define BN_MP_COPY_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_DIV_3_C
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_DR_IS_MODULUS_C)
+ #endif
+
+ #if defined(BN_MP_DR_REDUCE_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_S_MP_SUB_C
+ #endif
+
+ #if defined(BN_MP_DR_SETUP_C)
+ #endif
+
+ #if defined(BN_MP_EXCH_C)
+ #endif
+
+ #if defined(BN_MP_EXPORT_C)
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_EXPT_D_C)
+ #define BN_MP_EXPT_D_EX_C
+ #endif
+
+ #if defined(BN_MP_EXPT_D_EX_C)
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_SET_C
+ #define BN_MP_MUL_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_SQR_C
+ #endif
+
+ #if defined(BN_MP_EXPTMOD_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_INVMOD_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_ABS_C
+ #define BN_MP_CLEAR_MULTI_C
+ #define BN_MP_REDUCE_IS_2K_L_C
+ #define BN_S_MP_EXPTMOD_C
+ #define BN_MP_DR_IS_MODULUS_C
+ #define BN_MP_REDUCE_IS_2K_C
+ #define BN_MP_ISODD_C
+ #define BN_MP_EXPTMOD_FAST_C
+ #endif
+
+ #if defined(BN_MP_EXPTMOD_FAST_C)
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_INIT_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_MONTGOMERY_SETUP_C
+ #define BN_FAST_MP_MONTGOMERY_REDUCE_C
+ #define BN_MP_MONTGOMERY_REDUCE_C
+ #define BN_MP_DR_SETUP_C
+ #define BN_MP_DR_REDUCE_C
+ #define BN_MP_REDUCE_2K_SETUP_C
+ #define BN_MP_REDUCE_2K_C
+ #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+ #define BN_MP_MULMOD_C
+ #define BN_MP_SET_C
+ #define BN_MP_MOD_C
+ #define BN_MP_COPY_C
+ #define BN_MP_SQR_C
+ #define BN_MP_MUL_C
+ #define BN_MP_EXCH_C
+ #endif
+
+ #if defined(BN_MP_EXTEUCLID_C)
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_SET_C
+ #define BN_MP_COPY_C
+ #define BN_MP_ISZERO_C
+ #define BN_MP_DIV_C
+ #define BN_MP_MUL_C
+ #define BN_MP_SUB_C
+ #define BN_MP_NEG_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_MULTI_C
+ #endif
+
+ #if defined(BN_MP_FREAD_C)
+ #define BN_MP_ZERO_C
+ #define BN_MP_S_RMAP_C
+ #define BN_MP_MUL_D_C
+ #define BN_MP_ADD_D_C
+ #define BN_MP_CMP_D_C
+ #endif
+
+ #if defined(BN_MP_FWRITE_C)
+ #define BN_MP_RADIX_SIZE_C
+ #define BN_MP_TORADIX_C
+ #endif
+
+ #if defined(BN_MP_GCD_C)
+ #define BN_MP_ISZERO_C
+ #define BN_MP_ABS_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_CNT_LSB_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_MP_EXCH_C
+ #define BN_S_MP_SUB_C
+ #define BN_MP_MUL_2D_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_GET_INT_C)
+ #endif
+
+ #if defined(BN_MP_GET_LONG_C)
+ #endif
+
+ #if defined(BN_MP_GET_LONG_LONG_C)
+ #endif
+
+ #if defined(BN_MP_GROW_C)
+ #endif
+
+ #if defined(BN_MP_IMPORT_C)
+ #define BN_MP_ZERO_C
+ #define BN_MP_MUL_2D_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_INIT_C)
+ #endif
+
+ #if defined(BN_MP_INIT_COPY_C)
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_COPY_C
+ #endif
+
+ #if defined(BN_MP_INIT_MULTI_C)
+ #define BN_MP_ERR_C
+ #define BN_MP_INIT_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_INIT_SET_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_SET_C
+ #endif
+
+ #if defined(BN_MP_INIT_SET_INT_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_SET_INT_C
+ #endif
+
+ #if defined(BN_MP_INIT_SIZE_C)
+ #define BN_MP_INIT_C
+ #endif
+
+ #if defined(BN_MP_INVMOD_C)
+ #define BN_MP_ISZERO_C
+ #define BN_MP_ISODD_C
+ #define BN_FAST_MP_INVMOD_C
+ #define BN_MP_INVMOD_SLOW_C
+ #endif
+
+ #if defined(BN_MP_INVMOD_SLOW_C)
+ #define BN_MP_ISZERO_C
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_MOD_C
+ #define BN_MP_COPY_C
+ #define BN_MP_ISEVEN_C
+ #define BN_MP_SET_C
+ #define BN_MP_DIV_2_C
+ #define BN_MP_ISODD_C
+ #define BN_MP_ADD_C
+ #define BN_MP_SUB_C
+ #define BN_MP_CMP_C
+ #define BN_MP_CMP_D_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_MULTI_C
+ #endif
+
+ #if defined(BN_MP_IS_SQUARE_C)
+ #define BN_MP_MOD_D_C
+ #define BN_MP_INIT_SET_INT_C
+ #define BN_MP_MOD_C
+ #define BN_MP_GET_INT_C
+ #define BN_MP_SQRT_C
+ #define BN_MP_SQR_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_JACOBI_C)
+ #define BN_MP_CMP_D_C
+ #define BN_MP_ISZERO_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_CNT_LSB_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_MOD_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_KARATSUBA_MUL_C)
+ #define BN_MP_MUL_C
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_CLAMP_C
+ #define BN_S_MP_ADD_C
+ #define BN_MP_ADD_C
+ #define BN_S_MP_SUB_C
+ #define BN_MP_LSHD_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_KARATSUBA_SQR_C)
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_SQR_C
+ #define BN_S_MP_ADD_C
+ #define BN_S_MP_SUB_C
+ #define BN_MP_LSHD_C
+ #define BN_MP_ADD_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_LCM_C)
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_GCD_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_MP_DIV_C
+ #define BN_MP_MUL_C
+ #define BN_MP_CLEAR_MULTI_C
+ #endif
+
+ #if defined(BN_MP_LSHD_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_RSHD_C
+ #endif
+
+ #if defined(BN_MP_MOD_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_DIV_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_ISZERO_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_ADD_C
+ #endif
+
+ #if defined(BN_MP_MOD_2D_C)
+ #define BN_MP_ZERO_C
+ #define BN_MP_COPY_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_MOD_D_C)
+ #define BN_MP_DIV_D_C
+ #endif
+
+ #if defined(BN_MP_MONTGOMERY_CALC_NORMALIZATION_C)
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_2EXPT_C
+ #define BN_MP_SET_C
+ #define BN_MP_MUL_2_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_S_MP_SUB_C
+ #endif
+
+ #if defined(BN_MP_MONTGOMERY_REDUCE_C)
+ #define BN_FAST_MP_MONTGOMERY_REDUCE_C
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_S_MP_SUB_C
+ #endif
+
+ #if defined(BN_MP_MONTGOMERY_SETUP_C)
+ #endif
+
+ #if defined(BN_MP_MUL_C)
+ #define BN_MP_TOOM_MUL_C
+ #define BN_MP_KARATSUBA_MUL_C
+ #define BN_FAST_S_MP_MUL_DIGS_C
+ #define BN_S_MP_MUL_C
+ #define BN_S_MP_MUL_DIGS_C
+ #endif
+
+ #if defined(BN_MP_MUL_2_C)
+ #define BN_MP_GROW_C
+ #endif
+
+ #if defined(BN_MP_MUL_2D_C)
+ #define BN_MP_COPY_C
+ #define BN_MP_GROW_C
+ #define BN_MP_LSHD_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_MUL_D_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_MULMOD_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_MUL_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_MOD_C
+ #endif
+
+ #if defined(BN_MP_N_ROOT_C)
+ #define BN_MP_N_ROOT_EX_C
+ #endif
+
+ #if defined(BN_MP_N_ROOT_EX_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_SET_C
+ #define BN_MP_COPY_C
+ #define BN_MP_EXPT_D_EX_C
+ #define BN_MP_MUL_C
+ #define BN_MP_SUB_C
+ #define BN_MP_MUL_D_C
+ #define BN_MP_DIV_C
+ #define BN_MP_CMP_C
+ #define BN_MP_SUB_D_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_NEG_C)
+ #define BN_MP_COPY_C
+ #define BN_MP_ISZERO_C
+ #endif
+
+ #if defined(BN_MP_OR_C)
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_PRIME_FERMAT_C)
+ #define BN_MP_CMP_D_C
+ #define BN_MP_INIT_C
+ #define BN_MP_EXPTMOD_C
+ #define BN_MP_CMP_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_PRIME_IS_DIVISIBLE_C)
+ #define BN_MP_MOD_D_C
+ #endif
+
+ #if defined(BN_MP_PRIME_IS_PRIME_C)
+ #define BN_MP_CMP_D_C
+ #define BN_MP_PRIME_IS_DIVISIBLE_C
+ #define BN_MP_INIT_C
+ #define BN_MP_SET_C
+ #define BN_MP_PRIME_MILLER_RABIN_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_PRIME_MILLER_RABIN_C)
+ #define BN_MP_CMP_D_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_SUB_D_C
+ #define BN_MP_CNT_LSB_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_EXPTMOD_C
+ #define BN_MP_CMP_C
+ #define BN_MP_SQRMOD_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_PRIME_NEXT_PRIME_C)
+ #define BN_MP_CMP_D_C
+ #define BN_MP_SET_C
+ #define BN_MP_SUB_D_C
+ #define BN_MP_ISEVEN_C
+ #define BN_MP_MOD_D_C
+ #define BN_MP_INIT_C
+ #define BN_MP_ADD_D_C
+ #define BN_MP_PRIME_MILLER_RABIN_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_PRIME_RABIN_MILLER_TRIALS_C)
+ #endif
+
+ #if defined(BN_MP_PRIME_RANDOM_EX_C)
+ #define BN_MP_READ_UNSIGNED_BIN_C
+ #define BN_MP_PRIME_IS_PRIME_C
+ #define BN_MP_SUB_D_C
+ #define BN_MP_DIV_2_C
+ #define BN_MP_MUL_2_C
+ #define BN_MP_ADD_D_C
+ #endif
+
+ #if defined(BN_MP_RADIX_SIZE_C)
+ #define BN_MP_ISZERO_C
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_DIV_D_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_RADIX_SMAP_C)
+ #define BN_MP_S_RMAP_C
+ #endif
+
+ #if defined(BN_MP_RAND_C)
+ #define BN_MP_ZERO_C
+ #define BN_MP_ADD_D_C
+ #define BN_MP_LSHD_C
+ #endif
+
+ #if defined(BN_MP_READ_RADIX_C)
+ #define BN_MP_ZERO_C
+ #define BN_MP_S_RMAP_C
+ #define BN_MP_MUL_D_C
+ #define BN_MP_ADD_D_C
+ #define BN_MP_ISZERO_C
+ #endif
+
+ #if defined(BN_MP_READ_SIGNED_BIN_C)
+ #define BN_MP_READ_UNSIGNED_BIN_C
+ #endif
+
+ #if defined(BN_MP_READ_UNSIGNED_BIN_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_ZERO_C
+ #define BN_MP_MUL_2D_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_REDUCE_C)
+ #define BN_MP_REDUCE_SETUP_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_MUL_C
+ #define BN_S_MP_MUL_HIGH_DIGS_C
+ #define BN_FAST_S_MP_MUL_HIGH_DIGS_C
+ #define BN_MP_MOD_2D_C
+ #define BN_S_MP_MUL_DIGS_C
+ #define BN_MP_SUB_C
+ #define BN_MP_CMP_D_C
+ #define BN_MP_SET_C
+ #define BN_MP_LSHD_C
+ #define BN_MP_ADD_C
+ #define BN_MP_CMP_C
+ #define BN_S_MP_SUB_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_REDUCE_2K_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_MUL_D_C
+ #define BN_S_MP_ADD_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_S_MP_SUB_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_REDUCE_2K_L_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_MUL_C
+ #define BN_S_MP_ADD_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_S_MP_SUB_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_REDUCE_2K_SETUP_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_2EXPT_C
+ #define BN_MP_CLEAR_C
+ #define BN_S_MP_SUB_C
+ #endif
+
+ #if defined(BN_MP_REDUCE_2K_SETUP_L_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_2EXPT_C
+ #define BN_MP_COUNT_BITS_C
+ #define BN_S_MP_SUB_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_REDUCE_IS_2K_C)
+ #define BN_MP_REDUCE_2K_C
+ #define BN_MP_COUNT_BITS_C
+ #endif
+
+ #if defined(BN_MP_REDUCE_IS_2K_L_C)
+ #endif
+
+ #if defined(BN_MP_REDUCE_SETUP_C)
+ #define BN_MP_2EXPT_C
+ #define BN_MP_DIV_C
+ #endif
+
+ #if defined(BN_MP_RSHD_C)
+ #define BN_MP_ZERO_C
+ #endif
+
+ #if defined(BN_MP_SET_C)
+ #define BN_MP_ZERO_C
+ #endif
+
+ #if defined(BN_MP_SET_INT_C)
+ #define BN_MP_ZERO_C
+ #define BN_MP_MUL_2D_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_SET_LONG_C)
+ #endif
+
+ #if defined(BN_MP_SET_LONG_LONG_C)
+ #endif
+
+ #if defined(BN_MP_SHRINK_C)
+ #endif
+
+ #if defined(BN_MP_SIGNED_BIN_SIZE_C)
+ #define BN_MP_UNSIGNED_BIN_SIZE_C
+ #endif
+
+ #if defined(BN_MP_SQR_C)
+ #define BN_MP_TOOM_SQR_C
+ #define BN_MP_KARATSUBA_SQR_C
+ #define BN_FAST_S_MP_SQR_C
+ #define BN_S_MP_SQR_C
+ #endif
+
+ #if defined(BN_MP_SQRMOD_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_SQR_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_MOD_C
+ #endif
+
+ #if defined(BN_MP_SQRT_C)
+ #define BN_MP_N_ROOT_C
+ #define BN_MP_ISZERO_C
+ #define BN_MP_ZERO_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_DIV_C
+ #define BN_MP_ADD_C
+ #define BN_MP_DIV_2_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_SQRTMOD_PRIME_C)
+ #define BN_MP_CMP_D_C
+ #define BN_MP_ZERO_C
+ #define BN_MP_JACOBI_C
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_MOD_D_C
+ #define BN_MP_ADD_D_C
+ #define BN_MP_DIV_2_C
+ #define BN_MP_EXPTMOD_C
+ #define BN_MP_COPY_C
+ #define BN_MP_SUB_D_C
+ #define BN_MP_ISEVEN_C
+ #define BN_MP_SET_INT_C
+ #define BN_MP_SQRMOD_C
+ #define BN_MP_MULMOD_C
+ #define BN_MP_SET_C
+ #define BN_MP_CLEAR_MULTI_C
+ #endif
+
+ #if defined(BN_MP_SUB_C)
+ #define BN_S_MP_ADD_C
+ #define BN_MP_CMP_MAG_C
+ #define BN_S_MP_SUB_C
+ #endif
+
+ #if defined(BN_MP_SUB_D_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_ADD_D_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_MP_SUBMOD_C)
+ #define BN_MP_INIT_C
+ #define BN_MP_SUB_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_MOD_C
+ #endif
+
+ #if defined(BN_MP_TO_SIGNED_BIN_C)
+ #define BN_MP_TO_UNSIGNED_BIN_C
+ #endif
+
+ #if defined(BN_MP_TO_SIGNED_BIN_N_C)
+ #define BN_MP_SIGNED_BIN_SIZE_C
+ #define BN_MP_TO_SIGNED_BIN_C
+ #endif
+
+ #if defined(BN_MP_TO_UNSIGNED_BIN_C)
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_ISZERO_C
+ #define BN_MP_DIV_2D_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_TO_UNSIGNED_BIN_N_C)
+ #define BN_MP_UNSIGNED_BIN_SIZE_C
+ #define BN_MP_TO_UNSIGNED_BIN_C
+ #endif
+
+ #if defined(BN_MP_TOOM_MUL_C)
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_MOD_2D_C
+ #define BN_MP_COPY_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_MUL_C
+ #define BN_MP_MUL_2_C
+ #define BN_MP_ADD_C
+ #define BN_MP_SUB_C
+ #define BN_MP_DIV_2_C
+ #define BN_MP_MUL_2D_C
+ #define BN_MP_MUL_D_C
+ #define BN_MP_DIV_3_C
+ #define BN_MP_LSHD_C
+ #define BN_MP_CLEAR_MULTI_C
+ #endif
+
+ #if defined(BN_MP_TOOM_SQR_C)
+ #define BN_MP_INIT_MULTI_C
+ #define BN_MP_MOD_2D_C
+ #define BN_MP_COPY_C
+ #define BN_MP_RSHD_C
+ #define BN_MP_SQR_C
+ #define BN_MP_MUL_2_C
+ #define BN_MP_ADD_C
+ #define BN_MP_SUB_C
+ #define BN_MP_DIV_2_C
+ #define BN_MP_MUL_2D_C
+ #define BN_MP_MUL_D_C
+ #define BN_MP_DIV_3_C
+ #define BN_MP_LSHD_C
+ #define BN_MP_CLEAR_MULTI_C
+ #endif
+
+ #if defined(BN_MP_TORADIX_C)
+ #define BN_MP_ISZERO_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_DIV_D_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_S_RMAP_C
+ #endif
+
+ #if defined(BN_MP_TORADIX_N_C)
+ #define BN_MP_ISZERO_C
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_DIV_D_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_S_RMAP_C
+ #endif
+
+ #if defined(BN_MP_UNSIGNED_BIN_SIZE_C)
+ #define BN_MP_COUNT_BITS_C
+ #endif
+
+ #if defined(BN_MP_XOR_C)
+ #define BN_MP_INIT_COPY_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_MP_ZERO_C)
+ #endif
+
+ #if defined(BN_PRIME_TAB_C)
+ #endif
+
+ #if defined(BN_REVERSE_C)
+ #endif
+
+ #if defined(BN_S_MP_ADD_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BN_S_MP_EXPTMOD_C)
+ #define BN_MP_COUNT_BITS_C
+ #define BN_MP_INIT_C
+ #define BN_MP_CLEAR_C
+ #define BN_MP_REDUCE_SETUP_C
+ #define BN_MP_REDUCE_C
+ #define BN_MP_REDUCE_2K_SETUP_L_C
+ #define BN_MP_REDUCE_2K_L_C
+ #define BN_MP_MOD_C
+ #define BN_MP_COPY_C
+ #define BN_MP_SQR_C
+ #define BN_MP_MUL_C
+ #define BN_MP_SET_C
+ #define BN_MP_EXCH_C
+ #endif
+
+ #if defined(BN_S_MP_MUL_DIGS_C)
+ #define BN_FAST_S_MP_MUL_DIGS_C
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_S_MP_MUL_HIGH_DIGS_C)
+ #define BN_FAST_S_MP_MUL_HIGH_DIGS_C
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_S_MP_SQR_C)
+ #define BN_MP_INIT_SIZE_C
+ #define BN_MP_CLAMP_C
+ #define BN_MP_EXCH_C
+ #define BN_MP_CLEAR_C
+ #endif
+
+ #if defined(BN_S_MP_SUB_C)
+ #define BN_MP_GROW_C
+ #define BN_MP_CLAMP_C
+ #endif
+
+ #if defined(BNCORE_C)
+ #endif
+
+ #ifdef LTM3
+ #define LTM_LAST
+ #endif
+#else
+ #define LTM_LAST
+#endif
+#else
+ #define LTM_LAST
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* detect 64-bit mode if possible */
+#if defined(__x86_64__)
+ #if !(defined(MP_32BIT) || defined(MP_16BIT) || defined(MP_8BIT))
+ #define MP_64BIT
+ #endif
+#endif
+
+/* some default configurations.
+ *
+ * A "mp_digit" must be able to hold DIGIT_BIT + 1 bits
+ * A "mp_word" must be able to hold 2*DIGIT_BIT + 1 bits
+ *
+ * At the very least a mp_digit must be able to hold 7 bits
+ * [any size beyond that is ok provided it doesn't overflow the data type]
+ */
+#ifdef MP_8BIT
+typedef uint8_t mp_digit;
+typedef uint16_t mp_word;
+ #define MP_SIZEOF_MP_DIGIT 1
+ #ifdef DIGIT_BIT
+ #error You must not define DIGIT_BIT when using MP_8BIT
+ #endif
+#elif defined(MP_16BIT)
+typedef uint16_t mp_digit;
+typedef uint32_t mp_word;
+ #define MP_SIZEOF_MP_DIGIT 2
+ #ifdef DIGIT_BIT
+ #error You must not define DIGIT_BIT when using MP_16BIT
+ #endif
+#elif defined(MP_64BIT)
+/* for GCC only on supported platforms */
+ #ifndef CRYPT
+typedef unsigned long long ulong64;
+typedef signed long long long64;
+ #endif
+
+typedef uint64_t mp_digit;
+ #if defined(_WIN32)
+typedef unsigned __int128 mp_word;
+ #elif defined(__GNUC__)
+typedef unsigned long mp_word __attribute__ ((mode(TI)));
+ #else
+
+/* it seems you have a problem
+ * but we assume you can somewhere define your own uint128_t */
+typedef uint128_t mp_word;
+ #endif
+
+ #define DIGIT_BIT 60
+#else
+/* this is the default case, 28-bit digits */
+
+/* this is to make porting into LibTomCrypt easier :-) */
+ #ifndef CRYPT
+typedef unsigned long long ulong64;
+typedef signed long long long64;
+ #endif
+
+typedef uint32_t mp_digit;
+typedef uint64_t mp_word;
+
+ #ifdef MP_31BIT
+/* this is an extension that uses 31-bit digits */
+ #define DIGIT_BIT 31
+ #else
+/* default case is 28-bit digits, defines MP_28BIT as a handy macro to test */
+ #define DIGIT_BIT 28
+ #define MP_28BIT
+ #endif
+#endif
+
+/* otherwise the bits per digit is calculated automatically from the size of a mp_digit */
+#ifndef DIGIT_BIT
+ #define DIGIT_BIT (((CHAR_BIT * MP_SIZEOF_MP_DIGIT) - 1)) /* bits per digit */
+typedef uint_least32_t mp_min_u32;
+#else
+typedef mp_digit mp_min_u32;
+#endif
+
+/* platforms that can use a better rand function */
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
+ #define MP_USE_ALT_RAND 1
+#endif
+
+/* use arc4random on platforms that support it */
+#ifdef MP_USE_ALT_RAND
+ #define MP_GEN_RANDOM() arc4random()
+#else
+ #define MP_GEN_RANDOM() rand()
+#endif
+
+#define MP_DIGIT_BIT DIGIT_BIT
+#define MP_MASK ((((mp_digit)1) << ((mp_digit)DIGIT_BIT)) - ((mp_digit)1))
+#define MP_DIGIT_MAX MP_MASK
+
+/* equalities */
+#define MP_LT -1 /* less than */
+#define MP_EQ 0 /* equal to */
+#define MP_GT 1 /* greater than */
+
+#define MP_ZPOS 0 /* positive integer */
+#define MP_NEG 1 /* negative */
+
+#define MP_OKAY 0 /* ok result */
+#define MP_MEM -2 /* out of mem */
+#define MP_VAL -3 /* invalid input */
+#define MP_RANGE MP_VAL
+
+#define MP_YES 1 /* yes response */
+#define MP_NO 0 /* no response */
+
+/* Primality generation flags */
+#define LTM_PRIME_BBS 0x0001 /* BBS style prime */
+#define LTM_PRIME_SAFE 0x0002 /* Safe prime (p-1)/2 == prime */
+#define LTM_PRIME_2MSB_ON 0x0008 /* force 2nd MSB to 1 */
+
+typedef int mp_err;
+
+/* you'll have to tune these... */
+extern int KARATSUBA_MUL_CUTOFF,
+ KARATSUBA_SQR_CUTOFF,
+ TOOM_MUL_CUTOFF,
+ TOOM_SQR_CUTOFF;
+
+/* define this to use lower memory usage routines (exptmods mostly) */
+/* #define MP_LOW_MEM */
+
+/* default precision */
+#ifndef MP_PREC
+ #ifndef MP_LOW_MEM
+ #define MP_PREC 32 /* default digits of precision */
+ #else
+ #define MP_PREC 8 /* default digits of precision */
+ #endif
+#endif
+
+/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */
+#define MP_WARRAY (1 << (((sizeof(mp_word) * CHAR_BIT) - (2 * DIGIT_BIT)) + 1))
+
+/* the infamous mp_int structure */
+typedef struct {
+ int used, alloc, sign;
+ mp_digit *dp;
+} mp_int;
+
+/* callback for mp_prime_random, should fill dst with random bytes and return how many read [upto len] */
+typedef int ltm_prime_callback (unsigned char *dst, int len, void *dat);
+
+
+#define USED(m) ((m)->used)
+#define DIGIT(m, k) ((m)->dp[(k)])
+#define SIGN(m) ((m)->sign)
+
+/* error code to char* string */
+const char *mp_error_to_string(int code);
+
+/* ---> init and deinit bignum functions <--- */
+/* init a bignum */
+int mp_init(mp_int *a);
+
+/* free a bignum */
+void mp_clear(mp_int *a);
+
+/* init a null terminated series of arguments */
+int mp_init_multi(mp_int *mp, ...);
+
+/* clear a null terminated series of arguments */
+void mp_clear_multi(mp_int *mp, ...);
+
+/* exchange two ints */
+void mp_exch(mp_int *a, mp_int *b);
+
+/* shrink ram required for a bignum */
+int mp_shrink(mp_int *a);
+
+/* grow an int to a given size */
+int mp_grow(mp_int *a, int size);
+
+/* init to a given number of digits */
+int mp_init_size(mp_int *a, int size);
+
+/* ---> Basic Manipulations <--- */
+#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO)
+#define mp_iseven(a) ((((a)->used > 0) && (((a)->dp[0] & 1u) == 0u)) ? MP_YES : MP_NO)
+#define mp_isodd(a) ((((a)->used > 0) && (((a)->dp[0] & 1u) == 1u)) ? MP_YES : MP_NO)
+#define mp_isneg(a) (((a)->sign != MP_ZPOS) ? MP_YES : MP_NO)
+
+/* set to zero */
+void mp_zero(mp_int *a);
+
+/* set to a digit */
+void mp_set(mp_int *a, mp_digit b);
+
+/* set a 32-bit const */
+int mp_set_int(mp_int *a, unsigned long b);
+
+/* set a platform dependent unsigned long value */
+int mp_set_long(mp_int *a, unsigned long b);
+
+/* set a platform dependent unsigned long long value */
+int mp_set_long_long(mp_int *a, unsigned long long b);
+
+/* get a 32-bit value */
+unsigned long mp_get_int(mp_int *a);
+
+/* get a platform dependent unsigned long value */
+unsigned long mp_get_long(mp_int *a);
+
+/* get a platform dependent unsigned long long value */
+unsigned long long mp_get_long_long(mp_int *a);
+
+/* initialize and set a digit */
+int mp_init_set(mp_int *a, mp_digit b);
+
+/* initialize and set 32-bit value */
+int mp_init_set_int(mp_int *a, unsigned long b);
+
+/* copy, b = a */
+int mp_copy(mp_int *a, mp_int *b);
+
+/* inits and copies, a = b */
+int mp_init_copy(mp_int *a, mp_int *b);
+
+/* trim unused digits */
+void mp_clamp(mp_int *a);
+
+/* import binary data */
+int mp_import(mp_int *rop, size_t count, int order, size_t size, int endian, size_t nails, const void *op);
+
+/* export binary data */
+int mp_export(void *rop, size_t *countp, int order, size_t size, int endian, size_t nails, mp_int *op);
+
+/* ---> digit manipulation <--- */
+
+/* right shift by "b" digits */
+void mp_rshd(mp_int *a, int b);
+
+/* left shift by "b" digits */
+int mp_lshd(mp_int *a, int b);
+
+/* c = a / 2**b, implemented as c = a >> b */
+int mp_div_2d(mp_int *a, int b, mp_int *c, mp_int *d);
+
+/* b = a/2 */
+int mp_div_2(mp_int *a, mp_int *b);
+
+/* c = a * 2**b, implemented as c = a << b */
+int mp_mul_2d(mp_int *a, int b, mp_int *c);
+
+/* b = a*2 */
+int mp_mul_2(mp_int *a, mp_int *b);
+
+/* c = a mod 2**b */
+int mp_mod_2d(mp_int *a, int b, mp_int *c);
+
+/* computes a = 2**b */
+int mp_2expt(mp_int *a, int b);
+
+/* Counts the number of lsbs which are zero before the first zero bit */
+int mp_cnt_lsb(mp_int *a);
+
+/* I Love Earth! */
+
+/* makes a pseudo-random int of a given size */
+int mp_rand(mp_int *a, int digits);
+
+/* ---> binary operations <--- */
+/* c = a XOR b */
+int mp_xor(mp_int *a, mp_int *b, mp_int *c);
+
+/* c = a OR b */
+int mp_or(mp_int *a, mp_int *b, mp_int *c);
+
+/* c = a AND b */
+int mp_and(mp_int *a, mp_int *b, mp_int *c);
+
+/* ---> Basic arithmetic <--- */
+
+/* b = -a */
+int mp_neg(mp_int *a, mp_int *b);
+
+/* b = |a| */
+int mp_abs(mp_int *a, mp_int *b);
+
+/* compare a to b */
+int mp_cmp(mp_int *a, mp_int *b);
+
+/* compare |a| to |b| */
+int mp_cmp_mag(mp_int *a, mp_int *b);
+
+/* c = a + b */
+int mp_add(mp_int *a, mp_int *b, mp_int *c);
+
+/* c = a - b */
+int mp_sub(mp_int *a, mp_int *b, mp_int *c);
+
+/* c = a * b */
+int mp_mul(mp_int *a, mp_int *b, mp_int *c);
+
+/* b = a*a */
+int mp_sqr(mp_int *a, mp_int *b);
+
+/* a/b => cb + d == a */
+int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d);
+
+/* c = a mod b, 0 <= c < b */
+int mp_mod(mp_int *a, mp_int *b, mp_int *c);
+
+/* ---> single digit functions <--- */
+
+/* compare against a single digit */
+int mp_cmp_d(mp_int *a, mp_digit b);
+
+/* c = a + b */
+int mp_add_d(mp_int *a, mp_digit b, mp_int *c);
+
+/* c = a - b */
+int mp_sub_d(mp_int *a, mp_digit b, mp_int *c);
+
+/* c = a * b */
+int mp_mul_d(mp_int *a, mp_digit b, mp_int *c);
+
+/* a/b => cb + d == a */
+int mp_div_d(mp_int *a, mp_digit b, mp_int *c, mp_digit *d);
+
+/* a/3 => 3c + d == a */
+int mp_div_3(mp_int *a, mp_int *c, mp_digit *d);
+
+/* c = a**b */
+int mp_expt_d(mp_int *a, mp_digit b, mp_int *c);
+int mp_expt_d_ex(mp_int *a, mp_digit b, mp_int *c, int fast);
+
+/* c = a mod b, 0 <= c < b */
+int mp_mod_d(mp_int *a, mp_digit b, mp_digit *c);
+
+/* ---> number theory <--- */
+
+/* d = a + b (mod c) */
+int mp_addmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d);
+
+/* d = a - b (mod c) */
+int mp_submod(mp_int *a, mp_int *b, mp_int *c, mp_int *d);
+
+/* d = a * b (mod c) */
+int mp_mulmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d);
+
+/* c = a * a (mod b) */
+int mp_sqrmod(mp_int *a, mp_int *b, mp_int *c);
+
+/* c = 1/a (mod b) */
+int mp_invmod(mp_int *a, mp_int *b, mp_int *c);
+
+/* c = (a, b) */
+int mp_gcd(mp_int *a, mp_int *b, mp_int *c);
+
+/* produces value such that U1*a + U2*b = U3 */
+int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3);
+
+/* c = [a, b] or (a*b)/(a, b) */
+int mp_lcm(mp_int *a, mp_int *b, mp_int *c);
+
+/* finds one of the b'th root of a, such that |c|**b <= |a|
+ *
+ * returns error if a < 0 and b is even
+ */
+int mp_n_root(mp_int *a, mp_digit b, mp_int *c);
+int mp_n_root_ex(mp_int *a, mp_digit b, mp_int *c, int fast);
+
+/* special sqrt algo */
+int mp_sqrt(mp_int *arg, mp_int *ret);
+
+/* special sqrt (mod prime) */
+int mp_sqrtmod_prime(mp_int *arg, mp_int *prime, mp_int *ret);
+
+/* is number a square? */
+int mp_is_square(mp_int *arg, int *ret);
+
+/* computes the jacobi c = (a | n) (or Legendre if b is prime) */
+int mp_jacobi(mp_int *a, mp_int *n, int *c);
+
+/* used to setup the Barrett reduction for a given modulus b */
+int mp_reduce_setup(mp_int *a, mp_int *b);
+
+/* Barrett Reduction, computes a (mod b) with a precomputed value c
+ *
+ * Assumes that 0 < a <= b*b, note if 0 > a > -(b*b) then you can merely
+ * compute the reduction as -1 * mp_reduce(mp_abs(a)) [pseudo code].
+ */
+int mp_reduce(mp_int *a, mp_int *b, mp_int *c);
+
+/* setups the montgomery reduction */
+int mp_montgomery_setup(mp_int *a, mp_digit *mp);
+
+/* computes a = B**n mod b without division or multiplication useful for
+ * normalizing numbers in a Montgomery system.
+ */
+int mp_montgomery_calc_normalization(mp_int *a, mp_int *b);
+
+/* computes x/R == x (mod N) via Montgomery Reduction */
+int mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp);
+
+/* returns 1 if a is a valid DR modulus */
+int mp_dr_is_modulus(mp_int *a);
+
+/* sets the value of "d" required for mp_dr_reduce */
+void mp_dr_setup(mp_int *a, mp_digit *d);
+
+/* reduces a modulo b using the Diminished Radix method */
+int mp_dr_reduce(mp_int *a, mp_int *b, mp_digit mp);
+
+/* returns true if a can be reduced with mp_reduce_2k */
+int mp_reduce_is_2k(mp_int *a);
+
+/* determines k value for 2k reduction */
+int mp_reduce_2k_setup(mp_int *a, mp_digit *d);
+
+/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */
+int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d);
+
+/* returns true if a can be reduced with mp_reduce_2k_l */
+int mp_reduce_is_2k_l(mp_int *a);
+
+/* determines k value for 2k reduction */
+int mp_reduce_2k_setup_l(mp_int *a, mp_int *d);
+
+/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */
+int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d);
+
+/* d = a**b (mod c) */
+int mp_exptmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d);
+
+/* ---> Primes <--- */
+
+/* number of primes */
+#ifdef MP_8BIT
+ #define PRIME_SIZE 31
+#else
+ #define PRIME_SIZE 256
+#endif
+
+/* table of first PRIME_SIZE primes */
+extern const mp_digit ltm_prime_tab[PRIME_SIZE];
+
+/* result=1 if a is divisible by one of the first PRIME_SIZE primes */
+int mp_prime_is_divisible(mp_int *a, int *result);
+
+/* performs one Fermat test of "a" using base "b".
+ * Sets result to 0 if composite or 1 if probable prime
+ */
+int mp_prime_fermat(mp_int *a, mp_int *b, int *result);
+
+/* performs one Miller-Rabin test of "a" using base "b".
+ * Sets result to 0 if composite or 1 if probable prime
+ */
+int mp_prime_miller_rabin(mp_int *a, mp_int *b, int *result);
+
+/* This gives [for a given bit size] the number of trials required
+ * such that Miller-Rabin gives a prob of failure lower than 2^-96
+ */
+int mp_prime_rabin_miller_trials(int size);
+
+/* performs t rounds of Miller-Rabin on "a" using the first
+ * t prime bases. Also performs an initial sieve of trial
+ * division. Determines if "a" is prime with probability
+ * of error no more than (1/4)**t.
+ *
+ * Sets result to 1 if probably prime, 0 otherwise
+ */
+int mp_prime_is_prime(mp_int *a, int t, int *result);
+
+/* finds the next prime after the number "a" using "t" trials
+ * of Miller-Rabin.
+ *
+ * bbs_style = 1 means the prime must be congruent to 3 mod 4
+ */
+int mp_prime_next_prime(mp_int *a, int t, int bbs_style);
+
+/* makes a truly random prime of a given size (bytes),
+ * call with bbs = 1 if you want it to be congruent to 3 mod 4
+ *
+ * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can
+ * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself
+ * so it can be NULL
+ *
+ * The prime generated will be larger than 2^(8*size).
+ */
+#define mp_prime_random(a, t, size, bbs, cb, dat) mp_prime_random_ex(a, t, ((size) * 8) + 1, (bbs == 1) ? LTM_PRIME_BBS : 0, cb, dat)
+
+/* makes a truly random prime of a given size (bits),
+ *
+ * Flags are as follows:
+ *
+ * LTM_PRIME_BBS - make prime congruent to 3 mod 4
+ * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS)
+ * LTM_PRIME_2MSB_ON - make the 2nd highest bit one
+ *
+ * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can
+ * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself
+ * so it can be NULL
+ *
+ */
+int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat);
+
+/* ---> radix conversion <--- */
+int mp_count_bits(mp_int *a);
+
+int mp_unsigned_bin_size(mp_int *a);
+int mp_read_unsigned_bin(mp_int *a, const unsigned char *b, int c);
+int mp_to_unsigned_bin(mp_int *a, unsigned char *b);
+int mp_to_unsigned_bin_n(mp_int *a, unsigned char *b, unsigned long *outlen);
+
+int mp_signed_bin_size(mp_int *a);
+int mp_read_signed_bin(mp_int *a, const unsigned char *b, int c);
+int mp_to_signed_bin(mp_int *a, unsigned char *b);
+int mp_to_signed_bin_n(mp_int *a, unsigned char *b, unsigned long *outlen);
+
+int mp_read_radix(mp_int *a, const char *str, int radix);
+int mp_toradix(mp_int *a, char *str, int radix);
+int mp_toradix_n(mp_int *a, char *str, int radix, int maxlen);
+int mp_radix_size(mp_int *a, int radix, int *size);
+
+#ifndef LTM_NO_FILE
+int mp_fread(mp_int *a, int radix, FILE *stream);
+int mp_fwrite(mp_int *a, int radix, FILE *stream);
+#endif
+
+#define mp_read_raw(mp, str, len) mp_read_signed_bin((mp), (str), (len))
+#define mp_raw_size(mp) mp_signed_bin_size(mp)
+#define mp_toraw(mp, str) mp_to_signed_bin((mp), (str))
+#define mp_read_mag(mp, str, len) mp_read_unsigned_bin((mp), (str), (len))
+#define mp_mag_size(mp) mp_unsigned_bin_size(mp)
+#define mp_tomag(mp, str) mp_to_unsigned_bin((mp), (str))
+
+#define mp_tobinary(M, S) mp_toradix((M), (S), 2)
+#define mp_tooctal(M, S) mp_toradix((M), (S), 8)
+#define mp_todecimal(M, S) mp_toradix((M), (S), 10)
+#define mp_tohex(M, S) mp_toradix((M), (S), 16)
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://math.libtomcrypt.com
+ */
+#ifndef TOMMATH_PRIV_H_
+#define TOMMATH_PRIV_H_
+
+#include
+
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+
+#define MAX(x, y) (((x) > (y)) ? (x) : (y))
+
+#ifdef __cplusplus
+extern "C" {
+/* C++ compilers don't like assigning void * to mp_digit * */
+ #define OPT_CAST(x) (x *)
+
+#else
+
+/* C on the other hand doesn't care */
+ #define OPT_CAST(x)
+#endif
+
+/* define heap macros */
+#ifndef XMALLOC
+/* default to libc stuff */
+ #define XMALLOC malloc
+ #define XFREE free
+ #define XREALLOC realloc
+ #define XCALLOC calloc
+#else
+/* prototypes for our heap functions */
+extern void *XMALLOC(size_t n);
+extern void *XREALLOC(void *p, size_t n);
+extern void *XCALLOC(size_t n, size_t s);
+extern void XFREE(void *p);
+#endif
+
+/* lowlevel functions, do not call! */
+int s_mp_add(mp_int *a, mp_int *b, mp_int *c);
+int s_mp_sub(mp_int *a, mp_int *b, mp_int *c);
+
+#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1)
+int fast_s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs);
+int s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs);
+int fast_s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs);
+int s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs);
+int fast_s_mp_sqr(mp_int *a, mp_int *b);
+int s_mp_sqr(mp_int *a, mp_int *b);
+int mp_karatsuba_mul(mp_int *a, mp_int *b, mp_int *c);
+int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c);
+int mp_karatsuba_sqr(mp_int *a, mp_int *b);
+int mp_toom_sqr(mp_int *a, mp_int *b);
+int fast_mp_invmod(mp_int *a, mp_int *b, mp_int *c);
+int mp_invmod_slow(mp_int *a, mp_int *b, mp_int *c);
+int fast_mp_montgomery_reduce(mp_int *x, mp_int *n, mp_digit rho);
+int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode);
+int s_mp_exptmod(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode);
+void bn_reverse(unsigned char *s, int len);
+
+extern const char *mp_s_rmap;
+
+/* Fancy macro to set an MPI from another type.
+ * There are several things assumed:
+ * x is the counter and unsigned
+ * a is the pointer to the MPI
+ * b is the original value that should be set in the MPI.
+ */
+#define MP_SET_XLONG(func_name, type) \
+ int func_name(mp_int * a, type b) \
+ { \
+ unsigned int x; \
+ int res; \
+ \
+ mp_zero(a); \
+ \
+ /* set four bits at a time */ \
+ for (x = 0; x < (sizeof(type) * 2u); x++) { \
+ /* shift the number up four bits */ \
+ if ((res = mp_mul_2d(a, 4, a)) != MP_OKAY) { \
+ return res; \
+ } \
+ \
+ /* OR in the top four bits of the source */ \
+ a->dp[0] |= (b >> ((sizeof(type) * 8u) - 4u)) & 15u; \
+ \
+ /* shift the source up to the next four bits */ \
+ b <<= 4; \
+ \
+ /* ensure that digits are not clamped off */ \
+ a->used += 1; \
+ } \
+ mp_clamp(a); \
+ return MP_OKAY; \
+ }
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+#define BN_FAST_MP_INVMOD_C
+#ifdef BN_FAST_MP_INVMOD_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* computes the modular inverse via binary extended euclidean algorithm,
+ * that is c = 1/a mod b
+ *
+ * Based on slow invmod except this is optimized for the case where b is
+ * odd as per HAC Note 14.64 on pp. 610
+ */
+int fast_mp_invmod(mp_int *a, mp_int *b, mp_int *c) {
+ mp_int x, y, u, v, B, D;
+ int res, neg;
+
+ /* 2. [modified] b must be odd */
+ if (mp_iseven(b) == MP_YES) {
+ return MP_VAL;
+ }
+
+ /* init all our temps */
+ if ((res = mp_init_multi(&x, &y, &u, &v, &B, &D, NULL)) != MP_OKAY) {
+ return res;
+ }
+
+ /* x == modulus, y == value to invert */
+ if ((res = mp_copy(b, &x)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+
+ /* we need y = |a| */
+ if ((res = mp_mod(a, b, &y)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+
+ /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */
+ if ((res = mp_copy(&x, &u)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if ((res = mp_copy(&y, &v)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ mp_set(&D, 1);
+
+top:
+ /* 4. while u is even do */
+ while (mp_iseven(&u) == MP_YES) {
+ /* 4.1 u = u/2 */
+ if ((res = mp_div_2(&u, &u)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ /* 4.2 if B is odd then */
+ if (mp_isodd(&B) == MP_YES) {
+ if ((res = mp_sub(&B, &x, &B)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+ /* B = B/2 */
+ if ((res = mp_div_2(&B, &B)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* 5. while v is even do */
+ while (mp_iseven(&v) == MP_YES) {
+ /* 5.1 v = v/2 */
+ if ((res = mp_div_2(&v, &v)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ /* 5.2 if D is odd then */
+ if (mp_isodd(&D) == MP_YES) {
+ /* D = (D-x)/2 */
+ if ((res = mp_sub(&D, &x, &D)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+ /* D = D/2 */
+ if ((res = mp_div_2(&D, &D)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* 6. if u >= v then */
+ if (mp_cmp(&u, &v) != MP_LT) {
+ /* u = u - v, B = B - D */
+ if ((res = mp_sub(&u, &v, &u)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+
+ if ((res = mp_sub(&B, &D, &B)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ } else {
+ /* v - v - u, D = D - B */
+ if ((res = mp_sub(&v, &u, &v)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+
+ if ((res = mp_sub(&D, &B, &D)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* if not zero goto step 4 */
+ if (mp_iszero(&u) == MP_NO) {
+ goto top;
+ }
+
+ /* now a = C, b = D, gcd == g*v */
+
+ /* if v != 1 then there is no inverse */
+ if (mp_cmp_d(&v, 1) != MP_EQ) {
+ res = MP_VAL;
+ goto LBL_ERR;
+ }
+
+ /* b is now the inverse */
+ neg = a->sign;
+ while (D.sign == MP_NEG) {
+ if ((res = mp_add(&D, b, &D)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+ mp_exch(&D, c);
+ c->sign = neg;
+ res = MP_OKAY;
+
+LBL_ERR: mp_clear_multi(&x, &y, &u, &v, &B, &D, NULL);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* computes xR**-1 == x (mod N) via Montgomery Reduction
+ *
+ * This is an optimized implementation of montgomery_reduce
+ * which uses the comba method to quickly calculate the columns of the
+ * reduction.
+ *
+ * Based on Algorithm 14.32 on pp.601 of HAC.
+ */
+int fast_mp_montgomery_reduce(mp_int *x, mp_int *n, mp_digit rho) {
+ int ix, res, olduse;
+ mp_word W[MP_WARRAY];
+
+ /* get old used count */
+ olduse = x->used;
+
+ /* grow a as required */
+ if (x->alloc < (n->used + 1)) {
+ if ((res = mp_grow(x, n->used + 1)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* first we have to get the digits of the input into
+ * an array of double precision words W[...]
+ */
+ {
+ mp_word *_W;
+ mp_digit *tmpx;
+
+ /* alias for the W[] array */
+ _W = W;
+
+ /* alias for the digits of x*/
+ tmpx = x->dp;
+
+ /* copy the digits of a into W[0..a->used-1] */
+ for (ix = 0; ix < x->used; ix++) {
+ *_W++ = *tmpx++;
+ }
+
+ /* zero the high words of W[a->used..m->used*2] */
+ for ( ; ix < ((n->used * 2) + 1); ix++) {
+ *_W++ = 0;
+ }
+ }
+
+ /* now we proceed to zero successive digits
+ * from the least significant upwards
+ */
+ for (ix = 0; ix < n->used; ix++) {
+ /* mu = ai * m' mod b
+ *
+ * We avoid a double precision multiplication (which isn't required)
+ * by casting the value down to a mp_digit. Note this requires
+ * that W[ix-1] have the carry cleared (see after the inner loop)
+ */
+ mp_digit mu;
+ mu = (mp_digit)(((W[ix] & MP_MASK) * rho) & MP_MASK);
+
+ /* a = a + mu * m * b**i
+ *
+ * This is computed in place and on the fly. The multiplication
+ * by b**i is handled by offseting which columns the results
+ * are added to.
+ *
+ * Note the comba method normally doesn't handle carries in the
+ * inner loop In this case we fix the carry from the previous
+ * column since the Montgomery reduction requires digits of the
+ * result (so far) [see above] to work. This is
+ * handled by fixing up one carry after the inner loop. The
+ * carry fixups are done in order so after these loops the
+ * first m->used words of W[] have the carries fixed
+ */
+ {
+ int iy;
+ mp_digit *tmpn;
+ mp_word *_W;
+
+ /* alias for the digits of the modulus */
+ tmpn = n->dp;
+
+ /* Alias for the columns set by an offset of ix */
+ _W = W + ix;
+
+ /* inner loop */
+ for (iy = 0; iy < n->used; iy++) {
+ *_W++ += ((mp_word)mu) * ((mp_word) * tmpn++);
+ }
+ }
+
+ /* now fix carry for next digit, W[ix+1] */
+ W[ix + 1] += W[ix] >> ((mp_word)DIGIT_BIT);
+ }
+
+ /* now we have to propagate the carries and
+ * shift the words downward [all those least
+ * significant digits we zeroed].
+ */
+ {
+ mp_digit *tmpx;
+ mp_word *_W, *_W1;
+
+ /* nox fix rest of carries */
+
+ /* alias for current word */
+ _W1 = W + ix;
+
+ /* alias for next word, where the carry goes */
+ _W = W + ++ix;
+
+ for ( ; ix <= ((n->used * 2) + 1); ix++) {
+ *_W++ += *_W1++ >> ((mp_word)DIGIT_BIT);
+ }
+
+ /* copy out, A = A/b**n
+ *
+ * The result is A/b**n but instead of converting from an
+ * array of mp_word to mp_digit than calling mp_rshd
+ * we just copy them in the right order
+ */
+
+ /* alias for destination word */
+ tmpx = x->dp;
+
+ /* alias for shifted double precision result */
+ _W = W + n->used;
+
+ for (ix = 0; ix < (n->used + 1); ix++) {
+ *tmpx++ = (mp_digit)(*_W++ & ((mp_word)MP_MASK));
+ }
+
+ /* zero oldused digits, if the input a was larger than
+ * m->used+1 we'll have to clear the digits
+ */
+ for ( ; ix < olduse; ix++) {
+ *tmpx++ = 0;
+ }
+ }
+
+ /* set the max used and clamp */
+ x->used = n->used + 1;
+ mp_clamp(x);
+
+ /* if A >= m then A = A - m */
+ if (mp_cmp_mag(x, n) != MP_LT) {
+ return s_mp_sub(x, n, x);
+ }
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_FAST_S_MP_MUL_DIGS_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* Fast (comba) multiplier
+ *
+ * This is the fast column-array [comba] multiplier. It is
+ * designed to compute the columns of the product first
+ * then handle the carries afterwards. This has the effect
+ * of making the nested loops that compute the columns very
+ * simple and schedulable on super-scalar processors.
+ *
+ * This has been modified to produce a variable number of
+ * digits of output so if say only a half-product is required
+ * you don't have to compute the upper half (a feature
+ * required for fast Barrett reduction).
+ *
+ * Based on Algorithm 14.12 on pp.595 of HAC.
+ *
+ */
+int fast_s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs) {
+ int olduse, res, pa, ix, iz;
+ mp_digit W[MP_WARRAY];
+ mp_word _W;
+
+ /* grow the destination as required */
+ if (c->alloc < digs) {
+ if ((res = mp_grow(c, digs)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* number of output digits to produce */
+ pa = MIN(digs, a->used + b->used);
+
+ /* clear the carry */
+ _W = 0;
+ for (ix = 0; ix < pa; ix++) {
+ int tx, ty;
+ int iy;
+ mp_digit *tmpx, *tmpy;
+
+ /* get offsets into the two bignums */
+ ty = MIN(b->used - 1, ix);
+ tx = ix - ty;
+
+ /* setup temp aliases */
+ tmpx = a->dp + tx;
+ tmpy = b->dp + ty;
+
+ /* this is the number of times the loop will iterrate, essentially
+ while (tx++ < a->used && ty-- >= 0) { ... }
+ */
+ iy = MIN(a->used - tx, ty + 1);
+
+ /* execute loop */
+ for (iz = 0; iz < iy; ++iz) {
+ _W += ((mp_word) * tmpx++) * ((mp_word) * tmpy--);
+ }
+
+ /* store term */
+ W[ix] = ((mp_digit)_W) & MP_MASK;
+
+ /* make next carry */
+ _W = _W >> ((mp_word)DIGIT_BIT);
+ }
+
+ /* setup dest */
+ olduse = c->used;
+ c->used = pa;
+
+ {
+ mp_digit *tmpc;
+ tmpc = c->dp;
+ for (ix = 0; ix < (pa + 1); ix++) {
+ /* now extract the previous digit [below the carry] */
+ *tmpc++ = W[ix];
+ }
+
+ /* clear unused digits [that existed in the old copy of c] */
+ for ( ; ix < olduse; ix++) {
+ *tmpc++ = 0;
+ }
+ }
+ mp_clamp(c);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* this is a modified version of fast_s_mul_digs that only produces
+ * output digits *above* digs. See the comments for fast_s_mul_digs
+ * to see how it works.
+ *
+ * This is used in the Barrett reduction since for one of the multiplications
+ * only the higher digits were needed. This essentially halves the work.
+ *
+ * Based on Algorithm 14.12 on pp.595 of HAC.
+ */
+int fast_s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs) {
+ int olduse, res, pa, ix, iz;
+ mp_digit W[MP_WARRAY];
+ mp_word _W;
+
+ /* grow the destination as required */
+ pa = a->used + b->used;
+ if (c->alloc < pa) {
+ if ((res = mp_grow(c, pa)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* number of output digits to produce */
+ pa = a->used + b->used;
+ _W = 0;
+ for (ix = digs; ix < pa; ix++) {
+ int tx, ty, iy;
+ mp_digit *tmpx, *tmpy;
+
+ /* get offsets into the two bignums */
+ ty = MIN(b->used - 1, ix);
+ tx = ix - ty;
+
+ /* setup temp aliases */
+ tmpx = a->dp + tx;
+ tmpy = b->dp + ty;
+
+ /* this is the number of times the loop will iterrate, essentially its
+ while (tx++ < a->used && ty-- >= 0) { ... }
+ */
+ iy = MIN(a->used - tx, ty + 1);
+
+ /* execute loop */
+ for (iz = 0; iz < iy; iz++) {
+ _W += ((mp_word) * tmpx++) * ((mp_word) * tmpy--);
+ }
+
+ /* store term */
+ W[ix] = ((mp_digit)_W) & MP_MASK;
+
+ /* make next carry */
+ _W = _W >> ((mp_word)DIGIT_BIT);
+ }
+
+ /* setup dest */
+ olduse = c->used;
+ c->used = pa;
+
+ {
+ mp_digit *tmpc;
+
+ tmpc = c->dp + digs;
+ for (ix = digs; ix < pa; ix++) {
+ /* now extract the previous digit [below the carry] */
+ *tmpc++ = W[ix];
+ }
+
+ /* clear unused digits [that existed in the old copy of c] */
+ for ( ; ix < olduse; ix++) {
+ *tmpc++ = 0;
+ }
+ }
+ mp_clamp(c);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_FAST_S_MP_SQR_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* the jist of squaring...
+ * you do like mult except the offset of the tmpx [one that
+ * starts closer to zero] can't equal the offset of tmpy.
+ * So basically you set up iy like before then you min it with
+ * (ty-tx) so that it never happens. You double all those
+ * you add in the inner loop
+
+ After that loop you do the squares and add them in.
+ */
+
+int fast_s_mp_sqr(mp_int *a, mp_int *b) {
+ int olduse, res, pa, ix, iz;
+ mp_digit W[MP_WARRAY], *tmpx;
+ mp_word W1;
+
+ /* grow the destination as required */
+ pa = a->used + a->used;
+ if (b->alloc < pa) {
+ if ((res = mp_grow(b, pa)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* number of output digits to produce */
+ W1 = 0;
+ for (ix = 0; ix < pa; ix++) {
+ int tx, ty, iy;
+ mp_word _W;
+ mp_digit *tmpy;
+
+ /* clear counter */
+ _W = 0;
+
+ /* get offsets into the two bignums */
+ ty = MIN(a->used - 1, ix);
+ tx = ix - ty;
+
+ /* setup temp aliases */
+ tmpx = a->dp + tx;
+ tmpy = a->dp + ty;
+
+ /* this is the number of times the loop will iterrate, essentially
+ while (tx++ < a->used && ty-- >= 0) { ... }
+ */
+ iy = MIN(a->used - tx, ty + 1);
+
+ /* now for squaring tx can never equal ty
+ * we halve the distance since they approach at a rate of 2x
+ * and we have to round because odd cases need to be executed
+ */
+ iy = MIN(iy, ((ty - tx) + 1) >> 1);
+
+ /* execute loop */
+ for (iz = 0; iz < iy; iz++) {
+ _W += ((mp_word) * tmpx++) * ((mp_word) * tmpy--);
+ }
+
+ /* double the inner product and add carry */
+ _W = _W + _W + W1;
+
+ /* even columns have the square term in them */
+ if ((ix & 1) == 0) {
+ _W += ((mp_word)a->dp[ix >> 1]) * ((mp_word)a->dp[ix >> 1]);
+ }
+
+ /* store it */
+ W[ix] = (mp_digit)(_W & MP_MASK);
+
+ /* make next carry */
+ W1 = _W >> ((mp_word)DIGIT_BIT);
+ }
+
+ /* setup dest */
+ olduse = b->used;
+ b->used = a->used + a->used;
+
+ {
+ mp_digit *tmpb;
+ tmpb = b->dp;
+ for (ix = 0; ix < pa; ix++) {
+ *tmpb++ = W[ix] & MP_MASK;
+ }
+
+ /* clear unused digits [that existed in the old copy of c] */
+ for ( ; ix < olduse; ix++) {
+ *tmpb++ = 0;
+ }
+ }
+ mp_clamp(b);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_2EXPT_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* computes a = 2**b
+ *
+ * Simple algorithm which zeroes the int, grows it then just sets one bit
+ * as required.
+ */
+int
+mp_2expt(mp_int *a, int b) {
+ int res;
+
+ /* zero a as per default */
+ mp_zero(a);
+
+ /* grow a to accomodate the single bit */
+ if ((res = mp_grow(a, (b / DIGIT_BIT) + 1)) != MP_OKAY) {
+ return res;
+ }
+
+ /* set the used count of where the bit will go */
+ a->used = (b / DIGIT_BIT) + 1;
+
+ /* put the single bit in its place */
+ a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT);
+
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_ABS_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* b = |a|
+ *
+ * Simple function copies the input and fixes the sign to positive
+ */
+int
+mp_abs(mp_int *a, mp_int *b) {
+ int res;
+
+ /* copy a to b */
+ if (a != b) {
+ if ((res = mp_copy(a, b)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* force the sign of b to positive */
+ b->sign = MP_ZPOS;
+
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_ADD_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* high level addition (handles signs) */
+int mp_add(mp_int *a, mp_int *b, mp_int *c) {
+ int sa, sb, res;
+
+ /* get sign of both inputs */
+ sa = a->sign;
+ sb = b->sign;
+
+ /* handle two cases, not four */
+ if (sa == sb) {
+ /* both positive or both negative */
+ /* add their magnitudes, copy the sign */
+ c->sign = sa;
+ res = s_mp_add(a, b, c);
+ } else {
+ /* one positive, the other negative */
+ /* subtract the one with the greater magnitude from */
+ /* the one of the lesser magnitude. The result gets */
+ /* the sign of the one with the greater magnitude. */
+ if (mp_cmp_mag(a, b) == MP_LT) {
+ c->sign = sb;
+ res = s_mp_sub(b, a, c);
+ } else {
+ c->sign = sa;
+ res = s_mp_sub(a, b, c);
+ }
+ }
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_ADD_D_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* single digit addition */
+int
+mp_add_d(mp_int *a, mp_digit b, mp_int *c) {
+ int res, ix, oldused;
+ mp_digit *tmpa, *tmpc, mu;
+
+ /* grow c as required */
+ if (c->alloc < (a->used + 1)) {
+ if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* if a is negative and |a| >= b, call c = |a| - b */
+ if ((a->sign == MP_NEG) && ((a->used > 1) || (a->dp[0] >= b))) {
+ /* temporarily fix sign of a */
+ a->sign = MP_ZPOS;
+
+ /* c = |a| - b */
+ res = mp_sub_d(a, b, c);
+
+ /* fix sign */
+ a->sign = c->sign = MP_NEG;
+
+ /* clamp */
+ mp_clamp(c);
+
+ return res;
+ }
+
+ /* old number of used digits in c */
+ oldused = c->used;
+
+ /* sign always positive */
+ c->sign = MP_ZPOS;
+
+ /* source alias */
+ tmpa = a->dp;
+
+ /* destination alias */
+ tmpc = c->dp;
+
+ /* if a is positive */
+ if (a->sign == MP_ZPOS) {
+ /* add digit, after this we're propagating
+ * the carry.
+ */
+ *tmpc = *tmpa++ + b;
+ mu = *tmpc >> DIGIT_BIT;
+ *tmpc++ &= MP_MASK;
+
+ /* now handle rest of the digits */
+ for (ix = 1; ix < a->used; ix++) {
+ *tmpc = *tmpa++ + mu;
+ mu = *tmpc >> DIGIT_BIT;
+ *tmpc++ &= MP_MASK;
+ }
+ /* set final carry */
+ ix++;
+ *tmpc++ = mu;
+
+ /* setup size */
+ c->used = a->used + 1;
+ } else {
+ /* a was negative and |a| < b */
+ c->used = 1;
+
+ /* the result is a single digit */
+ if (a->used == 1) {
+ *tmpc++ = b - a->dp[0];
+ } else {
+ *tmpc++ = b;
+ }
+
+ /* setup count so the clearing of oldused
+ * can fall through correctly
+ */
+ ix = 1;
+ }
+
+ /* now zero to oldused */
+ while (ix++ < oldused) {
+ *tmpc++ = 0;
+ }
+ mp_clamp(c);
+
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_ADDMOD_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* d = a + b (mod c) */
+int
+mp_addmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d) {
+ int res;
+ mp_int t;
+
+ if ((res = mp_init(&t)) != MP_OKAY) {
+ return res;
+ }
+
+ if ((res = mp_add(a, b, &t)) != MP_OKAY) {
+ mp_clear(&t);
+ return res;
+ }
+ res = mp_mod(&t, c, d);
+ mp_clear(&t);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_AND_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* AND two ints together */
+int
+mp_and(mp_int *a, mp_int *b, mp_int *c) {
+ int res, ix, px;
+ mp_int t, *x;
+
+ if (a->used > b->used) {
+ if ((res = mp_init_copy(&t, a)) != MP_OKAY) {
+ return res;
+ }
+ px = b->used;
+ x = b;
+ } else {
+ if ((res = mp_init_copy(&t, b)) != MP_OKAY) {
+ return res;
+ }
+ px = a->used;
+ x = a;
+ }
+
+ for (ix = 0; ix < px; ix++) {
+ t.dp[ix] &= x->dp[ix];
+ }
+
+ /* zero digits above the last from the smallest mp_int */
+ for ( ; ix < t.used; ix++) {
+ t.dp[ix] = 0;
+ }
+
+ mp_clamp(&t);
+ mp_exch(c, &t);
+ mp_clear(&t);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_CLAMP_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* trim unused digits
+ *
+ * This is used to ensure that leading zero digits are
+ * trimed and the leading "used" digit will be non-zero
+ * Typically very fast. Also fixes the sign if there
+ * are no more leading digits
+ */
+void
+mp_clamp(mp_int *a) {
+ /* decrease used while the most significant digit is
+ * zero.
+ */
+ while ((a->used > 0) && (a->dp[a->used - 1] == 0)) {
+ --(a->used);
+ }
+
+ /* reset the sign flag if used == 0 */
+ if (a->used == 0) {
+ a->sign = MP_ZPOS;
+ }
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_CLEAR_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* clear one (frees) */
+void
+mp_clear(mp_int *a) {
+ int i;
+
+ /* only do anything if a hasn't been freed previously */
+ if (a->dp != NULL) {
+ /* first zero the digits */
+ for (i = 0; i < a->used; i++) {
+ a->dp[i] = 0;
+ }
+
+ /* free ram */
+ XFREE(a->dp);
+
+ /* reset members to make debugging easier */
+ a->dp = NULL;
+ a->alloc = a->used = 0;
+ a->sign = MP_ZPOS;
+ }
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_CLEAR_MULTI_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+#include
+
+void mp_clear_multi(mp_int *mp, ...) {
+ mp_int *next_mp = mp;
+ va_list args;
+
+ va_start(args, mp);
+ while (next_mp != NULL) {
+ mp_clear(next_mp);
+ next_mp = va_arg(args, mp_int *);
+ }
+ va_end(args);
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_CMP_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* compare two ints (signed)*/
+int
+mp_cmp(mp_int *a, mp_int *b) {
+ /* compare based on sign */
+ if (a->sign != b->sign) {
+ if (a->sign == MP_NEG) {
+ return MP_LT;
+ } else {
+ return MP_GT;
+ }
+ }
+
+ /* compare digits */
+ if (a->sign == MP_NEG) {
+ /* if negative compare opposite direction */
+ return mp_cmp_mag(b, a);
+ } else {
+ return mp_cmp_mag(a, b);
+ }
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_CMP_D_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* compare a digit */
+int mp_cmp_d(mp_int *a, mp_digit b) {
+ /* compare based on sign */
+ if (a->sign == MP_NEG) {
+ return MP_LT;
+ }
+
+ /* compare based on magnitude */
+ if (a->used > 1) {
+ return MP_GT;
+ }
+
+ /* compare the only digit of a to b */
+ if (a->dp[0] > b) {
+ return MP_GT;
+ } else if (a->dp[0] < b) {
+ return MP_LT;
+ } else {
+ return MP_EQ;
+ }
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_CMP_MAG_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* compare maginitude of two ints (unsigned) */
+int mp_cmp_mag(mp_int *a, mp_int *b) {
+ int n;
+ mp_digit *tmpa, *tmpb;
+
+ /* compare based on # of non-zero digits */
+ if (a->used > b->used) {
+ return MP_GT;
+ }
+
+ if (a->used < b->used) {
+ return MP_LT;
+ }
+
+ /* alias for a */
+ tmpa = a->dp + (a->used - 1);
+
+ /* alias for b */
+ tmpb = b->dp + (a->used - 1);
+
+ /* compare based on digits */
+ for (n = 0; n < a->used; ++n, --tmpa, --tmpb) {
+ if (*tmpa > *tmpb) {
+ return MP_GT;
+ }
+
+ if (*tmpa < *tmpb) {
+ return MP_LT;
+ }
+ }
+ return MP_EQ;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_CNT_LSB_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+static const int lnz[16] = {
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+};
+
+/* Counts the number of lsbs which are zero before the first zero bit */
+int mp_cnt_lsb(mp_int *a) {
+ int x;
+ mp_digit q, qq;
+
+ /* easy out */
+ if (mp_iszero(a) == MP_YES) {
+ return 0;
+ }
+
+ /* scan lower digits until non-zero */
+ for (x = 0; (x < a->used) && (a->dp[x] == 0); x++) {
+ }
+ q = a->dp[x];
+ x *= DIGIT_BIT;
+
+ /* now scan this digit until a 1 is found */
+ if ((q & 1) == 0) {
+ do {
+ qq = q & 15;
+ x += lnz[qq];
+ q >>= 4;
+ } while (qq == 0);
+ }
+ return x;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_COPY_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* copy, b = a */
+int
+mp_copy(mp_int *a, mp_int *b) {
+ int res, n;
+
+ /* if dst == src do nothing */
+ if (a == b) {
+ return MP_OKAY;
+ }
+
+ /* grow dest */
+ if (b->alloc < a->used) {
+ if ((res = mp_grow(b, a->used)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* zero b and copy the parameters over */
+ {
+ mp_digit *tmpa, *tmpb;
+
+ /* pointer aliases */
+
+ /* source */
+ tmpa = a->dp;
+
+ /* destination */
+ tmpb = b->dp;
+
+ /* copy all the digits */
+ for (n = 0; n < a->used; n++) {
+ *tmpb++ = *tmpa++;
+ }
+
+ /* clear high digits */
+ for ( ; n < b->used; n++) {
+ *tmpb++ = 0;
+ }
+ }
+
+ /* copy used count and sign */
+ b->used = a->used;
+ b->sign = a->sign;
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_COUNT_BITS_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* returns the number of bits in an int */
+int
+mp_count_bits(mp_int *a) {
+ int r;
+ mp_digit q;
+
+ /* shortcut */
+ if (a->used == 0) {
+ return 0;
+ }
+
+ /* get number of digits and add that */
+ r = (a->used - 1) * DIGIT_BIT;
+
+ /* take the last digit and count the bits in it */
+ q = a->dp[a->used - 1];
+ while (q > ((mp_digit)0)) {
+ ++r;
+ q >>= ((mp_digit)1);
+ }
+ return r;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_DIV_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+ #ifdef BN_MP_DIV_SMALL
+
+/* slower bit-bang division... also smaller */
+int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d) {
+ mp_int ta, tb, tq, q;
+ int res, n, n2;
+
+ /* is divisor zero ? */
+ if (mp_iszero(b) == MP_YES) {
+ return MP_VAL;
+ }
+
+ /* if a < b then q=0, r = a */
+ if (mp_cmp_mag(a, b) == MP_LT) {
+ if (d != NULL) {
+ res = mp_copy(a, d);
+ } else {
+ res = MP_OKAY;
+ }
+ if (c != NULL) {
+ mp_zero(c);
+ }
+ return res;
+ }
+
+ /* init our temps */
+ if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) {
+ return res;
+ }
+
+
+ mp_set(&tq, 1);
+ n = mp_count_bits(a) - mp_count_bits(b);
+ if (((res = mp_abs(a, &ta)) != MP_OKAY) ||
+ ((res = mp_abs(b, &tb)) != MP_OKAY) ||
+ ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) ||
+ ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) {
+ goto LBL_ERR;
+ }
+
+ while (n-- >= 0) {
+ if (mp_cmp(&tb, &ta) != MP_GT) {
+ if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) ||
+ ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) {
+ goto LBL_ERR;
+ }
+ }
+ if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) ||
+ ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* now q == quotient and ta == remainder */
+ n = a->sign;
+ n2 = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
+ if (c != NULL) {
+ mp_exch(c, &q);
+ c->sign = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2;
+ }
+ if (d != NULL) {
+ mp_exch(d, &ta);
+ d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n;
+ }
+LBL_ERR:
+ mp_clear_multi(&ta, &tb, &tq, &q, NULL);
+ return res;
+}
+
+ #else
+
+/* integer signed division.
+ * c*b + d == a [e.g. a/b, c=quotient, d=remainder]
+ * HAC pp.598 Algorithm 14.20
+ *
+ * Note that the description in HAC is horribly
+ * incomplete. For example, it doesn't consider
+ * the case where digits are removed from 'x' in
+ * the inner loop. It also doesn't consider the
+ * case that y has fewer than three digits, etc..
+ *
+ * The overall algorithm is as described as
+ * 14.20 from HAC but fixed to treat these cases.
+ */
+int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d) {
+ mp_int q, x, y, t1, t2;
+ int res, n, t, i, norm, neg;
+
+ /* is divisor zero ? */
+ if (mp_iszero(b) == MP_YES) {
+ return MP_VAL;
+ }
+
+ /* if a < b then q=0, r = a */
+ if (mp_cmp_mag(a, b) == MP_LT) {
+ if (d != NULL) {
+ res = mp_copy(a, d);
+ } else {
+ res = MP_OKAY;
+ }
+ if (c != NULL) {
+ mp_zero(c);
+ }
+ return res;
+ }
+
+ if ((res = mp_init_size(&q, a->used + 2)) != MP_OKAY) {
+ return res;
+ }
+ q.used = a->used + 2;
+
+ if ((res = mp_init(&t1)) != MP_OKAY) {
+ goto LBL_Q;
+ }
+
+ if ((res = mp_init(&t2)) != MP_OKAY) {
+ goto LBL_T1;
+ }
+
+ if ((res = mp_init_copy(&x, a)) != MP_OKAY) {
+ goto LBL_T2;
+ }
+
+ if ((res = mp_init_copy(&y, b)) != MP_OKAY) {
+ goto LBL_X;
+ }
+
+ /* fix the sign */
+ neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
+ x.sign = y.sign = MP_ZPOS;
+
+ /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */
+ norm = mp_count_bits(&y) % DIGIT_BIT;
+ if (norm < (int)(DIGIT_BIT - 1)) {
+ norm = (DIGIT_BIT - 1) - norm;
+ if ((res = mp_mul_2d(&x, norm, &x)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+ if ((res = mp_mul_2d(&y, norm, &y)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+ } else {
+ norm = 0;
+ }
+
+ /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */
+ n = x.used - 1;
+ t = y.used - 1;
+
+ /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */
+ if ((res = mp_lshd(&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */
+ goto LBL_Y;
+ }
+
+ while (mp_cmp(&x, &y) != MP_LT) {
+ ++(q.dp[n - t]);
+ if ((res = mp_sub(&x, &y, &x)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+ }
+
+ /* reset y by shifting it back down */
+ mp_rshd(&y, n - t);
+
+ /* step 3. for i from n down to (t + 1) */
+ for (i = n; i >= (t + 1); i--) {
+ if (i > x.used) {
+ continue;
+ }
+
+ /* step 3.1 if xi == yt then set q{i-t-1} to b-1,
+ * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */
+ if (x.dp[i] == y.dp[t]) {
+ q.dp[(i - t) - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1);
+ } else {
+ mp_word tmp;
+ tmp = ((mp_word)x.dp[i]) << ((mp_word)DIGIT_BIT);
+ tmp |= ((mp_word)x.dp[i - 1]);
+ tmp /= ((mp_word)y.dp[t]);
+ if (tmp > (mp_word)MP_MASK) {
+ tmp = MP_MASK;
+ }
+ q.dp[(i - t) - 1] = (mp_digit)(tmp & (mp_word)(MP_MASK));
+ }
+
+ /* while (q{i-t-1} * (yt * b + y{t-1})) >
+ xi * b**2 + xi-1 * b + xi-2
+
+ do q{i-t-1} -= 1;
+ */
+ q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] + 1) & MP_MASK;
+ do {
+ q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1) & MP_MASK;
+
+ /* find left hand */
+ mp_zero(&t1);
+ t1.dp[0] = ((t - 1) < 0) ? 0 : y.dp[t - 1];
+ t1.dp[1] = y.dp[t];
+ t1.used = 2;
+ if ((res = mp_mul_d(&t1, q.dp[(i - t) - 1], &t1)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+
+ /* find right hand */
+ t2.dp[0] = ((i - 2) < 0) ? 0 : x.dp[i - 2];
+ t2.dp[1] = ((i - 1) < 0) ? 0 : x.dp[i - 1];
+ t2.dp[2] = x.dp[i];
+ t2.used = 3;
+ } while (mp_cmp_mag(&t1, &t2) == MP_GT);
+
+ /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */
+ if ((res = mp_mul_d(&y, q.dp[(i - t) - 1], &t1)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+
+ if ((res = mp_lshd(&t1, (i - t) - 1)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+
+ if ((res = mp_sub(&x, &t1, &x)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+
+ /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */
+ if (x.sign == MP_NEG) {
+ if ((res = mp_copy(&y, &t1)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+ if ((res = mp_lshd(&t1, (i - t) - 1)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+ if ((res = mp_add(&x, &t1, &x)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+
+ q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1UL) & MP_MASK;
+ }
+ }
+
+ /* now q is the quotient and x is the remainder
+ * [which we have to normalize]
+ */
+
+ /* get sign before writing to c */
+ x.sign = (x.used == 0) ? MP_ZPOS : a->sign;
+
+ if (c != NULL) {
+ mp_clamp(&q);
+ mp_exch(&q, c);
+ c->sign = neg;
+ }
+
+ if (d != NULL) {
+ if ((res = mp_div_2d(&x, norm, &x, NULL)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+ mp_exch(&x, d);
+ }
+
+ res = MP_OKAY;
+
+LBL_Y: mp_clear(&y);
+LBL_X: mp_clear(&x);
+LBL_T2: mp_clear(&t2);
+LBL_T1: mp_clear(&t1);
+LBL_Q: mp_clear(&q);
+ return res;
+}
+ #endif
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_DIV_2_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* b = a/2 */
+int mp_div_2(mp_int *a, mp_int *b) {
+ int x, res, oldused;
+
+ /* copy */
+ if (b->alloc < a->used) {
+ if ((res = mp_grow(b, a->used)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ oldused = b->used;
+ b->used = a->used;
+ {
+ mp_digit r, rr, *tmpa, *tmpb;
+
+ /* source alias */
+ tmpa = a->dp + b->used - 1;
+
+ /* dest alias */
+ tmpb = b->dp + b->used - 1;
+
+ /* carry */
+ r = 0;
+ for (x = b->used - 1; x >= 0; x--) {
+ /* get the carry for the next iteration */
+ rr = *tmpa & 1;
+
+ /* shift the current digit, add in carry and store */
+ *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1));
+
+ /* forward carry to next iteration */
+ r = rr;
+ }
+
+ /* zero excess digits */
+ tmpb = b->dp + b->used;
+ for (x = b->used; x < oldused; x++) {
+ *tmpb++ = 0;
+ }
+ }
+ b->sign = a->sign;
+ mp_clamp(b);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_DIV_2D_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* shift right by a certain bit count (store quotient in c, optional remainder in d) */
+int mp_div_2d(mp_int *a, int b, mp_int *c, mp_int *d) {
+ mp_digit D, r, rr;
+ int x, res;
+ mp_int t;
+
+
+ /* if the shift count is <= 0 then we do no work */
+ if (b <= 0) {
+ res = mp_copy(a, c);
+ if (d != NULL) {
+ mp_zero(d);
+ }
+ return res;
+ }
+
+ if ((res = mp_init(&t)) != MP_OKAY) {
+ return res;
+ }
+
+ /* get the remainder */
+ if (d != NULL) {
+ if ((res = mp_mod_2d(a, b, &t)) != MP_OKAY) {
+ mp_clear(&t);
+ return res;
+ }
+ }
+
+ /* copy */
+ if ((res = mp_copy(a, c)) != MP_OKAY) {
+ mp_clear(&t);
+ return res;
+ }
+
+ /* shift by as many digits in the bit count */
+ if (b >= (int)DIGIT_BIT) {
+ mp_rshd(c, b / DIGIT_BIT);
+ }
+
+ /* shift any bit count < DIGIT_BIT */
+ D = (mp_digit)(b % DIGIT_BIT);
+ if (D != 0) {
+ mp_digit *tmpc, mask, shift;
+
+ /* mask */
+ mask = (((mp_digit)1) << D) - 1;
+
+ /* shift for lsb */
+ shift = DIGIT_BIT - D;
+
+ /* alias */
+ tmpc = c->dp + (c->used - 1);
+
+ /* carry */
+ r = 0;
+ for (x = c->used - 1; x >= 0; x--) {
+ /* get the lower bits of this word in a temp */
+ rr = *tmpc & mask;
+
+ /* shift the current word and mix in the carry bits from the previous word */
+ *tmpc = (*tmpc >> D) | (r << shift);
+ --tmpc;
+
+ /* set the carry to the carry bits of the current word found above */
+ r = rr;
+ }
+ }
+ mp_clamp(c);
+ if (d != NULL) {
+ mp_exch(&t, d);
+ }
+ mp_clear(&t);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_DIV_3_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* divide by three (based on routine from MPI and the GMP manual) */
+int
+mp_div_3(mp_int *a, mp_int *c, mp_digit *d) {
+ mp_int q;
+ mp_word w, t;
+ mp_digit b;
+ int res, ix;
+
+ /* b = 2**DIGIT_BIT / 3 */
+ b = (((mp_word)1) << ((mp_word)DIGIT_BIT)) / ((mp_word)3);
+
+ if ((res = mp_init_size(&q, a->used)) != MP_OKAY) {
+ return res;
+ }
+
+ q.used = a->used;
+ q.sign = a->sign;
+ w = 0;
+ for (ix = a->used - 1; ix >= 0; ix--) {
+ w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]);
+
+ if (w >= 3) {
+ /* multiply w by [1/3] */
+ t = (w * ((mp_word)b)) >> ((mp_word)DIGIT_BIT);
+
+ /* now subtract 3 * [w/3] from w, to get the remainder */
+ w -= t + t + t;
+
+ /* fixup the remainder as required since
+ * the optimization is not exact.
+ */
+ while (w >= 3) {
+ t += 1;
+ w -= 3;
+ }
+ } else {
+ t = 0;
+ }
+ q.dp[ix] = (mp_digit)t;
+ }
+
+ /* [optional] store the remainder */
+ if (d != NULL) {
+ *d = (mp_digit)w;
+ }
+
+ /* [optional] store the quotient */
+ if (c != NULL) {
+ mp_clamp(&q);
+ mp_exch(&q, c);
+ }
+ mp_clear(&q);
+
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_DIV_D_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+static int s_is_power_of_two(mp_digit b, int *p) {
+ int x;
+
+ /* fast return if no power of two */
+ if ((b == 0) || ((b & (b - 1)) != 0)) {
+ return 0;
+ }
+
+ for (x = 0; x < DIGIT_BIT; x++) {
+ if (b == (((mp_digit)1) << x)) {
+ *p = x;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* single digit division (based on routine from MPI) */
+int mp_div_d(mp_int *a, mp_digit b, mp_int *c, mp_digit *d) {
+ mp_int q;
+ mp_word w;
+ mp_digit t;
+ int res, ix;
+
+ /* cannot divide by zero */
+ if (b == 0) {
+ return MP_VAL;
+ }
+
+ /* quick outs */
+ if ((b == 1) || (mp_iszero(a) == MP_YES)) {
+ if (d != NULL) {
+ *d = 0;
+ }
+ if (c != NULL) {
+ return mp_copy(a, c);
+ }
+ return MP_OKAY;
+ }
+
+ /* power of two ? */
+ if (s_is_power_of_two(b, &ix) == 1) {
+ if (d != NULL) {
+ *d = a->dp[0] & ((((mp_digit)1) << ix) - 1);
+ }
+ if (c != NULL) {
+ return mp_div_2d(a, ix, c, NULL);
+ }
+ return MP_OKAY;
+ }
+
+ #ifdef BN_MP_DIV_3_C
+ /* three? */
+ if (b == 3) {
+ return mp_div_3(a, c, d);
+ }
+ #endif
+
+ /* no easy answer [c'est la vie]. Just division */
+ if ((res = mp_init_size(&q, a->used)) != MP_OKAY) {
+ return res;
+ }
+
+ q.used = a->used;
+ q.sign = a->sign;
+ w = 0;
+ for (ix = a->used - 1; ix >= 0; ix--) {
+ w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]);
+
+ if (w >= b) {
+ t = (mp_digit)(w / b);
+ w -= ((mp_word)t) * ((mp_word)b);
+ } else {
+ t = 0;
+ }
+ q.dp[ix] = (mp_digit)t;
+ }
+
+ if (d != NULL) {
+ *d = (mp_digit)w;
+ }
+
+ if (c != NULL) {
+ mp_clamp(&q);
+ mp_exch(&q, c);
+ }
+ mp_clear(&q);
+
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_DR_IS_MODULUS_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* determines if a number is a valid DR modulus */
+int mp_dr_is_modulus(mp_int *a) {
+ int ix;
+
+ /* must be at least two digits */
+ if (a->used < 2) {
+ return 0;
+ }
+
+ /* must be of the form b**k - a [a <= b] so all
+ * but the first digit must be equal to -1 (mod b).
+ */
+ for (ix = 1; ix < a->used; ix++) {
+ if (a->dp[ix] != MP_MASK) {
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_DR_REDUCE_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* reduce "x" in place modulo "n" using the Diminished Radix algorithm.
+ *
+ * Based on algorithm from the paper
+ *
+ * "Generating Efficient Primes for Discrete Log Cryptosystems"
+ * Chae Hoon Lim, Pil Joong Lee,
+ * POSTECH Information Research Laboratories
+ *
+ * The modulus must be of a special format [see manual]
+ *
+ * Has been modified to use algorithm 7.10 from the LTM book instead
+ *
+ * Input x must be in the range 0 <= x <= (n-1)**2
+ */
+int
+mp_dr_reduce(mp_int *x, mp_int *n, mp_digit k) {
+ int err, i, m;
+ mp_word r;
+ mp_digit mu, *tmpx1, *tmpx2;
+
+ /* m = digits in modulus */
+ m = n->used;
+
+ /* ensure that "x" has at least 2m digits */
+ if (x->alloc < (m + m)) {
+ if ((err = mp_grow(x, m + m)) != MP_OKAY) {
+ return err;
+ }
+ }
+
+/* top of loop, this is where the code resumes if
+ * another reduction pass is required.
+ */
+top:
+ /* aliases for digits */
+ /* alias for lower half of x */
+ tmpx1 = x->dp;
+
+ /* alias for upper half of x, or x/B**m */
+ tmpx2 = x->dp + m;
+
+ /* set carry to zero */
+ mu = 0;
+
+ /* compute (x mod B**m) + k * [x/B**m] inline and inplace */
+ for (i = 0; i < m; i++) {
+ r = (((mp_word) * tmpx2++) * (mp_word)k) + *tmpx1 + mu;
+ *tmpx1++ = (mp_digit)(r & MP_MASK);
+ mu = (mp_digit)(r >> ((mp_word)DIGIT_BIT));
+ }
+
+ /* set final carry */
+ *tmpx1++ = mu;
+
+ /* zero words above m */
+ for (i = m + 1; i < x->used; i++) {
+ *tmpx1++ = 0;
+ }
+
+ /* clamp, sub and return */
+ mp_clamp(x);
+
+ /* if x >= n then subtract and reduce again
+ * Each successive "recursion" makes the input smaller and smaller.
+ */
+ if (mp_cmp_mag(x, n) != MP_LT) {
+ if ((err = s_mp_sub(x, n, x)) != MP_OKAY) {
+ return err;
+ }
+ goto top;
+ }
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_DR_SETUP_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* determines the setup value */
+void mp_dr_setup(mp_int *a, mp_digit *d) {
+ /* the casts are required if DIGIT_BIT is one less than
+ * the number of bits in a mp_digit [e.g. DIGIT_BIT==31]
+ */
+ *d = (mp_digit)((((mp_word)1) << ((mp_word)DIGIT_BIT)) -
+ ((mp_word)a->dp[0]));
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_EXCH_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* swap the elements of two integers, for cases where you can't simply swap the
+ * mp_int pointers around
+ */
+void
+mp_exch(mp_int *a, mp_int *b) {
+ mp_int t;
+
+ t = *a;
+ *a = *b;
+ *b = t;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_EXPORT_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* based on gmp's mpz_export.
+ * see http://gmplib.org/manual/Integer-Import-and-Export.html
+ */
+int mp_export(void *rop, size_t *countp, int order, size_t size,
+ int endian, size_t nails, mp_int *op) {
+ int result;
+ size_t odd_nails, nail_bytes, i, j, bits, count;
+ unsigned char odd_nail_mask;
+
+ mp_int t;
+
+ if ((result = mp_init_copy(&t, op)) != MP_OKAY) {
+ return result;
+ }
+
+ if (endian == 0) {
+ union {
+ unsigned int i;
+ char c[4];
+ } lint;
+ lint.i = 0x01020304;
+
+ endian = (lint.c[0] == 4) ? -1 : 1;
+ }
+
+ odd_nails = (nails % 8);
+ odd_nail_mask = 0xff;
+ for (i = 0; i < odd_nails; ++i) {
+ odd_nail_mask ^= (1 << (7 - i));
+ }
+ nail_bytes = nails / 8;
+
+ bits = mp_count_bits(&t);
+ count = (bits / ((size * 8) - nails)) + (((bits % ((size * 8) - nails)) != 0) ? 1 : 0);
+
+ for (i = 0; i < count; ++i) {
+ for (j = 0; j < size; ++j) {
+ unsigned char *byte = (
+ (unsigned char *)rop +
+ (((order == -1) ? i : ((count - 1) - i)) * size) +
+ ((endian == -1) ? j : ((size - 1) - j))
+ );
+
+ if (j >= (size - nail_bytes)) {
+ *byte = 0;
+ continue;
+ }
+
+ *byte = (unsigned char)((j == ((size - nail_bytes) - 1)) ? (t.dp[0] & odd_nail_mask) : (t.dp[0] & 0xFF));
+
+ if ((result = mp_div_2d(&t, ((j == ((size - nail_bytes) - 1)) ? (8 - odd_nails) : 8), &t, NULL)) != MP_OKAY) {
+ mp_clear(&t);
+ return result;
+ }
+ }
+ }
+
+ mp_clear(&t);
+
+ if (countp != NULL) {
+ *countp = count;
+ }
+
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_EXPT_D_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* wrapper function for mp_expt_d_ex() */
+int mp_expt_d(mp_int *a, mp_digit b, mp_int *c) {
+ return mp_expt_d_ex(a, b, c, 0);
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_EXPT_D_EX_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* calculate c = a**b using a square-multiply algorithm */
+int mp_expt_d_ex(mp_int *a, mp_digit b, mp_int *c, int fast) {
+ int res;
+ unsigned int x;
+
+ mp_int g;
+
+ if ((res = mp_init_copy(&g, a)) != MP_OKAY) {
+ return res;
+ }
+
+ /* set initial result */
+ mp_set(c, 1);
+
+ if (fast != 0) {
+ while (b > 0) {
+ /* if the bit is set multiply */
+ if ((b & 1) != 0) {
+ if ((res = mp_mul(c, &g, c)) != MP_OKAY) {
+ mp_clear(&g);
+ return res;
+ }
+ }
+
+ /* square */
+ if (b > 1) {
+ if ((res = mp_sqr(&g, &g)) != MP_OKAY) {
+ mp_clear(&g);
+ return res;
+ }
+ }
+
+ /* shift to next bit */
+ b >>= 1;
+ }
+ } else {
+ for (x = 0; x < DIGIT_BIT; x++) {
+ /* square */
+ if ((res = mp_sqr(c, c)) != MP_OKAY) {
+ mp_clear(&g);
+ return res;
+ }
+
+ /* if the bit is set multiply */
+ if ((b & (mp_digit)(((mp_digit)1) << (DIGIT_BIT - 1))) != 0) {
+ if ((res = mp_mul(c, &g, c)) != MP_OKAY) {
+ mp_clear(&g);
+ return res;
+ }
+ }
+
+ /* shift to next bit */
+ b <<= 1;
+ }
+ } /* if ... else */
+
+ mp_clear(&g);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_EXPTMOD_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+
+/* this is a shell function that calls either the normal or Montgomery
+ * exptmod functions. Originally the call to the montgomery code was
+ * embedded in the normal function but that wasted alot of stack space
+ * for nothing (since 99% of the time the Montgomery code would be called)
+ */
+int mp_exptmod(mp_int *G, mp_int *X, mp_int *P, mp_int *Y) {
+ int dr;
+
+ /* modulus P must be positive */
+ if (P->sign == MP_NEG) {
+ return MP_VAL;
+ }
+
+ /* if exponent X is negative we have to recurse */
+ if (X->sign == MP_NEG) {
+ #ifdef BN_MP_INVMOD_C
+ mp_int tmpG, tmpX;
+ int err;
+
+ /* first compute 1/G mod P */
+ if ((err = mp_init(&tmpG)) != MP_OKAY) {
+ return err;
+ }
+ if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) {
+ mp_clear(&tmpG);
+ return err;
+ }
+
+ /* now get |X| */
+ if ((err = mp_init(&tmpX)) != MP_OKAY) {
+ mp_clear(&tmpG);
+ return err;
+ }
+ if ((err = mp_abs(X, &tmpX)) != MP_OKAY) {
+ mp_clear_multi(&tmpG, &tmpX, NULL);
+ return err;
+ }
+
+ /* and now compute (1/G)**|X| instead of G**X [X < 0] */
+ err = mp_exptmod(&tmpG, &tmpX, P, Y);
+ mp_clear_multi(&tmpG, &tmpX, NULL);
+ return err;
+ #else
+ /* no invmod */
+ return MP_VAL;
+ #endif
+ }
+
+/* modified diminished radix reduction */
+ #if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C)
+ if (mp_reduce_is_2k_l(P) == MP_YES) {
+ return s_mp_exptmod(G, X, P, Y, 1);
+ }
+ #endif
+
+ #ifdef BN_MP_DR_IS_MODULUS_C
+ /* is it a DR modulus? */
+ dr = mp_dr_is_modulus(P);
+ #else
+ /* default to no */
+ dr = 0;
+ #endif
+
+ #ifdef BN_MP_REDUCE_IS_2K_C
+ /* if not, is it a unrestricted DR modulus? */
+ if (dr == 0) {
+ dr = mp_reduce_is_2k(P) << 1;
+ }
+ #endif
+
+ /* if the modulus is odd or dr != 0 use the montgomery method */
+ #ifdef BN_MP_EXPTMOD_FAST_C
+ if ((mp_isodd(P) == MP_YES) || (dr != 0)) {
+ return mp_exptmod_fast(G, X, P, Y, dr);
+ } else {
+ #endif
+ #ifdef BN_S_MP_EXPTMOD_C
+ /* otherwise use the generic Barrett reduction technique */
+ return s_mp_exptmod(G, X, P, Y, 0);
+ #else
+ /* no exptmod for evens */
+ return MP_VAL;
+ #endif
+ #ifdef BN_MP_EXPTMOD_FAST_C
+}
+ #endif
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_EXPTMOD_FAST_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85
+ *
+ * Uses a left-to-right k-ary sliding window to compute the modular exponentiation.
+ * The value of k changes based on the size of the exponent.
+ *
+ * Uses Montgomery or Diminished Radix reduction [whichever appropriate]
+ */
+
+ #ifdef MP_LOW_MEM
+ #define TAB_SIZE 32
+ #else
+ #define TAB_SIZE 256
+ #endif
+
+int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode) {
+ mp_int M[TAB_SIZE], res;
+ mp_digit buf, mp;
+ int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
+
+ /* use a pointer to the reduction algorithm. This allows us to use
+ * one of many reduction algorithms without modding the guts of
+ * the code with if statements everywhere.
+ */
+ int (*redux)(mp_int *, mp_int *, mp_digit);
+
+ /* find window size */
+ x = mp_count_bits(X);
+ if (x <= 7) {
+ winsize = 2;
+ } else if (x <= 36) {
+ winsize = 3;
+ } else if (x <= 140) {
+ winsize = 4;
+ } else if (x <= 450) {
+ winsize = 5;
+ } else if (x <= 1303) {
+ winsize = 6;
+ } else if (x <= 3529) {
+ winsize = 7;
+ } else {
+ winsize = 8;
+ }
+
+ #ifdef MP_LOW_MEM
+ if (winsize > 5) {
+ winsize = 5;
+ }
+ #endif
+
+ /* init M array */
+ /* init first cell */
+ if ((err = mp_init(&M[1])) != MP_OKAY) {
+ return err;
+ }
+
+ /* now init the second half of the array */
+ for (x = 1 << (winsize - 1); x < (1 << winsize); x++) {
+ if ((err = mp_init(&M[x])) != MP_OKAY) {
+ for (y = 1 << (winsize - 1); y < x; y++) {
+ mp_clear(&M[y]);
+ }
+ mp_clear(&M[1]);
+ return err;
+ }
+ }
+
+ /* determine and setup reduction code */
+ if (redmode == 0) {
+ #ifdef BN_MP_MONTGOMERY_SETUP_C
+ /* now setup montgomery */
+ if ((err = mp_montgomery_setup(P, &mp)) != MP_OKAY) {
+ goto LBL_M;
+ }
+ #else
+ err = MP_VAL;
+ goto LBL_M;
+ #endif
+
+ /* automatically pick the comba one if available (saves quite a few calls/ifs) */
+ #ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C
+ if ((((P->used * 2) + 1) < MP_WARRAY) &&
+ (P->used < (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) {
+ redux = fast_mp_montgomery_reduce;
+ } else
+ #endif
+ {
+ #ifdef BN_MP_MONTGOMERY_REDUCE_C
+ /* use slower baseline Montgomery method */
+ redux = mp_montgomery_reduce;
+ #else
+ err = MP_VAL;
+ goto LBL_M;
+ #endif
+ }
+ } else if (redmode == 1) {
+ #if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C)
+ /* setup DR reduction for moduli of the form B**k - b */
+ mp_dr_setup(P, &mp);
+ redux = mp_dr_reduce;
+ #else
+ err = MP_VAL;
+ goto LBL_M;
+ #endif
+ } else {
+ #if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C)
+ /* setup DR reduction for moduli of the form 2**k - b */
+ if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) {
+ goto LBL_M;
+ }
+ redux = mp_reduce_2k;
+ #else
+ err = MP_VAL;
+ goto LBL_M;
+ #endif
+ }
+
+ /* setup result */
+ if ((err = mp_init(&res)) != MP_OKAY) {
+ goto LBL_M;
+ }
+
+ /* create M table
+ *
+
+ *
+ * The first half of the table is not computed though accept for M[0] and M[1]
+ */
+
+ if (redmode == 0) {
+ #ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+ /* now we need R mod m */
+ if ((err = mp_montgomery_calc_normalization(&res, P)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ #else
+ err = MP_VAL;
+ goto LBL_RES;
+ #endif
+
+ /* now set M[1] to G * R mod m */
+ if ((err = mp_mulmod(G, &res, P, &M[1])) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ } else {
+ mp_set(&res, 1);
+ if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ }
+
+ /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */
+ if ((err = mp_copy(&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) {
+ goto LBL_RES;
+ }
+
+ for (x = 0; x < (winsize - 1); x++) {
+ if ((err = mp_sqr(&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux(&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ }
+
+ /* create upper table */
+ for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
+ if ((err = mp_mul(&M[x - 1], &M[1], &M[x])) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux(&M[x], P, mp)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ }
+
+ /* set initial mode and bit cnt */
+ mode = 0;
+ bitcnt = 1;
+ buf = 0;
+ digidx = X->used - 1;
+ bitcpy = 0;
+ bitbuf = 0;
+
+ for ( ; ; ) {
+ /* grab next digit as required */
+ if (--bitcnt == 0) {
+ /* if digidx == -1 we are out of digits so break */
+ if (digidx == -1) {
+ break;
+ }
+ /* read next digit and reset bitcnt */
+ buf = X->dp[digidx--];
+ bitcnt = (int)DIGIT_BIT;
+ }
+
+ /* grab the next msb from the exponent */
+ y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1;
+ buf <<= (mp_digit)1;
+
+ /* if the bit is zero and mode == 0 then we ignore it
+ * These represent the leading zero bits before the first 1 bit
+ * in the exponent. Technically this opt is not required but it
+ * does lower the # of trivial squaring/reductions used
+ */
+ if ((mode == 0) && (y == 0)) {
+ continue;
+ }
+
+ /* if the bit is zero and mode == 1 then we square */
+ if ((mode == 1) && (y == 0)) {
+ if ((err = mp_sqr(&res, &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux(&res, P, mp)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ continue;
+ }
+
+ /* else we add it to the window */
+ bitbuf |= (y << (winsize - ++bitcpy));
+ mode = 2;
+
+ if (bitcpy == winsize) {
+ /* ok window is filled so square as required and multiply */
+ /* square first */
+ for (x = 0; x < winsize; x++) {
+ if ((err = mp_sqr(&res, &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux(&res, P, mp)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ }
+
+ /* then multiply */
+ if ((err = mp_mul(&res, &M[bitbuf], &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux(&res, P, mp)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+
+ /* empty window and reset */
+ bitcpy = 0;
+ bitbuf = 0;
+ mode = 1;
+ }
+ }
+
+ /* if bits remain then square/multiply */
+ if ((mode == 2) && (bitcpy > 0)) {
+ /* square then multiply if the bit is set */
+ for (x = 0; x < bitcpy; x++) {
+ if ((err = mp_sqr(&res, &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux(&res, P, mp)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+
+ /* get next bit of the window */
+ bitbuf <<= 1;
+ if ((bitbuf & (1 << winsize)) != 0) {
+ /* then multiply */
+ if ((err = mp_mul(&res, &M[1], &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux(&res, P, mp)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ }
+ }
+ }
+
+ if (redmode == 0) {
+ /* fixup result if Montgomery reduction is used
+ * recall that any value in a Montgomery system is
+ * actually multiplied by R mod n. So we have
+ * to reduce one more time to cancel out the factor
+ * of R.
+ */
+ if ((err = redux(&res, P, mp)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ }
+
+ /* swap res with Y */
+ mp_exch(&res, Y);
+ err = MP_OKAY;
+LBL_RES: mp_clear(&res);
+LBL_M:
+ mp_clear(&M[1]);
+ for (x = 1 << (winsize - 1); x < (1 << winsize); x++) {
+ mp_clear(&M[x]);
+ }
+ return err;
+}
+#endif
+
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_EXTEUCLID_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* Extended euclidean algorithm of (a, b) produces
+ a*u1 + b*u2 = u3
+ */
+int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3) {
+ mp_int u1, u2, u3, v1, v2, v3, t1, t2, t3, q, tmp;
+ int err;
+
+ if ((err = mp_init_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL)) != MP_OKAY) {
+ return err;
+ }
+
+ /* initialize, (u1,u2,u3) = (1,0,a) */
+ mp_set(&u1, 1);
+ if ((err = mp_copy(a, &u3)) != MP_OKAY) {
+ goto _ERR;
+ }
+
+ /* initialize, (v1,v2,v3) = (0,1,b) */
+ mp_set(&v2, 1);
+ if ((err = mp_copy(b, &v3)) != MP_OKAY) {
+ goto _ERR;
+ }
+
+ /* loop while v3 != 0 */
+ while (mp_iszero(&v3) == MP_NO) {
+ /* q = u3/v3 */
+ if ((err = mp_div(&u3, &v3, &q, NULL)) != MP_OKAY) {
+ goto _ERR;
+ }
+
+ /* (t1,t2,t3) = (u1,u2,u3) - (v1,v2,v3)q */
+ if ((err = mp_mul(&v1, &q, &tmp)) != MP_OKAY) {
+ goto _ERR;
+ }
+ if ((err = mp_sub(&u1, &tmp, &t1)) != MP_OKAY) {
+ goto _ERR;
+ }
+ if ((err = mp_mul(&v2, &q, &tmp)) != MP_OKAY) {
+ goto _ERR;
+ }
+ if ((err = mp_sub(&u2, &tmp, &t2)) != MP_OKAY) {
+ goto _ERR;
+ }
+ if ((err = mp_mul(&v3, &q, &tmp)) != MP_OKAY) {
+ goto _ERR;
+ }
+ if ((err = mp_sub(&u3, &tmp, &t3)) != MP_OKAY) {
+ goto _ERR;
+ }
+
+ /* (u1,u2,u3) = (v1,v2,v3) */
+ if ((err = mp_copy(&v1, &u1)) != MP_OKAY) {
+ goto _ERR;
+ }
+ if ((err = mp_copy(&v2, &u2)) != MP_OKAY) {
+ goto _ERR;
+ }
+ if ((err = mp_copy(&v3, &u3)) != MP_OKAY) {
+ goto _ERR;
+ }
+
+ /* (v1,v2,v3) = (t1,t2,t3) */
+ if ((err = mp_copy(&t1, &v1)) != MP_OKAY) {
+ goto _ERR;
+ }
+ if ((err = mp_copy(&t2, &v2)) != MP_OKAY) {
+ goto _ERR;
+ }
+ if ((err = mp_copy(&t3, &v3)) != MP_OKAY) {
+ goto _ERR;
+ }
+ }
+
+ /* make sure U3 >= 0 */
+ if (u3.sign == MP_NEG) {
+ if ((err = mp_neg(&u1, &u1)) != MP_OKAY) {
+ goto _ERR;
+ }
+ if ((err = mp_neg(&u2, &u2)) != MP_OKAY) {
+ goto _ERR;
+ }
+ if ((err = mp_neg(&u3, &u3)) != MP_OKAY) {
+ goto _ERR;
+ }
+ }
+
+ /* copy result out */
+ if (U1 != NULL) {
+ mp_exch(U1, &u1);
+ }
+ if (U2 != NULL) {
+ mp_exch(U2, &u2);
+ }
+ if (U3 != NULL) {
+ mp_exch(U3, &u3);
+ }
+
+ err = MP_OKAY;
+_ERR: mp_clear_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL);
+ return err;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_FREAD_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* read a bigint from a file stream in ASCII */
+int mp_fread(mp_int *a, int radix, FILE *stream) {
+ int err, ch, neg, y;
+
+ /* clear a */
+ mp_zero(a);
+
+ /* if first digit is - then set negative */
+ ch = fgetc(stream);
+ if (ch == '-') {
+ neg = MP_NEG;
+ ch = fgetc(stream);
+ } else {
+ neg = MP_ZPOS;
+ }
+
+ for ( ; ; ) {
+ /* find y in the radix map */
+ for (y = 0; y < radix; y++) {
+ if (mp_s_rmap[y] == ch) {
+ break;
+ }
+ }
+ if (y == radix) {
+ break;
+ }
+
+ /* shift up and add */
+ if ((err = mp_mul_d(a, radix, a)) != MP_OKAY) {
+ return err;
+ }
+ if ((err = mp_add_d(a, y, a)) != MP_OKAY) {
+ return err;
+ }
+
+ ch = fgetc(stream);
+ }
+ if (mp_cmp_d(a, 0) != MP_EQ) {
+ a->sign = neg;
+ }
+
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_FWRITE_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+int mp_fwrite(mp_int *a, int radix, FILE *stream) {
+ char *buf;
+ int err, len, x;
+
+ if ((err = mp_radix_size(a, radix, &len)) != MP_OKAY) {
+ return err;
+ }
+
+ buf = OPT_CAST(char) XMALLOC(len);
+ if (buf == NULL) {
+ return MP_MEM;
+ }
+
+ if ((err = mp_toradix(a, buf, radix)) != MP_OKAY) {
+ XFREE(buf);
+ return err;
+ }
+
+ for (x = 0; x < len; x++) {
+ if (fputc(buf[x], stream) == EOF) {
+ XFREE(buf);
+ return MP_VAL;
+ }
+ }
+
+ XFREE(buf);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_GCD_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* Greatest Common Divisor using the binary method */
+int mp_gcd(mp_int *a, mp_int *b, mp_int *c) {
+ mp_int u, v;
+ int k, u_lsb, v_lsb, res;
+
+ /* either zero than gcd is the largest */
+ if (mp_iszero(a) == MP_YES) {
+ return mp_abs(b, c);
+ }
+ if (mp_iszero(b) == MP_YES) {
+ return mp_abs(a, c);
+ }
+
+ /* get copies of a and b we can modify */
+ if ((res = mp_init_copy(&u, a)) != MP_OKAY) {
+ return res;
+ }
+
+ if ((res = mp_init_copy(&v, b)) != MP_OKAY) {
+ goto LBL_U;
+ }
+
+ /* must be positive for the remainder of the algorithm */
+ u.sign = v.sign = MP_ZPOS;
+
+ /* B1. Find the common power of two for u and v */
+ u_lsb = mp_cnt_lsb(&u);
+ v_lsb = mp_cnt_lsb(&v);
+ k = MIN(u_lsb, v_lsb);
+
+ if (k > 0) {
+ /* divide the power of two out */
+ if ((res = mp_div_2d(&u, k, &u, NULL)) != MP_OKAY) {
+ goto LBL_V;
+ }
+
+ if ((res = mp_div_2d(&v, k, &v, NULL)) != MP_OKAY) {
+ goto LBL_V;
+ }
+ }
+
+ /* divide any remaining factors of two out */
+ if (u_lsb != k) {
+ if ((res = mp_div_2d(&u, u_lsb - k, &u, NULL)) != MP_OKAY) {
+ goto LBL_V;
+ }
+ }
+
+ if (v_lsb != k) {
+ if ((res = mp_div_2d(&v, v_lsb - k, &v, NULL)) != MP_OKAY) {
+ goto LBL_V;
+ }
+ }
+
+ while (mp_iszero(&v) == MP_NO) {
+ /* make sure v is the largest */
+ if (mp_cmp_mag(&u, &v) == MP_GT) {
+ /* swap u and v to make sure v is >= u */
+ mp_exch(&u, &v);
+ }
+
+ /* subtract smallest from largest */
+ if ((res = s_mp_sub(&v, &u, &v)) != MP_OKAY) {
+ goto LBL_V;
+ }
+
+ /* Divide out all factors of two */
+ if ((res = mp_div_2d(&v, mp_cnt_lsb(&v), &v, NULL)) != MP_OKAY) {
+ goto LBL_V;
+ }
+ }
+
+ /* multiply by 2**k which we divided out at the beginning */
+ if ((res = mp_mul_2d(&u, k, c)) != MP_OKAY) {
+ goto LBL_V;
+ }
+ c->sign = MP_ZPOS;
+ res = MP_OKAY;
+LBL_V: mp_clear(&u);
+LBL_U: mp_clear(&v);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_GET_INT_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* get the lower 32-bits of an mp_int */
+unsigned long mp_get_int(mp_int *a) {
+ int i;
+ mp_min_u32 res;
+
+ if (a->used == 0) {
+ return 0;
+ }
+
+ /* get number of digits of the lsb we have to read */
+ i = MIN(a->used, (int)(((sizeof(unsigned long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1;
+
+ /* get most significant digit of result */
+ res = DIGIT(a, i);
+
+ while (--i >= 0) {
+ res = (res << DIGIT_BIT) | DIGIT(a, i);
+ }
+
+ /* force result to 32-bits always so it is consistent on non 32-bit platforms */
+ return res & 0xFFFFFFFFUL;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_GET_LONG_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* get the lower unsigned long of an mp_int, platform dependent */
+unsigned long mp_get_long(mp_int *a) {
+ int i;
+ unsigned long res;
+
+ if (a->used == 0) {
+ return 0;
+ }
+
+ /* get number of digits of the lsb we have to read */
+ i = MIN(a->used, (int)(((sizeof(unsigned long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1;
+
+ /* get most significant digit of result */
+ res = DIGIT(a, i);
+
+ #if (ULONG_MAX != 0xffffffffuL) || (DIGIT_BIT < 32)
+ while (--i >= 0) {
+ res = (res << DIGIT_BIT) | DIGIT(a, i);
+ }
+ #endif
+ return res;
+}
+#endif
+
+
+
+#ifdef BN_MP_GET_LONG_LONG_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* get the lower unsigned long long of an mp_int, platform dependent */
+unsigned long long mp_get_long_long(mp_int *a) {
+ int i;
+ unsigned long long res;
+
+ if (a->used == 0) {
+ return 0;
+ }
+
+ /* get number of digits of the lsb we have to read */
+ i = MIN(a->used, (int)(((sizeof(unsigned long long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1;
+
+ /* get most significant digit of result */
+ res = DIGIT(a, i);
+
+ #if DIGIT_BIT < 64
+ while (--i >= 0) {
+ res = (res << DIGIT_BIT) | DIGIT(a, i);
+ }
+ #endif
+ return res;
+}
+#endif
+
+
+
+#ifdef BN_MP_GROW_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* grow as required */
+int mp_grow(mp_int *a, int size) {
+ int i;
+ mp_digit *tmp;
+
+ /* if the alloc size is smaller alloc more ram */
+ if (a->alloc < size) {
+ /* ensure there are always at least MP_PREC digits extra on top */
+ size += (MP_PREC * 2) - (size % MP_PREC);
+
+ /* reallocate the array a->dp
+ *
+ * We store the return in a temporary variable
+ * in case the operation failed we don't want
+ * to overwrite the dp member of a.
+ */
+ tmp = OPT_CAST(mp_digit) XREALLOC(a->dp, sizeof(mp_digit) * size);
+ if (tmp == NULL) {
+ /* reallocation failed but "a" is still valid [can be freed] */
+ return MP_MEM;
+ }
+
+ /* reallocation succeeded so set a->dp */
+ a->dp = tmp;
+
+ /* zero excess digits */
+ i = a->alloc;
+ a->alloc = size;
+ for ( ; i < a->alloc; i++) {
+ a->dp[i] = 0;
+ }
+ }
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_IMPORT_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* based on gmp's mpz_import.
+ * see http://gmplib.org/manual/Integer-Import-and-Export.html
+ */
+int mp_import(mp_int *rop, size_t count, int order, size_t size,
+ int endian, size_t nails, const void *op) {
+ int result;
+ size_t odd_nails, nail_bytes, i, j;
+ unsigned char odd_nail_mask;
+
+ mp_zero(rop);
+
+ if (endian == 0) {
+ union {
+ unsigned int i;
+ char c[4];
+ } lint;
+ lint.i = 0x01020304;
+
+ endian = (lint.c[0] == 4) ? -1 : 1;
+ }
+
+ odd_nails = (nails % 8);
+ odd_nail_mask = 0xff;
+ for (i = 0; i < odd_nails; ++i) {
+ odd_nail_mask ^= (1 << (7 - i));
+ }
+ nail_bytes = nails / 8;
+
+ for (i = 0; i < count; ++i) {
+ for (j = 0; j < (size - nail_bytes); ++j) {
+ unsigned char byte = *(
+ (unsigned char *)op +
+ (((order == 1) ? i : ((count - 1) - i)) * size) +
+ ((endian == 1) ? (j + nail_bytes) : (((size - 1) - j) - nail_bytes))
+ );
+
+ if (
+ (result = mp_mul_2d(rop, ((j == 0) ? (8 - odd_nails) : 8), rop)) != MP_OKAY) {
+ return result;
+ }
+
+ rop->dp[0] |= (j == 0) ? (byte & odd_nail_mask) : byte;
+ rop->used += 1;
+ }
+ }
+
+ mp_clamp(rop);
+
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_INIT_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* init a new mp_int */
+int mp_init(mp_int *a) {
+ int i;
+
+ /* allocate memory required and clear it */
+ a->dp = OPT_CAST(mp_digit) XMALLOC(sizeof(mp_digit) * MP_PREC);
+ if (a->dp == NULL) {
+ return MP_MEM;
+ }
+
+ /* set the digits to zero */
+ for (i = 0; i < MP_PREC; i++) {
+ a->dp[i] = 0;
+ }
+
+ /* set the used to zero, allocated digits to the default precision
+ * and sign to positive */
+ a->used = 0;
+ a->alloc = MP_PREC;
+ a->sign = MP_ZPOS;
+
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_INIT_COPY_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* creates "a" then copies b into it */
+int mp_init_copy(mp_int *a, mp_int *b) {
+ int res;
+
+ if ((res = mp_init_size(a, b->used)) != MP_OKAY) {
+ return res;
+ }
+ return mp_copy(b, a);
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_INIT_MULTI_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+#include
+
+int mp_init_multi(mp_int *mp, ...) {
+ mp_err res = MP_OKAY; /* Assume ok until proven otherwise */
+ int n = 0; /* Number of ok inits */
+ mp_int *cur_arg = mp;
+ va_list args;
+
+ va_start(args, mp); /* init args to next argument from caller */
+ while (cur_arg != NULL) {
+ if (mp_init(cur_arg) != MP_OKAY) {
+ /* Oops - error! Back-track and mp_clear what we already
+ succeeded in init-ing, then return error.
+ */
+ va_list clean_args;
+
+ /* end the current list */
+ va_end(args);
+
+ /* now start cleaning up */
+ cur_arg = mp;
+ va_start(clean_args, mp);
+ while (n-- != 0) {
+ mp_clear(cur_arg);
+ cur_arg = va_arg(clean_args, mp_int *);
+ }
+ va_end(clean_args);
+ res = MP_MEM;
+ break;
+ }
+ n++;
+ cur_arg = va_arg(args, mp_int *);
+ }
+ va_end(args);
+ return res; /* Assumed ok, if error flagged above. */
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_INIT_SET_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* initialize and set a digit */
+int mp_init_set(mp_int *a, mp_digit b) {
+ int err;
+
+ if ((err = mp_init(a)) != MP_OKAY) {
+ return err;
+ }
+ mp_set(a, b);
+ return err;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_INIT_SET_INT_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* initialize and set a digit */
+int mp_init_set_int(mp_int *a, unsigned long b) {
+ int err;
+
+ if ((err = mp_init(a)) != MP_OKAY) {
+ return err;
+ }
+ return mp_set_int(a, b);
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_INIT_SIZE_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* init an mp_init for a given size */
+int mp_init_size(mp_int *a, int size) {
+ int x;
+
+ /* pad size so there are always extra digits */
+ size += (MP_PREC * 2) - (size % MP_PREC);
+
+ /* alloc mem */
+ a->dp = OPT_CAST(mp_digit) XMALLOC(sizeof(mp_digit) * size);
+ if (a->dp == NULL) {
+ return MP_MEM;
+ }
+
+ /* set the members */
+ a->used = 0;
+ a->alloc = size;
+ a->sign = MP_ZPOS;
+
+ /* zero the digits */
+ for (x = 0; x < size; x++) {
+ a->dp[x] = 0;
+ }
+
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_INVMOD_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* hac 14.61, pp608 */
+int mp_invmod(mp_int *a, mp_int *b, mp_int *c) {
+ /* b cannot be negative */
+ if ((b->sign == MP_NEG) || (mp_iszero(b) == MP_YES)) {
+ return MP_VAL;
+ }
+
+ #ifdef BN_FAST_MP_INVMOD_C
+ /* if the modulus is odd we can use a faster routine instead */
+ if (mp_isodd(b) == MP_YES) {
+ return fast_mp_invmod(a, b, c);
+ }
+ #endif
+
+ #ifdef BN_MP_INVMOD_SLOW_C
+ return mp_invmod_slow(a, b, c);
+ #else
+ return MP_VAL;
+ #endif
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_INVMOD_SLOW_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* hac 14.61, pp608 */
+int mp_invmod_slow(mp_int *a, mp_int *b, mp_int *c) {
+ mp_int x, y, u, v, A, B, C, D;
+ int res;
+
+ /* b cannot be negative */
+ if ((b->sign == MP_NEG) || (mp_iszero(b) == MP_YES)) {
+ return MP_VAL;
+ }
+
+ /* init temps */
+ if ((res = mp_init_multi(&x, &y, &u, &v,
+ &A, &B, &C, &D, NULL)) != MP_OKAY) {
+ return res;
+ }
+
+ /* x = a, y = b */
+ if ((res = mp_mod(a, b, &x)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if ((res = mp_copy(b, &y)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+
+ /* 2. [modified] if x,y are both even then return an error! */
+ if ((mp_iseven(&x) == MP_YES) && (mp_iseven(&y) == MP_YES)) {
+ res = MP_VAL;
+ goto LBL_ERR;
+ }
+
+ /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */
+ if ((res = mp_copy(&x, &u)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if ((res = mp_copy(&y, &v)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ mp_set(&A, 1);
+ mp_set(&D, 1);
+
+top:
+ /* 4. while u is even do */
+ while (mp_iseven(&u) == MP_YES) {
+ /* 4.1 u = u/2 */
+ if ((res = mp_div_2(&u, &u)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ /* 4.2 if A or B is odd then */
+ if ((mp_isodd(&A) == MP_YES) || (mp_isodd(&B) == MP_YES)) {
+ /* A = (A+y)/2, B = (B-x)/2 */
+ if ((res = mp_add(&A, &y, &A)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if ((res = mp_sub(&B, &x, &B)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+ /* A = A/2, B = B/2 */
+ if ((res = mp_div_2(&A, &A)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if ((res = mp_div_2(&B, &B)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* 5. while v is even do */
+ while (mp_iseven(&v) == MP_YES) {
+ /* 5.1 v = v/2 */
+ if ((res = mp_div_2(&v, &v)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ /* 5.2 if C or D is odd then */
+ if ((mp_isodd(&C) == MP_YES) || (mp_isodd(&D) == MP_YES)) {
+ /* C = (C+y)/2, D = (D-x)/2 */
+ if ((res = mp_add(&C, &y, &C)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if ((res = mp_sub(&D, &x, &D)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+ /* C = C/2, D = D/2 */
+ if ((res = mp_div_2(&C, &C)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if ((res = mp_div_2(&D, &D)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* 6. if u >= v then */
+ if (mp_cmp(&u, &v) != MP_LT) {
+ /* u = u - v, A = A - C, B = B - D */
+ if ((res = mp_sub(&u, &v, &u)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+
+ if ((res = mp_sub(&A, &C, &A)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+
+ if ((res = mp_sub(&B, &D, &B)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ } else {
+ /* v - v - u, C = C - A, D = D - B */
+ if ((res = mp_sub(&v, &u, &v)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+
+ if ((res = mp_sub(&C, &A, &C)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+
+ if ((res = mp_sub(&D, &B, &D)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* if not zero goto step 4 */
+ if (mp_iszero(&u) == MP_NO)
+ goto top;
+
+ /* now a = C, b = D, gcd == g*v */
+
+ /* if v != 1 then there is no inverse */
+ if (mp_cmp_d(&v, 1) != MP_EQ) {
+ res = MP_VAL;
+ goto LBL_ERR;
+ }
+
+ /* if its too low */
+ while (mp_cmp_d(&C, 0) == MP_LT) {
+ if ((res = mp_add(&C, b, &C)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* too big */
+ while (mp_cmp_mag(&C, b) != MP_LT) {
+ if ((res = mp_sub(&C, b, &C)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* C is now the inverse */
+ mp_exch(&C, c);
+ res = MP_OKAY;
+LBL_ERR: mp_clear_multi(&x, &y, &u, &v, &A, &B, &C, &D, NULL);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_IS_SQUARE_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* Check if remainders are possible squares - fast exclude non-squares */
+static const char rem_128[128] = {
+ 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
+ 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
+ 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
+ 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
+ 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
+ 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
+ 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1
+};
+
+static const char rem_105[105] = {
+ 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
+ 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1,
+ 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
+ 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1,
+ 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1
+};
+
+/* Store non-zero to ret if arg is square, and zero if not */
+int mp_is_square(mp_int *arg, int *ret) {
+ int res;
+ mp_digit c;
+ mp_int t;
+ unsigned long r;
+
+ /* Default to Non-square :) */
+ *ret = MP_NO;
+
+ if (arg->sign == MP_NEG) {
+ return MP_VAL;
+ }
+
+ /* digits used? (TSD) */
+ if (arg->used == 0) {
+ return MP_OKAY;
+ }
+
+ /* First check mod 128 (suppose that DIGIT_BIT is at least 7) */
+ if (rem_128[127 & DIGIT(arg, 0)] == 1) {
+ return MP_OKAY;
+ }
+
+ /* Next check mod 105 (3*5*7) */
+ if ((res = mp_mod_d(arg, 105, &c)) != MP_OKAY) {
+ return res;
+ }
+ if (rem_105[c] == 1) {
+ return MP_OKAY;
+ }
+
+
+ if ((res = mp_init_set_int(&t, 11L * 13L * 17L * 19L * 23L * 29L * 31L)) != MP_OKAY) {
+ return res;
+ }
+ if ((res = mp_mod(arg, &t, &t)) != MP_OKAY) {
+ goto ERR;
+ }
+ r = mp_get_int(&t);
+
+ /* Check for other prime modules, note it's not an ERROR but we must
+ * free "t" so the easiest way is to goto ERR. We know that res
+ * is already equal to MP_OKAY from the mp_mod call
+ */
+ if (((1L << (r % 11)) & 0x5C4L) != 0L) goto ERR;
+ if (((1L << (r % 13)) & 0x9E4L) != 0L) goto ERR;
+ if (((1L << (r % 17)) & 0x5CE8L) != 0L) goto ERR;
+ if (((1L << (r % 19)) & 0x4F50CL) != 0L) goto ERR;
+ if (((1L << (r % 23)) & 0x7ACCA0L) != 0L) goto ERR;
+ if (((1L << (r % 29)) & 0xC2EDD0CL) != 0L) goto ERR;
+ if (((1L << (r % 31)) & 0x6DE2B848L) != 0L) goto ERR;
+
+ /* Final check - is sqr(sqrt(arg)) == arg ? */
+ if ((res = mp_sqrt(arg, &t)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_sqr(&t, &t)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ *ret = (mp_cmp_mag(&t, arg) == MP_EQ) ? MP_YES : MP_NO;
+ERR: mp_clear(&t);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_JACOBI_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* computes the jacobi c = (a | n) (or Legendre if n is prime)
+ * HAC pp. 73 Algorithm 2.149
+ * HAC is wrong here, as the special case of (0 | 1) is not
+ * handled correctly.
+ */
+int mp_jacobi(mp_int *a, mp_int *n, int *c) {
+ mp_int a1, p1;
+ int k, s, r, res;
+ mp_digit residue;
+
+ /* if n <= 0 return MP_VAL */
+ if (mp_cmp_d(n, 0) != MP_GT) {
+ return MP_VAL;
+ }
+
+ /* step 1. handle case of a == 0 */
+ if (mp_iszero(a) == MP_YES) {
+ /* special case of a == 0 and n == 1 */
+ if (mp_cmp_d(n, 1) == MP_EQ) {
+ *c = 1;
+ } else {
+ *c = 0;
+ }
+ return MP_OKAY;
+ }
+
+ /* step 2. if a == 1, return 1 */
+ if (mp_cmp_d(a, 1) == MP_EQ) {
+ *c = 1;
+ return MP_OKAY;
+ }
+
+ /* default */
+ s = 0;
+
+ /* step 3. write a = a1 * 2**k */
+ if ((res = mp_init_copy(&a1, a)) != MP_OKAY) {
+ return res;
+ }
+
+ if ((res = mp_init(&p1)) != MP_OKAY) {
+ goto LBL_A1;
+ }
+
+ /* divide out larger power of two */
+ k = mp_cnt_lsb(&a1);
+ if ((res = mp_div_2d(&a1, k, &a1, NULL)) != MP_OKAY) {
+ goto LBL_P1;
+ }
+
+ /* step 4. if e is even set s=1 */
+ if ((k & 1) == 0) {
+ s = 1;
+ } else {
+ /* else set s=1 if p = 1/7 (mod 8) or s=-1 if p = 3/5 (mod 8) */
+ residue = n->dp[0] & 7;
+
+ if ((residue == 1) || (residue == 7)) {
+ s = 1;
+ } else if ((residue == 3) || (residue == 5)) {
+ s = -1;
+ }
+ }
+
+ /* step 5. if p == 3 (mod 4) *and* a1 == 3 (mod 4) then s = -s */
+ if (((n->dp[0] & 3) == 3) && ((a1.dp[0] & 3) == 3)) {
+ s = -s;
+ }
+
+ /* if a1 == 1 we're done */
+ if (mp_cmp_d(&a1, 1) == MP_EQ) {
+ *c = s;
+ } else {
+ /* n1 = n mod a1 */
+ if ((res = mp_mod(n, &a1, &p1)) != MP_OKAY) {
+ goto LBL_P1;
+ }
+ if ((res = mp_jacobi(&p1, &a1, &r)) != MP_OKAY) {
+ goto LBL_P1;
+ }
+ *c = s * r;
+ }
+
+ /* done */
+ res = MP_OKAY;
+LBL_P1: mp_clear(&p1);
+LBL_A1: mp_clear(&a1);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_KARATSUBA_MUL_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* c = |a| * |b| using Karatsuba Multiplication using
+ * three half size multiplications
+ *
+ * Let B represent the radix [e.g. 2**DIGIT_BIT] and
+ * let n represent half of the number of digits in
+ * the min(a,b)
+ *
+ * a = a1 * B**n + a0
+ * b = b1 * B**n + b0
+ *
+ * Then, a * b =>
+ a1b1 * B**2n + ((a1 + a0)(b1 + b0) - (a0b0 + a1b1)) * B + a0b0
+ *
+ * Note that a1b1 and a0b0 are used twice and only need to be
+ * computed once. So in total three half size (half # of
+ * digit) multiplications are performed, a0b0, a1b1 and
+ * (a1+b1)(a0+b0)
+ *
+ * Note that a multiplication of half the digits requires
+ * 1/4th the number of single precision multiplications so in
+ * total after one call 25% of the single precision multiplications
+ * are saved. Note also that the call to mp_mul can end up back
+ * in this function if the a0, a1, b0, or b1 are above the threshold.
+ * This is known as divide-and-conquer and leads to the famous
+ * O(N**lg(3)) or O(N**1.584) work which is asymptopically lower than
+ * the standard O(N**2) that the baseline/comba methods use.
+ * Generally though the overhead of this method doesn't pay off
+ * until a certain size (N ~ 80) is reached.
+ */
+int mp_karatsuba_mul(mp_int *a, mp_int *b, mp_int *c) {
+ mp_int x0, x1, y0, y1, t1, x0y0, x1y1;
+ int B, err;
+
+ /* default the return code to an error */
+ err = MP_MEM;
+
+ /* min # of digits */
+ B = MIN(a->used, b->used);
+
+ /* now divide in two */
+ B = B >> 1;
+
+ /* init copy all the temps */
+ if (mp_init_size(&x0, B) != MP_OKAY)
+ goto ERR;
+ if (mp_init_size(&x1, a->used - B) != MP_OKAY)
+ goto X0;
+ if (mp_init_size(&y0, B) != MP_OKAY)
+ goto X1;
+ if (mp_init_size(&y1, b->used - B) != MP_OKAY)
+ goto Y0;
+
+ /* init temps */
+ if (mp_init_size(&t1, B * 2) != MP_OKAY)
+ goto Y1;
+ if (mp_init_size(&x0y0, B * 2) != MP_OKAY)
+ goto T1;
+ if (mp_init_size(&x1y1, B * 2) != MP_OKAY)
+ goto X0Y0;
+
+ /* now shift the digits */
+ x0.used = y0.used = B;
+ x1.used = a->used - B;
+ y1.used = b->used - B;
+
+ {
+ int x;
+ mp_digit *tmpa, *tmpb, *tmpx, *tmpy;
+
+ /* we copy the digits directly instead of using higher level functions
+ * since we also need to shift the digits
+ */
+ tmpa = a->dp;
+ tmpb = b->dp;
+
+ tmpx = x0.dp;
+ tmpy = y0.dp;
+ for (x = 0; x < B; x++) {
+ *tmpx++ = *tmpa++;
+ *tmpy++ = *tmpb++;
+ }
+
+ tmpx = x1.dp;
+ for (x = B; x < a->used; x++) {
+ *tmpx++ = *tmpa++;
+ }
+
+ tmpy = y1.dp;
+ for (x = B; x < b->used; x++) {
+ *tmpy++ = *tmpb++;
+ }
+ }
+
+ /* only need to clamp the lower words since by definition the
+ * upper words x1/y1 must have a known number of digits
+ */
+ mp_clamp(&x0);
+ mp_clamp(&y0);
+
+ /* now calc the products x0y0 and x1y1 */
+ /* after this x0 is no longer required, free temp [x0==t2]! */
+ if (mp_mul(&x0, &y0, &x0y0) != MP_OKAY)
+ goto X1Y1; /* x0y0 = x0*y0 */
+ if (mp_mul(&x1, &y1, &x1y1) != MP_OKAY)
+ goto X1Y1; /* x1y1 = x1*y1 */
+
+ /* now calc x1+x0 and y1+y0 */
+ if (s_mp_add(&x1, &x0, &t1) != MP_OKAY)
+ goto X1Y1; /* t1 = x1 - x0 */
+ if (s_mp_add(&y1, &y0, &x0) != MP_OKAY)
+ goto X1Y1; /* t2 = y1 - y0 */
+ if (mp_mul(&t1, &x0, &t1) != MP_OKAY)
+ goto X1Y1; /* t1 = (x1 + x0) * (y1 + y0) */
+
+ /* add x0y0 */
+ if (mp_add(&x0y0, &x1y1, &x0) != MP_OKAY)
+ goto X1Y1; /* t2 = x0y0 + x1y1 */
+ if (s_mp_sub(&t1, &x0, &t1) != MP_OKAY)
+ goto X1Y1; /* t1 = (x1+x0)*(y1+y0) - (x1y1 + x0y0) */
+
+ /* shift by B */
+ if (mp_lshd(&t1, B) != MP_OKAY)
+ goto X1Y1; /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<used;
+
+ /* now divide in two */
+ B = B >> 1;
+
+ /* init copy all the temps */
+ if (mp_init_size(&x0, B) != MP_OKAY)
+ goto ERR;
+ if (mp_init_size(&x1, a->used - B) != MP_OKAY)
+ goto X0;
+
+ /* init temps */
+ if (mp_init_size(&t1, a->used * 2) != MP_OKAY)
+ goto X1;
+ if (mp_init_size(&t2, a->used * 2) != MP_OKAY)
+ goto T1;
+ if (mp_init_size(&x0x0, B * 2) != MP_OKAY)
+ goto T2;
+ if (mp_init_size(&x1x1, (a->used - B) * 2) != MP_OKAY)
+ goto X0X0;
+
+ {
+ int x;
+ mp_digit *dst, *src;
+
+ src = a->dp;
+
+ /* now shift the digits */
+ dst = x0.dp;
+ for (x = 0; x < B; x++) {
+ *dst++ = *src++;
+ }
+
+ dst = x1.dp;
+ for (x = B; x < a->used; x++) {
+ *dst++ = *src++;
+ }
+ }
+
+ x0.used = B;
+ x1.used = a->used - B;
+
+ mp_clamp(&x0);
+
+ /* now calc the products x0*x0 and x1*x1 */
+ if (mp_sqr(&x0, &x0x0) != MP_OKAY)
+ goto X1X1; /* x0x0 = x0*x0 */
+ if (mp_sqr(&x1, &x1x1) != MP_OKAY)
+ goto X1X1; /* x1x1 = x1*x1 */
+
+ /* now calc (x1+x0)**2 */
+ if (s_mp_add(&x1, &x0, &t1) != MP_OKAY)
+ goto X1X1; /* t1 = x1 - x0 */
+ if (mp_sqr(&t1, &t1) != MP_OKAY)
+ goto X1X1; /* t1 = (x1 - x0) * (x1 - x0) */
+
+ /* add x0y0 */
+ if (s_mp_add(&x0x0, &x1x1, &t2) != MP_OKAY)
+ goto X1X1; /* t2 = x0x0 + x1x1 */
+ if (s_mp_sub(&t1, &t2, &t1) != MP_OKAY)
+ goto X1X1; /* t1 = (x1+x0)**2 - (x0x0 + x1x1) */
+
+ /* shift by B */
+ if (mp_lshd(&t1, B) != MP_OKAY)
+ goto X1X1; /* t1 = (x0x0 + x1x1 - (x1-x0)*(x1-x0))<sign = MP_ZPOS;
+
+LBL_T:
+ mp_clear_multi(&t1, &t2, NULL);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_LSHD_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* shift left a certain amount of digits */
+int mp_lshd(mp_int *a, int b) {
+ int x, res;
+
+ /* if its less than zero return */
+ if (b <= 0) {
+ return MP_OKAY;
+ }
+
+ /* grow to fit the new digits */
+ if (a->alloc < (a->used + b)) {
+ if ((res = mp_grow(a, a->used + b)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ {
+ mp_digit *top, *bottom;
+
+ /* increment the used by the shift amount then copy upwards */
+ a->used += b;
+
+ /* top */
+ top = a->dp + a->used - 1;
+
+ /* base */
+ bottom = (a->dp + a->used - 1) - b;
+
+ /* much like mp_rshd this is implemented using a sliding window
+ * except the window goes the otherway around. Copying from
+ * the bottom to the top. see bn_mp_rshd.c for more info.
+ */
+ for (x = a->used - 1; x >= b; x--) {
+ *top-- = *bottom--;
+ }
+
+ /* zero the lower digits */
+ top = a->dp;
+ for (x = 0; x < b; x++) {
+ *top++ = 0;
+ }
+ }
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_MOD_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* c = a mod b, 0 <= c < b if b > 0, b < c <= 0 if b < 0 */
+int
+mp_mod(mp_int *a, mp_int *b, mp_int *c) {
+ mp_int t;
+ int res;
+
+ if ((res = mp_init(&t)) != MP_OKAY) {
+ return res;
+ }
+
+ if ((res = mp_div(a, b, NULL, &t)) != MP_OKAY) {
+ mp_clear(&t);
+ return res;
+ }
+
+ if ((mp_iszero(&t) != MP_NO) || (t.sign == b->sign)) {
+ res = MP_OKAY;
+ mp_exch(&t, c);
+ } else {
+ res = mp_add(b, &t, c);
+ }
+
+ mp_clear(&t);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_MOD_2D_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* calc a value mod 2**b */
+int
+mp_mod_2d(mp_int *a, int b, mp_int *c) {
+ int x, res;
+
+ /* if b is <= 0 then zero the int */
+ if (b <= 0) {
+ mp_zero(c);
+ return MP_OKAY;
+ }
+
+ /* if the modulus is larger than the value than return */
+ if (b >= (int)(a->used * DIGIT_BIT)) {
+ res = mp_copy(a, c);
+ return res;
+ }
+
+ /* copy */
+ if ((res = mp_copy(a, c)) != MP_OKAY) {
+ return res;
+ }
+
+ /* zero digits above the last digit of the modulus */
+ for (x = (b / DIGIT_BIT) + (((b % DIGIT_BIT) == 0) ? 0 : 1); x < c->used; x++) {
+ c->dp[x] = 0;
+ }
+ /* clear the digit that is not completely outside/inside the modulus */
+ c->dp[b / DIGIT_BIT] &=
+ (mp_digit)((((mp_digit)1) << (((mp_digit)b) % DIGIT_BIT)) - ((mp_digit)1));
+ mp_clamp(c);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_MOD_D_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+int
+mp_mod_d(mp_int *a, mp_digit b, mp_digit *c) {
+ return mp_div_d(a, b, NULL, c);
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/*
+ * shifts with subtractions when the result is greater than b.
+ *
+ * The method is slightly modified to shift B unconditionally upto just under
+ * the leading bit of b. This saves alot of multiple precision shifting.
+ */
+int mp_montgomery_calc_normalization(mp_int *a, mp_int *b) {
+ int x, bits, res;
+
+ /* how many bits of last digit does b use */
+ bits = mp_count_bits(b) % DIGIT_BIT;
+
+ if (b->used > 1) {
+ if ((res = mp_2expt(a, ((b->used - 1) * DIGIT_BIT) + bits - 1)) != MP_OKAY) {
+ return res;
+ }
+ } else {
+ mp_set(a, 1);
+ bits = 1;
+ }
+
+
+ /* now compute C = A * B mod b */
+ for (x = bits - 1; x < (int)DIGIT_BIT; x++) {
+ if ((res = mp_mul_2(a, a)) != MP_OKAY) {
+ return res;
+ }
+ if (mp_cmp_mag(a, b) != MP_LT) {
+ if ((res = s_mp_sub(a, b, a)) != MP_OKAY) {
+ return res;
+ }
+ }
+ }
+
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_MONTGOMERY_REDUCE_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* computes xR**-1 == x (mod N) via Montgomery Reduction */
+int
+mp_montgomery_reduce(mp_int *x, mp_int *n, mp_digit rho) {
+ int ix, res, digs;
+ mp_digit mu;
+
+ /* can the fast reduction [comba] method be used?
+ *
+ * Note that unlike in mul you're safely allowed *less*
+ * than the available columns [255 per default] since carries
+ * are fixed up in the inner loop.
+ */
+ digs = (n->used * 2) + 1;
+ if ((digs < MP_WARRAY) &&
+ (n->used <
+ (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) {
+ return fast_mp_montgomery_reduce(x, n, rho);
+ }
+
+ /* grow the input as required */
+ if (x->alloc < digs) {
+ if ((res = mp_grow(x, digs)) != MP_OKAY) {
+ return res;
+ }
+ }
+ x->used = digs;
+
+ for (ix = 0; ix < n->used; ix++) {
+ /* mu = ai * rho mod b
+ *
+ * The value of rho must be precalculated via
+ * montgomery_setup() such that
+ * it equals -1/n0 mod b this allows the
+ * following inner loop to reduce the
+ * input one digit at a time
+ */
+ mu = (mp_digit)(((mp_word)x->dp[ix] * (mp_word)rho) & MP_MASK);
+
+ /* a = a + mu * m * b**i */
+ {
+ int iy;
+ mp_digit *tmpn, *tmpx, u;
+ mp_word r;
+
+ /* alias for digits of the modulus */
+ tmpn = n->dp;
+
+ /* alias for the digits of x [the input] */
+ tmpx = x->dp + ix;
+
+ /* set the carry to zero */
+ u = 0;
+
+ /* Multiply and add in place */
+ for (iy = 0; iy < n->used; iy++) {
+ /* compute product and sum */
+ r = ((mp_word)mu * (mp_word) * tmpn++) +
+ (mp_word)u + (mp_word) * tmpx;
+
+ /* get carry */
+ u = (mp_digit)(r >> ((mp_word)DIGIT_BIT));
+
+ /* fix digit */
+ *tmpx++ = (mp_digit)(r & ((mp_word)MP_MASK));
+ }
+ /* At this point the ix'th digit of x should be zero */
+
+
+ /* propagate carries upwards as required*/
+ while (u != 0) {
+ *tmpx += u;
+ u = *tmpx >> DIGIT_BIT;
+ *tmpx++ &= MP_MASK;
+ }
+ }
+ }
+
+ /* at this point the n.used'th least
+ * significant digits of x are all zero
+ * which means we can shift x to the
+ * right by n.used digits and the
+ * residue is unchanged.
+ */
+
+ /* x = x/b**n.used */
+ mp_clamp(x);
+ mp_rshd(x, n->used);
+
+ /* if x >= n then x = x - n */
+ if (mp_cmp_mag(x, n) != MP_LT) {
+ return s_mp_sub(x, n, x);
+ }
+
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_MONTGOMERY_SETUP_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* setups the montgomery reduction stuff */
+int
+mp_montgomery_setup(mp_int *n, mp_digit *rho) {
+ mp_digit x, b;
+
+/* fast inversion mod 2**k
+ *
+ * Based on the fact that
+ *
+ * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n)
+ * => 2*X*A - X*X*A*A = 1
+ * => 2*(1) - (1) = 1
+ */
+ b = n->dp[0];
+
+ if ((b & 1) == 0) {
+ return MP_VAL;
+ }
+
+ x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */
+ x *= 2 - (b * x); /* here x*a==1 mod 2**8 */
+ #if !defined(MP_8BIT)
+ x *= 2 - (b * x); /* here x*a==1 mod 2**16 */
+ #endif
+ #if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT))
+ x *= 2 - (b * x); /* here x*a==1 mod 2**32 */
+ #endif
+ #ifdef MP_64BIT
+ x *= 2 - (b * x); /* here x*a==1 mod 2**64 */
+ #endif
+
+ /* rho = -1/m mod b */
+ *rho = (mp_digit)(((mp_word)1 << ((mp_word)DIGIT_BIT)) - x) & MP_MASK;
+
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_MUL_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* high level multiplication (handles sign) */
+int mp_mul(mp_int *a, mp_int *b, mp_int *c) {
+ int res, neg;
+
+ neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
+
+ /* use Toom-Cook? */
+ #ifdef BN_MP_TOOM_MUL_C
+ if (MIN(a->used, b->used) >= TOOM_MUL_CUTOFF) {
+ res = mp_toom_mul(a, b, c);
+ } else
+ #endif
+ #ifdef BN_MP_KARATSUBA_MUL_C
+ /* use Karatsuba? */
+ if (MIN(a->used, b->used) >= KARATSUBA_MUL_CUTOFF) {
+ res = mp_karatsuba_mul(a, b, c);
+ } else
+ #endif
+ {
+ /* can we use the fast multiplier?
+ *
+ * The fast multiplier can be used if the output will
+ * have less than MP_WARRAY digits and the number of
+ * digits won't affect carry propagation
+ */
+ int digs = a->used + b->used + 1;
+
+ #ifdef BN_FAST_S_MP_MUL_DIGS_C
+ if ((digs < MP_WARRAY) &&
+ (MIN(a->used, b->used) <=
+ (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) {
+ res = fast_s_mp_mul_digs(a, b, c, digs);
+ } else
+ #endif
+ {
+ #ifdef BN_S_MP_MUL_DIGS_C
+ res = s_mp_mul(a, b, c); /* uses s_mp_mul_digs */
+ #else
+ res = MP_VAL;
+ #endif
+ }
+ }
+ c->sign = (c->used > 0) ? neg : MP_ZPOS;
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_MUL_2_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* b = a*2 */
+int mp_mul_2(mp_int *a, mp_int *b) {
+ int x, res, oldused;
+
+ /* grow to accomodate result */
+ if (b->alloc < (a->used + 1)) {
+ if ((res = mp_grow(b, a->used + 1)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ oldused = b->used;
+ b->used = a->used;
+
+ {
+ mp_digit r, rr, *tmpa, *tmpb;
+
+ /* alias for source */
+ tmpa = a->dp;
+
+ /* alias for dest */
+ tmpb = b->dp;
+
+ /* carry */
+ r = 0;
+ for (x = 0; x < a->used; x++) {
+ /* get what will be the *next* carry bit from the
+ * MSB of the current digit
+ */
+ rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1));
+
+ /* now shift up this digit, add in the carry [from the previous] */
+ *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK;
+
+ /* copy the carry that would be from the source
+ * digit into the next iteration
+ */
+ r = rr;
+ }
+
+ /* new leading digit? */
+ if (r != 0) {
+ /* add a MSB which is always 1 at this point */
+ *tmpb = 1;
+ ++(b->used);
+ }
+
+ /* now zero any excess digits on the destination
+ * that we didn't write to
+ */
+ tmpb = b->dp + b->used;
+ for (x = b->used; x < oldused; x++) {
+ *tmpb++ = 0;
+ }
+ }
+ b->sign = a->sign;
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_MUL_2D_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* shift left by a certain bit count */
+int mp_mul_2d(mp_int *a, int b, mp_int *c) {
+ mp_digit d;
+ int res;
+
+ /* copy */
+ if (a != c) {
+ if ((res = mp_copy(a, c)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ if (c->alloc < (int)(c->used + (b / DIGIT_BIT) + 1)) {
+ if ((res = mp_grow(c, c->used + (b / DIGIT_BIT) + 1)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* shift by as many digits in the bit count */
+ if (b >= (int)DIGIT_BIT) {
+ if ((res = mp_lshd(c, b / DIGIT_BIT)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* shift any bit count < DIGIT_BIT */
+ d = (mp_digit)(b % DIGIT_BIT);
+ if (d != 0) {
+ mp_digit *tmpc, shift, mask, r, rr;
+ int x;
+
+ /* bitmask for carries */
+ mask = (((mp_digit)1) << d) - 1;
+
+ /* shift for msbs */
+ shift = DIGIT_BIT - d;
+
+ /* alias */
+ tmpc = c->dp;
+
+ /* carry */
+ r = 0;
+ for (x = 0; x < c->used; x++) {
+ /* get the higher bits of the current word */
+ rr = (*tmpc >> shift) & mask;
+
+ /* shift the current word and OR in the carry */
+ *tmpc = ((*tmpc << d) | r) & MP_MASK;
+ ++tmpc;
+
+ /* set the carry to the carry bits of the current word */
+ r = rr;
+ }
+
+ /* set final carry */
+ if (r != 0) {
+ c->dp[(c->used)++] = r;
+ }
+ }
+ mp_clamp(c);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_MUL_D_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* multiply by a digit */
+int
+mp_mul_d(mp_int *a, mp_digit b, mp_int *c) {
+ mp_digit u, *tmpa, *tmpc;
+ mp_word r;
+ int ix, res, olduse;
+
+ /* make sure c is big enough to hold a*b */
+ if (c->alloc < (a->used + 1)) {
+ if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* get the original destinations used count */
+ olduse = c->used;
+
+ /* set the sign */
+ c->sign = a->sign;
+
+ /* alias for a->dp [source] */
+ tmpa = a->dp;
+
+ /* alias for c->dp [dest] */
+ tmpc = c->dp;
+
+ /* zero carry */
+ u = 0;
+
+ /* compute columns */
+ for (ix = 0; ix < a->used; ix++) {
+ /* compute product and carry sum for this term */
+ r = (mp_word)u + ((mp_word) * tmpa++ *(mp_word)b);
+
+ /* mask off higher bits to get a single digit */
+ *tmpc++ = (mp_digit)(r & ((mp_word)MP_MASK));
+
+ /* send carry into next iteration */
+ u = (mp_digit)(r >> ((mp_word)DIGIT_BIT));
+ }
+
+ /* store final carry [if any] and increment ix offset */
+ *tmpc++ = u;
+ ++ix;
+
+ /* now zero digits above the top */
+ while (ix++ < olduse) {
+ *tmpc++ = 0;
+ }
+
+ /* set used count */
+ c->used = a->used + 1;
+ mp_clamp(c);
+
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_MULMOD_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* d = a * b (mod c) */
+int mp_mulmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d) {
+ int res;
+ mp_int t;
+
+ if ((res = mp_init(&t)) != MP_OKAY) {
+ return res;
+ }
+
+ if ((res = mp_mul(a, b, &t)) != MP_OKAY) {
+ mp_clear(&t);
+ return res;
+ }
+ res = mp_mod(&t, c, d);
+ mp_clear(&t);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_N_ROOT_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* wrapper function for mp_n_root_ex()
+ * computes c = (a)**(1/b) such that (c)**b <= a and (c+1)**b > a
+ */
+int mp_n_root(mp_int *a, mp_digit b, mp_int *c) {
+ return mp_n_root_ex(a, b, c, 0);
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_N_ROOT_EX_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* find the n'th root of an integer
+ *
+ * Result found such that (c)**b <= a and (c+1)**b > a
+ *
+ * This algorithm uses Newton's approximation
+ * x[i+1] = x[i] - f(x[i])/f'(x[i])
+ * which will find the root in log(N) time where
+ * each step involves a fair bit. This is not meant to
+ * find huge roots [square and cube, etc].
+ */
+int mp_n_root_ex(mp_int *a, mp_digit b, mp_int *c, int fast) {
+ mp_int t1, t2, t3;
+ int res, neg;
+
+ /* input must be positive if b is even */
+ if (((b & 1) == 0) && (a->sign == MP_NEG)) {
+ return MP_VAL;
+ }
+
+ if ((res = mp_init(&t1)) != MP_OKAY) {
+ return res;
+ }
+
+ if ((res = mp_init(&t2)) != MP_OKAY) {
+ goto LBL_T1;
+ }
+
+ if ((res = mp_init(&t3)) != MP_OKAY) {
+ goto LBL_T2;
+ }
+
+ /* if a is negative fudge the sign but keep track */
+ neg = a->sign;
+ a->sign = MP_ZPOS;
+
+ /* t2 = 2 */
+ mp_set(&t2, 2);
+
+ do {
+ /* t1 = t2 */
+ if ((res = mp_copy(&t2, &t1)) != MP_OKAY) {
+ goto LBL_T3;
+ }
+
+ /* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */
+
+ /* t3 = t1**(b-1) */
+ if ((res = mp_expt_d_ex(&t1, b - 1, &t3, fast)) != MP_OKAY) {
+ goto LBL_T3;
+ }
+
+ /* numerator */
+ /* t2 = t1**b */
+ if ((res = mp_mul(&t3, &t1, &t2)) != MP_OKAY) {
+ goto LBL_T3;
+ }
+
+ /* t2 = t1**b - a */
+ if ((res = mp_sub(&t2, a, &t2)) != MP_OKAY) {
+ goto LBL_T3;
+ }
+
+ /* denominator */
+ /* t3 = t1**(b-1) * b */
+ if ((res = mp_mul_d(&t3, b, &t3)) != MP_OKAY) {
+ goto LBL_T3;
+ }
+
+ /* t3 = (t1**b - a)/(b * t1**(b-1)) */
+ if ((res = mp_div(&t2, &t3, &t3, NULL)) != MP_OKAY) {
+ goto LBL_T3;
+ }
+
+ if ((res = mp_sub(&t1, &t3, &t2)) != MP_OKAY) {
+ goto LBL_T3;
+ }
+ } while (mp_cmp(&t1, &t2) != MP_EQ);
+
+ /* result can be off by a few so check */
+ for ( ; ; ) {
+ if ((res = mp_expt_d_ex(&t1, b, &t2, fast)) != MP_OKAY) {
+ goto LBL_T3;
+ }
+
+ if (mp_cmp(&t2, a) == MP_GT) {
+ if ((res = mp_sub_d(&t1, 1, &t1)) != MP_OKAY) {
+ goto LBL_T3;
+ }
+ } else {
+ break;
+ }
+ }
+
+ /* reset the sign of a first */
+ a->sign = neg;
+
+ /* set the result */
+ mp_exch(&t1, c);
+
+ /* set the sign of the result */
+ c->sign = neg;
+
+ res = MP_OKAY;
+
+LBL_T3: mp_clear(&t3);
+LBL_T2: mp_clear(&t2);
+LBL_T1: mp_clear(&t1);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_NEG_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* b = -a */
+int mp_neg(mp_int *a, mp_int *b) {
+ int res;
+
+ if (a != b) {
+ if ((res = mp_copy(a, b)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ if (mp_iszero(b) != MP_YES) {
+ b->sign = (a->sign == MP_ZPOS) ? MP_NEG : MP_ZPOS;
+ } else {
+ b->sign = MP_ZPOS;
+ }
+
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_OR_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* OR two ints together */
+int mp_or(mp_int *a, mp_int *b, mp_int *c) {
+ int res, ix, px;
+ mp_int t, *x;
+
+ if (a->used > b->used) {
+ if ((res = mp_init_copy(&t, a)) != MP_OKAY) {
+ return res;
+ }
+ px = b->used;
+ x = b;
+ } else {
+ if ((res = mp_init_copy(&t, b)) != MP_OKAY) {
+ return res;
+ }
+ px = a->used;
+ x = a;
+ }
+
+ for (ix = 0; ix < px; ix++) {
+ t.dp[ix] |= x->dp[ix];
+ }
+ mp_clamp(&t);
+ mp_exch(c, &t);
+ mp_clear(&t);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_PRIME_FERMAT_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* performs one Fermat test.
+ *
+ * If "a" were prime then b**a == b (mod a) since the order of
+ * the multiplicative sub-group would be phi(a) = a-1. That means
+ * it would be the same as b**(a mod (a-1)) == b**1 == b (mod a).
+ *
+ * Sets result to 1 if the congruence holds, or zero otherwise.
+ */
+int mp_prime_fermat(mp_int *a, mp_int *b, int *result) {
+ mp_int t;
+ int err;
+
+ /* default to composite */
+ *result = MP_NO;
+
+ /* ensure b > 1 */
+ if (mp_cmp_d(b, 1) != MP_GT) {
+ return MP_VAL;
+ }
+
+ /* init t */
+ if ((err = mp_init(&t)) != MP_OKAY) {
+ return err;
+ }
+
+ /* compute t = b**a mod a */
+ if ((err = mp_exptmod(b, a, a, &t)) != MP_OKAY) {
+ goto LBL_T;
+ }
+
+ /* is it equal to b? */
+ if (mp_cmp(&t, b) == MP_EQ) {
+ *result = MP_YES;
+ }
+
+ err = MP_OKAY;
+LBL_T: mp_clear(&t);
+ return err;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_PRIME_IS_DIVISIBLE_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* determines if an integers is divisible by one
+ * of the first PRIME_SIZE primes or not
+ *
+ * sets result to 0 if not, 1 if yes
+ */
+int mp_prime_is_divisible(mp_int *a, int *result) {
+ int err, ix;
+ mp_digit res;
+
+ /* default to not */
+ *result = MP_NO;
+
+ for (ix = 0; ix < PRIME_SIZE; ix++) {
+ /* what is a mod LBL_prime_tab[ix] */
+ if ((err = mp_mod_d(a, ltm_prime_tab[ix], &res)) != MP_OKAY) {
+ return err;
+ }
+
+ /* is the residue zero? */
+ if (res == 0) {
+ *result = MP_YES;
+ return MP_OKAY;
+ }
+ }
+
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_PRIME_IS_PRIME_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* performs a variable number of rounds of Miller-Rabin
+ *
+ * Probability of error after t rounds is no more than
+
+ *
+ * Sets result to 1 if probably prime, 0 otherwise
+ */
+int mp_prime_is_prime(mp_int *a, int t, int *result) {
+ mp_int b;
+ int ix, err, res;
+
+ /* default to no */
+ *result = MP_NO;
+
+ /* valid value of t? */
+ if ((t <= 0) || (t > PRIME_SIZE)) {
+ return MP_VAL;
+ }
+
+ /* is the input equal to one of the primes in the table? */
+ for (ix = 0; ix < PRIME_SIZE; ix++) {
+ if (mp_cmp_d(a, ltm_prime_tab[ix]) == MP_EQ) {
+ *result = 1;
+ return MP_OKAY;
+ }
+ }
+
+ /* first perform trial division */
+ if ((err = mp_prime_is_divisible(a, &res)) != MP_OKAY) {
+ return err;
+ }
+
+ /* return if it was trivially divisible */
+ if (res == MP_YES) {
+ return MP_OKAY;
+ }
+
+ /* now perform the miller-rabin rounds */
+ if ((err = mp_init(&b)) != MP_OKAY) {
+ return err;
+ }
+
+ for (ix = 0; ix < t; ix++) {
+ /* set the prime */
+ mp_set(&b, ltm_prime_tab[ix]);
+
+ if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) {
+ goto LBL_B;
+ }
+
+ if (res == MP_NO) {
+ goto LBL_B;
+ }
+ }
+
+ /* passed the test */
+ *result = MP_YES;
+LBL_B: mp_clear(&b);
+ return err;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_PRIME_MILLER_RABIN_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* Miller-Rabin test of "a" to the base of "b" as described in
+ * HAC pp. 139 Algorithm 4.24
+ *
+ * Sets result to 0 if definitely composite or 1 if probably prime.
+ * Randomly the chance of error is no more than 1/4 and often
+ * very much lower.
+ */
+int mp_prime_miller_rabin(mp_int *a, mp_int *b, int *result) {
+ mp_int n1, y, r;
+ int s, j, err;
+
+ /* default */
+ *result = MP_NO;
+
+ /* ensure b > 1 */
+ if (mp_cmp_d(b, 1) != MP_GT) {
+ return MP_VAL;
+ }
+
+ /* get n1 = a - 1 */
+ if ((err = mp_init_copy(&n1, a)) != MP_OKAY) {
+ return err;
+ }
+ if ((err = mp_sub_d(&n1, 1, &n1)) != MP_OKAY) {
+ goto LBL_N1;
+ }
+
+ /* set 2**s * r = n1 */
+ if ((err = mp_init_copy(&r, &n1)) != MP_OKAY) {
+ goto LBL_N1;
+ }
+
+ /* count the number of least significant bits
+ * which are zero
+ */
+ s = mp_cnt_lsb(&r);
+
+ /* now divide n - 1 by 2**s */
+ if ((err = mp_div_2d(&r, s, &r, NULL)) != MP_OKAY) {
+ goto LBL_R;
+ }
+
+ /* compute y = b**r mod a */
+ if ((err = mp_init(&y)) != MP_OKAY) {
+ goto LBL_R;
+ }
+ if ((err = mp_exptmod(b, &r, a, &y)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+
+ /* if y != 1 and y != n1 do */
+ if ((mp_cmp_d(&y, 1) != MP_EQ) && (mp_cmp(&y, &n1) != MP_EQ)) {
+ j = 1;
+ /* while j <= s-1 and y != n1 */
+ while ((j <= (s - 1)) && (mp_cmp(&y, &n1) != MP_EQ)) {
+ if ((err = mp_sqrmod(&y, a, &y)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+
+ /* if y == 1 then composite */
+ if (mp_cmp_d(&y, 1) == MP_EQ) {
+ goto LBL_Y;
+ }
+
+ ++j;
+ }
+
+ /* if y != n1 then composite */
+ if (mp_cmp(&y, &n1) != MP_EQ) {
+ goto LBL_Y;
+ }
+ }
+
+ /* probably prime now */
+ *result = MP_YES;
+LBL_Y: mp_clear(&y);
+LBL_R: mp_clear(&r);
+LBL_N1: mp_clear(&n1);
+ return err;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_PRIME_NEXT_PRIME_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* finds the next prime after the number "a" using "t" trials
+ * of Miller-Rabin.
+ *
+ * bbs_style = 1 means the prime must be congruent to 3 mod 4
+ */
+int mp_prime_next_prime(mp_int *a, int t, int bbs_style) {
+ int err, res = MP_NO, x, y;
+ mp_digit res_tab[PRIME_SIZE], step, kstep;
+ mp_int b;
+
+ /* ensure t is valid */
+ if ((t <= 0) || (t > PRIME_SIZE)) {
+ return MP_VAL;
+ }
+
+ /* force positive */
+ a->sign = MP_ZPOS;
+
+ /* simple algo if a is less than the largest prime in the table */
+ if (mp_cmp_d(a, ltm_prime_tab[PRIME_SIZE - 1]) == MP_LT) {
+ /* find which prime it is bigger than */
+ for (x = PRIME_SIZE - 2; x >= 0; x--) {
+ if (mp_cmp_d(a, ltm_prime_tab[x]) != MP_LT) {
+ if (bbs_style == 1) {
+ /* ok we found a prime smaller or
+ * equal [so the next is larger]
+ *
+ * however, the prime must be
+ * congruent to 3 mod 4
+ */
+ if ((ltm_prime_tab[x + 1] & 3) != 3) {
+ /* scan upwards for a prime congruent to 3 mod 4 */
+ for (y = x + 1; y < PRIME_SIZE; y++) {
+ if ((ltm_prime_tab[y] & 3) == 3) {
+ mp_set(a, ltm_prime_tab[y]);
+ return MP_OKAY;
+ }
+ }
+ }
+ } else {
+ mp_set(a, ltm_prime_tab[x + 1]);
+ return MP_OKAY;
+ }
+ }
+ }
+ /* at this point a maybe 1 */
+ if (mp_cmp_d(a, 1) == MP_EQ) {
+ mp_set(a, 2);
+ return MP_OKAY;
+ }
+ /* fall through to the sieve */
+ }
+
+ /* generate a prime congruent to 3 mod 4 or 1/3 mod 4? */
+ if (bbs_style == 1) {
+ kstep = 4;
+ } else {
+ kstep = 2;
+ }
+
+ /* at this point we will use a combination of a sieve and Miller-Rabin */
+
+ if (bbs_style == 1) {
+ /* if a mod 4 != 3 subtract the correct value to make it so */
+ if ((a->dp[0] & 3) != 3) {
+ if ((err = mp_sub_d(a, (a->dp[0] & 3) + 1, a)) != MP_OKAY) {
+ return err;
+ }
+ }
+ } else {
+ if (mp_iseven(a) == MP_YES) {
+ /* force odd */
+ if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) {
+ return err;
+ }
+ }
+ }
+
+ /* generate the restable */
+ for (x = 1; x < PRIME_SIZE; x++) {
+ if ((err = mp_mod_d(a, ltm_prime_tab[x], res_tab + x)) != MP_OKAY) {
+ return err;
+ }
+ }
+
+ /* init temp used for Miller-Rabin Testing */
+ if ((err = mp_init(&b)) != MP_OKAY) {
+ return err;
+ }
+
+ for ( ; ; ) {
+ /* skip to the next non-trivially divisible candidate */
+ step = 0;
+ do {
+ /* y == 1 if any residue was zero [e.g. cannot be prime] */
+ y = 0;
+
+ /* increase step to next candidate */
+ step += kstep;
+
+ /* compute the new residue without using division */
+ for (x = 1; x < PRIME_SIZE; x++) {
+ /* add the step to each residue */
+ res_tab[x] += kstep;
+
+ /* subtract the modulus [instead of using division] */
+ if (res_tab[x] >= ltm_prime_tab[x]) {
+ res_tab[x] -= ltm_prime_tab[x];
+ }
+
+ /* set flag if zero */
+ if (res_tab[x] == 0) {
+ y = 1;
+ }
+ }
+ } while ((y == 1) && (step < ((((mp_digit)1) << DIGIT_BIT) - kstep)));
+
+ /* add the step */
+ if ((err = mp_add_d(a, step, a)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+
+ /* if didn't pass sieve and step == MAX then skip test */
+ if ((y == 1) && (step >= ((((mp_digit)1) << DIGIT_BIT) - kstep))) {
+ continue;
+ }
+
+ /* is this prime? */
+ for (x = 0; x < t; x++) {
+ mp_set(&b, ltm_prime_tab[x]);
+ if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if (res == MP_NO) {
+ break;
+ }
+ }
+
+ if (res == MP_YES) {
+ break;
+ }
+ }
+
+ err = MP_OKAY;
+LBL_ERR:
+ mp_clear(&b);
+ return err;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_PRIME_RABIN_MILLER_TRIALS_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+
+static const struct {
+ int k, t;
+} sizes[] = {
+ { 128, 28 },
+ { 256, 16 },
+ { 384, 10 },
+ { 512, 7 },
+ { 640, 6 },
+ { 768, 5 },
+ { 896, 4 },
+ { 1024, 4 }
+};
+
+/* returns # of RM trials required for a given bit size */
+int mp_prime_rabin_miller_trials(int size) {
+ int x;
+
+ for (x = 0; x < (int)(sizeof(sizes) / (sizeof(sizes[0]))); x++) {
+ if (sizes[x].k == size) {
+ return sizes[x].t;
+ } else if (sizes[x].k > size) {
+ return (x == 0) ? sizes[0].t : sizes[x - 1].t;
+ }
+ }
+ return sizes[x - 1].t + 1;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_PRIME_RANDOM_EX_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* makes a truly random prime of a given size (bits),
+ *
+ * Flags are as follows:
+ *
+ * LTM_PRIME_BBS - make prime congruent to 3 mod 4
+ * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS)
+ * LTM_PRIME_2MSB_ON - make the 2nd highest bit one
+ *
+ * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can
+ * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself
+ * so it can be NULL
+ *
+ */
+
+/* This is possibly the mother of all prime generation functions, muahahahahaha! */
+int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat) {
+ unsigned char *tmp, maskAND, maskOR_msb, maskOR_lsb;
+ int res, err, bsize, maskOR_msb_offset;
+
+ /* sanity check the input */
+ if ((size <= 1) || (t <= 0)) {
+ return MP_VAL;
+ }
+
+ /* LTM_PRIME_SAFE implies LTM_PRIME_BBS */
+ if ((flags & LTM_PRIME_SAFE) != 0) {
+ flags |= LTM_PRIME_BBS;
+ }
+
+ /* calc the byte size */
+ bsize = (size >> 3) + ((size & 7) ? 1 : 0);
+
+ /* we need a buffer of bsize bytes */
+ tmp = OPT_CAST(unsigned char) XMALLOC(bsize);
+ if (tmp == NULL) {
+ return MP_MEM;
+ }
+
+ /* calc the maskAND value for the MSbyte*/
+ maskAND = ((size & 7) == 0) ? 0xFF : (0xFF >> (8 - (size & 7)));
+
+ /* calc the maskOR_msb */
+ maskOR_msb = 0;
+ maskOR_msb_offset = ((size & 7) == 1) ? 1 : 0;
+ if ((flags & LTM_PRIME_2MSB_ON) != 0) {
+ maskOR_msb |= 0x80 >> ((9 - size) & 7);
+ }
+
+ /* get the maskOR_lsb */
+ maskOR_lsb = 1;
+ if ((flags & LTM_PRIME_BBS) != 0) {
+ maskOR_lsb |= 3;
+ }
+
+ do {
+ /* read the bytes */
+ if (cb(tmp, bsize, dat) != bsize) {
+ err = MP_VAL;
+ goto error;
+ }
+
+ /* work over the MSbyte */
+ tmp[0] &= maskAND;
+ tmp[0] |= 1 << ((size - 1) & 7);
+
+ /* mix in the maskORs */
+ tmp[maskOR_msb_offset] |= maskOR_msb;
+ tmp[bsize - 1] |= maskOR_lsb;
+
+ /* read it in */
+ if ((err = mp_read_unsigned_bin(a, tmp, bsize)) != MP_OKAY) {
+ goto error;
+ }
+
+ /* is it prime? */
+ if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) {
+ goto error;
+ }
+ if (res == MP_NO) {
+ continue;
+ }
+
+ if ((flags & LTM_PRIME_SAFE) != 0) {
+ /* see if (a-1)/2 is prime */
+ if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) {
+ goto error;
+ }
+ if ((err = mp_div_2(a, a)) != MP_OKAY) {
+ goto error;
+ }
+
+ /* is it prime? */
+ if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) {
+ goto error;
+ }
+ }
+ } while (res == MP_NO);
+
+ if ((flags & LTM_PRIME_SAFE) != 0) {
+ /* restore a to the original value */
+ if ((err = mp_mul_2(a, a)) != MP_OKAY) {
+ goto error;
+ }
+ if ((err = mp_add_d(a, 1, a)) != MP_OKAY) {
+ goto error;
+ }
+ }
+
+ err = MP_OKAY;
+error:
+ XFREE(tmp);
+ return err;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_RADIX_SIZE_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* returns size of ASCII reprensentation */
+int mp_radix_size(mp_int *a, int radix, int *size) {
+ int res, digs;
+ mp_int t;
+ mp_digit d;
+
+ *size = 0;
+
+ /* make sure the radix is in range */
+ if ((radix < 2) || (radix > 64)) {
+ return MP_VAL;
+ }
+
+ if (mp_iszero(a) == MP_YES) {
+ *size = 2;
+ return MP_OKAY;
+ }
+
+ /* special case for binary */
+ if (radix == 2) {
+ *size = mp_count_bits(a) + ((a->sign == MP_NEG) ? 1 : 0) + 1;
+ return MP_OKAY;
+ }
+
+ /* digs is the digit count */
+ digs = 0;
+
+ /* if it's negative add one for the sign */
+ if (a->sign == MP_NEG) {
+ ++digs;
+ }
+
+ /* init a copy of the input */
+ if ((res = mp_init_copy(&t, a)) != MP_OKAY) {
+ return res;
+ }
+
+ /* force temp to positive */
+ t.sign = MP_ZPOS;
+
+ /* fetch out all of the digits */
+ while (mp_iszero(&t) == MP_NO) {
+ if ((res = mp_div_d(&t, (mp_digit)radix, &t, &d)) != MP_OKAY) {
+ mp_clear(&t);
+ return res;
+ }
+ ++digs;
+ }
+ mp_clear(&t);
+
+ /* return digs + 1, the 1 is for the NULL byte that would be required. */
+ *size = digs + 1;
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_RADIX_SMAP_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* chars used in radix conversions */
+const char *mp_s_rmap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_RAND_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* makes a pseudo-random int of a given size */
+int
+mp_rand(mp_int *a, int digits) {
+ int res;
+ mp_digit d;
+
+ mp_zero(a);
+ if (digits <= 0) {
+ return MP_OKAY;
+ }
+
+ /* first place a random non-zero digit */
+ do {
+ d = ((mp_digit)abs(MP_GEN_RANDOM())) & MP_MASK;
+ } while (d == 0);
+
+ if ((res = mp_add_d(a, d, a)) != MP_OKAY) {
+ return res;
+ }
+
+ while (--digits > 0) {
+ if ((res = mp_lshd(a, 1)) != MP_OKAY) {
+ return res;
+ }
+
+ if ((res = mp_add_d(a, ((mp_digit)abs(MP_GEN_RANDOM())), a)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_READ_RADIX_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* read a string [ASCII] in a given radix */
+int mp_read_radix(mp_int *a, const char *str, int radix) {
+ int y, res, neg;
+ char ch;
+
+ /* zero the digit bignum */
+ mp_zero(a);
+
+ /* make sure the radix is ok */
+ if ((radix < 2) || (radix > 64)) {
+ return MP_VAL;
+ }
+
+ /* if the leading digit is a
+ * minus set the sign to negative.
+ */
+ if (*str == '-') {
+ ++str;
+ neg = MP_NEG;
+ } else {
+ neg = MP_ZPOS;
+ }
+
+ /* set the integer to the default of zero */
+ mp_zero(a);
+
+ /* process each digit of the string */
+ while (*str != '\0') {
+ /* if the radix <= 36 the conversion is case insensitive
+ * this allows numbers like 1AB and 1ab to represent the same value
+ * [e.g. in hex]
+ */
+ ch = (radix <= 36) ? (char)toupper((int)*str) : *str;
+ for (y = 0; y < 64; y++) {
+ if (ch == mp_s_rmap[y]) {
+ break;
+ }
+ }
+
+ /* if the char was found in the map
+ * and is less than the given radix add it
+ * to the number, otherwise exit the loop.
+ */
+ if (y < radix) {
+ if ((res = mp_mul_d(a, (mp_digit)radix, a)) != MP_OKAY) {
+ return res;
+ }
+ if ((res = mp_add_d(a, (mp_digit)y, a)) != MP_OKAY) {
+ return res;
+ }
+ } else {
+ break;
+ }
+ ++str;
+ }
+
+ /* set the sign only if a != 0 */
+ if (mp_iszero(a) != MP_YES) {
+ a->sign = neg;
+ }
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_READ_SIGNED_BIN_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* read signed bin, big endian, first byte is 0==positive or 1==negative */
+int mp_read_signed_bin(mp_int *a, const unsigned char *b, int c) {
+ int res;
+
+ /* read magnitude */
+ if ((res = mp_read_unsigned_bin(a, b + 1, c - 1)) != MP_OKAY) {
+ return res;
+ }
+
+ /* first byte is 0 for positive, non-zero for negative */
+ if (b[0] == 0) {
+ a->sign = MP_ZPOS;
+ } else {
+ a->sign = MP_NEG;
+ }
+
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_READ_UNSIGNED_BIN_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* reads a unsigned char array, assumes the msb is stored first [big endian] */
+int mp_read_unsigned_bin(mp_int *a, const unsigned char *b, int c) {
+ int res;
+
+ /* make sure there are at least two digits */
+ if (a->alloc < 2) {
+ if ((res = mp_grow(a, 2)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* zero the int */
+ mp_zero(a);
+
+ /* read the bytes in */
+ while (c-- > 0) {
+ if ((res = mp_mul_2d(a, 8, a)) != MP_OKAY) {
+ return res;
+ }
+
+ #ifndef MP_8BIT
+ a->dp[0] |= *b++;
+ a->used += 1;
+ #else
+ a->dp[0] = (*b & MP_MASK);
+ a->dp[1] |= ((*b++ >> 7U) & 1);
+ a->used += 2;
+ #endif
+ }
+ mp_clamp(a);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_REDUCE_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* reduces x mod m, assumes 0 < x < m**2, mu is
+ * precomputed via mp_reduce_setup.
+ * From HAC pp.604 Algorithm 14.42
+ */
+int mp_reduce(mp_int *x, mp_int *m, mp_int *mu) {
+ mp_int q;
+ int res, um = m->used;
+
+ /* q = x */
+ if ((res = mp_init_copy(&q, x)) != MP_OKAY) {
+ return res;
+ }
+
+ /* q1 = x / b**(k-1) */
+ mp_rshd(&q, um - 1);
+
+ /* according to HAC this optimization is ok */
+ if (((mp_digit)um) > (((mp_digit)1) << (DIGIT_BIT - 1))) {
+ if ((res = mp_mul(&q, mu, &q)) != MP_OKAY) {
+ goto CLEANUP;
+ }
+ } else {
+ #ifdef BN_S_MP_MUL_HIGH_DIGS_C
+ if ((res = s_mp_mul_high_digs(&q, mu, &q, um)) != MP_OKAY) {
+ goto CLEANUP;
+ }
+ #elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C)
+ if ((res = fast_s_mp_mul_high_digs(&q, mu, &q, um)) != MP_OKAY) {
+ goto CLEANUP;
+ }
+ #else
+ {
+ res = MP_VAL;
+ goto CLEANUP;
+ }
+ #endif
+ }
+
+ /* q3 = q2 / b**(k+1) */
+ mp_rshd(&q, um + 1);
+
+ /* x = x mod b**(k+1), quick (no division) */
+ if ((res = mp_mod_2d(x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) {
+ goto CLEANUP;
+ }
+
+ /* q = q * m mod b**(k+1), quick (no division) */
+ if ((res = s_mp_mul_digs(&q, m, &q, um + 1)) != MP_OKAY) {
+ goto CLEANUP;
+ }
+
+ /* x = x - q */
+ if ((res = mp_sub(x, &q, x)) != MP_OKAY) {
+ goto CLEANUP;
+ }
+
+ /* If x < 0, add b**(k+1) to it */
+ if (mp_cmp_d(x, 0) == MP_LT) {
+ mp_set(&q, 1);
+ if ((res = mp_lshd(&q, um + 1)) != MP_OKAY)
+ goto CLEANUP;
+ if ((res = mp_add(x, &q, x)) != MP_OKAY)
+ goto CLEANUP;
+ }
+
+ /* Back off if it's too big */
+ while (mp_cmp(x, m) != MP_LT) {
+ if ((res = s_mp_sub(x, m, x)) != MP_OKAY) {
+ goto CLEANUP;
+ }
+ }
+
+CLEANUP:
+ mp_clear(&q);
+
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_REDUCE_2K_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* reduces a modulo n where n is of the form 2**p - d */
+int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d) {
+ mp_int q;
+ int p, res;
+
+ if ((res = mp_init(&q)) != MP_OKAY) {
+ return res;
+ }
+
+ p = mp_count_bits(n);
+top:
+ /* q = a/2**p, a = a mod 2**p */
+ if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if (d != 1) {
+ /* q = q * d */
+ if ((res = mp_mul_d(&q, d, &q)) != MP_OKAY) {
+ goto ERR;
+ }
+ }
+
+ /* a = a + q */
+ if ((res = s_mp_add(a, &q, a)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if (mp_cmp_mag(a, n) != MP_LT) {
+ if ((res = s_mp_sub(a, n, a)) != MP_OKAY) {
+ goto ERR;
+ }
+ goto top;
+ }
+
+ERR:
+ mp_clear(&q);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_REDUCE_2K_L_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* reduces a modulo n where n is of the form 2**p - d
+ This differs from reduce_2k since "d" can be larger
+ than a single digit.
+ */
+int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) {
+ mp_int q;
+ int p, res;
+
+ if ((res = mp_init(&q)) != MP_OKAY) {
+ return res;
+ }
+
+ p = mp_count_bits(n);
+top:
+ /* q = a/2**p, a = a mod 2**p */
+ if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ /* q = q * d */
+ if ((res = mp_mul(&q, d, &q)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ /* a = a + q */
+ if ((res = s_mp_add(a, &q, a)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if (mp_cmp_mag(a, n) != MP_LT) {
+ if ((res = s_mp_sub(a, n, a)) != MP_OKAY) {
+ goto ERR;
+ }
+ goto top;
+ }
+
+ERR:
+ mp_clear(&q);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_REDUCE_2K_SETUP_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* determines the setup value */
+int mp_reduce_2k_setup(mp_int *a, mp_digit *d) {
+ int res, p;
+ mp_int tmp;
+
+ if ((res = mp_init(&tmp)) != MP_OKAY) {
+ return res;
+ }
+
+ p = mp_count_bits(a);
+ if ((res = mp_2expt(&tmp, p)) != MP_OKAY) {
+ mp_clear(&tmp);
+ return res;
+ }
+
+ if ((res = s_mp_sub(&tmp, a, &tmp)) != MP_OKAY) {
+ mp_clear(&tmp);
+ return res;
+ }
+
+ *d = tmp.dp[0];
+ mp_clear(&tmp);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_REDUCE_2K_SETUP_L_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* determines the setup value */
+int mp_reduce_2k_setup_l(mp_int *a, mp_int *d) {
+ int res;
+ mp_int tmp;
+
+ if ((res = mp_init(&tmp)) != MP_OKAY) {
+ return res;
+ }
+
+ if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ERR:
+ mp_clear(&tmp);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_REDUCE_IS_2K_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* determines if mp_reduce_2k can be used */
+int mp_reduce_is_2k(mp_int *a) {
+ int ix, iy, iw;
+ mp_digit iz;
+
+ if (a->used == 0) {
+ return MP_NO;
+ } else if (a->used == 1) {
+ return MP_YES;
+ } else if (a->used > 1) {
+ iy = mp_count_bits(a);
+ iz = 1;
+ iw = 1;
+
+ /* Test every bit from the second digit up, must be 1 */
+ for (ix = DIGIT_BIT; ix < iy; ix++) {
+ if ((a->dp[iw] & iz) == 0) {
+ return MP_NO;
+ }
+ iz <<= 1;
+ if (iz > (mp_digit)MP_MASK) {
+ ++iw;
+ iz = 1;
+ }
+ }
+ }
+ return MP_YES;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_REDUCE_IS_2K_L_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* determines if reduce_2k_l can be used */
+int mp_reduce_is_2k_l(mp_int *a) {
+ int ix, iy;
+
+ if (a->used == 0) {
+ return MP_NO;
+ } else if (a->used == 1) {
+ return MP_YES;
+ } else if (a->used > 1) {
+ /* if more than half of the digits are -1 we're sold */
+ for (iy = ix = 0; ix < a->used; ix++) {
+ if (a->dp[ix] == MP_MASK) {
+ ++iy;
+ }
+ }
+ return (iy >= (a->used / 2)) ? MP_YES : MP_NO;
+ }
+ return MP_NO;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_REDUCE_SETUP_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* pre-calculate the value required for Barrett reduction
+ * For a given modulus "b" it calulates the value required in "a"
+ */
+int mp_reduce_setup(mp_int *a, mp_int *b) {
+ int res;
+
+ if ((res = mp_2expt(a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) {
+ return res;
+ }
+ return mp_div(a, b, a, NULL);
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_RSHD_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* shift right a certain amount of digits */
+void mp_rshd(mp_int *a, int b) {
+ int x;
+
+ /* if b <= 0 then ignore it */
+ if (b <= 0) {
+ return;
+ }
+
+ /* if b > used then simply zero it and return */
+ if (a->used <= b) {
+ mp_zero(a);
+ return;
+ }
+
+ {
+ mp_digit *bottom, *top;
+
+ /* shift the digits down */
+
+ /* bottom */
+ bottom = a->dp;
+
+ /* top [offset into digits] */
+ top = a->dp + b;
+
+ /* this is implemented as a sliding window where
+ * the window is b-digits long and digits from
+ * the top of the window are copied to the bottom
+ *
+ * e.g.
+
+ b-2 | b-1 | b0 | b1 | b2 | ... | bb | ---->
+ /\ | ---->
+ **\-------------------/ ---->
+ */
+ for (x = 0; x < (a->used - b); x++) {
+ *bottom++ = *top++;
+ }
+
+ /* zero the top digits */
+ for ( ; x < a->used; x++) {
+ *bottom++ = 0;
+ }
+ }
+
+ /* remove excess digits */
+ a->used -= b;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_SET_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* set to a digit */
+void mp_set(mp_int *a, mp_digit b) {
+ mp_zero(a);
+ a->dp[0] = b & MP_MASK;
+ a->used = (a->dp[0] != 0) ? 1 : 0;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_SET_INT_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* set a 32-bit const */
+int mp_set_int(mp_int *a, unsigned long b) {
+ int x, res;
+
+ mp_zero(a);
+
+ /* set four bits at a time */
+ for (x = 0; x < 8; x++) {
+ /* shift the number up four bits */
+ if ((res = mp_mul_2d(a, 4, a)) != MP_OKAY) {
+ return res;
+ }
+
+ /* OR in the top four bits of the source */
+ a->dp[0] |= (b >> 28) & 15;
+
+ /* shift the source up to the next four bits */
+ b <<= 4;
+
+ /* ensure that digits are not clamped off */
+ a->used += 1;
+ }
+ mp_clamp(a);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_SET_LONG_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* set a platform dependent unsigned long int */
+MP_SET_XLONG(mp_set_long, unsigned long)
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_SET_LONG_LONG_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* set a platform dependent unsigned long long int */
+MP_SET_XLONG(mp_set_long_long, unsigned long long)
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_SHRINK_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* shrink a bignum */
+int mp_shrink(mp_int *a) {
+ mp_digit *tmp;
+ int used = 1;
+
+ if (a->used > 0) {
+ used = a->used;
+ }
+
+ if (a->alloc != used) {
+ if ((tmp = OPT_CAST(mp_digit) XREALLOC(a->dp, sizeof(mp_digit) * used)) == NULL) {
+ return MP_MEM;
+ }
+ a->dp = tmp;
+ a->alloc = used;
+ }
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_SIGNED_BIN_SIZE_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* get the size for an signed equivalent */
+int mp_signed_bin_size(mp_int *a) {
+ return 1 + mp_unsigned_bin_size(a);
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_SQR_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* computes b = a*a */
+int
+mp_sqr(mp_int *a, mp_int *b) {
+ int res;
+
+ #ifdef BN_MP_TOOM_SQR_C
+ /* use Toom-Cook? */
+ if (a->used >= TOOM_SQR_CUTOFF) {
+ res = mp_toom_sqr(a, b);
+ /* Karatsuba? */
+ } else
+ #endif
+ #ifdef BN_MP_KARATSUBA_SQR_C
+ if (a->used >= KARATSUBA_SQR_CUTOFF) {
+ res = mp_karatsuba_sqr(a, b);
+ } else
+ #endif
+ {
+ #ifdef BN_FAST_S_MP_SQR_C
+ /* can we use the fast comba multiplier? */
+ if ((((a->used * 2) + 1) < MP_WARRAY) &&
+ (a->used <
+ (1 << (((sizeof(mp_word) * CHAR_BIT) - (2 * DIGIT_BIT)) - 1)))) {
+ res = fast_s_mp_sqr(a, b);
+ } else
+ #endif
+ {
+ #ifdef BN_S_MP_SQR_C
+ res = s_mp_sqr(a, b);
+ #else
+ res = MP_VAL;
+ #endif
+ }
+ }
+ b->sign = MP_ZPOS;
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_SQRMOD_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* c = a * a (mod b) */
+int
+mp_sqrmod(mp_int *a, mp_int *b, mp_int *c) {
+ int res;
+ mp_int t;
+
+ if ((res = mp_init(&t)) != MP_OKAY) {
+ return res;
+ }
+
+ if ((res = mp_sqr(a, &t)) != MP_OKAY) {
+ mp_clear(&t);
+ return res;
+ }
+ res = mp_mod(&t, b, c);
+ mp_clear(&t);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_SQRT_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* this function is less generic than mp_n_root, simpler and faster */
+int mp_sqrt(mp_int *arg, mp_int *ret) {
+ int res;
+ mp_int t1, t2;
+
+ /* must be positive */
+ if (arg->sign == MP_NEG) {
+ return MP_VAL;
+ }
+
+ /* easy out */
+ if (mp_iszero(arg) == MP_YES) {
+ mp_zero(ret);
+ return MP_OKAY;
+ }
+
+ if ((res = mp_init_copy(&t1, arg)) != MP_OKAY) {
+ return res;
+ }
+
+ if ((res = mp_init(&t2)) != MP_OKAY) {
+ goto E2;
+ }
+
+ /* First approx. (not very bad for large arg) */
+ mp_rshd(&t1, t1.used / 2);
+
+ /* t1 > 0 */
+ if ((res = mp_div(arg, &t1, &t2, NULL)) != MP_OKAY) {
+ goto E1;
+ }
+ if ((res = mp_add(&t1, &t2, &t1)) != MP_OKAY) {
+ goto E1;
+ }
+ if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) {
+ goto E1;
+ }
+ /* And now t1 > sqrt(arg) */
+ do {
+ if ((res = mp_div(arg, &t1, &t2, NULL)) != MP_OKAY) {
+ goto E1;
+ }
+ if ((res = mp_add(&t1, &t2, &t1)) != MP_OKAY) {
+ goto E1;
+ }
+ if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) {
+ goto E1;
+ }
+ /* t1 >= sqrt(arg) >= t2 at this point */
+ } while (mp_cmp_mag(&t1, &t2) == MP_GT);
+
+ mp_exch(&t1, ret);
+
+E1: mp_clear(&t2);
+E2: mp_clear(&t1);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_SQRTMOD_PRIME_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ */
+
+/* Tonelli-Shanks algorithm
+ * https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm
+ * https://gmplib.org/list-archives/gmp-discuss/2013-April/005300.html
+ *
+ */
+
+int mp_sqrtmod_prime(mp_int *n, mp_int *prime, mp_int *ret) {
+ int res, legendre;
+ mp_int t1, C, Q, S, Z, M, T, R, two;
+ mp_digit i;
+
+ /* first handle the simple cases */
+ if (mp_cmp_d(n, 0) == MP_EQ) {
+ mp_zero(ret);
+ return MP_OKAY;
+ }
+ if (mp_cmp_d(prime, 2) == MP_EQ) return MP_VAL; /* prime must be odd */
+ if ((res = mp_jacobi(n, prime, &legendre)) != MP_OKAY) return res;
+ if (legendre == -1) return MP_VAL; /* quadratic non-residue mod prime */
+
+ if ((res = mp_init_multi(&t1, &C, &Q, &S, &Z, &M, &T, &R, &two, NULL)) != MP_OKAY) {
+ return res;
+ }
+
+ /* SPECIAL CASE: if prime mod 4 == 3
+ * compute directly: res = n^(prime+1)/4 mod prime
+ * Handbook of Applied Cryptography algorithm 3.36
+ */
+ if ((res = mp_mod_d(prime, 4, &i)) != MP_OKAY) goto cleanup;
+ if (i == 3) {
+ if ((res = mp_add_d(prime, 1, &t1)) != MP_OKAY) goto cleanup;
+ if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) goto cleanup;
+ if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) goto cleanup;
+ if ((res = mp_exptmod(n, &t1, prime, ret)) != MP_OKAY) goto cleanup;
+ res = MP_OKAY;
+ goto cleanup;
+ }
+
+ /* NOW: Tonelli-Shanks algorithm */
+
+ /* factor out powers of 2 from prime-1, defining Q and S as: prime-1 = Q*2^S */
+ if ((res = mp_copy(prime, &Q)) != MP_OKAY) goto cleanup;
+ if ((res = mp_sub_d(&Q, 1, &Q)) != MP_OKAY) goto cleanup;
+ /* Q = prime - 1 */
+ mp_zero(&S);
+ /* S = 0 */
+ while (mp_iseven(&Q) != MP_NO) {
+ if ((res = mp_div_2(&Q, &Q)) != MP_OKAY) goto cleanup;
+ /* Q = Q / 2 */
+ if ((res = mp_add_d(&S, 1, &S)) != MP_OKAY) goto cleanup;
+ /* S = S + 1 */
+ }
+
+ /* find a Z such that the Legendre symbol (Z|prime) == -1 */
+ if ((res = mp_set_int(&Z, 2)) != MP_OKAY) goto cleanup;
+ /* Z = 2 */
+ while (1) {
+ if ((res = mp_jacobi(&Z, prime, &legendre)) != MP_OKAY) goto cleanup;
+ if (legendre == -1) break;
+ if ((res = mp_add_d(&Z, 1, &Z)) != MP_OKAY) goto cleanup;
+ /* Z = Z + 1 */
+ }
+
+ if ((res = mp_exptmod(&Z, &Q, prime, &C)) != MP_OKAY) goto cleanup;
+ /* C = Z ^ Q mod prime */
+ if ((res = mp_add_d(&Q, 1, &t1)) != MP_OKAY) goto cleanup;
+ if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) goto cleanup;
+ /* t1 = (Q + 1) / 2 */
+ if ((res = mp_exptmod(n, &t1, prime, &R)) != MP_OKAY) goto cleanup;
+ /* R = n ^ ((Q + 1) / 2) mod prime */
+ if ((res = mp_exptmod(n, &Q, prime, &T)) != MP_OKAY) goto cleanup;
+ /* T = n ^ Q mod prime */
+ if ((res = mp_copy(&S, &M)) != MP_OKAY) goto cleanup;
+ /* M = S */
+ if ((res = mp_set_int(&two, 2)) != MP_OKAY) goto cleanup;
+
+ res = MP_VAL;
+ while (1) {
+ if ((res = mp_copy(&T, &t1)) != MP_OKAY) goto cleanup;
+ i = 0;
+ while (1) {
+ if (mp_cmp_d(&t1, 1) == MP_EQ) break;
+ if ((res = mp_exptmod(&t1, &two, prime, &t1)) != MP_OKAY) goto cleanup;
+ i++;
+ }
+ if (i == 0) {
+ if ((res = mp_copy(&R, ret)) != MP_OKAY) goto cleanup;
+ res = MP_OKAY;
+ goto cleanup;
+ }
+ if ((res = mp_sub_d(&M, i, &t1)) != MP_OKAY) goto cleanup;
+ if ((res = mp_sub_d(&t1, 1, &t1)) != MP_OKAY) goto cleanup;
+ if ((res = mp_exptmod(&two, &t1, prime, &t1)) != MP_OKAY) goto cleanup;
+ /* t1 = 2 ^ (M - i - 1) */
+ if ((res = mp_exptmod(&C, &t1, prime, &t1)) != MP_OKAY) goto cleanup;
+ /* t1 = C ^ (2 ^ (M - i - 1)) mod prime */
+ if ((res = mp_sqrmod(&t1, prime, &C)) != MP_OKAY) goto cleanup;
+ /* C = (t1 * t1) mod prime */
+ if ((res = mp_mulmod(&R, &t1, prime, &R)) != MP_OKAY) goto cleanup;
+ /* R = (R * t1) mod prime */
+ if ((res = mp_mulmod(&T, &C, prime, &T)) != MP_OKAY) goto cleanup;
+ /* T = (T * C) mod prime */
+ mp_set(&M, i);
+ /* M = i */
+ }
+
+cleanup:
+ mp_clear_multi(&t1, &C, &Q, &S, &Z, &M, &T, &R, &two, NULL);
+ return res;
+}
+#endif
+
+
+
+#ifdef BN_MP_SUB_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* high level subtraction (handles signs) */
+int
+mp_sub(mp_int *a, mp_int *b, mp_int *c) {
+ int sa, sb, res;
+
+ sa = a->sign;
+ sb = b->sign;
+
+ if (sa != sb) {
+ /* subtract a negative from a positive, OR */
+ /* subtract a positive from a negative. */
+ /* In either case, ADD their magnitudes, */
+ /* and use the sign of the first number. */
+ c->sign = sa;
+ res = s_mp_add(a, b, c);
+ } else {
+ /* subtract a positive from a positive, OR */
+ /* subtract a negative from a negative. */
+ /* First, take the difference between their */
+ /* magnitudes, then... */
+ if (mp_cmp_mag(a, b) != MP_LT) {
+ /* Copy the sign from the first */
+ c->sign = sa;
+ /* The first has a larger or equal magnitude */
+ res = s_mp_sub(a, b, c);
+ } else {
+ /* The result has the *opposite* sign from */
+ /* the first number. */
+ c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS;
+ /* The second has a larger magnitude */
+ res = s_mp_sub(b, a, c);
+ }
+ }
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_SUB_D_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* single digit subtraction */
+int
+mp_sub_d(mp_int *a, mp_digit b, mp_int *c) {
+ mp_digit *tmpa, *tmpc, mu;
+ int res, ix, oldused;
+
+ /* grow c as required */
+ if (c->alloc < (a->used + 1)) {
+ if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* if a is negative just do an unsigned
+ * addition [with fudged signs]
+ */
+ if (a->sign == MP_NEG) {
+ a->sign = MP_ZPOS;
+ res = mp_add_d(a, b, c);
+ a->sign = c->sign = MP_NEG;
+
+ /* clamp */
+ mp_clamp(c);
+
+ return res;
+ }
+
+ /* setup regs */
+ oldused = c->used;
+ tmpa = a->dp;
+ tmpc = c->dp;
+
+ /* if a <= b simply fix the single digit */
+ if (((a->used == 1) && (a->dp[0] <= b)) || (a->used == 0)) {
+ if (a->used == 1) {
+ *tmpc++ = b - *tmpa;
+ } else {
+ *tmpc++ = b;
+ }
+ ix = 1;
+
+ /* negative/1digit */
+ c->sign = MP_NEG;
+ c->used = 1;
+ } else {
+ /* positive/size */
+ c->sign = MP_ZPOS;
+ c->used = a->used;
+
+ /* subtract first digit */
+ *tmpc = *tmpa++ - b;
+ mu = *tmpc >> ((sizeof(mp_digit) * CHAR_BIT) - 1);
+ *tmpc++ &= MP_MASK;
+
+ /* handle rest of the digits */
+ for (ix = 1; ix < a->used; ix++) {
+ *tmpc = *tmpa++ - mu;
+ mu = *tmpc >> ((sizeof(mp_digit) * CHAR_BIT) - 1);
+ *tmpc++ &= MP_MASK;
+ }
+ }
+
+ /* zero excess digits */
+ while (ix++ < oldused) {
+ *tmpc++ = 0;
+ }
+ mp_clamp(c);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_SUBMOD_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* d = a - b (mod c) */
+int
+mp_submod(mp_int *a, mp_int *b, mp_int *c, mp_int *d) {
+ int res;
+ mp_int t;
+
+
+ if ((res = mp_init(&t)) != MP_OKAY) {
+ return res;
+ }
+
+ if ((res = mp_sub(a, b, &t)) != MP_OKAY) {
+ mp_clear(&t);
+ return res;
+ }
+ res = mp_mod(&t, c, d);
+ mp_clear(&t);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_TO_SIGNED_BIN_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* store in signed [big endian] format */
+int mp_to_signed_bin(mp_int *a, unsigned char *b) {
+ int res;
+
+ if ((res = mp_to_unsigned_bin(a, b + 1)) != MP_OKAY) {
+ return res;
+ }
+ b[0] = (a->sign == MP_ZPOS) ? (unsigned char)0 : (unsigned char)1;
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_TO_SIGNED_BIN_N_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* store in signed [big endian] format */
+int mp_to_signed_bin_n(mp_int *a, unsigned char *b, unsigned long *outlen) {
+ if (*outlen < (unsigned long)mp_signed_bin_size(a)) {
+ return MP_VAL;
+ }
+ *outlen = mp_signed_bin_size(a);
+ return mp_to_signed_bin(a, b);
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_TO_UNSIGNED_BIN_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* store in unsigned [big endian] format */
+int mp_to_unsigned_bin(mp_int *a, unsigned char *b) {
+ int x, res;
+ mp_int t;
+
+ if ((res = mp_init_copy(&t, a)) != MP_OKAY) {
+ return res;
+ }
+
+ x = 0;
+ while (mp_iszero(&t) == MP_NO) {
+ #ifndef MP_8BIT
+ b[x++] = (unsigned char)(t.dp[0] & 255);
+ #else
+ b[x++] = (unsigned char)(t.dp[0] | ((t.dp[1] & 0x01) << 7));
+ #endif
+ if ((res = mp_div_2d(&t, 8, &t, NULL)) != MP_OKAY) {
+ mp_clear(&t);
+ return res;
+ }
+ }
+ bn_reverse(b, x);
+ mp_clear(&t);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_TOOM_MUL_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* multiplication using the Toom-Cook 3-way algorithm
+ *
+ * Much more complicated than Karatsuba but has a lower
+ * asymptotic running time of O(N**1.464). This algorithm is
+ * only particularly useful on VERY large inputs
+ * (we're talking 1000s of digits here...).
+ */
+int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c) {
+ mp_int w0, w1, w2, w3, w4, tmp1, tmp2, a0, a1, a2, b0, b1, b2;
+ int res, B;
+
+ /* init temps */
+ if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4,
+ &a0, &a1, &a2, &b0, &b1,
+ &b2, &tmp1, &tmp2, NULL)) != MP_OKAY) {
+ return res;
+ }
+
+ /* B */
+ B = MIN(a->used, b->used) / 3;
+
+ /* a = a2 * B**2 + a1 * B + a0 */
+ if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if ((res = mp_copy(a, &a1)) != MP_OKAY) {
+ goto ERR;
+ }
+ mp_rshd(&a1, B);
+ if ((res = mp_mod_2d(&a1, DIGIT_BIT * B, &a1)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if ((res = mp_copy(a, &a2)) != MP_OKAY) {
+ goto ERR;
+ }
+ mp_rshd(&a2, B * 2);
+
+ /* b = b2 * B**2 + b1 * B + b0 */
+ if ((res = mp_mod_2d(b, DIGIT_BIT * B, &b0)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if ((res = mp_copy(b, &b1)) != MP_OKAY) {
+ goto ERR;
+ }
+ mp_rshd(&b1, B);
+ (void)mp_mod_2d(&b1, DIGIT_BIT * B, &b1);
+
+ if ((res = mp_copy(b, &b2)) != MP_OKAY) {
+ goto ERR;
+ }
+ mp_rshd(&b2, B * 2);
+
+ /* w0 = a0*b0 */
+ if ((res = mp_mul(&a0, &b0, &w0)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ /* w4 = a2 * b2 */
+ if ((res = mp_mul(&a2, &b2, &w4)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ /* w1 = (a2 + 2(a1 + 2a0))(b2 + 2(b1 + 2b0)) */
+ if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if ((res = mp_mul_2(&b0, &tmp2)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp2, &b2, &tmp2)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if ((res = mp_mul(&tmp1, &tmp2, &w1)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ /* w3 = (a0 + 2(a1 + 2a2))(b0 + 2(b1 + 2b2)) */
+ if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if ((res = mp_mul_2(&b2, &tmp2)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if ((res = mp_mul(&tmp1, &tmp2, &w3)) != MP_OKAY) {
+ goto ERR;
+ }
+
+
+ /* w2 = (a2 + a1 + a0)(b2 + b1 + b0) */
+ if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&b2, &b1, &tmp2)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_mul(&tmp1, &tmp2, &w2)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ /* now solve the matrix
+
+ 0 0 0 0 1
+ 1 2 4 8 16
+ 1 1 1 1 1
+ 16 8 4 2 1
+ 1 0 0 0 0
+
+ using 12 subtractions, 4 shifts,
+ 2 small divisions and 1 small multiplication
+ */
+
+ /* r1 - r4 */
+ if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r3 - r0 */
+ if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r1/2 */
+ if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r3/2 */
+ if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r2 - r0 - r4 */
+ if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r1 - r2 */
+ if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r3 - r2 */
+ if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r1 - 8r0 */
+ if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r3 - 8r4 */
+ if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* 3r2 - r1 - r3 */
+ if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r1 - r2 */
+ if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r3 - r2 */
+ if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r1/3 */
+ if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r3/3 */
+ if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ /* at this point shift W[n] by B*n */
+ if ((res = mp_lshd(&w1, 1 * B)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_lshd(&w2, 2 * B)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_lshd(&w3, 3 * B)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_lshd(&w4, 4 * B)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if ((res = mp_add(&w0, &w1, c)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp1, c, c)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ERR:
+ mp_clear_multi(&w0, &w1, &w2, &w3, &w4,
+ &a0, &a1, &a2, &b0, &b1,
+ &b2, &tmp1, &tmp2, NULL);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_TOOM_SQR_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* squaring using Toom-Cook 3-way algorithm */
+int
+mp_toom_sqr(mp_int *a, mp_int *b) {
+ mp_int w0, w1, w2, w3, w4, tmp1, a0, a1, a2;
+ int res, B;
+
+ /* init temps */
+ if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL)) != MP_OKAY) {
+ return res;
+ }
+
+ /* B */
+ B = a->used / 3;
+
+ /* a = a2 * B**2 + a1 * B + a0 */
+ if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if ((res = mp_copy(a, &a1)) != MP_OKAY) {
+ goto ERR;
+ }
+ mp_rshd(&a1, B);
+ if ((res = mp_mod_2d(&a1, DIGIT_BIT * B, &a1)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if ((res = mp_copy(a, &a2)) != MP_OKAY) {
+ goto ERR;
+ }
+ mp_rshd(&a2, B * 2);
+
+ /* w0 = a0*a0 */
+ if ((res = mp_sqr(&a0, &w0)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ /* w4 = a2 * a2 */
+ if ((res = mp_sqr(&a2, &w4)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ /* w1 = (a2 + 2(a1 + 2a0))**2 */
+ if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if ((res = mp_sqr(&tmp1, &w1)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ /* w3 = (a0 + 2(a1 + 2a2))**2 */
+ if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if ((res = mp_sqr(&tmp1, &w3)) != MP_OKAY) {
+ goto ERR;
+ }
+
+
+ /* w2 = (a2 + a1 + a0)**2 */
+ if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_sqr(&tmp1, &w2)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ /* now solve the matrix
+
+ 0 0 0 0 1
+ 1 2 4 8 16
+ 1 1 1 1 1
+ 16 8 4 2 1
+ 1 0 0 0 0
+
+ using 12 subtractions, 4 shifts, 2 small divisions and 1 small multiplication.
+ */
+
+ /* r1 - r4 */
+ if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r3 - r0 */
+ if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r1/2 */
+ if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r3/2 */
+ if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r2 - r0 - r4 */
+ if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r1 - r2 */
+ if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r3 - r2 */
+ if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r1 - 8r0 */
+ if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r3 - 8r4 */
+ if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* 3r2 - r1 - r3 */
+ if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r1 - r2 */
+ if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r3 - r2 */
+ if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r1/3 */
+ if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) {
+ goto ERR;
+ }
+ /* r3/3 */
+ if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ /* at this point shift W[n] by B*n */
+ if ((res = mp_lshd(&w1, 1 * B)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_lshd(&w2, 2 * B)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_lshd(&w3, 3 * B)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_lshd(&w4, 4 * B)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if ((res = mp_add(&w0, &w1, b)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) {
+ goto ERR;
+ }
+ if ((res = mp_add(&tmp1, b, b)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ERR:
+ mp_clear_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL);
+ return res;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_TORADIX_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* stores a bignum as a ASCII string in a given radix (2..64) */
+int mp_toradix(mp_int *a, char *str, int radix) {
+ int res, digs;
+ mp_int t;
+ mp_digit d;
+ char *_s = str;
+
+ /* check range of the radix */
+ if ((radix < 2) || (radix > 64)) {
+ return MP_VAL;
+ }
+
+ /* quick out if its zero */
+ if (mp_iszero(a) == MP_YES) {
+ *str++ = '0';
+ *str = '\0';
+ return MP_OKAY;
+ }
+
+ if ((res = mp_init_copy(&t, a)) != MP_OKAY) {
+ return res;
+ }
+
+ /* if it is negative output a - */
+ if (t.sign == MP_NEG) {
+ ++_s;
+ *str++ = '-';
+ t.sign = MP_ZPOS;
+ }
+
+ digs = 0;
+ while (mp_iszero(&t) == MP_NO) {
+ if ((res = mp_div_d(&t, (mp_digit)radix, &t, &d)) != MP_OKAY) {
+ mp_clear(&t);
+ return res;
+ }
+ *str++ = mp_s_rmap[d];
+ ++digs;
+ }
+
+ /* reverse the digits of the string. In this case _s points
+ * to the first digit [exluding the sign] of the number]
+ */
+ bn_reverse((unsigned char *)_s, digs);
+
+ /* append a NULL so the string is properly terminated */
+ *str = '\0';
+
+ mp_clear(&t);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_UNSIGNED_BIN_SIZE_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* get the size for an unsigned equivalent */
+int mp_unsigned_bin_size(mp_int *a) {
+ int size = mp_count_bits(a);
+
+ return (size / 8) + (((size & 7) != 0) ? 1 : 0);
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_XOR_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* XOR two ints together */
+int
+mp_xor(mp_int *a, mp_int *b, mp_int *c) {
+ int res, ix, px;
+ mp_int t, *x;
+
+ if (a->used > b->used) {
+ if ((res = mp_init_copy(&t, a)) != MP_OKAY) {
+ return res;
+ }
+ px = b->used;
+ x = b;
+ } else {
+ if ((res = mp_init_copy(&t, b)) != MP_OKAY) {
+ return res;
+ }
+ px = a->used;
+ x = a;
+ }
+
+ for (ix = 0; ix < px; ix++) {
+ t.dp[ix] ^= x->dp[ix];
+ }
+ mp_clamp(&t);
+ mp_exch(c, &t);
+ mp_clear(&t);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_MP_ZERO_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* set to zero */
+void mp_zero(mp_int *a) {
+ int n;
+ mp_digit *tmp;
+
+ a->sign = MP_ZPOS;
+ a->used = 0;
+
+ tmp = a->dp;
+ for (n = 0; n < a->alloc; n++) {
+ *tmp++ = 0;
+ }
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_PRIME_TAB_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+const mp_digit ltm_prime_tab[] = {
+ 0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013,
+ 0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035,
+ 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059,
+ 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F,
+ #ifndef MP_8BIT
+ 0x0083,
+ 0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD,
+ 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF,
+ 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107,
+ 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137,
+
+ 0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167,
+ 0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199,
+ 0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9,
+ 0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7,
+ 0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239,
+ 0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265,
+ 0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293,
+ 0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF,
+
+ 0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301,
+ 0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B,
+ 0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371,
+ 0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD,
+ 0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5,
+ 0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419,
+ 0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449,
+ 0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B,
+
+ 0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7,
+ 0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503,
+ 0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529,
+ 0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F,
+ 0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3,
+ 0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7,
+ 0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623,
+ 0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653
+ #endif
+};
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_REVERSE_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* reverse an array, used for radix code */
+void
+bn_reverse(unsigned char *s, int len) {
+ int ix, iy;
+ unsigned char t;
+
+ ix = 0;
+ iy = len - 1;
+ while (ix < iy) {
+ t = s[ix];
+ s[ix] = s[iy];
+ s[iy] = t;
+ ++ix;
+ --iy;
+ }
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_S_MP_ADD_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* low level addition, based on HAC pp.594, Algorithm 14.7 */
+int
+s_mp_add(mp_int *a, mp_int *b, mp_int *c) {
+ mp_int *x;
+ int olduse, res, min, max;
+
+ /* find sizes, we let |a| <= |b| which means we have to sort
+ * them. "x" will point to the input with the most digits
+ */
+ if (a->used > b->used) {
+ min = b->used;
+ max = a->used;
+ x = a;
+ } else {
+ min = a->used;
+ max = b->used;
+ x = b;
+ }
+
+ /* init result */
+ if (c->alloc < (max + 1)) {
+ if ((res = mp_grow(c, max + 1)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* get old used digit count and set new one */
+ olduse = c->used;
+ c->used = max + 1;
+
+ {
+ mp_digit u, *tmpa, *tmpb, *tmpc;
+ int i;
+
+ /* alias for digit pointers */
+
+ /* first input */
+ tmpa = a->dp;
+
+ /* second input */
+ tmpb = b->dp;
+
+ /* destination */
+ tmpc = c->dp;
+
+ /* zero the carry */
+ u = 0;
+ for (i = 0; i < min; i++) {
+ /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */
+ *tmpc = *tmpa++ + *tmpb++ + u;
+
+ /* U = carry bit of T[i] */
+ u = *tmpc >> ((mp_digit)DIGIT_BIT);
+
+ /* take away carry bit from T[i] */
+ *tmpc++ &= MP_MASK;
+ }
+
+ /* now copy higher words if any, that is in A+B
+ * if A or B has more digits add those in
+ */
+ if (min != max) {
+ for ( ; i < max; i++) {
+ /* T[i] = X[i] + U */
+ *tmpc = x->dp[i] + u;
+
+ /* U = carry bit of T[i] */
+ u = *tmpc >> ((mp_digit)DIGIT_BIT);
+
+ /* take away carry bit from T[i] */
+ *tmpc++ &= MP_MASK;
+ }
+ }
+
+ /* add carry */
+ *tmpc++ = u;
+
+ /* clear digits above oldused */
+ for (i = c->used; i < olduse; i++) {
+ *tmpc++ = 0;
+ }
+ }
+
+ mp_clamp(c);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_S_MP_EXPTMOD_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+int s_mp_exptmod(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode) {
+ mp_int M[TAB_SIZE], res, mu;
+ mp_digit buf;
+ int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
+
+ int (*redux)(mp_int *, mp_int *, mp_int *);
+
+ /* find window size */
+ x = mp_count_bits(X);
+ if (x <= 7) {
+ winsize = 2;
+ } else if (x <= 36) {
+ winsize = 3;
+ } else if (x <= 140) {
+ winsize = 4;
+ } else if (x <= 450) {
+ winsize = 5;
+ } else if (x <= 1303) {
+ winsize = 6;
+ } else if (x <= 3529) {
+ winsize = 7;
+ } else {
+ winsize = 8;
+ }
+
+ #ifdef MP_LOW_MEM
+ if (winsize > 5) {
+ winsize = 5;
+ }
+ #endif
+
+ /* init M array */
+ /* init first cell */
+ if ((err = mp_init(&M[1])) != MP_OKAY) {
+ return err;
+ }
+
+ /* now init the second half of the array */
+ for (x = 1 << (winsize - 1); x < (1 << winsize); x++) {
+ if ((err = mp_init(&M[x])) != MP_OKAY) {
+ for (y = 1 << (winsize - 1); y < x; y++) {
+ mp_clear(&M[y]);
+ }
+ mp_clear(&M[1]);
+ return err;
+ }
+ }
+
+ /* create mu, used for Barrett reduction */
+ if ((err = mp_init(&mu)) != MP_OKAY) {
+ goto LBL_M;
+ }
+
+ if (redmode == 0) {
+ if ((err = mp_reduce_setup(&mu, P)) != MP_OKAY) {
+ goto LBL_MU;
+ }
+ redux = mp_reduce;
+ } else {
+ if ((err = mp_reduce_2k_setup_l(P, &mu)) != MP_OKAY) {
+ goto LBL_MU;
+ }
+ redux = mp_reduce_2k_l;
+ }
+
+ /* create M table
+ *
+ * The M table contains powers of the base,
+ * e.g. M[x] = G**x mod P
+ *
+ * The first half of the table is not
+ * computed though accept for M[0] and M[1]
+ */
+ if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) {
+ goto LBL_MU;
+ }
+
+ /* compute the value at M[1<<(winsize-1)] by squaring
+ * M[1] (winsize-1) times
+ */
+ if ((err = mp_copy(&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) {
+ goto LBL_MU;
+ }
+
+ for (x = 0; x < (winsize - 1); x++) {
+ /* square it */
+ if ((err = mp_sqr(&M[1 << (winsize - 1)],
+ &M[1 << (winsize - 1)])) != MP_OKAY) {
+ goto LBL_MU;
+ }
+
+ /* reduce modulo P */
+ if ((err = redux(&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) {
+ goto LBL_MU;
+ }
+ }
+
+ /* create upper table, that is M[x] = M[x-1] * M[1] (mod P)
+ * for x = (2**(winsize - 1) + 1) to (2**winsize - 1)
+ */
+ for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
+ if ((err = mp_mul(&M[x - 1], &M[1], &M[x])) != MP_OKAY) {
+ goto LBL_MU;
+ }
+ if ((err = redux(&M[x], P, &mu)) != MP_OKAY) {
+ goto LBL_MU;
+ }
+ }
+
+ /* setup result */
+ if ((err = mp_init(&res)) != MP_OKAY) {
+ goto LBL_MU;
+ }
+ mp_set(&res, 1);
+
+ /* set initial mode and bit cnt */
+ mode = 0;
+ bitcnt = 1;
+ buf = 0;
+ digidx = X->used - 1;
+ bitcpy = 0;
+ bitbuf = 0;
+
+ for ( ; ; ) {
+ /* grab next digit as required */
+ if (--bitcnt == 0) {
+ /* if digidx == -1 we are out of digits */
+ if (digidx == -1) {
+ break;
+ }
+ /* read next digit and reset the bitcnt */
+ buf = X->dp[digidx--];
+ bitcnt = (int)DIGIT_BIT;
+ }
+
+ /* grab the next msb from the exponent */
+ y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1;
+ buf <<= (mp_digit)1;
+
+ /* if the bit is zero and mode == 0 then we ignore it
+ * These represent the leading zero bits before the first 1 bit
+ * in the exponent. Technically this opt is not required but it
+ * does lower the # of trivial squaring/reductions used
+ */
+ if ((mode == 0) && (y == 0)) {
+ continue;
+ }
+
+ /* if the bit is zero and mode == 1 then we square */
+ if ((mode == 1) && (y == 0)) {
+ if ((err = mp_sqr(&res, &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux(&res, P, &mu)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ continue;
+ }
+
+ /* else we add it to the window */
+ bitbuf |= (y << (winsize - ++bitcpy));
+ mode = 2;
+
+ if (bitcpy == winsize) {
+ /* ok window is filled so square as required and multiply */
+ /* square first */
+ for (x = 0; x < winsize; x++) {
+ if ((err = mp_sqr(&res, &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux(&res, P, &mu)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ }
+
+ /* then multiply */
+ if ((err = mp_mul(&res, &M[bitbuf], &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux(&res, P, &mu)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+
+ /* empty window and reset */
+ bitcpy = 0;
+ bitbuf = 0;
+ mode = 1;
+ }
+ }
+
+ /* if bits remain then square/multiply */
+ if ((mode == 2) && (bitcpy > 0)) {
+ /* square then multiply if the bit is set */
+ for (x = 0; x < bitcpy; x++) {
+ if ((err = mp_sqr(&res, &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux(&res, P, &mu)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+
+ bitbuf <<= 1;
+ if ((bitbuf & (1 << winsize)) != 0) {
+ /* then multiply */
+ if ((err = mp_mul(&res, &M[1], &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux(&res, P, &mu)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ }
+ }
+ }
+
+ mp_exch(&res, Y);
+ err = MP_OKAY;
+LBL_RES: mp_clear(&res);
+LBL_MU: mp_clear(&mu);
+LBL_M:
+ mp_clear(&M[1]);
+ for (x = 1 << (winsize - 1); x < (1 << winsize); x++) {
+ mp_clear(&M[x]);
+ }
+ return err;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_S_MP_MUL_DIGS_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* multiplies |a| * |b| and only computes upto digs digits of result
+ * HAC pp. 595, Algorithm 14.12 Modified so you can control how
+ * many digits of output are created.
+ */
+int s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs) {
+ mp_int t;
+ int res, pa, pb, ix, iy;
+ mp_digit u;
+ mp_word r;
+ mp_digit tmpx, *tmpt, *tmpy;
+
+ /* can we use the fast multiplier? */
+ if (((digs) < MP_WARRAY) &&
+ (MIN(a->used, b->used) <
+ (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) {
+ return fast_s_mp_mul_digs(a, b, c, digs);
+ }
+
+ if ((res = mp_init_size(&t, digs)) != MP_OKAY) {
+ return res;
+ }
+ t.used = digs;
+
+ /* compute the digits of the product directly */
+ pa = a->used;
+ for (ix = 0; ix < pa; ix++) {
+ /* set the carry to zero */
+ u = 0;
+
+ /* limit ourselves to making digs digits of output */
+ pb = MIN(b->used, digs - ix);
+
+ /* setup some aliases */
+ /* copy of the digit from a used within the nested loop */
+ tmpx = a->dp[ix];
+
+ /* an alias for the destination shifted ix places */
+ tmpt = t.dp + ix;
+
+ /* an alias for the digits of b */
+ tmpy = b->dp;
+
+ /* compute the columns of the output and propagate the carry */
+ for (iy = 0; iy < pb; iy++) {
+ /* compute the column as a mp_word */
+ r = (mp_word) * tmpt +
+ ((mp_word)tmpx * (mp_word) * tmpy++) +
+ (mp_word)u;
+
+ /* the new column is the lower part of the result */
+ *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK));
+
+ /* get the carry word from the result */
+ u = (mp_digit)(r >> ((mp_word)DIGIT_BIT));
+ }
+ /* set carry if it is placed below digs */
+ if ((ix + iy) < digs) {
+ *tmpt = u;
+ }
+ }
+
+ mp_clamp(&t);
+ mp_exch(&t, c);
+
+ mp_clear(&t);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_S_MP_MUL_HIGH_DIGS_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* multiplies |a| * |b| and does not compute the lower digs digits
+ * [meant to get the higher part of the product]
+ */
+int
+s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs) {
+ mp_int t;
+ int res, pa, pb, ix, iy;
+ mp_digit u;
+ mp_word r;
+ mp_digit tmpx, *tmpt, *tmpy;
+
+ /* can we use the fast multiplier? */
+ #ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C
+ if (((a->used + b->used + 1) < MP_WARRAY) &&
+ (MIN(a->used, b->used) < (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) {
+ return fast_s_mp_mul_high_digs(a, b, c, digs);
+ }
+ #endif
+
+ if ((res = mp_init_size(&t, a->used + b->used + 1)) != MP_OKAY) {
+ return res;
+ }
+ t.used = a->used + b->used + 1;
+
+ pa = a->used;
+ pb = b->used;
+ for (ix = 0; ix < pa; ix++) {
+ /* clear the carry */
+ u = 0;
+
+ /* left hand side of A[ix] * B[iy] */
+ tmpx = a->dp[ix];
+
+ /* alias to the address of where the digits will be stored */
+ tmpt = &(t.dp[digs]);
+
+ /* alias for where to read the right hand side from */
+ tmpy = b->dp + (digs - ix);
+
+ for (iy = digs - ix; iy < pb; iy++) {
+ /* calculate the double precision result */
+ r = (mp_word) * tmpt +
+ ((mp_word)tmpx * (mp_word) * tmpy++) +
+ (mp_word)u;
+
+ /* get the lower part */
+ *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK));
+
+ /* carry the carry */
+ u = (mp_digit)(r >> ((mp_word)DIGIT_BIT));
+ }
+ *tmpt = u;
+ }
+ mp_clamp(&t);
+ mp_exch(&t, c);
+ mp_clear(&t);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_S_MP_SQR_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */
+int s_mp_sqr(mp_int *a, mp_int *b) {
+ mp_int t;
+ int res, ix, iy, pa;
+ mp_word r;
+ mp_digit u, tmpx, *tmpt;
+
+ pa = a->used;
+ if ((res = mp_init_size(&t, (2 * pa) + 1)) != MP_OKAY) {
+ return res;
+ }
+
+ /* default used is maximum possible size */
+ t.used = (2 * pa) + 1;
+
+ for (ix = 0; ix < pa; ix++) {
+ /* first calculate the digit at 2*ix */
+ /* calculate double precision result */
+ r = (mp_word)t.dp[2 * ix] +
+ ((mp_word)a->dp[ix] * (mp_word)a->dp[ix]);
+
+ /* store lower part in result */
+ t.dp[ix + ix] = (mp_digit)(r & ((mp_word)MP_MASK));
+
+ /* get the carry */
+ u = (mp_digit)(r >> ((mp_word)DIGIT_BIT));
+
+ /* left hand side of A[ix] * A[iy] */
+ tmpx = a->dp[ix];
+
+ /* alias for where to store the results */
+ tmpt = t.dp + ((2 * ix) + 1);
+
+ for (iy = ix + 1; iy < pa; iy++) {
+ /* first calculate the product */
+ r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]);
+
+ /* now calculate the double precision result, note we use
+ * addition instead of *2 since it's easier to optimize
+ */
+ r = ((mp_word) * tmpt) + r + r + ((mp_word)u);
+
+ /* store lower part */
+ *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK));
+
+ /* get carry */
+ u = (mp_digit)(r >> ((mp_word)DIGIT_BIT));
+ }
+ /* propagate upwards */
+ while (u != ((mp_digit)0)) {
+ r = ((mp_word) * tmpt) + ((mp_word)u);
+ *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK));
+ u = (mp_digit)(r >> ((mp_word)DIGIT_BIT));
+ }
+ }
+
+ mp_clamp(&t);
+ mp_exch(&t, b);
+ mp_clear(&t);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BN_S_MP_SUB_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */
+int
+s_mp_sub(mp_int *a, mp_int *b, mp_int *c) {
+ int olduse, res, min, max;
+
+ /* find sizes */
+ min = b->used;
+ max = a->used;
+
+ /* init result */
+ if (c->alloc < max) {
+ if ((res = mp_grow(c, max)) != MP_OKAY) {
+ return res;
+ }
+ }
+ olduse = c->used;
+ c->used = max;
+
+ {
+ mp_digit u, *tmpa, *tmpb, *tmpc;
+ int i;
+
+ /* alias for digit pointers */
+ tmpa = a->dp;
+ tmpb = b->dp;
+ tmpc = c->dp;
+
+ /* set carry to zero */
+ u = 0;
+ for (i = 0; i < min; i++) {
+ /* T[i] = A[i] - B[i] - U */
+ *tmpc = (*tmpa++ - *tmpb++) - u;
+
+ /* U = carry bit of T[i]
+ * Note this saves performing an AND operation since
+ * if a carry does occur it will propagate all the way to the
+ * MSB. As a result a single shift is enough to get the carry
+ */
+ u = *tmpc >> ((mp_digit)((CHAR_BIT * sizeof(mp_digit)) - 1));
+
+ /* Clear carry from T[i] */
+ *tmpc++ &= MP_MASK;
+ }
+
+ /* now copy higher words if any, e.g. if A has more digits than B */
+ for ( ; i < max; i++) {
+ /* T[i] = A[i] - U */
+ *tmpc = *tmpa++ - u;
+
+ /* U = carry bit of T[i] */
+ u = *tmpc >> ((mp_digit)((CHAR_BIT * sizeof(mp_digit)) - 1));
+
+ /* Clear carry from T[i] */
+ *tmpc++ &= MP_MASK;
+ }
+
+ /* clear digits above used (since we may not have grown result above) */
+ for (i = c->used; i < olduse; i++) {
+ *tmpc++ = 0;
+ }
+ }
+
+ mp_clamp(c);
+ return MP_OKAY;
+}
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+
+#ifdef BNCORE_C
+
+/* LibTomMath, multiple-precision integer library -- Tom St Denis
+ *
+ * LibTomMath is a library that provides multiple-precision
+ * integer arithmetic as well as number theoretic functionality.
+ *
+ * The library was designed directly after the MPI library by
+ * Michael Fromberger but has been written from scratch with
+ * additional optimizations in place.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
+ */
+
+/* Known optimal configurations
+
+ CPU /Compiler /MUL CUTOFF/SQR CUTOFF
+ -------------------------------------------------------------
+ Intel P4 Northwood /GCC v3.4.1 / 88/ 128/LTM 0.32 ;-)
+ AMD Athlon64 /GCC v3.4.4 / 80/ 120/LTM 0.35
+
+ */
+
+int KARATSUBA_MUL_CUTOFF = 80, /* Min. number of digits before Karatsuba multiplication is used. */
+ KARATSUBA_SQR_CUTOFF = 120, /* Min. number of digits before Karatsuba squaring is used. */
+
+ TOOM_MUL_CUTOFF = 350, /* no optimal values of these are known yet so set em high */
+ TOOM_SQR_CUTOFF = 400;
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/**
+ @file crypt.c
+ Build strings, Tom St Denis
+ */
+
+const char *crypt_build_settings =
+ "LibTomCrypt ""1.17"" (Tom St Denis, tomstdenis@gmail.com)\n"
+ "LibTomCrypt is public domain software.\n"
+ "Built on " __DATE__ " at " __TIME__ "\n\n\n"
+ "Endianess: "
+#if defined(ENDIAN_NEUTRAL)
+ "neutral\n"
+#elif defined(ENDIAN_LITTLE)
+ "little"
+ #if defined(ENDIAN_32BITWORD)
+ " (32-bit words)\n"
+ #else
+ " (64-bit words)\n"
+ #endif
+#elif defined(ENDIAN_BIG)
+ "big"
+ #if defined(ENDIAN_32BITWORD)
+ " (32-bit words)\n"
+ #else
+ " (64-bit words)\n"
+ #endif
+#endif
+ "Clean stack: "
+#if defined(LTC_CLEAN_STACK)
+ "enabled\n"
+#else
+ "disabled\n"
+#endif
+ "Ciphers built-in:\n"
+#if defined(LTC_BLOWFISH)
+ " Blowfish\n"
+#endif
+#if defined(LTC_RC2)
+ " LTC_RC2\n"
+#endif
+#if defined(LTC_RC5)
+ " LTC_RC5\n"
+#endif
+#if defined(LTC_RC6)
+ " LTC_RC6\n"
+#endif
+#if defined(LTC_SAFERP)
+ " Safer+\n"
+#endif
+#if defined(LTC_SAFER)
+ " Safer\n"
+#endif
+#if defined(LTC_RIJNDAEL)
+ " Rijndael\n"
+#endif
+#if defined(LTC_XTEA)
+ " LTC_XTEA\n"
+#endif
+#if defined(LTC_TWOFISH)
+ " Twofish "
+ #if defined(LTC_TWOFISH_SMALL) && defined(LTC_TWOFISH_TABLES) && defined(LTC_TWOFISH_ALL_TABLES)
+ "(small, tables, all_tables)\n"
+ #elif defined(LTC_TWOFISH_SMALL) && defined(LTC_TWOFISH_TABLES)
+ "(small, tables)\n"
+ #elif defined(LTC_TWOFISH_SMALL) && defined(LTC_TWOFISH_ALL_TABLES)
+ "(small, all_tables)\n"
+ #elif defined(LTC_TWOFISH_TABLES) && defined(LTC_TWOFISH_ALL_TABLES)
+ "(tables, all_tables)\n"
+ #elif defined(LTC_TWOFISH_SMALL)
+ "(small)\n"
+ #elif defined(LTC_TWOFISH_TABLES)
+ "(tables)\n"
+ #elif defined(LTC_TWOFISH_ALL_TABLES)
+ "(all_tables)\n"
+ #else
+ "\n"
+ #endif
+#endif
+#if defined(LTC_DES)
+ " LTC_DES\n"
+#endif
+#if defined(LTC_CAST5)
+ " LTC_CAST5\n"
+#endif
+#if defined(LTC_NOEKEON)
+ " Noekeon\n"
+#endif
+#if defined(LTC_SKIPJACK)
+ " Skipjack\n"
+#endif
+#if defined(LTC_KHAZAD)
+ " Khazad\n"
+#endif
+#if defined(LTC_ANUBIS)
+ " Anubis "
+#endif
+#if defined(LTC_ANUBIS_TWEAK)
+ " (tweaked)"
+#endif
+ "\n"
+#if defined(LTC_KSEED)
+ " LTC_KSEED\n"
+#endif
+#if defined(LTC_KASUMI)
+ " KASUMI\n"
+#endif
+
+ "\nHashes built-in:\n"
+#if defined(LTC_SHA512)
+ " LTC_SHA-512\n"
+#endif
+#if defined(LTC_SHA384)
+ " LTC_SHA-384\n"
+#endif
+#if defined(LTC_SHA256)
+ " LTC_SHA-256\n"
+#endif
+#if defined(LTC_SHA224)
+ " LTC_SHA-224\n"
+#endif
+#if defined(LTC_TIGER)
+ " LTC_TIGER\n"
+#endif
+#if defined(LTC_SHA1)
+ " LTC_SHA1\n"
+#endif
+#if defined(LTC_MD5)
+ " LTC_MD5\n"
+#endif
+#if defined(LTC_MD4)
+ " LTC_MD4\n"
+#endif
+#if defined(LTC_MD2)
+ " LTC_MD2\n"
+#endif
+#if defined(LTC_RIPEMD128)
+ " LTC_RIPEMD128\n"
+#endif
+#if defined(LTC_RIPEMD160)
+ " LTC_RIPEMD160\n"
+#endif
+#if defined(LTC_RIPEMD256)
+ " LTC_RIPEMD256\n"
+#endif
+#if defined(LTC_RIPEMD320)
+ " LTC_RIPEMD320\n"
+#endif
+#if defined(LTC_WHIRLPOOL)
+ " LTC_WHIRLPOOL\n"
+#endif
+#if defined(LTC_CHC_HASH)
+ " LTC_CHC_HASH \n"
+#endif
+
+ "\nBlock Chaining Modes:\n"
+#if defined(LTC_CFB_MODE)
+ " CFB\n"
+#endif
+#if defined(LTC_OFB_MODE)
+ " OFB\n"
+#endif
+#if defined(LTC_ECB_MODE)
+ " ECB\n"
+#endif
+#if defined(LTC_CBC_MODE)
+ " CBC\n"
+#endif
+#if defined(LTC_CTR_MODE)
+ " CTR "
+#endif
+#if defined(LTC_CTR_OLD)
+ " (CTR_OLD) "
+#endif
+ "\n"
+#if defined(LRW_MODE)
+ " LRW_MODE"
+ #if defined(LRW_TABLES)
+ " (LRW_TABLES) "
+ #endif
+ "\n"
+#endif
+#if defined(LTC_F8_MODE)
+ " F8 MODE\n"
+#endif
+#if defined(LTC_XTS_MODE)
+ " LTC_XTS_MODE\n"
+#endif
+
+ "\nMACs:\n"
+#if defined(LTC_HMAC)
+ " LTC_HMAC\n"
+#endif
+#if defined(LTC_OMAC)
+ " LTC_OMAC\n"
+#endif
+#if defined(LTC_PMAC)
+ " PMAC\n"
+#endif
+#if defined(LTC_PELICAN)
+ " LTC_PELICAN\n"
+#endif
+#if defined(LTC_XCBC)
+ " XCBC-MAC\n"
+#endif
+#if defined(LTC_F9_MODE)
+ " F9-MAC\n"
+#endif
+
+ "\nENC + AUTH modes:\n"
+#if defined(LTC_EAX_MODE)
+ " LTC_EAX_MODE\n"
+#endif
+#if defined(LTC_OCB_MODE)
+ " LTC_OCB_MODE\n"
+#endif
+#if defined(LTC_CCM_MODE)
+ " LTC_CCM_MODE\n"
+#endif
+#if defined(LTC_GCM_MODE)
+ " LTC_GCM_MODE "
+#endif
+#if defined(LTC_GCM_TABLES)
+ " (LTC_GCM_TABLES) "
+#endif
+ "\n"
+
+ "\nPRNG:\n"
+#if defined(LTC_YARROW)
+ " Yarrow\n"
+#endif
+#if defined(LTC_SPRNG)
+ " LTC_SPRNG\n"
+#endif
+#if defined(LTC_RC4)
+ " LTC_RC4\n"
+#endif
+#if defined(LTC_FORTUNA)
+ " Fortuna\n"
+#endif
+#if defined(LTC_SOBER128)
+ " LTC_SOBER128\n"
+#endif
+
+ "\nPK Algs:\n"
+#if defined(LTC_MRSA)
+ " RSA \n"
+#endif
+#if defined(LTC_MECC)
+ " ECC\n"
+#endif
+#if defined(LTC_MDSA)
+ " DSA\n"
+#endif
+#if defined(MKAT)
+ " Katja\n"
+#endif
+
+ "\nCompiler:\n"
+#if defined(WIN32)
+ " WIN32 platform detected.\n"
+#endif
+#if defined(__CYGWIN__)
+ " CYGWIN Detected.\n"
+#endif
+#if defined(__DJGPP__)
+ " DJGPP Detected.\n"
+#endif
+#if defined(_MSC_VER)
+ " MSVC compiler detected.\n"
+#endif
+#if defined(__GNUC__)
+ " GCC compiler detected.\n"
+#endif
+#if defined(INTEL_CC)
+ " Intel C Compiler detected.\n"
+#endif
+#if defined(__x86_64__)
+ " x86-64 detected.\n"
+#endif
+#if defined(LTC_PPC32)
+ " LTC_PPC32 defined \n"
+#endif
+
+ "\nVarious others: "
+#if defined(LTC_BASE64)
+ " LTC_BASE64 "
+#endif
+#if defined(MPI)
+ " MPI "
+#endif
+#if defined(TRY_UNRANDOM_FIRST)
+ " TRY_UNRANDOM_FIRST "
+#endif
+#if defined(LTC_TEST)
+ " LTC_TEST "
+#endif
+#if defined(LTC_PKCS_1)
+ " LTC_PKCS#1 "
+#endif
+#if defined(LTC_PKCS_5)
+ " LTC_PKCS#5 "
+#endif
+#if defined(LTC_SMALL_CODE)
+ " LTC_SMALL_CODE "
+#endif
+#if defined(LTC_NO_FILE)
+ " LTC_NO_FILE "
+#endif
+#if defined(LTC_DER)
+ " LTC_DER "
+#endif
+#if defined(LTC_FAST)
+ " LTC_FAST "
+#endif
+#if defined(LTC_NO_FAST)
+ " LTC_NO_FAST "
+#endif
+#if defined(LTC_NO_BSWAP)
+ " LTC_NO_BSWAP "
+#endif
+#if defined(LTC_NO_ASM)
+ " LTC_NO_ASM "
+#endif
+#if defined(LTC_NO_TEST)
+ " LTC_NO_TEST "
+#endif
+#if defined(LTC_NO_TABLES)
+ " LTC_NO_TABLES "
+#endif
+#if defined(LTC_PTHREAD)
+ " LTC_PTHREAD "
+#endif
+#if defined(LTM_LTC_DESC)
+ " LTM_DESC "
+#endif
+#if defined(TFM_LTC_DESC)
+ " TFM_DESC "
+#endif
+#if defined(LTC_MECC_ACCEL)
+ " LTC_MECC_ACCEL "
+#endif
+#if defined(GMP_LTC_DESC)
+ " GMP_DESC "
+#endif
+#if defined(LTC_EASY)
+ " (easy) "
+#endif
+#if defined(LTC_MECC_FP)
+ " LTC_MECC_FP "
+#endif
+#if defined(LTC_ECC_SHAMIR)
+ " LTC_ECC_SHAMIR "
+#endif
+ "\n"
+ "\n\n\n"
+;
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt.c,v $ */
+/* $Revision: 1.36 $ */
+/* $Date: 2007/05/12 14:46:12 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+#include
+
+/**
+ @file crypt_argchk.c
+ Perform argument checking, Tom St Denis
+ */
+
+#if (ARGTYPE == 0)
+void crypt_argchk(char *v, char *s, int d) {
+ fprintf(stderr, "LTC_ARGCHK '%s' failure on line %d of file %s\n",
+ v, d, s);
+ (void)raise(SIGABRT);
+}
+#endif
+
+#ifndef TOMCRYPT_H_
+#define TOMCRYPT_H_
+#define USE_LTM
+#define LTM_DESC
+#define LTC_SHA1
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* use configuration data */
+#ifndef TOMCRYPT_CUSTOM_H_
+#define TOMCRYPT_CUSTOM_H_
+
+/* macros for various libc functions you can change for embedded targets */
+#ifndef XMALLOC
+ #ifdef malloc
+ #define LTC_NO_PROTOTYPES
+ #endif
+ #define XMALLOC malloc
+#endif
+#ifndef XREALLOC
+ #ifdef realloc
+ #define LTC_NO_PROTOTYPES
+ #endif
+ #define XREALLOC realloc
+#endif
+#ifndef XCALLOC
+ #ifdef calloc
+ #define LTC_NO_PROTOTYPES
+ #endif
+ #define XCALLOC calloc
+#endif
+#ifndef XFREE
+ #ifdef free
+ #define LTC_NO_PROTOTYPES
+ #endif
+ #define XFREE free
+#endif
+
+#ifndef XMEMSET
+ #ifdef memset
+ #define LTC_NO_PROTOTYPES
+ #endif
+ #define XMEMSET memset
+#endif
+#ifndef XMEMCPY
+ #ifdef memcpy
+ #define LTC_NO_PROTOTYPES
+ #endif
+ #define XMEMCPY memcpy
+#endif
+#ifndef XMEMCMP
+ #ifdef memcmp
+ #define LTC_NO_PROTOTYPES
+ #endif
+ #define XMEMCMP memcmp
+#endif
+#ifndef XSTRCMP
+ #ifdef strcmp
+ #define LTC_NO_PROTOTYPES
+ #endif
+ #define XSTRCMP strcmp
+#endif
+
+#ifndef XCLOCK
+ #define XCLOCK clock
+#endif
+#ifndef XCLOCKS_PER_SEC
+ #define XCLOCKS_PER_SEC CLOCKS_PER_SEC
+#endif
+
+#ifndef XQSORT
+ #ifdef qsort
+ #define LTC_NO_PROTOTYPES
+ #endif
+ #define XQSORT qsort
+#endif
+
+/* Easy button? */
+#ifdef LTC_EASY
+ #define LTC_NO_CIPHERS
+ #define LTC_RIJNDAEL
+ #define LTC_BLOWFISH
+ #define LTC_DES
+ #define LTC_CAST5
+
+ #define LTC_NO_MODES
+ #define LTC_ECB_MODE
+ #define LTC_CBC_MODE
+ #define LTC_CTR_MODE
+
+ #define LTC_NO_HASHES
+ #define LTC_SHA1
+ #define LTC_SHA512
+ #define LTC_SHA384
+ #define LTC_SHA256
+ #define LTC_SHA224
+
+ #define LTC_NO_MACS
+ #define LTC_HMAC
+ #define LTC_OMAC
+ #define LTC_CCM_MODE
+
+ #define LTC_NO_PRNGS
+ #define LTC_SPRNG
+ #define LTC_YARROW
+ #define LTC_DEVRANDOM
+ #define TRY_URANDOM_FIRST
+
+ #define LTC_NO_PK
+ #define LTC_MRSA
+ #define LTC_MECC
+#endif
+
+/* Use small code where possible */
+/* #define LTC_SMALL_CODE */
+
+/* Enable self-test test vector checking */
+#ifndef LTC_NO_TEST
+ #define LTC_TEST
+#endif
+
+/* clean the stack of functions which put private information on stack */
+/* #define LTC_CLEAN_STACK */
+
+/* disable all file related functions */
+/* #define LTC_NO_FILE */
+
+/* disable all forms of ASM */
+/* #define LTC_NO_ASM */
+
+/* disable FAST mode */
+/* #define LTC_NO_FAST */
+
+/* disable BSWAP on x86 */
+/* #define LTC_NO_BSWAP */
+
+/* ---> Symmetric Block Ciphers <--- */
+#ifndef LTC_NO_CIPHERS
+
+ #define LTC_BLOWFISH
+ #define LTC_RC2
+ #define LTC_RC5
+ #define LTC_RC6
+ #define LTC_SAFERP
+ #define LTC_RIJNDAEL
+ #define LTC_XTEA
+
+/* _TABLES tells it to use tables during setup, _SMALL means to use the smaller scheduled key format
+ * (saves 4KB of ram), _ALL_TABLES enables all tables during setup */
+ #define LTC_TWOFISH
+ #ifndef LTC_NO_TABLES
+ #define LTC_TWOFISH_TABLES
+/* #define LTC_TWOFISH_ALL_TABLES */
+ #else
+ #define LTC_TWOFISH_SMALL
+ #endif
+/* #define LTC_TWOFISH_SMALL */
+/* LTC_DES includes EDE triple-LTC_DES */
+ #define LTC_DES
+ #define LTC_CAST5
+ #define LTC_NOEKEON
+ #define LTC_SKIPJACK
+ #define LTC_SAFER
+ #define LTC_KHAZAD
+ #define LTC_ANUBIS
+ #define LTC_ANUBIS_TWEAK
+ #define LTC_KSEED
+ #define LTC_KASUMI
+#endif /* LTC_NO_CIPHERS */
+
+
+/* ---> Block Cipher Modes of Operation <--- */
+#ifndef LTC_NO_MODES
+
+ #define LTC_CFB_MODE
+ #define LTC_OFB_MODE
+ #define LTC_ECB_MODE
+ #define LTC_CBC_MODE
+ #define LTC_CTR_MODE
+
+/* F8 chaining mode */
+ #define LTC_F8_MODE
+
+/* LRW mode */
+ #define LTC_LRW_MODE
+ #ifndef LTC_NO_TABLES
+
+/* like GCM mode this will enable 16 8x128 tables [64KB] that make
+ * seeking very fast.
+ */
+ #define LRW_TABLES
+ #endif
+
+/* XTS mode */
+ #define LTC_XTS_MODE
+#endif /* LTC_NO_MODES */
+
+/* ---> One-Way Hash Functions <--- */
+#ifndef LTC_NO_HASHES
+
+ #define LTC_CHC_HASH
+ #define LTC_WHIRLPOOL
+ #define LTC_SHA512
+ #define LTC_SHA384
+ #define LTC_SHA256
+ #define LTC_SHA224
+ #define LTC_TIGER
+ #define LTC_SHA1
+ #define LTC_MD5
+ #define LTC_MD4
+ #define LTC_MD2
+ #define LTC_RIPEMD128
+ #define LTC_RIPEMD160
+ #define LTC_RIPEMD256
+ #define LTC_RIPEMD320
+#endif /* LTC_NO_HASHES */
+
+/* ---> MAC functions <--- */
+#ifndef LTC_NO_MACS
+
+ #define LTC_HMAC
+ #define LTC_OMAC
+ #define LTC_PMAC
+ #define LTC_XCBC
+ #define LTC_F9_MODE
+ #define LTC_PELICAN
+
+ #if defined(LTC_PELICAN) && !defined(LTC_RIJNDAEL)
+ #error Pelican-MAC requires LTC_RIJNDAEL
+ #endif
+
+/* ---> Encrypt + Authenticate Modes <--- */
+
+ #define LTC_EAX_MODE
+ #if defined(LTC_EAX_MODE) && !(defined(LTC_CTR_MODE) && defined(LTC_OMAC))
+ #error LTC_EAX_MODE requires CTR and LTC_OMAC mode
+ #endif
+
+ #define LTC_OCB_MODE
+ #define LTC_CCM_MODE
+ #define LTC_GCM_MODE
+
+/* Use 64KiB tables */
+ #ifndef LTC_NO_TABLES
+ #define LTC_GCM_TABLES
+ #endif
+
+/* USE SSE2? requires GCC works on x86_32 and x86_64*/
+ #ifdef LTC_GCM_TABLES
+/* #define LTC_GCM_TABLES_SSE2 */
+ #endif
+#endif /* LTC_NO_MACS */
+
+/* Various tidbits of modern neatoness */
+#define LTC_BASE64
+
+/* --> Pseudo Random Number Generators <--- */
+#ifndef LTC_NO_PRNGS
+
+/* Yarrow */
+ #define LTC_YARROW
+/* which descriptor of AES to use? */
+/* 0 = rijndael_enc 1 = aes_enc, 2 = rijndael [full], 3 = aes [full] */
+ #define LTC_YARROW_AES 0
+
+ #if defined(LTC_YARROW) && !defined(LTC_CTR_MODE)
+ #error LTC_YARROW requires LTC_CTR_MODE chaining mode to be defined!
+ #endif
+
+/* a PRNG that simply reads from an available system source */
+ #define LTC_SPRNG
+
+/* The LTC_RC4 stream cipher */
+ #define LTC_RC4
+
+/* Fortuna PRNG */
+ #define LTC_FORTUNA
+/* reseed every N calls to the read function */
+ #define LTC_FORTUNA_WD 10
+/* number of pools (4..32) can save a bit of ram by lowering the count */
+ #define LTC_FORTUNA_POOLS 32
+
+/* Greg's LTC_SOBER128 PRNG ;-0 */
+ #define LTC_SOBER128
+
+/* the *nix style /dev/random device */
+ #define LTC_DEVRANDOM
+/* try /dev/urandom before trying /dev/random */
+ #define TRY_URANDOM_FIRST
+#endif /* LTC_NO_PRNGS */
+
+/* ---> math provider? <--- */
+#ifndef LTC_NO_MATH
+
+/* LibTomMath */
+/* #define LTM_LTC_DESC */
+
+/* TomsFastMath */
+/* #define TFM_LTC_DESC */
+#endif /* LTC_NO_MATH */
+
+/* ---> Public Key Crypto <--- */
+#ifndef LTC_NO_PK
+
+/* Include RSA support */
+ #define LTC_MRSA
+
+/* Include Katja (a Rabin variant like RSA) */
+/* #define MKAT */
+
+/* Digital Signature Algorithm */
+ #define LTC_MDSA
+
+/* ECC */
+ #define LTC_MECC
+
+/* use Shamir's trick for point mul (speeds up signature verification) */
+ #define LTC_ECC_SHAMIR
+
+ #if defined(TFM_LTC_DESC) && defined(LTC_MECC)
+ #define LTC_MECC_ACCEL
+ #endif
+
+/* do we want fixed point ECC */
+/* #define LTC_MECC_FP */
+
+/* Timing Resistant? */
+/* #define LTC_ECC_TIMING_RESISTANT */
+#endif /* LTC_NO_PK */
+
+/* LTC_PKCS #1 (RSA) and #5 (Password Handling) stuff */
+#ifndef LTC_NO_PKCS
+
+ #define LTC_PKCS_1
+ #define LTC_PKCS_5
+
+/* Include ASN.1 DER (required by DSA/RSA) */
+ #define LTC_DER
+#endif /* LTC_NO_PKCS */
+
+/* cleanup */
+
+#ifdef LTC_MECC
+/* Supported ECC Key Sizes */
+ #ifndef LTC_NO_CURVES
+ #define ECC112
+ #define ECC128
+ #define ECC160
+ #define ECC192
+ #define ECC224
+ #define ECC256
+ #define ECC384
+ #define ECC521
+ #endif
+#endif
+
+#if defined(LTC_MECC) || defined(LTC_MRSA) || defined(LTC_MDSA) || defined(MKATJA)
+/* Include the MPI functionality? (required by the PK algorithms) */
+ #define MPI
+#endif
+
+#ifdef LTC_MRSA
+ #define LTC_PKCS_1
+#endif
+
+#if defined(LTC_DER) && !defined(MPI)
+ #error ASN.1 DER requires MPI functionality
+#endif
+
+#if (defined(LTC_MDSA) || defined(LTC_MRSA) || defined(LTC_MECC) || defined(MKATJA)) && !defined(LTC_DER)
+ #error PK requires ASN.1 DER functionality, make sure LTC_DER is enabled
+#endif
+
+/* THREAD management */
+#ifdef LTC_PTHREAD
+
+ #include
+
+ #define LTC_MUTEX_GLOBAL(x) pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER;
+ #define LTC_MUTEX_PROTO(x) extern pthread_mutex_t x;
+ #define LTC_MUTEX_TYPE(x) pthread_mutex_t x;
+ #define LTC_MUTEX_INIT(x) pthread_mutex_init(x, NULL);
+ #define LTC_MUTEX_LOCK(x) pthread_mutex_lock(x);
+ #define LTC_MUTEX_UNLOCK(x) pthread_mutex_unlock(x);
+
+#else
+
+/* default no functions */
+ #define LTC_MUTEX_GLOBAL(x)
+ #define LTC_MUTEX_PROTO(x)
+ #define LTC_MUTEX_TYPE(x)
+ #define LTC_MUTEX_INIT(x)
+ #define LTC_MUTEX_LOCK(x)
+ #define LTC_MUTEX_UNLOCK(x)
+#endif
+
+/* Debuggers */
+
+/* define this if you use Valgrind, note: it CHANGES the way SOBER-128 and LTC_RC4 work (see the code) */
+/* #define LTC_VALGRIND */
+#endif
+
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_custom.h,v $ */
+/* $Revision: 1.73 $ */
+/* $Date: 2007/05/12 14:37:41 $ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* version */
+#define CRYPT 0x0117
+#define SCRYPT "1.17"
+
+/* max size of either a cipher/hash block or symmetric key [largest of the two] */
+#define MAXBLOCKSIZE 128
+
+/* descriptor table size */
+
+/* error codes [will be expanded in future releases] */
+enum {
+ CRYPT_OK=0, /* Result OK */
+ CRYPT_ERROR, /* Generic Error */
+ CRYPT_NOP, /* Not a failure but no operation was performed */
+
+ CRYPT_INVALID_KEYSIZE, /* Invalid key size given */
+ CRYPT_INVALID_ROUNDS, /* Invalid number of rounds */
+ CRYPT_FAIL_TESTVECTOR, /* Algorithm failed test vectors */
+
+ CRYPT_BUFFER_OVERFLOW, /* Not enough space for output */
+ CRYPT_INVALID_PACKET, /* Invalid input packet given */
+
+ CRYPT_INVALID_PRNGSIZE, /* Invalid number of bits for a PRNG */
+ CRYPT_ERROR_READPRNG, /* Could not read enough from PRNG */
+
+ CRYPT_INVALID_CIPHER, /* Invalid cipher specified */
+ CRYPT_INVALID_HASH, /* Invalid hash specified */
+ CRYPT_INVALID_PRNG, /* Invalid PRNG specified */
+
+ CRYPT_MEM, /* Out of memory */
+
+ CRYPT_PK_TYPE_MISMATCH, /* Not equivalent types of PK keys */
+ CRYPT_PK_NOT_PRIVATE, /* Requires a private PK key */
+
+ CRYPT_INVALID_ARG, /* Generic invalid argument */
+ CRYPT_FILE_NOTFOUND, /* File Not Found */
+
+ CRYPT_PK_INVALID_TYPE, /* Invalid type of PK key */
+ CRYPT_PK_INVALID_SYSTEM, /* Invalid PK system specified */
+ CRYPT_PK_DUP, /* Duplicate key already in key ring */
+ CRYPT_PK_NOT_FOUND, /* Key not found in keyring */
+ CRYPT_PK_INVALID_SIZE, /* Invalid size input for PK parameters */
+
+ CRYPT_INVALID_PRIME_SIZE, /* Invalid size of prime requested */
+ CRYPT_PK_INVALID_PADDING /* Invalid padding on input */
+};
+
+/* This is the build config file.
+ *
+ * With this you can setup what to inlcude/exclude automatically during any build. Just comment
+ * out the line that #define's the word for the thing you want to remove. phew!
+ */
+
+#ifndef TOMCRYPT_CFG_H
+#define TOMCRYPT_CFG_H
+
+#if defined(_WIN32) || defined(_MSC_VER)
+ #define LTC_CALL __cdecl
+#else
+ #ifndef LTC_CALL
+ #define LTC_CALL
+ #endif
+#endif
+
+#ifndef LTC_EXPORT
+ #define LTC_EXPORT
+#endif
+
+/* certain platforms use macros for these, making the prototypes broken */
+#ifndef LTC_NO_PROTOTYPES
+
+/* you can change how memory allocation works ... */
+LTC_EXPORT void *LTC_CALL XMALLOC(size_t n);
+LTC_EXPORT void *LTC_CALL XREALLOC(void *p, size_t n);
+LTC_EXPORT void *LTC_CALL XCALLOC(size_t n, size_t s);
+LTC_EXPORT void LTC_CALL XFREE(void *p);
+
+LTC_EXPORT void LTC_CALL XQSORT(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
+
+
+/* change the clock function too */
+LTC_EXPORT clock_t LTC_CALL XCLOCK(void);
+
+/* various other functions */
+LTC_EXPORT void *LTC_CALL XMEMCPY(void *dest, const void *src, size_t n);
+LTC_EXPORT int LTC_CALL XMEMCMP(const void *s1, const void *s2, size_t n);
+LTC_EXPORT void *LTC_CALL XMEMSET(void *s, int c, size_t n);
+
+LTC_EXPORT int LTC_CALL XSTRCMP(const char *s1, const char *s2);
+#endif
+
+/* type of argument checking, 0=default, 1=fatal and 2=error+continue, 3=nothing */
+#ifndef ARGTYPE
+ #define ARGTYPE 0
+#endif
+
+/* Controls endianess and size of registers. Leave uncommented to get platform neutral [slower] code
+ *
+ * Note: in order to use the optimized macros your platform must support unaligned 32 and 64 bit read/writes.
+ * The x86 platforms allow this but some others [ARM for instance] do not. On those platforms you **MUST**
+ * use the portable [slower] macros.
+ */
+
+/* detect x86-32 machines somewhat */
+#if !defined(__STRICT_ANSI__) && (defined(INTEL_CC) || (defined(_MSC_VER) && defined(WIN32)) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__))))
+ #define ENDIAN_LITTLE
+ #define ENDIAN_32BITWORD
+ #define LTC_FAST
+ #define LTC_FAST_TYPE unsigned long
+#endif
+
+/* detects MIPS R5900 processors (PS2) */
+#if (defined(__R5900) || defined(R5900) || defined(__R5900__)) && (defined(_mips) || defined(__mips__) || defined(mips))
+ #define ENDIAN_LITTLE
+ #define ENDIAN_64BITWORD
+#endif
+
+/* detect amd64 */
+#if !defined(__STRICT_ANSI__) && defined(__x86_64__)
+ #define ENDIAN_LITTLE
+ #define ENDIAN_64BITWORD
+ #define LTC_FAST
+ #define LTC_FAST_TYPE unsigned long
+#endif
+
+/* detect PPC32 */
+#if !defined(__STRICT_ANSI__) && defined(LTC_PPC32)
+ #define ENDIAN_BIG
+ #define ENDIAN_32BITWORD
+ #define LTC_FAST
+ #define LTC_FAST_TYPE unsigned long
+#endif
+
+/* detect sparc and sparc64 */
+#if defined(__sparc__)
+ #define ENDIAN_BIG
+ #if defined(__arch64__)
+ #define ENDIAN_64BITWORD
+ #else
+ #define ENDIAN_32BITWORD
+ #endif
+#endif
+
+
+#ifdef LTC_NO_FAST
+ #ifdef LTC_FAST
+ #undef LTC_FAST
+ #endif
+#endif
+
+/* No asm is a quick way to disable anything "not portable" */
+#ifdef LTC_NO_ASM
+ #undef ENDIAN_LITTLE
+ #undef ENDIAN_BIG
+ #undef ENDIAN_32BITWORD
+ #undef ENDIAN_64BITWORD
+ #undef LTC_FAST
+ #undef LTC_FAST_TYPE
+ #define LTC_NO_ROLC
+ #define LTC_NO_BSWAP
+#endif
+
+/* #define ENDIAN_LITTLE */
+/* #define ENDIAN_BIG */
+
+/* #define ENDIAN_32BITWORD */
+/* #define ENDIAN_64BITWORD */
+
+#if (defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE)) && !(defined(ENDIAN_32BITWORD) || defined(ENDIAN_64BITWORD))
+ #error You must specify a word size as well as endianess in tomcrypt_cfg.h
+#endif
+
+#if !(defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE))
+ #define ENDIAN_NEUTRAL
+#endif
+#endif
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_cfg.h,v $ */
+/* $Revision: 1.19 $ */
+/* $Date: 2006/12/04 02:19:48 $ */
+
+/* fix for MSVC ...evil! */
+#ifdef _MSC_VER
+ #define CONST64(n) n ## ui64
+typedef unsigned __int64 ulong64;
+#else
+ #define CONST64(n) n ## ULL
+typedef unsigned long long ulong64;
+#endif
+
+/* this is the "32-bit at least" data type
+ * Re-define it to suit your platform but it must be at least 32-bits
+ */
+#if defined(__x86_64__) || (defined(__sparc__) && defined(__arch64__))
+typedef unsigned ulong32;
+#else
+typedef unsigned long ulong32;
+#endif
+
+/* ---- HELPER MACROS ---- */
+#ifdef ENDIAN_NEUTRAL
+
+ #define STORE32L(x, y) \
+ { (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \
+ (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); }
+
+ #define LOAD32L(x, y) \
+ { x = ((unsigned long)((y)[3] & 255) << 24) | \
+ ((unsigned long)((y)[2] & 255) << 16) | \
+ ((unsigned long)((y)[1] & 255) << 8) | \
+ ((unsigned long)((y)[0] & 255)); }
+
+ #define STORE64L(x, y) \
+ { (y)[7] = (unsigned char)(((x) >> 56) & 255); (y)[6] = (unsigned char)(((x) >> 48) & 255); \
+ (y)[5] = (unsigned char)(((x) >> 40) & 255); (y)[4] = (unsigned char)(((x) >> 32) & 255); \
+ (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \
+ (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); }
+
+ #define LOAD64L(x, y) \
+ { x = (((ulong64)((y)[7] & 255)) << 56) | (((ulong64)((y)[6] & 255)) << 48) | \
+ (((ulong64)((y)[5] & 255)) << 40) | (((ulong64)((y)[4] & 255)) << 32) | \
+ (((ulong64)((y)[3] & 255)) << 24) | (((ulong64)((y)[2] & 255)) << 16) | \
+ (((ulong64)((y)[1] & 255)) << 8) | (((ulong64)((y)[0] & 255))); }
+
+ #define STORE32H(x, y) \
+ { (y)[0] = (unsigned char)(((x) >> 24) & 255); (y)[1] = (unsigned char)(((x) >> 16) & 255); \
+ (y)[2] = (unsigned char)(((x) >> 8) & 255); (y)[3] = (unsigned char)((x) & 255); }
+
+ #define LOAD32H(x, y) \
+ { x = ((unsigned long)((y)[0] & 255) << 24) | \
+ ((unsigned long)((y)[1] & 255) << 16) | \
+ ((unsigned long)((y)[2] & 255) << 8) | \
+ ((unsigned long)((y)[3] & 255)); }
+
+ #define STORE64H(x, y) \
+ { (y)[0] = (unsigned char)(((x) >> 56) & 255); (y)[1] = (unsigned char)(((x) >> 48) & 255); \
+ (y)[2] = (unsigned char)(((x) >> 40) & 255); (y)[3] = (unsigned char)(((x) >> 32) & 255); \
+ (y)[4] = (unsigned char)(((x) >> 24) & 255); (y)[5] = (unsigned char)(((x) >> 16) & 255); \
+ (y)[6] = (unsigned char)(((x) >> 8) & 255); (y)[7] = (unsigned char)((x) & 255); }
+
+ #define LOAD64H(x, y) \
+ { x = (((ulong64)((y)[0] & 255)) << 56) | (((ulong64)((y)[1] & 255)) << 48) | \
+ (((ulong64)((y)[2] & 255)) << 40) | (((ulong64)((y)[3] & 255)) << 32) | \
+ (((ulong64)((y)[4] & 255)) << 24) | (((ulong64)((y)[5] & 255)) << 16) | \
+ (((ulong64)((y)[6] & 255)) << 8) | (((ulong64)((y)[7] & 255))); }
+#endif /* ENDIAN_NEUTRAL */
+
+#ifdef ENDIAN_LITTLE
+
+ #if !defined(LTC_NO_BSWAP) && (defined(INTEL_CC) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__) || defined(__x86_64__))))
+
+ #define STORE32H(x, y) \
+ asm __volatile__ ( \
+ "bswapl %0 \n\t" \
+ "movl %0,(%1)\n\t" \
+ "bswapl %0 \n\t" \
+ ::"r" (x), "r" (y));
+
+ #define LOAD32H(x, y) \
+ asm __volatile__ ( \
+ "movl (%1),%0\n\t" \
+ "bswapl %0\n\t" \
+ : "=r" (x) : "r" (y));
+
+ #else
+
+ #define STORE32H(x, y) \
+ { (y)[0] = (unsigned char)(((x) >> 24) & 255); (y)[1] = (unsigned char)(((x) >> 16) & 255); \
+ (y)[2] = (unsigned char)(((x) >> 8) & 255); (y)[3] = (unsigned char)((x) & 255); }
+
+ #define LOAD32H(x, y) \
+ { x = ((unsigned long)((y)[0] & 255) << 24) | \
+ ((unsigned long)((y)[1] & 255) << 16) | \
+ ((unsigned long)((y)[2] & 255) << 8) | \
+ ((unsigned long)((y)[3] & 255)); }
+ #endif
+
+
+/* x86_64 processor */
+ #if !defined(LTC_NO_BSWAP) && (defined(__GNUC__) && defined(__x86_64__))
+
+ #define STORE64H(x, y) \
+ asm __volatile__ ( \
+ "bswapq %0 \n\t" \
+ "movq %0,(%1)\n\t" \
+ "bswapq %0 \n\t" \
+ ::"r" (x), "r" (y));
+
+ #define LOAD64H(x, y) \
+ asm __volatile__ ( \
+ "movq (%1),%0\n\t" \
+ "bswapq %0\n\t" \
+ : "=r" (x) : "r" (y));
+
+ #else
+
+ #define STORE64H(x, y) \
+ { (y)[0] = (unsigned char)(((x) >> 56) & 255); (y)[1] = (unsigned char)(((x) >> 48) & 255); \
+ (y)[2] = (unsigned char)(((x) >> 40) & 255); (y)[3] = (unsigned char)(((x) >> 32) & 255); \
+ (y)[4] = (unsigned char)(((x) >> 24) & 255); (y)[5] = (unsigned char)(((x) >> 16) & 255); \
+ (y)[6] = (unsigned char)(((x) >> 8) & 255); (y)[7] = (unsigned char)((x) & 255); }
+
+ #define LOAD64H(x, y) \
+ { x = (((ulong64)((y)[0] & 255)) << 56) | (((ulong64)((y)[1] & 255)) << 48) | \
+ (((ulong64)((y)[2] & 255)) << 40) | (((ulong64)((y)[3] & 255)) << 32) | \
+ (((ulong64)((y)[4] & 255)) << 24) | (((ulong64)((y)[5] & 255)) << 16) | \
+ (((ulong64)((y)[6] & 255)) << 8) | (((ulong64)((y)[7] & 255))); }
+ #endif
+
+ #ifdef ENDIAN_32BITWORD
+
+ #define STORE32L(x, y) \
+ { ulong32 __t = (x); XMEMCPY(y, &__t, 4); }
+
+ #define LOAD32L(x, y) \
+ XMEMCPY(&(x), y, 4);
+
+ #define STORE64L(x, y) \
+ { (y)[7] = (unsigned char)(((x) >> 56) & 255); (y)[6] = (unsigned char)(((x) >> 48) & 255); \
+ (y)[5] = (unsigned char)(((x) >> 40) & 255); (y)[4] = (unsigned char)(((x) >> 32) & 255); \
+ (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \
+ (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); }
+
+ #define LOAD64L(x, y) \
+ { x = (((ulong64)((y)[7] & 255)) << 56) | (((ulong64)((y)[6] & 255)) << 48) | \
+ (((ulong64)((y)[5] & 255)) << 40) | (((ulong64)((y)[4] & 255)) << 32) | \
+ (((ulong64)((y)[3] & 255)) << 24) | (((ulong64)((y)[2] & 255)) << 16) | \
+ (((ulong64)((y)[1] & 255)) << 8) | (((ulong64)((y)[0] & 255))); }
+
+ #else /* 64-bit words then */
+
+ #define STORE32L(x, y) \
+ { ulong32 __t = (x); XMEMCPY(y, &__t, 4); }
+
+ #define LOAD32L(x, y) \
+ { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; }
+
+ #define STORE64L(x, y) \
+ { ulong64 __t = (x); XMEMCPY(y, &__t, 8); }
+
+ #define LOAD64L(x, y) \
+ { XMEMCPY(&(x), y, 8); }
+ #endif /* ENDIAN_64BITWORD */
+#endif /* ENDIAN_LITTLE */
+
+#ifdef ENDIAN_BIG
+ #define STORE32L(x, y) \
+ { (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \
+ (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); }
+
+ #define LOAD32L(x, y) \
+ { x = ((unsigned long)((y)[3] & 255) << 24) | \
+ ((unsigned long)((y)[2] & 255) << 16) | \
+ ((unsigned long)((y)[1] & 255) << 8) | \
+ ((unsigned long)((y)[0] & 255)); }
+
+ #define STORE64L(x, y) \
+ { (y)[7] = (unsigned char)(((x) >> 56) & 255); (y)[6] = (unsigned char)(((x) >> 48) & 255); \
+ (y)[5] = (unsigned char)(((x) >> 40) & 255); (y)[4] = (unsigned char)(((x) >> 32) & 255); \
+ (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \
+ (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); }
+
+ #define LOAD64L(x, y) \
+ { x = (((ulong64)((y)[7] & 255)) << 56) | (((ulong64)((y)[6] & 255)) << 48) | \
+ (((ulong64)((y)[5] & 255)) << 40) | (((ulong64)((y)[4] & 255)) << 32) | \
+ (((ulong64)((y)[3] & 255)) << 24) | (((ulong64)((y)[2] & 255)) << 16) | \
+ (((ulong64)((y)[1] & 255)) << 8) | (((ulong64)((y)[0] & 255))); }
+
+ #ifdef ENDIAN_32BITWORD
+
+ #define STORE32H(x, y) \
+ { ulong32 __t = (x); XMEMCPY(y, &__t, 4); }
+
+ #define LOAD32H(x, y) \
+ XMEMCPY(&(x), y, 4);
+
+ #define STORE64H(x, y) \
+ { (y)[0] = (unsigned char)(((x) >> 56) & 255); (y)[1] = (unsigned char)(((x) >> 48) & 255); \
+ (y)[2] = (unsigned char)(((x) >> 40) & 255); (y)[3] = (unsigned char)(((x) >> 32) & 255); \
+ (y)[4] = (unsigned char)(((x) >> 24) & 255); (y)[5] = (unsigned char)(((x) >> 16) & 255); \
+ (y)[6] = (unsigned char)(((x) >> 8) & 255); (y)[7] = (unsigned char)((x) & 255); }
+
+ #define LOAD64H(x, y) \
+ { x = (((ulong64)((y)[0] & 255)) << 56) | (((ulong64)((y)[1] & 255)) << 48) | \
+ (((ulong64)((y)[2] & 255)) << 40) | (((ulong64)((y)[3] & 255)) << 32) | \
+ (((ulong64)((y)[4] & 255)) << 24) | (((ulong64)((y)[5] & 255)) << 16) | \
+ (((ulong64)((y)[6] & 255)) << 8) | (((ulong64)((y)[7] & 255))); }
+
+ #else /* 64-bit words then */
+
+ #define STORE32H(x, y) \
+ { ulong32 __t = (x); XMEMCPY(y, &__t, 4); }
+
+ #define LOAD32H(x, y) \
+ { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; }
+
+ #define STORE64H(x, y) \
+ { ulong64 __t = (x); XMEMCPY(y, &__t, 8); }
+
+ #define LOAD64H(x, y) \
+ { XMEMCPY(&(x), y, 8); }
+ #endif /* ENDIAN_64BITWORD */
+#endif /* ENDIAN_BIG */
+
+#define BSWAP(x) \
+ (((x >> 24) & 0x000000FFUL) | ((x << 24) & 0xFF000000UL) | \
+ ((x >> 8) & 0x0000FF00UL) | ((x << 8) & 0x00FF0000UL))
+
+
+/* 32-bit Rotates */
+#if defined(_MSC_VER)
+
+/* instrinsic rotate */
+ #include
+ #pragma intrinsic(_lrotr,_lrotl)
+ #define ROR(x, n) _lrotr(x, n)
+ #define ROL(x, n) _lrotl(x, n)
+ #define RORc(x, n) _lrotr(x, n)
+ #define ROLc(x, n) _lrotl(x, n)
+
+#elif !defined(__STRICT_ANSI__) && defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) && !defined(INTEL_CC) && !defined(LTC_NO_ASM)
+
+static inline unsigned ROL(unsigned word, int i) {
+ asm ("roll %%cl,%0"
+ : "=r" (word)
+ : "0" (word), "c" (i));
+ return word;
+}
+
+static inline unsigned ROR(unsigned word, int i) {
+ asm ("rorl %%cl,%0"
+ : "=r" (word)
+ : "0" (word), "c" (i));
+ return word;
+}
+
+ #ifndef LTC_NO_ROLC
+
+static inline unsigned ROLc(unsigned word, const int i) {
+ asm ("roll %2,%0"
+ : "=r" (word)
+ : "0" (word), "I" (i));
+ return word;
+}
+
+static inline unsigned RORc(unsigned word, const int i) {
+ asm ("rorl %2,%0"
+ : "=r" (word)
+ : "0" (word), "I" (i));
+ return word;
+}
+
+ #else
+
+ #define ROLc ROL
+ #define RORc ROR
+ #endif
+
+#elif !defined(__STRICT_ANSI__) && defined(LTC_PPC32)
+
+static inline unsigned ROL(unsigned word, int i) {
+ asm ("rotlw %0,%0,%2"
+ : "=r" (word)
+ : "0" (word), "r" (i));
+ return word;
+}
+
+static inline unsigned ROR(unsigned word, int i) {
+ asm ("rotlw %0,%0,%2"
+ : "=r" (word)
+ : "0" (word), "r" (32 - i));
+ return word;
+}
+
+ #ifndef LTC_NO_ROLC
+
+static inline unsigned ROLc(unsigned word, const int i) {
+ asm ("rotlwi %0,%0,%2"
+ : "=r" (word)
+ : "0" (word), "I" (i));
+ return word;
+}
+
+static inline unsigned RORc(unsigned word, const int i) {
+ asm ("rotrwi %0,%0,%2"
+ : "=r" (word)
+ : "0" (word), "I" (i));
+ return word;
+}
+
+ #else
+
+ #define ROLc ROL
+ #define RORc ROR
+ #endif
+
+
+#else
+
+/* rotates the hard way */
+ #define ROL(x, y) ((((unsigned long)(x) << (unsigned long)((y) & 31)) | (((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+ #define ROR(x, y) (((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y) & 31)) | ((unsigned long)(x) << (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+ #define ROLc(x, y) ((((unsigned long)(x) << (unsigned long)((y) & 31)) | (((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+ #define RORc(x, y) (((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y) & 31)) | ((unsigned long)(x) << (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+#endif
+
+
+/* 64-bit Rotates */
+#if !defined(__STRICT_ANSI__) && defined(__GNUC__) && defined(__x86_64__) && !defined(LTC_NO_ASM)
+
+static inline unsigned long ROL64(unsigned long word, int i) {
+ asm ("rolq %%cl,%0"
+ : "=r" (word)
+ : "0" (word), "c" (i));
+ return word;
+}
+
+static inline unsigned long ROR64(unsigned long word, int i) {
+ asm ("rorq %%cl,%0"
+ : "=r" (word)
+ : "0" (word), "c" (i));
+ return word;
+}
+
+ #ifndef LTC_NO_ROLC
+
+static inline unsigned long ROL64c(unsigned long word, const int i) {
+ asm ("rolq %2,%0"
+ : "=r" (word)
+ : "0" (word), "J" (i));
+ return word;
+}
+
+static inline unsigned long ROR64c(unsigned long word, const int i) {
+ asm ("rorq %2,%0"
+ : "=r" (word)
+ : "0" (word), "J" (i));
+ return word;
+}
+
+ #else /* LTC_NO_ROLC */
+
+ #define ROL64c ROL64
+ #define ROR64c ROR64
+ #endif
+
+#else /* Not x86_64 */
+
+ #define ROL64(x, y) \
+ ((((x) << ((ulong64)(y) & 63)) | \
+ (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((ulong64)64 - ((y) & 63)))) & CONST64(0xFFFFFFFFFFFFFFFF))
+
+ #define ROR64(x, y) \
+ (((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((ulong64)(y) & CONST64(63))) | \
+ ((x) << ((ulong64)(64 - ((y) & CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF))
+
+ #define ROL64c(x, y) \
+ ((((x) << ((ulong64)(y) & 63)) | \
+ (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((ulong64)64 - ((y) & 63)))) & CONST64(0xFFFFFFFFFFFFFFFF))
+
+ #define ROR64c(x, y) \
+ (((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((ulong64)(y) & CONST64(63))) | \
+ ((x) << ((ulong64)(64 - ((y) & CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF))
+#endif
+
+#ifndef MAX
+ #define MAX(x, y) (((x) > (y)) ? (x) : (y))
+#endif
+
+#ifndef MIN
+ #define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+/* extract a byte portably */
+#ifdef _MSC_VER
+ #define byte(x, n) ((unsigned char)((x) >> (8 * (n))))
+#else
+ #define byte(x, n) (((x) >> (8 * (n))) & 255)
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_macros.h,v $ */
+/* $Revision: 1.15 $ */
+/* $Date: 2006/11/29 23:43:57 $ */
+
+/* ---- SYMMETRIC KEY STUFF -----
+ *
+ * We put each of the ciphers scheduled keys in their own structs then we put all of
+ * the key formats in one union. This makes the function prototypes easier to use.
+ */
+#ifdef LTC_BLOWFISH
+struct blowfish_key {
+ ulong32 S[4][256];
+ ulong32 K[18];
+};
+#endif
+
+#ifdef LTC_RC5
+struct rc5_key {
+ int rounds;
+ ulong32 K[50];
+};
+#endif
+
+#ifdef LTC_RC6
+struct rc6_key {
+ ulong32 K[44];
+};
+#endif
+
+#ifdef LTC_SAFERP
+struct saferp_key {
+ unsigned char K[33][16];
+ long rounds;
+};
+#endif
+
+#ifdef LTC_RIJNDAEL
+struct rijndael_key {
+ ulong32 eK[60], dK[60];
+ int Nr;
+};
+#endif
+
+#ifdef LTC_KSEED
+struct kseed_key {
+ ulong32 K[32], dK[32];
+};
+#endif
+
+#ifdef LTC_KASUMI
+struct kasumi_key {
+ ulong32 KLi1[8], KLi2[8],
+ KOi1[8], KOi2[8], KOi3[8],
+ KIi1[8], KIi2[8], KIi3[8];
+};
+#endif
+
+#ifdef LTC_XTEA
+struct xtea_key {
+ unsigned long A[32], B[32];
+};
+#endif
+
+#ifdef LTC_TWOFISH
+ #ifndef LTC_TWOFISH_SMALL
+struct twofish_key {
+ ulong32 S[4][256], K[40];
+};
+ #else
+struct twofish_key {
+ ulong32 K[40];
+ unsigned char S[32], start;
+};
+ #endif
+#endif
+
+#ifdef LTC_SAFER
+ #define LTC_SAFER_K64_DEFAULT_NOF_ROUNDS 6
+ #define LTC_SAFER_K128_DEFAULT_NOF_ROUNDS 10
+ #define LTC_SAFER_SK64_DEFAULT_NOF_ROUNDS 8
+ #define LTC_SAFER_SK128_DEFAULT_NOF_ROUNDS 10
+ #define LTC_SAFER_MAX_NOF_ROUNDS 13
+ #define LTC_SAFER_BLOCK_LEN 8
+ #define LTC_SAFER_KEY_LEN (1 + LTC_SAFER_BLOCK_LEN * (1 + 2 * LTC_SAFER_MAX_NOF_ROUNDS))
+typedef unsigned char safer_block_t[LTC_SAFER_BLOCK_LEN];
+typedef unsigned char safer_key_t[LTC_SAFER_KEY_LEN];
+struct safer_key {
+ safer_key_t key;
+};
+#endif
+
+#ifdef LTC_RC2
+struct rc2_key {
+ unsigned xkey[64];
+};
+#endif
+
+#ifdef LTC_DES
+struct des_key {
+ ulong32 ek[32], dk[32];
+};
+
+struct des3_key {
+ ulong32 ek[3][32], dk[3][32];
+};
+#endif
+
+#ifdef LTC_CAST5
+struct cast5_key {
+ ulong32 K[32], keylen;
+};
+#endif
+
+#ifdef LTC_NOEKEON
+struct noekeon_key {
+ ulong32 K[4], dK[4];
+};
+#endif
+
+#ifdef LTC_SKIPJACK
+struct skipjack_key {
+ unsigned char key[10];
+};
+#endif
+
+#ifdef LTC_KHAZAD
+struct khazad_key {
+ ulong64 roundKeyEnc[8 + 1];
+ ulong64 roundKeyDec[8 + 1];
+};
+#endif
+
+#ifdef LTC_ANUBIS
+struct anubis_key {
+ int keyBits;
+ int R;
+ ulong32 roundKeyEnc[18 + 1][4];
+ ulong32 roundKeyDec[18 + 1][4];
+};
+#endif
+
+#ifdef LTC_MULTI2
+struct multi2_key {
+ int N;
+ ulong32 uk[8];
+};
+#endif
+
+typedef union Symmetric_key {
+#ifdef LTC_DES
+ struct des_key des;
+ struct des3_key des3;
+#endif
+#ifdef LTC_RC2
+ struct rc2_key rc2;
+#endif
+#ifdef LTC_SAFER
+ struct safer_key safer;
+#endif
+#ifdef LTC_TWOFISH
+ struct twofish_key twofish;
+#endif
+#ifdef LTC_BLOWFISH
+ struct blowfish_key blowfish;
+#endif
+#ifdef LTC_RC5
+ struct rc5_key rc5;
+#endif
+#ifdef LTC_RC6
+ struct rc6_key rc6;
+#endif
+#ifdef LTC_SAFERP
+ struct saferp_key saferp;
+#endif
+#ifdef LTC_RIJNDAEL
+ struct rijndael_key rijndael;
+#endif
+#ifdef LTC_XTEA
+ struct xtea_key xtea;
+#endif
+#ifdef LTC_CAST5
+ struct cast5_key cast5;
+#endif
+#ifdef LTC_NOEKEON
+ struct noekeon_key noekeon;
+#endif
+#ifdef LTC_SKIPJACK
+ struct skipjack_key skipjack;
+#endif
+#ifdef LTC_KHAZAD
+ struct khazad_key khazad;
+#endif
+#ifdef LTC_ANUBIS
+ struct anubis_key anubis;
+#endif
+#ifdef LTC_KSEED
+ struct kseed_key kseed;
+#endif
+#ifdef LTC_KASUMI
+ struct kasumi_key kasumi;
+#endif
+#ifdef LTC_MULTI2
+ struct multi2_key multi2;
+#endif
+ void *data;
+} symmetric_key;
+
+#ifdef LTC_ECB_MODE
+/** A block cipher ECB structure */
+typedef struct {
+ /** The index of the cipher chosen */
+ int cipher,
+ /** The block size of the given cipher */
+ blocklen;
+ /** The scheduled key */
+ symmetric_key key;
+} symmetric_ECB;
+#endif
+
+#ifdef LTC_CFB_MODE
+/** A block cipher CFB structure */
+typedef struct {
+ /** The index of the cipher chosen */
+ int cipher,
+ /** The block size of the given cipher */
+ blocklen,
+ /** The padding offset */
+ padlen;
+ /** The current IV */
+ unsigned char IV[MAXBLOCKSIZE],
+ /** The pad used to encrypt/decrypt */
+ pad[MAXBLOCKSIZE];
+ /** The scheduled key */
+ symmetric_key key;
+} symmetric_CFB;
+#endif
+
+#ifdef LTC_OFB_MODE
+/** A block cipher OFB structure */
+typedef struct {
+ /** The index of the cipher chosen */
+ int cipher,
+ /** The block size of the given cipher */
+ blocklen,
+ /** The padding offset */
+ padlen;
+ /** The current IV */
+ unsigned char IV[MAXBLOCKSIZE];
+ /** The scheduled key */
+ symmetric_key key;
+} symmetric_OFB;
+#endif
+
+#ifdef LTC_CBC_MODE
+/** A block cipher CBC structure */
+typedef struct {
+ /** The index of the cipher chosen */
+ int cipher,
+ /** The block size of the given cipher */
+ blocklen;
+ /** The current IV */
+ unsigned char IV[MAXBLOCKSIZE];
+ /** The scheduled key */
+ symmetric_key key;
+} symmetric_CBC;
+#endif
+
+
+#ifdef LTC_CTR_MODE
+/** A block cipher CTR structure */
+typedef struct {
+ /** The index of the cipher chosen */
+ int cipher,
+ /** The block size of the given cipher */
+ blocklen,
+ /** The padding offset */
+ padlen,
+ /** The mode (endianess) of the CTR, 0==little, 1==big */
+ mode,
+ /** counter width */
+ ctrlen;
+
+ /** The counter */
+ unsigned char ctr[MAXBLOCKSIZE],
+ /** The pad used to encrypt/decrypt */
+ pad[MAXBLOCKSIZE];
+ /** The scheduled key */
+ symmetric_key key;
+} symmetric_CTR;
+#endif
+
+
+#ifdef LTC_LRW_MODE
+/** A LRW structure */
+typedef struct {
+ /** The index of the cipher chosen (must be a 128-bit block cipher) */
+ int cipher;
+
+ /** The current IV */
+ unsigned char IV[16],
+
+ /** the tweak key */
+ tweak[16],
+
+ /** The current pad, it's the product of the first 15 bytes against the tweak key */
+ pad[16];
+
+ /** The scheduled symmetric key */
+ symmetric_key key;
+
+ #ifdef LRW_TABLES
+ /** The pre-computed multiplication table */
+ unsigned char PC[16][256][16];
+ #endif
+} symmetric_LRW;
+#endif
+
+#ifdef LTC_F8_MODE
+/** A block cipher F8 structure */
+typedef struct {
+ /** The index of the cipher chosen */
+ int cipher,
+ /** The block size of the given cipher */
+ blocklen,
+ /** The padding offset */
+ padlen;
+ /** The current IV */
+ unsigned char IV[MAXBLOCKSIZE],
+ MIV[MAXBLOCKSIZE];
+ /** Current block count */
+ ulong32 blockcnt;
+ /** The scheduled key */
+ symmetric_key key;
+} symmetric_F8;
+#endif
+
+
+/** cipher descriptor table, last entry has "name == NULL" to mark the end of table */
+extern struct ltc_cipher_descriptor {
+ /** name of cipher */
+ char *name;
+ /** internal ID */
+ unsigned char ID;
+ /** min keysize (octets) */
+ int min_key_length,
+ /** max keysize (octets) */
+ max_key_length,
+ /** block size (octets) */
+ block_length,
+ /** default number of rounds */
+ default_rounds;
+
+ /** Setup the cipher
+ @param key The input symmetric key
+ @param keylen The length of the input key (octets)
+ @param num_rounds The requested number of rounds (0==default)
+ @param skey [out] The destination of the scheduled key
+ @return CRYPT_OK if successful
+ */
+ int (*setup)(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+
+ /** Encrypt a block
+ @param pt The plaintext
+ @param ct [out] The ciphertext
+ @param skey The scheduled key
+ @return CRYPT_OK if successful
+ */
+ int (*ecb_encrypt)(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+
+ /** Decrypt a block
+ @param ct The ciphertext
+ @param pt [out] The plaintext
+ @param skey The scheduled key
+ @return CRYPT_OK if successful
+ */
+ int (*ecb_decrypt)(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+
+ /** Test the block cipher
+ @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
+ */
+ int (*test)(void);
+
+ /** Terminate the context
+ @param skey The scheduled key
+ */
+ void (*done)(symmetric_key *skey);
+
+ /** Determine a key size
+ @param keysize [in/out] The size of the key desired and the suggested size
+ @return CRYPT_OK if successful
+ */
+ int (*keysize)(int *keysize);
+
+/** Accelerators **/
+
+ /** Accelerated ECB encryption
+ @param pt Plaintext
+ @param ct Ciphertext
+ @param blocks The number of complete blocks to process
+ @param skey The scheduled key context
+ @return CRYPT_OK if successful
+ */
+ int (*accel_ecb_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, symmetric_key *skey);
+
+ /** Accelerated ECB decryption
+ @param pt Plaintext
+ @param ct Ciphertext
+ @param blocks The number of complete blocks to process
+ @param skey The scheduled key context
+ @return CRYPT_OK if successful
+ */
+ int (*accel_ecb_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, symmetric_key *skey);
+
+ /** Accelerated CBC encryption
+ @param pt Plaintext
+ @param ct Ciphertext
+ @param blocks The number of complete blocks to process
+ @param IV The initial value (input/output)
+ @param skey The scheduled key context
+ @return CRYPT_OK if successful
+ */
+ int (*accel_cbc_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, symmetric_key *skey);
+
+ /** Accelerated CBC decryption
+ @param pt Plaintext
+ @param ct Ciphertext
+ @param blocks The number of complete blocks to process
+ @param IV The initial value (input/output)
+ @param skey The scheduled key context
+ @return CRYPT_OK if successful
+ */
+ int (*accel_cbc_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, unsigned char *IV, symmetric_key *skey);
+
+ /** Accelerated CTR encryption
+ @param pt Plaintext
+ @param ct Ciphertext
+ @param blocks The number of complete blocks to process
+ @param IV The initial value (input/output)
+ @param mode little or big endian counter (mode=0 or mode=1)
+ @param skey The scheduled key context
+ @return CRYPT_OK if successful
+ */
+ int (*accel_ctr_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, int mode, symmetric_key *skey);
+
+ /** Accelerated LRW
+ @param pt Plaintext
+ @param ct Ciphertext
+ @param blocks The number of complete blocks to process
+ @param IV The initial value (input/output)
+ @param tweak The LRW tweak
+ @param skey The scheduled key context
+ @return CRYPT_OK if successful
+ */
+ int (*accel_lrw_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, const unsigned char *tweak, symmetric_key *skey);
+
+ /** Accelerated LRW
+ @param ct Ciphertext
+ @param pt Plaintext
+ @param blocks The number of complete blocks to process
+ @param IV The initial value (input/output)
+ @param tweak The LRW tweak
+ @param skey The scheduled key context
+ @return CRYPT_OK if successful
+ */
+ int (*accel_lrw_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, unsigned char *IV, const unsigned char *tweak, symmetric_key *skey);
+
+ /** Accelerated CCM packet (one-shot)
+ @param key The secret key to use
+ @param keylen The length of the secret key (octets)
+ @param uskey A previously scheduled key [optional can be NULL]
+ @param nonce The session nonce [use once]
+ @param noncelen The length of the nonce
+ @param header The header for the session
+ @param headerlen The length of the header (octets)
+ @param pt [out] The plaintext
+ @param ptlen The length of the plaintext (octets)
+ @param ct [out] The ciphertext
+ @param tag [out] The destination tag
+ @param taglen [in/out] The max size and resulting size of the authentication tag
+ @param direction Encrypt or Decrypt direction (0 or 1)
+ @return CRYPT_OK if successful
+ */
+ int (*accel_ccm_memory)(
+ const unsigned char *key, unsigned long keylen,
+ symmetric_key *uskey,
+ const unsigned char *nonce, unsigned long noncelen,
+ const unsigned char *header, unsigned long headerlen,
+ unsigned char *pt, unsigned long ptlen,
+ unsigned char *ct,
+ unsigned char *tag, unsigned long *taglen,
+ int direction);
+
+ /** Accelerated GCM packet (one shot)
+ @param key The secret key
+ @param keylen The length of the secret key
+ @param IV The initial vector
+ @param IVlen The length of the initial vector
+ @param adata The additional authentication data (header)
+ @param adatalen The length of the adata
+ @param pt The plaintext
+ @param ptlen The length of the plaintext (ciphertext length is the same)
+ @param ct The ciphertext
+ @param tag [out] The MAC tag
+ @param taglen [in/out] The MAC tag length
+ @param direction Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT)
+ @return CRYPT_OK on success
+ */
+ int (*accel_gcm_memory)(
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *IV, unsigned long IVlen,
+ const unsigned char *adata, unsigned long adatalen,
+ unsigned char *pt, unsigned long ptlen,
+ unsigned char *ct,
+ unsigned char *tag, unsigned long *taglen,
+ int direction);
+
+ /** Accelerated one shot LTC_OMAC
+ @param key The secret key
+ @param keylen The key length (octets)
+ @param in The message
+ @param inlen Length of message (octets)
+ @param out [out] Destination for tag
+ @param outlen [in/out] Initial and final size of out
+ @return CRYPT_OK on success
+ */
+ int (*omac_memory)(
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+
+ /** Accelerated one shot XCBC
+ @param key The secret key
+ @param keylen The key length (octets)
+ @param in The message
+ @param inlen Length of message (octets)
+ @param out [out] Destination for tag
+ @param outlen [in/out] Initial and final size of out
+ @return CRYPT_OK on success
+ */
+ int (*xcbc_memory)(
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+
+ /** Accelerated one shot F9
+ @param key The secret key
+ @param keylen The key length (octets)
+ @param in The message
+ @param inlen Length of message (octets)
+ @param out [out] Destination for tag
+ @param outlen [in/out] Initial and final size of out
+ @return CRYPT_OK on success
+ @remark Requires manual padding
+ */
+ int (*f9_memory)(
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+} cipher_descriptor[];
+
+#ifdef LTC_BLOWFISH
+int blowfish_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int blowfish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int blowfish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int blowfish_test(void);
+void blowfish_done(symmetric_key *skey);
+int blowfish_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor blowfish_desc;
+#endif
+
+#ifdef LTC_RC5
+int rc5_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int rc5_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int rc5_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int rc5_test(void);
+void rc5_done(symmetric_key *skey);
+int rc5_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor rc5_desc;
+#endif
+
+#ifdef LTC_RC6
+int rc6_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int rc6_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int rc6_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int rc6_test(void);
+void rc6_done(symmetric_key *skey);
+int rc6_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor rc6_desc;
+#endif
+
+#ifdef LTC_RC2
+int rc2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int rc2_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int rc2_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int rc2_test(void);
+void rc2_done(symmetric_key *skey);
+int rc2_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor rc2_desc;
+#endif
+
+#ifdef LTC_SAFERP
+int saferp_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int saferp_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int saferp_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int saferp_test(void);
+void saferp_done(symmetric_key *skey);
+int saferp_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor saferp_desc;
+#endif
+
+#ifdef LTC_SAFER
+int safer_k64_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int safer_sk64_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int safer_k128_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int safer_sk128_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int safer_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *key);
+int safer_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *key);
+int safer_k64_test(void);
+int safer_sk64_test(void);
+int safer_sk128_test(void);
+void safer_done(symmetric_key *skey);
+int safer_64_keysize(int *keysize);
+int safer_128_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor safer_k64_desc, safer_k128_desc, safer_sk64_desc, safer_sk128_desc;
+#endif
+
+#ifdef LTC_RIJNDAEL
+
+/* make aes an alias */
+ #define aes_setup rijndael_setup
+ #define aes_ecb_encrypt rijndael_ecb_encrypt
+ #define aes_ecb_decrypt rijndael_ecb_decrypt
+ #define aes_test rijndael_test
+ #define aes_done rijndael_done
+ #define aes_keysize rijndael_keysize
+
+ #define aes_enc_setup rijndael_enc_setup
+ #define aes_enc_ecb_encrypt rijndael_enc_ecb_encrypt
+ #define aes_enc_keysize rijndael_enc_keysize
+
+int rijndael_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int rijndael_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int rijndael_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int rijndael_test(void);
+void rijndael_done(symmetric_key *skey);
+int rijndael_keysize(int *keysize);
+int rijndael_enc_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int rijndael_enc_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+void rijndael_enc_done(symmetric_key *skey);
+int rijndael_enc_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor rijndael_desc, aes_desc;
+extern const struct ltc_cipher_descriptor rijndael_enc_desc, aes_enc_desc;
+#endif
+
+#ifdef LTC_XTEA
+int xtea_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int xtea_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int xtea_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int xtea_test(void);
+void xtea_done(symmetric_key *skey);
+int xtea_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor xtea_desc;
+#endif
+
+#ifdef LTC_TWOFISH
+int twofish_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int twofish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int twofish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int twofish_test(void);
+void twofish_done(symmetric_key *skey);
+int twofish_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor twofish_desc;
+#endif
+
+#ifdef LTC_DES
+int des_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int des_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int des_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int des_test(void);
+void des_done(symmetric_key *skey);
+int des_keysize(int *keysize);
+int des3_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int des3_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int des3_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int des3_test(void);
+void des3_done(symmetric_key *skey);
+int des3_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor des_desc, des3_desc;
+#endif
+
+#ifdef LTC_CAST5
+int cast5_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int cast5_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int cast5_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int cast5_test(void);
+void cast5_done(symmetric_key *skey);
+int cast5_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor cast5_desc;
+#endif
+
+#ifdef LTC_NOEKEON
+int noekeon_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int noekeon_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int noekeon_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int noekeon_test(void);
+void noekeon_done(symmetric_key *skey);
+int noekeon_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor noekeon_desc;
+#endif
+
+#ifdef LTC_SKIPJACK
+int skipjack_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int skipjack_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int skipjack_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int skipjack_test(void);
+void skipjack_done(symmetric_key *skey);
+int skipjack_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor skipjack_desc;
+#endif
+
+#ifdef LTC_KHAZAD
+int khazad_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int khazad_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int khazad_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int khazad_test(void);
+void khazad_done(symmetric_key *skey);
+int khazad_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor khazad_desc;
+#endif
+
+#ifdef LTC_ANUBIS
+int anubis_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int anubis_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int anubis_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int anubis_test(void);
+void anubis_done(symmetric_key *skey);
+int anubis_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor anubis_desc;
+#endif
+
+#ifdef LTC_KSEED
+int kseed_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int kseed_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int kseed_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int kseed_test(void);
+void kseed_done(symmetric_key *skey);
+int kseed_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor kseed_desc;
+#endif
+
+#ifdef LTC_KASUMI
+int kasumi_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int kasumi_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int kasumi_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int kasumi_test(void);
+void kasumi_done(symmetric_key *skey);
+int kasumi_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor kasumi_desc;
+#endif
+
+
+#ifdef LTC_MULTI2
+int multi2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int multi2_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
+int multi2_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
+int multi2_test(void);
+void multi2_done(symmetric_key *skey);
+int multi2_keysize(int *keysize);
+
+extern const struct ltc_cipher_descriptor multi2_desc;
+#endif
+
+#ifdef LTC_ECB_MODE
+int ecb_start(int cipher, const unsigned char *key,
+ int keylen, int num_rounds, symmetric_ECB *ecb);
+int ecb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_ECB *ecb);
+int ecb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_ECB *ecb);
+int ecb_done(symmetric_ECB *ecb);
+#endif
+
+#ifdef LTC_CFB_MODE
+int cfb_start(int cipher, const unsigned char *IV, const unsigned char *key,
+ int keylen, int num_rounds, symmetric_CFB *cfb);
+int cfb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CFB *cfb);
+int cfb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CFB *cfb);
+int cfb_getiv(unsigned char *IV, unsigned long *len, symmetric_CFB *cfb);
+int cfb_setiv(const unsigned char *IV, unsigned long len, symmetric_CFB *cfb);
+int cfb_done(symmetric_CFB *cfb);
+#endif
+
+#ifdef LTC_OFB_MODE
+int ofb_start(int cipher, const unsigned char *IV, const unsigned char *key,
+ int keylen, int num_rounds, symmetric_OFB *ofb);
+int ofb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_OFB *ofb);
+int ofb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_OFB *ofb);
+int ofb_getiv(unsigned char *IV, unsigned long *len, symmetric_OFB *ofb);
+int ofb_setiv(const unsigned char *IV, unsigned long len, symmetric_OFB *ofb);
+int ofb_done(symmetric_OFB *ofb);
+#endif
+
+#ifdef LTC_CBC_MODE
+int cbc_start(int cipher, const unsigned char *IV, const unsigned char *key,
+ int keylen, int num_rounds, symmetric_CBC *cbc);
+int cbc_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CBC *cbc);
+int cbc_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CBC *cbc);
+int cbc_getiv(unsigned char *IV, unsigned long *len, symmetric_CBC *cbc);
+int cbc_setiv(const unsigned char *IV, unsigned long len, symmetric_CBC *cbc);
+int cbc_done(symmetric_CBC *cbc);
+#endif
+
+#ifdef LTC_CTR_MODE
+
+ #define CTR_COUNTER_LITTLE_ENDIAN 0x0000
+ #define CTR_COUNTER_BIG_ENDIAN 0x1000
+ #define LTC_CTR_RFC3686 0x2000
+
+int ctr_start(int cipher,
+ const unsigned char *IV,
+ const unsigned char *key, int keylen,
+ int num_rounds, int ctr_mode,
+ symmetric_CTR *ctr);
+int ctr_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CTR *ctr);
+int ctr_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CTR *ctr);
+int ctr_getiv(unsigned char *IV, unsigned long *len, symmetric_CTR *ctr);
+int ctr_setiv(const unsigned char *IV, unsigned long len, symmetric_CTR *ctr);
+int ctr_done(symmetric_CTR *ctr);
+int ctr_test(void);
+#endif
+
+#ifdef LTC_LRW_MODE
+
+ #define LRW_ENCRYPT 0
+ #define LRW_DECRYPT 1
+
+int lrw_start(int cipher,
+ const unsigned char *IV,
+ const unsigned char *key, int keylen,
+ const unsigned char *tweak,
+ int num_rounds,
+ symmetric_LRW *lrw);
+int lrw_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_LRW *lrw);
+int lrw_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_LRW *lrw);
+int lrw_getiv(unsigned char *IV, unsigned long *len, symmetric_LRW *lrw);
+int lrw_setiv(const unsigned char *IV, unsigned long len, symmetric_LRW *lrw);
+int lrw_done(symmetric_LRW *lrw);
+int lrw_test(void);
+
+/* don't call */
+int lrw_process(const unsigned char *pt, unsigned char *ct, unsigned long len, int mode, symmetric_LRW *lrw);
+#endif
+
+#ifdef LTC_F8_MODE
+int f8_start(int cipher, const unsigned char *IV,
+ const unsigned char *key, int keylen,
+ const unsigned char *salt_key, int skeylen,
+ int num_rounds, symmetric_F8 *f8);
+int f8_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_F8 *f8);
+int f8_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_F8 *f8);
+int f8_getiv(unsigned char *IV, unsigned long *len, symmetric_F8 *f8);
+int f8_setiv(const unsigned char *IV, unsigned long len, symmetric_F8 *f8);
+int f8_done(symmetric_F8 *f8);
+int f8_test_mode(void);
+#endif
+
+#ifdef LTC_XTS_MODE
+typedef struct {
+ symmetric_key key1, key2;
+ int cipher;
+} symmetric_xts;
+
+int xts_start(int cipher,
+ const unsigned char *key1,
+ const unsigned char *key2,
+ unsigned long keylen,
+ int num_rounds,
+ symmetric_xts *xts);
+
+int xts_encrypt(
+ const unsigned char *pt, unsigned long ptlen,
+ unsigned char *ct,
+ const unsigned char *tweak,
+ symmetric_xts *xts);
+int xts_decrypt(
+ const unsigned char *ct, unsigned long ptlen,
+ unsigned char *pt,
+ const unsigned char *tweak,
+ symmetric_xts *xts);
+
+void xts_done(symmetric_xts *xts);
+int xts_test(void);
+void xts_mult_x(unsigned char *I);
+#endif
+
+int find_cipher(const char *name);
+int find_cipher_any(const char *name, int blocklen, int keylen);
+int find_cipher_id(unsigned char ID);
+int register_cipher(const struct ltc_cipher_descriptor *cipher);
+int unregister_cipher(const struct ltc_cipher_descriptor *cipher);
+int cipher_is_valid(int idx);
+
+LTC_MUTEX_PROTO(ltc_cipher_mutex)
+
+/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_cipher.h,v $ */
+/* $Revision: 1.54 $ */
+/* $Date: 2007/05/12 14:37:41 $ */
+
+#define LTC_SHA1
+/* ---- HASH FUNCTIONS ---- */
+#ifdef LTC_SHA512
+struct sha512_state {
+ ulong64 length, state[8];
+ unsigned long curlen;
+ unsigned char buf[128];
+};
+#endif
+
+#ifdef LTC_SHA256
+struct sha256_state {
+ ulong64 length;
+ ulong32 state[8], curlen;
+ unsigned char buf[64];
+};
+#endif
+
+#ifdef LTC_SHA1
+struct sha1_state {
+ ulong64 length;
+ ulong32 state[5], curlen;
+ unsigned char buf[64];
+};
+#endif
+
+#ifdef LTC_MD5
+struct md5_state {
+ ulong64 length;
+ ulong32 state[4], curlen;
+ unsigned char buf[64];
+};
+#endif
+
+#ifdef LTC_MD4
+struct md4_state {
+ ulong64 length;
+ ulong32 state[4], curlen;
+ unsigned char buf[64];
+};
+#endif
+
+#ifdef LTC_TIGER
+struct tiger_state {
+ ulong64 state[3], length;
+ unsigned long curlen;
+ unsigned char buf[64];
+};
+#endif
+
+#ifdef LTC_MD2
+struct md2_state {
+ unsigned char chksum[16], X[48], buf[16];
+ unsigned long curlen;
+};
+#endif
+
+#ifdef LTC_RIPEMD128
+struct rmd128_state {
+ ulong64 length;
+ unsigned char buf[64];
+ ulong32 curlen, state[4];
+};
+#endif
+
+#ifdef LTC_RIPEMD160
+struct rmd160_state {
+ ulong64 length;
+ unsigned char buf[64];
+ ulong32 curlen, state[5];
+};
+#endif
+
+#ifdef LTC_RIPEMD256
+struct rmd256_state {
+ ulong64 length;
+ unsigned char buf[64];
+ ulong32 curlen, state[8];
+};
+#endif
+
+#ifdef LTC_RIPEMD320
+struct rmd320_state {
+ ulong64 length;
+ unsigned char buf[64];
+ ulong32 curlen, state[10];
+};
+#endif
+
+#ifdef LTC_WHIRLPOOL
+struct whirlpool_state {
+ ulong64 length, state[8];
+ unsigned char buf[64];
+ ulong32 curlen;
+};
+#endif
+
+#ifdef LTC_CHC_HASH
+struct chc_state {
+ ulong64 length;
+ unsigned char state[MAXBLOCKSIZE], buf[MAXBLOCKSIZE];
+ ulong32 curlen;
+};
+#endif
+
+typedef union Hash_state {
+ char dummy[1];
+#ifdef LTC_CHC_HASH
+ struct chc_state chc;
+#endif
+#ifdef LTC_WHIRLPOOL
+ struct whirlpool_state whirlpool;
+#endif
+#ifdef LTC_SHA512
+ struct sha512_state sha512;
+#endif
+#ifdef LTC_SHA256
+ struct sha256_state sha256;
+#endif
+#ifdef LTC_SHA1
+ struct sha1_state sha1;
+#endif
+#ifdef LTC_MD5
+ struct md5_state md5;
+#endif
+#ifdef LTC_MD4
+ struct md4_state md4;
+#endif
+#ifdef LTC_MD2
+ struct md2_state md2;
+#endif
+#ifdef LTC_TIGER
+ struct tiger_state tiger;
+#endif
+#ifdef LTC_RIPEMD128
+ struct rmd128_state rmd128;
+#endif
+#ifdef LTC_RIPEMD160
+ struct rmd160_state rmd160;
+#endif
+#ifdef LTC_RIPEMD256
+ struct rmd256_state rmd256;
+#endif
+#ifdef LTC_RIPEMD320
+ struct rmd320_state rmd320;
+#endif
+ void *data;
+} hash_state;
+
+/** hash descriptor */
+extern struct ltc_hash_descriptor {
+ /** name of hash */
+ char *name;
+ /** internal ID */
+ unsigned char ID;
+ /** Size of digest in octets */
+ unsigned long hashsize;
+ /** Input block size in octets */
+ unsigned long blocksize;
+ /** ASN.1 OID */
+ unsigned long OID[16];
+ /** Length of DER encoding */
+ unsigned long OIDlen;
+
+ /** Init a hash state
+ @param hash The hash to initialize
+ @return CRYPT_OK if successful
+ */
+ int (*init)(hash_state *hash);
+
+ /** Process a block of data
+ @param hash The hash state
+ @param in The data to hash
+ @param inlen The length of the data (octets)
+ @return CRYPT_OK if successful
+ */
+ int (*process)(hash_state *hash, const unsigned char *in, unsigned long inlen);
+
+ /** Produce the digest and store it
+ @param hash The hash state
+ @param out [out] The destination of the digest
+ @return CRYPT_OK if successful
+ */
+ int (*done)(hash_state *hash, unsigned char *out);
+
+ /** Self-test
+ @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
+ */
+ int (*test)(void);
+
+ /* accelerated hmac callback: if you need to-do multiple packets just use the generic hmac_memory and provide a hash callback */
+ int (*hmac_block)(const unsigned char *key, unsigned long keylen,
+ const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+} hash_descriptor[];
+
+#ifdef LTC_CHC_HASH
+int chc_register(int cipher);
+int chc_init(hash_state *md);
+int chc_process(hash_state *md, const unsigned char *in, unsigned long inlen);
+int chc_done(hash_state *md, unsigned char *hash);
+int chc_test(void);
+
+extern const struct ltc_hash_descriptor chc_desc;
+#endif
+
+#ifdef LTC_WHIRLPOOL
+int whirlpool_init(hash_state *md);
+int whirlpool_process(hash_state *md, const unsigned char *in, unsigned long inlen);
+int whirlpool_done(hash_state *md, unsigned char *hash);
+int whirlpool_test(void);
+
+extern const struct ltc_hash_descriptor whirlpool_desc;
+#endif
+
+#ifdef LTC_SHA512
+int sha512_init(hash_state *md);
+int sha512_process(hash_state *md, const unsigned char *in, unsigned long inlen);
+int sha512_done(hash_state *md, unsigned char *hash);
+int sha512_test(void);
+
+extern const struct ltc_hash_descriptor sha512_desc;
+#endif
+
+#ifdef LTC_SHA384
+ #ifndef LTC_SHA512
+ #error LTC_SHA512 is required for LTC_SHA384
+ #endif
+int sha384_init(hash_state *md);
+
+ #define sha384_process sha512_process
+int sha384_done(hash_state *md, unsigned char *hash);
+int sha384_test(void);
+
+extern const struct ltc_hash_descriptor sha384_desc;
+#endif
+
+#ifdef LTC_SHA256
+int sha256_init(hash_state *md);
+int sha256_process(hash_state *md, const unsigned char *in, unsigned long inlen);
+int sha256_done(hash_state *md, unsigned char *hash);
+int sha256_test(void);
+
+extern const struct ltc_hash_descriptor sha256_desc;
+
+ #ifdef LTC_SHA224
+ #ifndef LTC_SHA256
+ #error LTC_SHA256 is required for LTC_SHA224
+ #endif
+int sha224_init(hash_state *md);
+
+ #define sha224_process sha256_process
+int sha224_done(hash_state *md, unsigned char *hash);
+int sha224_test(void);
+
+extern const struct ltc_hash_descriptor sha224_desc;
+ #endif
+#endif
+
+#ifdef LTC_SHA1
+int sha1_init(hash_state *md);
+int sha1_process(hash_state *md, const unsigned char *in, unsigned long inlen);
+int sha1_done(hash_state *md, unsigned char *hash);
+int sha1_test(void);
+
+extern const struct ltc_hash_descriptor sha1_desc;
+#endif
+
+#ifdef LTC_MD5
+int md5_init(hash_state *md);
+int md5_process(hash_state *md, const unsigned char *in, unsigned long inlen);
+int md5_done(hash_state *md, unsigned char *hash);
+int md5_test(void);
+
+extern const struct ltc_hash_descriptor md5_desc;
+#endif
+
+#ifdef LTC_MD4
+int md4_init(hash_state *md);
+int md4_process(hash_state *md, const unsigned char *in, unsigned long inlen);
+int md4_done(hash_state *md, unsigned char *hash);
+int md4_test(void);
+
+extern const struct ltc_hash_descriptor md4_desc;
+#endif
+
+#ifdef LTC_MD2
+int md2_init(hash_state *md);
+int md2_process(hash_state *md, const unsigned char *in, unsigned long inlen);
+int md2_done(hash_state *md, unsigned char *hash);
+int md2_test(void);
+
+extern const struct ltc_hash_descriptor md2_desc;
+#endif
+
+#ifdef LTC_TIGER
+int tiger_init(hash_state *md);
+int tiger_process(hash_state *md, const unsigned char *in, unsigned long inlen);
+int tiger_done(hash_state *md, unsigned char *hash);
+int tiger_test(void);
+
+extern const struct ltc_hash_descriptor tiger_desc;
+#endif
+
+#ifdef LTC_RIPEMD128
+int rmd128_init(hash_state *md);
+int rmd128_process(hash_state *md, const unsigned char *in, unsigned long inlen);
+int rmd128_done(hash_state *md, unsigned char *hash);
+int rmd128_test(void);
+
+extern const struct ltc_hash_descriptor rmd128_desc;
+#endif
+
+#ifdef LTC_RIPEMD160
+int rmd160_init(hash_state *md);
+int rmd160_process(hash_state *md, const unsigned char *in, unsigned long inlen);
+int rmd160_done(hash_state *md, unsigned char *hash);
+int rmd160_test(void);
+
+extern const struct ltc_hash_descriptor rmd160_desc;
+#endif
+
+#ifdef LTC_RIPEMD256
+int rmd256_init(hash_state *md);
+int rmd256_process(hash_state *md, const unsigned char *in, unsigned long inlen);
+int rmd256_done(hash_state *md, unsigned char *hash);
+int rmd256_test(void);
+
+extern const struct ltc_hash_descriptor rmd256_desc;
+#endif
+
+#ifdef LTC_RIPEMD320
+int rmd320_init(hash_state *md);
+int rmd320_process(hash_state *md, const unsigned char *in, unsigned long inlen);
+int rmd320_done(hash_state *md, unsigned char *hash);
+int rmd320_test(void);
+
+extern const struct ltc_hash_descriptor rmd320_desc;
+#endif
+
+
+int find_hash(const char *name);
+int find_hash_id(unsigned char ID);
+int find_hash_oid(const unsigned long *ID, unsigned long IDlen);
+int find_hash_any(const char *name, int digestlen);
+int register_hash(const struct ltc_hash_descriptor *hash);
+int unregister_hash(const struct ltc_hash_descriptor *hash);
+int hash_is_valid(int idx);
+
+LTC_MUTEX_PROTO(ltc_hash_mutex)
+
+int hash_memory(int hash,
+ const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+int hash_memory_multi(int hash, unsigned char *out, unsigned long *outlen,
+ const unsigned char *in, unsigned long inlen, ...);
+int hash_filehandle(int hash, FILE *in, unsigned char *out, unsigned long *outlen);
+int hash_file(int hash, const char *fname, unsigned char *out, unsigned long *outlen);
+
+/* a simple macro for making hash "process" functions */
+#define HASH_PROCESS(func_name, compress_name, state_var, block_size) \
+ int func_name(hash_state * md, const unsigned char *in, unsigned long inlen) \
+ { \
+ unsigned long n; \
+ int err; \
+ LTC_ARGCHK(md != NULL); \
+ LTC_ARGCHK(in != NULL); \
+ if (md->state_var.curlen > sizeof(md->state_var.buf)) { \
+ return CRYPT_INVALID_ARG; \
+ } \
+ while (inlen > 0) { \
+ if (md->state_var.curlen == 0 && inlen >= block_size) { \
+ if ((err = compress_name(md, (unsigned char *)in)) != CRYPT_OK) { \
+ return err; \
+ } \
+ md->state_var.length += block_size * 8; \
+ in += block_size; \
+ inlen -= block_size; \
+ } else { \
+ n = MIN(inlen, (block_size - md->state_var.curlen)); \
+ memcpy(md->state_var.buf + md->state_var.curlen, in, (size_t)n); \
+ md->state_var.curlen += n; \
+ in += n; \
+ inlen -= n; \
+ if (md->state_var.curlen == block_size) { \
+ if ((err = compress_name(md, md->state_var.buf)) != CRYPT_OK) { \
+ return err; \
+ } \
+ md->state_var.length += 8 * block_size; \
+ md->state_var.curlen = 0; \
+ } \
+ } \
+ } \
+ return CRYPT_OK; \
+ }
+
+/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_hash.h,v $ */
+/* $Revision: 1.22 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+#ifdef LTC_HMAC
+typedef struct Hmac_state {
+ hash_state md;
+ int hash;
+ hash_state hashstate;
+ unsigned char *key;
+} hmac_state;
+
+int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned long keylen);
+int hmac_process(hmac_state *hmac, const unsigned char *in, unsigned long inlen);
+int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen);
+int hmac_test(void);
+int hmac_memory(int hash,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+int hmac_memory_multi(int hash,
+ const unsigned char *key, unsigned long keylen,
+ unsigned char *out, unsigned long *outlen,
+ const unsigned char *in, unsigned long inlen, ...);
+int hmac_file(int hash, const char *fname, const unsigned char *key,
+ unsigned long keylen,
+ unsigned char *dst, unsigned long *dstlen);
+#endif
+
+#ifdef LTC_OMAC
+
+typedef struct {
+ int cipher_idx,
+ buflen,
+ blklen;
+ unsigned char block[MAXBLOCKSIZE],
+ prev[MAXBLOCKSIZE],
+ Lu[2][MAXBLOCKSIZE];
+ symmetric_key key;
+} omac_state;
+
+int omac_init(omac_state *omac, int cipher, const unsigned char *key, unsigned long keylen);
+int omac_process(omac_state *omac, const unsigned char *in, unsigned long inlen);
+int omac_done(omac_state *omac, unsigned char *out, unsigned long *outlen);
+int omac_memory(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+int omac_memory_multi(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ unsigned char *out, unsigned long *outlen,
+ const unsigned char *in, unsigned long inlen, ...);
+int omac_file(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ const char *filename,
+ unsigned char *out, unsigned long *outlen);
+int omac_test(void);
+#endif /* LTC_OMAC */
+
+#ifdef LTC_PMAC
+
+typedef struct {
+ unsigned char Ls[32][MAXBLOCKSIZE], /* L shifted by i bits to the left */
+ Li[MAXBLOCKSIZE], /* value of Li [current value, we calc from previous recall] */
+ Lr[MAXBLOCKSIZE], /* L * x^-1 */
+ block[MAXBLOCKSIZE], /* currently accumulated block */
+ checksum[MAXBLOCKSIZE]; /* current checksum */
+
+ symmetric_key key; /* scheduled key for cipher */
+ unsigned long block_index; /* index # for current block */
+ int cipher_idx, /* cipher idx */
+ block_len, /* length of block */
+ buflen; /* number of bytes in the buffer */
+} pmac_state;
+
+int pmac_init(pmac_state *pmac, int cipher, const unsigned char *key, unsigned long keylen);
+int pmac_process(pmac_state *pmac, const unsigned char *in, unsigned long inlen);
+int pmac_done(pmac_state *pmac, unsigned char *out, unsigned long *outlen);
+
+int pmac_memory(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *msg, unsigned long msglen,
+ unsigned char *out, unsigned long *outlen);
+
+int pmac_memory_multi(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ unsigned char *out, unsigned long *outlen,
+ const unsigned char *in, unsigned long inlen, ...);
+
+int pmac_file(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ const char *filename,
+ unsigned char *out, unsigned long *outlen);
+
+int pmac_test(void);
+
+/* internal functions */
+int pmac_ntz(unsigned long x);
+void pmac_shift_xor(pmac_state *pmac);
+#endif /* PMAC */
+
+#ifdef LTC_EAX_MODE
+
+ #if !(defined(LTC_OMAC) && defined(LTC_CTR_MODE))
+ #error LTC_EAX_MODE requires LTC_OMAC and CTR
+ #endif
+
+typedef struct {
+ unsigned char N[MAXBLOCKSIZE];
+ symmetric_CTR ctr;
+ omac_state headeromac, ctomac;
+} eax_state;
+
+int eax_init(eax_state *eax, int cipher, const unsigned char *key, unsigned long keylen,
+ const unsigned char *nonce, unsigned long noncelen,
+ const unsigned char *header, unsigned long headerlen);
+
+int eax_encrypt(eax_state *eax, const unsigned char *pt, unsigned char *ct, unsigned long length);
+int eax_decrypt(eax_state *eax, const unsigned char *ct, unsigned char *pt, unsigned long length);
+int eax_addheader(eax_state *eax, const unsigned char *header, unsigned long length);
+int eax_done(eax_state *eax, unsigned char *tag, unsigned long *taglen);
+
+int eax_encrypt_authenticate_memory(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *nonce, unsigned long noncelen,
+ const unsigned char *header, unsigned long headerlen,
+ const unsigned char *pt, unsigned long ptlen,
+ unsigned char *ct,
+ unsigned char *tag, unsigned long *taglen);
+
+int eax_decrypt_verify_memory(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *nonce, unsigned long noncelen,
+ const unsigned char *header, unsigned long headerlen,
+ const unsigned char *ct, unsigned long ctlen,
+ unsigned char *pt,
+ unsigned char *tag, unsigned long taglen,
+ int *stat);
+
+int eax_test(void);
+#endif /* EAX MODE */
+
+#ifdef LTC_OCB_MODE
+typedef struct {
+ unsigned char L[MAXBLOCKSIZE], /* L value */
+ Ls[32][MAXBLOCKSIZE], /* L shifted by i bits to the left */
+ Li[MAXBLOCKSIZE], /* value of Li [current value, we calc from previous recall] */
+ Lr[MAXBLOCKSIZE], /* L * x^-1 */
+ R[MAXBLOCKSIZE], /* R value */
+ checksum[MAXBLOCKSIZE]; /* current checksum */
+
+ symmetric_key key; /* scheduled key for cipher */
+ unsigned long block_index; /* index # for current block */
+ int cipher, /* cipher idx */
+ block_len; /* length of block */
+} ocb_state;
+
+int ocb_init(ocb_state *ocb, int cipher,
+ const unsigned char *key, unsigned long keylen, const unsigned char *nonce);
+
+int ocb_encrypt(ocb_state *ocb, const unsigned char *pt, unsigned char *ct);
+int ocb_decrypt(ocb_state *ocb, const unsigned char *ct, unsigned char *pt);
+
+int ocb_done_encrypt(ocb_state *ocb,
+ const unsigned char *pt, unsigned long ptlen,
+ unsigned char *ct,
+ unsigned char *tag, unsigned long *taglen);
+
+int ocb_done_decrypt(ocb_state *ocb,
+ const unsigned char *ct, unsigned long ctlen,
+ unsigned char *pt,
+ const unsigned char *tag, unsigned long taglen, int *stat);
+
+int ocb_encrypt_authenticate_memory(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *nonce,
+ const unsigned char *pt, unsigned long ptlen,
+ unsigned char *ct,
+ unsigned char *tag, unsigned long *taglen);
+
+int ocb_decrypt_verify_memory(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *nonce,
+ const unsigned char *ct, unsigned long ctlen,
+ unsigned char *pt,
+ const unsigned char *tag, unsigned long taglen,
+ int *stat);
+
+int ocb_test(void);
+
+/* internal functions */
+void ocb_shift_xor(ocb_state *ocb, unsigned char *Z);
+int ocb_ntz(unsigned long x);
+int s_ocb_done(ocb_state *ocb, const unsigned char *pt, unsigned long ptlen,
+ unsigned char *ct, unsigned char *tag, unsigned long *taglen, int mode);
+#endif /* LTC_OCB_MODE */
+
+#ifdef LTC_CCM_MODE
+
+ #define CCM_ENCRYPT 0
+ #define CCM_DECRYPT 1
+
+int ccm_memory(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ symmetric_key *uskey,
+ const unsigned char *nonce, unsigned long noncelen,
+ const unsigned char *header, unsigned long headerlen,
+ unsigned char *pt, unsigned long ptlen,
+ unsigned char *ct,
+ unsigned char *tag, unsigned long *taglen,
+ int direction);
+
+int ccm_test(void);
+#endif /* LTC_CCM_MODE */
+
+#if defined(LRW_MODE) || defined(LTC_GCM_MODE)
+void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c);
+#endif
+
+
+/* table shared between GCM and LRW */
+#if defined(LTC_GCM_TABLES) || defined(LRW_TABLES) || ((defined(LTC_GCM_MODE) || defined(LTC_GCM_MODE)) && defined(LTC_FAST))
+extern const unsigned char gcm_shift_table[];
+#endif
+
+#ifdef LTC_GCM_MODE
+
+ #define GCM_ENCRYPT 0
+ #define GCM_DECRYPT 1
+
+ #define LTC_GCM_MODE_IV 0
+ #define LTC_GCM_MODE_AAD 1
+ #define LTC_GCM_MODE_TEXT 2
+
+typedef struct {
+ symmetric_key K;
+ unsigned char H[16], /* multiplier */
+ X[16], /* accumulator */
+ Y[16], /* counter */
+ Y_0[16], /* initial counter */
+ buf[16]; /* buffer for stuff */
+
+ int cipher, /* which cipher */
+ ivmode, /* Which mode is the IV in? */
+ mode, /* mode the GCM code is in */
+ buflen; /* length of data in buf */
+
+ ulong64 totlen, /* 64-bit counter used for IV and AAD */
+ pttotlen; /* 64-bit counter for the PT */
+
+ #ifdef LTC_GCM_TABLES
+ unsigned char PC[16][256][16] /* 16 tables of 8x128 */
+ #ifdef LTC_GCM_TABLES_SSE2
+ __attribute__ ((aligned(16)))
+ #endif
+ ;
+ #endif
+} gcm_state;
+
+void gcm_mult_h(gcm_state *gcm, unsigned char *I);
+
+int gcm_init(gcm_state *gcm, int cipher,
+ const unsigned char *key, int keylen);
+
+int gcm_reset(gcm_state *gcm);
+
+int gcm_add_iv(gcm_state *gcm,
+ const unsigned char *IV, unsigned long IVlen);
+
+int gcm_add_aad(gcm_state *gcm,
+ const unsigned char *adata, unsigned long adatalen);
+
+int gcm_process(gcm_state *gcm,
+ unsigned char *pt, unsigned long ptlen,
+ unsigned char *ct,
+ int direction);
+
+int gcm_done(gcm_state *gcm,
+ unsigned char *tag, unsigned long *taglen);
+
+int gcm_memory(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *IV, unsigned long IVlen,
+ const unsigned char *adata, unsigned long adatalen,
+ unsigned char *pt, unsigned long ptlen,
+ unsigned char *ct,
+ unsigned char *tag, unsigned long *taglen,
+ int direction);
+int gcm_test(void);
+#endif /* LTC_GCM_MODE */
+
+#ifdef LTC_PELICAN
+
+typedef struct pelican_state {
+ symmetric_key K;
+ unsigned char state[16];
+ int buflen;
+} pelican_state;
+
+int pelican_init(pelican_state *pelmac, const unsigned char *key, unsigned long keylen);
+int pelican_process(pelican_state *pelmac, const unsigned char *in, unsigned long inlen);
+int pelican_done(pelican_state *pelmac, unsigned char *out);
+int pelican_test(void);
+
+int pelican_memory(const unsigned char *key, unsigned long keylen,
+ const unsigned char *in, unsigned long inlen,
+ unsigned char *out);
+#endif
+
+#ifdef LTC_XCBC
+
+/* add this to "keylen" to xcbc_init to use a pure three-key XCBC MAC */
+ #define LTC_XCBC_PURE 0x8000UL
+
+typedef struct {
+ unsigned char K[3][MAXBLOCKSIZE],
+ IV[MAXBLOCKSIZE];
+
+ symmetric_key key;
+
+ int cipher,
+ buflen,
+ blocksize;
+} xcbc_state;
+
+int xcbc_init(xcbc_state *xcbc, int cipher, const unsigned char *key, unsigned long keylen);
+int xcbc_process(xcbc_state *xcbc, const unsigned char *in, unsigned long inlen);
+int xcbc_done(xcbc_state *xcbc, unsigned char *out, unsigned long *outlen);
+int xcbc_memory(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+int xcbc_memory_multi(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ unsigned char *out, unsigned long *outlen,
+ const unsigned char *in, unsigned long inlen, ...);
+int xcbc_file(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ const char *filename,
+ unsigned char *out, unsigned long *outlen);
+int xcbc_test(void);
+#endif
+
+#ifdef LTC_F9_MODE
+
+typedef struct {
+ unsigned char akey[MAXBLOCKSIZE],
+ ACC[MAXBLOCKSIZE],
+ IV[MAXBLOCKSIZE];
+
+ symmetric_key key;
+
+ int cipher,
+ buflen,
+ keylen,
+ blocksize;
+} f9_state;
+
+int f9_init(f9_state *f9, int cipher, const unsigned char *key, unsigned long keylen);
+int f9_process(f9_state *f9, const unsigned char *in, unsigned long inlen);
+int f9_done(f9_state *f9, unsigned char *out, unsigned long *outlen);
+int f9_memory(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+int f9_memory_multi(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ unsigned char *out, unsigned long *outlen,
+ const unsigned char *in, unsigned long inlen, ...);
+int f9_file(int cipher,
+ const unsigned char *key, unsigned long keylen,
+ const char *filename,
+ unsigned char *out, unsigned long *outlen);
+int f9_test(void);
+#endif
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_mac.h,v $ */
+/* $Revision: 1.23 $ */
+/* $Date: 2007/05/12 14:37:41 $ */
+
+/* ---- PRNG Stuff ---- */
+#ifdef LTC_YARROW
+struct yarrow_prng {
+ int cipher, hash;
+ unsigned char pool[MAXBLOCKSIZE];
+ symmetric_CTR ctr;
+ LTC_MUTEX_TYPE(prng_lock)
+};
+#endif
+
+#ifdef LTC_RC4
+struct rc4_prng {
+ int x, y;
+ unsigned char buf[256];
+};
+#endif
+
+#ifdef LTC_FORTUNA
+struct fortuna_prng {
+ hash_state pool[LTC_FORTUNA_POOLS]; /* the pools */
+
+ symmetric_key skey;
+
+ unsigned char K[32], /* the current key */
+ IV[16]; /* IV for CTR mode */
+
+ unsigned long pool_idx, /* current pool we will add to */
+ pool0_len, /* length of 0'th pool */
+ wd;
+
+ ulong64 reset_cnt; /* number of times we have reset */
+ LTC_MUTEX_TYPE(prng_lock)
+};
+#endif
+
+#ifdef LTC_SOBER128
+struct sober128_prng {
+ ulong32 R[17], /* Working storage for the shift register */
+ initR[17], /* saved register contents */
+ konst, /* key dependent constant */
+ sbuf; /* partial word encryption buffer */
+
+ int nbuf, /* number of part-word stream bits buffered */
+ flag, /* first add_entropy call or not? */
+ set; /* did we call add_entropy to set key? */
+};
+#endif
+
+typedef union Prng_state {
+ char dummy[1];
+#ifdef LTC_YARROW
+ struct yarrow_prng yarrow;
+#endif
+#ifdef LTC_RC4
+ struct rc4_prng rc4;
+#endif
+#ifdef LTC_FORTUNA
+ struct fortuna_prng fortuna;
+#endif
+#ifdef LTC_SOBER128
+ struct sober128_prng sober128;
+#endif
+} prng_state;
+
+/** PRNG descriptor */
+extern struct ltc_prng_descriptor {
+ /** Name of the PRNG */
+ char *name;
+ /** size in bytes of exported state */
+ int export_size;
+
+ /** Start a PRNG state
+ @param prng [out] The state to initialize
+ @return CRYPT_OK if successful
+ */
+ int (*start)(prng_state *prng);
+
+ /** Add entropy to the PRNG
+ @param in The entropy
+ @param inlen Length of the entropy (octets)\
+ @param prng The PRNG state
+ @return CRYPT_OK if successful
+ */
+ int (*add_entropy)(const unsigned char *in, unsigned long inlen, prng_state *prng);
+
+ /** Ready a PRNG state to read from
+ @param prng The PRNG state to ready
+ @return CRYPT_OK if successful
+ */
+ int (*ready)(prng_state *prng);
+
+ /** Read from the PRNG
+ @param out [out] Where to store the data
+ @param outlen Length of data desired (octets)
+ @param prng The PRNG state to read from
+ @return Number of octets read
+ */
+ unsigned long (*read)(unsigned char *out, unsigned long outlen, prng_state *prng);
+
+ /** Terminate a PRNG state
+ @param prng The PRNG state to terminate
+ @return CRYPT_OK if successful
+ */
+ int (*done)(prng_state *prng);
+
+ /** Export a PRNG state
+ @param out [out] The destination for the state
+ @param outlen [in/out] The max size and resulting size of the PRNG state
+ @param prng The PRNG to export
+ @return CRYPT_OK if successful
+ */
+ int (*pexport)(unsigned char *out, unsigned long *outlen, prng_state *prng);
+
+ /** Import a PRNG state
+ @param in The data to import
+ @param inlen The length of the data to import (octets)
+ @param prng The PRNG to initialize/import
+ @return CRYPT_OK if successful
+ */
+ int (*pimport)(const unsigned char *in, unsigned long inlen, prng_state *prng);
+
+ /** Self-test the PRNG
+ @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
+ */
+ int (*test)(void);
+} prng_descriptor[];
+
+#ifdef LTC_YARROW
+int yarrow_start(prng_state *prng);
+int yarrow_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng);
+int yarrow_ready(prng_state *prng);
+unsigned long yarrow_read(unsigned char *out, unsigned long outlen, prng_state *prng);
+int yarrow_done(prng_state *prng);
+int yarrow_export(unsigned char *out, unsigned long *outlen, prng_state *prng);
+int yarrow_import(const unsigned char *in, unsigned long inlen, prng_state *prng);
+int yarrow_test(void);
+
+extern const struct ltc_prng_descriptor yarrow_desc;
+#endif
+
+#ifdef LTC_FORTUNA
+int fortuna_start(prng_state *prng);
+int fortuna_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng);
+int fortuna_ready(prng_state *prng);
+unsigned long fortuna_read(unsigned char *out, unsigned long outlen, prng_state *prng);
+int fortuna_done(prng_state *prng);
+int fortuna_export(unsigned char *out, unsigned long *outlen, prng_state *prng);
+int fortuna_import(const unsigned char *in, unsigned long inlen, prng_state *prng);
+int fortuna_test(void);
+
+extern const struct ltc_prng_descriptor fortuna_desc;
+#endif
+
+#ifdef LTC_RC4
+int rc4_start(prng_state *prng);
+int rc4_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng);
+int rc4_ready(prng_state *prng);
+unsigned long rc4_read(unsigned char *out, unsigned long outlen, prng_state *prng);
+int rc4_done(prng_state *prng);
+int rc4_export(unsigned char *out, unsigned long *outlen, prng_state *prng);
+int rc4_import(const unsigned char *in, unsigned long inlen, prng_state *prng);
+int rc4_test(void);
+
+extern const struct ltc_prng_descriptor rc4_desc;
+#endif
+
+#ifdef LTC_SPRNG
+int sprng_start(prng_state *prng);
+int sprng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng);
+int sprng_ready(prng_state *prng);
+unsigned long sprng_read(unsigned char *out, unsigned long outlen, prng_state *prng);
+int sprng_done(prng_state *prng);
+int sprng_export(unsigned char *out, unsigned long *outlen, prng_state *prng);
+int sprng_import(const unsigned char *in, unsigned long inlen, prng_state *prng);
+int sprng_test(void);
+
+extern const struct ltc_prng_descriptor sprng_desc;
+#endif
+
+#ifdef LTC_SOBER128
+int sober128_start(prng_state *prng);
+int sober128_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng);
+int sober128_ready(prng_state *prng);
+unsigned long sober128_read(unsigned char *out, unsigned long outlen, prng_state *prng);
+int sober128_done(prng_state *prng);
+int sober128_export(unsigned char *out, unsigned long *outlen, prng_state *prng);
+int sober128_import(const unsigned char *in, unsigned long inlen, prng_state *prng);
+int sober128_test(void);
+
+extern const struct ltc_prng_descriptor sober128_desc;
+#endif
+
+int find_prng(const char *name);
+int register_prng(const struct ltc_prng_descriptor *prng);
+int unregister_prng(const struct ltc_prng_descriptor *prng);
+int prng_is_valid(int idx);
+
+LTC_MUTEX_PROTO(ltc_prng_mutex)
+
+/* Slow RNG you **might** be able to use to seed a PRNG with. Be careful as this
+ * might not work on all platforms as planned
+ */
+unsigned long rng_get_bytes(unsigned char *out,
+ unsigned long outlen,
+ void ( *callback)(void));
+
+int rng_make_prng(int bits, int wprng, prng_state *prng, void (*callback)(void));
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_prng.h,v $ */
+/* $Revision: 1.9 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+/* ---- NUMBER THEORY ---- */
+
+enum {
+ PK_PUBLIC =0,
+ PK_PRIVATE=1
+};
+
+int rand_prime(void *N, long len, prng_state *prng, int wprng);
+
+/* ---- RSA ---- */
+#ifdef LTC_MRSA
+
+/* Min and Max RSA key sizes (in bits) */
+ #define MIN_RSA_SIZE 1024
+ #define MAX_RSA_SIZE 4096
+
+/** RSA LTC_PKCS style key */
+typedef struct Rsa_key {
+ /** Type of key, PK_PRIVATE or PK_PUBLIC */
+ int type;
+ /** The public exponent */
+ void *e;
+ /** The private exponent */
+ void *d;
+ /** The modulus */
+ void *N;
+ /** The p factor of N */
+ void *p;
+ /** The q factor of N */
+ void *q;
+ /** The 1/q mod p CRT param */
+ void *qP;
+ /** The d mod (p - 1) CRT param */
+ void *dP;
+ /** The d mod (q - 1) CRT param */
+ void *dQ;
+} rsa_key;
+
+int rsa_make_key(prng_state *prng, int wprng, int size, long e, rsa_key *key);
+
+int rsa_exptmod(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen, int which,
+ rsa_key *key);
+
+void rsa_free(rsa_key *key);
+
+/* These use LTC_PKCS #1 v2.0 padding */
+ #define rsa_encrypt_key(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _prng, _prng_idx, _hash_idx, _key) \
+ rsa_encrypt_key_ex(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _prng, _prng_idx, _hash_idx, LTC_LTC_PKCS_1_OAEP, _key)
+
+ #define rsa_decrypt_key(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _hash_idx, _stat, _key) \
+ rsa_decrypt_key_ex(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _hash_idx, LTC_LTC_PKCS_1_OAEP, _stat, _key)
+
+ #define rsa_sign_hash(_in, _inlen, _out, _outlen, _prng, _prng_idx, _hash_idx, _saltlen, _key) \
+ rsa_sign_hash_ex(_in, _inlen, _out, _outlen, LTC_LTC_PKCS_1_PSS, _prng, _prng_idx, _hash_idx, _saltlen, _key)
+
+ #define rsa_verify_hash(_sig, _siglen, _hash, _hashlen, _hash_idx, _saltlen, _stat, _key) \
+ rsa_verify_hash_ex(_sig, _siglen, _hash, _hashlen, LTC_LTC_PKCS_1_PSS, _hash_idx, _saltlen, _stat, _key)
+
+/* These can be switched between LTC_PKCS #1 v2.x and LTC_PKCS #1 v1.5 paddings */
+int rsa_encrypt_key_ex(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ const unsigned char *lparam, unsigned long lparamlen,
+ prng_state *prng, int prng_idx, int hash_idx, int padding, rsa_key *key);
+
+int rsa_decrypt_key_ex(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ const unsigned char *lparam, unsigned long lparamlen,
+ int hash_idx, int padding,
+ int *stat, rsa_key *key);
+
+int rsa_sign_hash_ex(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ int padding,
+ prng_state *prng, int prng_idx,
+ int hash_idx, unsigned long saltlen,
+ rsa_key *key);
+
+int rsa_verify_hash_ex(const unsigned char *sig, unsigned long siglen,
+ const unsigned char *hash, unsigned long hashlen,
+ int padding,
+ int hash_idx, unsigned long saltlen,
+ int *stat, rsa_key *key);
+
+/* LTC_PKCS #1 import/export */
+int rsa_export(unsigned char *out, unsigned long *outlen, int type, rsa_key *key);
+int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key);
+#endif
+
+/* ---- Katja ---- */
+#ifdef MKAT
+
+/* Min and Max KAT key sizes (in bits) */
+ #define MIN_KAT_SIZE 1024
+ #define MAX_KAT_SIZE 4096
+
+/** Katja LTC_PKCS style key */
+typedef struct KAT_key {
+ /** Type of key, PK_PRIVATE or PK_PUBLIC */
+ int type;
+ /** The private exponent */
+ void *d;
+ /** The modulus */
+ void *N;
+ /** The p factor of N */
+ void *p;
+ /** The q factor of N */
+ void *q;
+ /** The 1/q mod p CRT param */
+ void *qP;
+ /** The d mod (p - 1) CRT param */
+ void *dP;
+ /** The d mod (q - 1) CRT param */
+ void *dQ;
+ /** The pq param */
+ void *pq;
+} katja_key;
+
+int katja_make_key(prng_state *prng, int wprng, int size, katja_key *key);
+
+int katja_exptmod(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen, int which,
+ katja_key *key);
+
+void katja_free(katja_key *key);
+
+/* These use LTC_PKCS #1 v2.0 padding */
+int katja_encrypt_key(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ const unsigned char *lparam, unsigned long lparamlen,
+ prng_state *prng, int prng_idx, int hash_idx, katja_key *key);
+
+int katja_decrypt_key(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ const unsigned char *lparam, unsigned long lparamlen,
+ int hash_idx, int *stat,
+ katja_key *key);
+
+/* LTC_PKCS #1 import/export */
+int katja_export(unsigned char *out, unsigned long *outlen, int type, katja_key *key);
+int katja_import(const unsigned char *in, unsigned long inlen, katja_key *key);
+#endif
+
+/* ---- ECC Routines ---- */
+#ifdef LTC_MECC
+
+/* size of our temp buffers for exported keys */
+ #define ECC_BUF_SIZE 256
+
+/* max private key size */
+ #define ECC_MAXSIZE 66
+
+/** Structure defines a NIST GF(p) curve */
+typedef struct {
+ /** The size of the curve in octets */
+ int size;
+
+ /** name of curve */
+ char *name;
+
+ /** The prime that defines the field the curve is in (encoded in hex) */
+ char *prime;
+
+ /** The fields B param (hex) */
+ char *B;
+
+ /** The order of the curve (hex) */
+ char *order;
+
+ /** The x co-ordinate of the base point on the curve (hex) */
+ char *Gx;
+
+ /** The y co-ordinate of the base point on the curve (hex) */
+ char *Gy;
+} ltc_ecc_set_type;
+
+/** A point on a ECC curve, stored in Jacbobian format such that (x,y,z) => (x/z^2, y/z^3, 1) when interpretted as affine */
+typedef struct {
+ /** The x co-ordinate */
+ void *x;
+
+ /** The y co-ordinate */
+ void *y;
+
+ /** The z co-ordinate */
+ void *z;
+} ecc_point;
+
+/** An ECC key */
+typedef struct {
+ /** Type of key, PK_PRIVATE or PK_PUBLIC */
+ int type;
+
+ /** Index into the ltc_ecc_sets[] for the parameters of this curve; if -1, then this key is using user supplied curve in dp */
+ int idx;
+
+ /** pointer to domain parameters; either points to NIST curves (identified by idx >= 0) or user supplied curve */
+ const ltc_ecc_set_type *dp;
+
+ /** The public key */
+ ecc_point pubkey;
+
+ /** The private key */
+ void *k;
+} ecc_key;
+
+/** the ECC params provided */
+extern const ltc_ecc_set_type ltc_ecc_sets[];
+
+int ecc_test(void);
+void ecc_sizes(int *low, int *high);
+int ecc_get_size(ecc_key *key);
+
+int ecc_make_key(prng_state *prng, int wprng, int keysize, ecc_key *key);
+int ecc_make_key_ex(prng_state *prng, int wprng, ecc_key *key, const ltc_ecc_set_type *dp);
+void ecc_free(ecc_key *key);
+
+int ecc_export(unsigned char *out, unsigned long *outlen, int type, ecc_key *key);
+int ecc_import(const unsigned char *in, unsigned long inlen, ecc_key *key);
+int ecc_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_set_type *dp);
+
+int ecc_ansi_x963_export(ecc_key *key, unsigned char *out, unsigned long *outlen);
+int ecc_ansi_x963_import(const unsigned char *in, unsigned long inlen, ecc_key *key);
+int ecc_ansi_x963_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, ltc_ecc_set_type *dp);
+
+int ecc_shared_secret(ecc_key *private_key, ecc_key *public_key,
+ unsigned char *out, unsigned long *outlen);
+
+int ecc_encrypt_key(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ prng_state *prng, int wprng, int hash,
+ ecc_key *key);
+
+int ecc_decrypt_key(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ ecc_key *key);
+
+int ecc_sign_hash(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ prng_state *prng, int wprng, ecc_key *key);
+
+int ecc_verify_hash(const unsigned char *sig, unsigned long siglen,
+ const unsigned char *hash, unsigned long hashlen,
+ int *stat, ecc_key *key);
+
+/* low level functions */
+ecc_point *ltc_ecc_new_point(void);
+void ltc_ecc_del_point(ecc_point *p);
+int ltc_ecc_is_valid_idx(int n);
+
+/* point ops (mp == montgomery digit) */
+ #if !defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC) || defined(GMP_LTC_DESC)
+/* R = 2P */
+int ltc_ecc_projective_dbl_point(ecc_point *P, ecc_point *R, void *modulus, void *mp);
+
+/* R = P + Q */
+int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp);
+ #endif
+
+ #if defined(LTC_MECC_FP)
+/* optimized point multiplication using fixed point cache (HAC algorithm 14.117) */
+int ltc_ecc_fp_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map);
+
+/* functions for saving/loading/freeing/adding to fixed point cache */
+int ltc_ecc_fp_save_state(unsigned char **out, unsigned long *outlen);
+int ltc_ecc_fp_restore_state(unsigned char *in, unsigned long inlen);
+void ltc_ecc_fp_free(void);
+int ltc_ecc_fp_add_point(ecc_point *g, void *modulus, int lock);
+
+/* lock/unlock all points currently in fixed point cache */
+void ltc_ecc_fp_tablelock(int lock);
+ #endif
+
+/* R = kG */
+int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map);
+
+ #ifdef LTC_ECC_SHAMIR
+/* kA*A + kB*B = C */
+int ltc_ecc_mul2add(ecc_point *A, void *kA,
+ ecc_point *B, void *kB,
+ ecc_point *C,
+ void *modulus);
+
+ #ifdef LTC_MECC_FP
+/* Shamir's trick with optimized point multiplication using fixed point cache */
+int ltc_ecc_fp_mul2add(ecc_point *A, void *kA,
+ ecc_point *B, void *kB,
+ ecc_point *C, void *modulus);
+ #endif
+ #endif
+
+
+/* map P to affine from projective */
+int ltc_ecc_map(ecc_point *P, void *modulus, void *mp);
+#endif
+
+#ifdef LTC_MDSA
+
+/* Max diff between group and modulus size in bytes */
+ #define LTC_MDSA_DELTA 512
+
+/* Max DSA group size in bytes (default allows 4k-bit groups) */
+ #define LTC_MDSA_MAX_GROUP 512
+
+/** DSA key structure */
+typedef struct {
+ /** The key type, PK_PRIVATE or PK_PUBLIC */
+ int type;
+
+ /** The order of the sub-group used in octets */
+ int qord;
+
+ /** The generator */
+ void *g;
+
+ /** The prime used to generate the sub-group */
+ void *q;
+
+ /** The large prime that generats the field the contains the sub-group */
+ void *p;
+
+ /** The private key */
+ void *x;
+
+ /** The public key */
+ void *y;
+} dsa_key;
+
+int dsa_make_key(prng_state *prng, int wprng, int group_size, int modulus_size, dsa_key *key);
+void dsa_free(dsa_key *key);
+
+int dsa_sign_hash_raw(const unsigned char *in, unsigned long inlen,
+ void *r, void *s,
+ prng_state *prng, int wprng, dsa_key *key);
+
+int dsa_sign_hash(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ prng_state *prng, int wprng, dsa_key *key);
+
+int dsa_verify_hash_raw(void *r, void *s,
+ const unsigned char *hash, unsigned long hashlen,
+ int *stat, dsa_key *key);
+
+int dsa_verify_hash(const unsigned char *sig, unsigned long siglen,
+ const unsigned char *hash, unsigned long hashlen,
+ int *stat, dsa_key *key);
+
+int dsa_encrypt_key(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ prng_state *prng, int wprng, int hash,
+ dsa_key *key);
+
+int dsa_decrypt_key(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ dsa_key *key);
+
+int dsa_import(const unsigned char *in, unsigned long inlen, dsa_key *key);
+int dsa_export(unsigned char *out, unsigned long *outlen, int type, dsa_key *key);
+int dsa_verify_key(dsa_key *key, int *stat);
+
+int dsa_shared_secret(void *private_key, void *base,
+ dsa_key *public_key,
+ unsigned char *out, unsigned long *outlen);
+#endif
+
+#ifdef LTC_DER
+/* DER handling */
+
+enum {
+ LTC_ASN1_EOL,
+ LTC_ASN1_BOOLEAN,
+ LTC_ASN1_INTEGER,
+ LTC_ASN1_SHORT_INTEGER,
+ LTC_ASN1_BIT_STRING,
+ LTC_ASN1_OCTET_STRING,
+ LTC_ASN1_NULL,
+ LTC_ASN1_OBJECT_IDENTIFIER,
+ LTC_ASN1_IA5_STRING,
+ LTC_ASN1_PRINTABLE_STRING,
+ LTC_ASN1_UTF8_STRING,
+ LTC_ASN1_UTCTIME,
+ LTC_ASN1_CHOICE,
+ LTC_ASN1_SEQUENCE,
+ LTC_ASN1_SET,
+ LTC_ASN1_SETOF
+};
+
+/** A LTC ASN.1 list type */
+typedef struct ltc_asn1_list_ {
+ /** The LTC ASN.1 enumerated type identifier */
+ int type;
+ /** The data to encode or place for decoding */
+ void *data;
+ /** The size of the input or resulting output */
+ unsigned long size;
+ /** The used flag, this is used by the CHOICE ASN.1 type to indicate which choice was made */
+ int used;
+ /** prev/next entry in the list */
+ struct ltc_asn1_list_ *prev, *next, *child, *parent;
+} ltc_asn1_list;
+
+ #define LTC_SET_ASN1(list, index, Type, Data, Size) \
+ do { \
+ int LTC_MACRO_temp = (index); \
+ ltc_asn1_list *LTC_MACRO_list = (list); \
+ LTC_MACRO_list[LTC_MACRO_temp].type = (Type); \
+ LTC_MACRO_list[LTC_MACRO_temp].data = (void *)(Data); \
+ LTC_MACRO_list[LTC_MACRO_temp].size = (Size); \
+ LTC_MACRO_list[LTC_MACRO_temp].used = 0; \
+ } while (0);
+
+/* SEQUENCE */
+int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen, int type_of);
+
+ #define der_encode_sequence(list, inlen, out, outlen) der_encode_sequence_ex(list, inlen, out, outlen, LTC_ASN1_SEQUENCE)
+
+int der_decode_sequence_ex(const unsigned char *in, unsigned long inlen,
+ ltc_asn1_list *list, unsigned long outlen, int ordered);
+
+ #define der_decode_sequence(in, inlen, list, outlen) der_decode_sequence_ex(in, inlen, list, outlen, 1)
+
+int der_length_sequence(ltc_asn1_list *list, unsigned long inlen,
+ unsigned long *outlen);
+
+/* SET */
+ #define der_decode_set(in, inlen, list, outlen) der_decode_sequence_ex(in, inlen, list, outlen, 0)
+ #define der_length_set der_length_sequence
+int der_encode_set(ltc_asn1_list *list, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+
+int der_encode_setof(ltc_asn1_list *list, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+
+/* VA list handy helpers with triplets of */
+int der_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...);
+int der_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...);
+
+/* FLEXI DECODER handle unknown list decoder */
+int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out);
+void der_free_sequence_flexi(ltc_asn1_list *list);
+void der_sequence_free(ltc_asn1_list *in);
+
+/* BOOLEAN */
+int der_length_boolean(unsigned long *outlen);
+int der_encode_boolean(int in,
+ unsigned char *out, unsigned long *outlen);
+int der_decode_boolean(const unsigned char *in, unsigned long inlen,
+ int *out);
+
+/* INTEGER */
+int der_encode_integer(void *num, unsigned char *out, unsigned long *outlen);
+int der_decode_integer(const unsigned char *in, unsigned long inlen, void *num);
+int der_length_integer(void *num, unsigned long *len);
+
+/* INTEGER -- handy for 0..2^32-1 values */
+int der_decode_short_integer(const unsigned char *in, unsigned long inlen, unsigned long *num);
+int der_encode_short_integer(unsigned long num, unsigned char *out, unsigned long *outlen);
+int der_length_short_integer(unsigned long num, unsigned long *outlen);
+
+/* BIT STRING */
+int der_encode_bit_string(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+int der_decode_bit_string(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+int der_length_bit_string(unsigned long nbits, unsigned long *outlen);
+
+/* OCTET STRING */
+int der_encode_octet_string(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+int der_decode_octet_string(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+int der_length_octet_string(unsigned long noctets, unsigned long *outlen);
+
+/* OBJECT IDENTIFIER */
+int der_encode_object_identifier(unsigned long *words, unsigned long nwords,
+ unsigned char *out, unsigned long *outlen);
+int der_decode_object_identifier(const unsigned char *in, unsigned long inlen,
+ unsigned long *words, unsigned long *outlen);
+int der_length_object_identifier(unsigned long *words, unsigned long nwords, unsigned long *outlen);
+unsigned long der_object_identifier_bits(unsigned long x);
+
+/* IA5 STRING */
+int der_encode_ia5_string(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+int der_decode_ia5_string(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+int der_length_ia5_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen);
+
+int der_ia5_char_encode(int c);
+int der_ia5_value_decode(int v);
+
+/* Printable STRING */
+int der_encode_printable_string(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+int der_decode_printable_string(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+int der_length_printable_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen);
+
+int der_printable_char_encode(int c);
+int der_printable_value_decode(int v);
+
+/* UTF-8 */
+ #if (defined(SIZE_MAX) || __STDC_VERSION__ >= 199901L || defined(WCHAR_MAX) || defined(_WCHAR_T) || defined(_WCHAR_T_DEFINED) || defined (__WCHAR_TYPE__)) && !defined(LTC_NO_WCHAR)
+ #include
+ #else
+typedef ulong32 wchar_t;
+ #endif
+
+int der_encode_utf8_string(const wchar_t *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+
+int der_decode_utf8_string(const unsigned char *in, unsigned long inlen,
+ wchar_t *out, unsigned long *outlen);
+unsigned long der_utf8_charsize(const wchar_t c);
+int der_length_utf8_string(const wchar_t *in, unsigned long noctets, unsigned long *outlen);
+
+
+/* CHOICE */
+int der_decode_choice(const unsigned char *in, unsigned long *inlen,
+ ltc_asn1_list *list, unsigned long outlen);
+
+/* UTCTime */
+typedef struct {
+ unsigned YY, /* year */
+ MM, /* month */
+ DD, /* day */
+ hh, /* hour */
+ mm, /* minute */
+ ss, /* second */
+ off_dir, /* timezone offset direction 0 == +, 1 == - */
+ off_hh, /* timezone offset hours */
+ off_mm; /* timezone offset minutes */
+} ltc_utctime;
+
+int der_encode_utctime(ltc_utctime *utctime,
+ unsigned char *out, unsigned long *outlen);
+
+int der_decode_utctime(const unsigned char *in, unsigned long *inlen,
+ ltc_utctime *out);
+
+int der_length_utctime(ltc_utctime *utctime, unsigned long *outlen);
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_pk.h,v $ */
+/* $Revision: 1.81 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+/** math functions **/
+#define LTC_SOURCE
+#define LTC_MP_LT -1
+#define LTC_MP_EQ 0
+#define LTC_MP_GT 1
+
+#define LTC_MP_NO 0
+#define LTC_MP_YES 1
+
+#ifndef LTC_MECC
+typedef void ecc_point;
+#endif
+
+#ifndef LTC_MRSA
+typedef void rsa_key;
+#endif
+
+/** math descriptor */
+typedef struct {
+ /** Name of the math provider */
+ char *name;
+
+ /** Bits per digit, amount of bits must fit in an unsigned long */
+ int bits_per_digit;
+
+/* ---- init/deinit functions ---- */
+
+ /** initialize a bignum
+ @param a The number to initialize
+ @return CRYPT_OK on success
+ */
+ int (*init)(void **a);
+
+ /** init copy
+ @param dst The number to initialize and write to
+ @param src The number to copy from
+ @return CRYPT_OK on success
+ */
+ int (*init_copy)(void **dst, void *src);
+
+ /** deinit
+ @param a The number to free
+ @return CRYPT_OK on success
+ */
+ void (*deinit)(void *a);
+
+/* ---- data movement ---- */
+
+ /** negate
+ @param src The number to negate
+ @param dst The destination
+ @return CRYPT_OK on success
+ */
+ int (*neg)(void *src, void *dst);
+
+ /** copy
+ @param src The number to copy from
+ @param dst The number to write to
+ @return CRYPT_OK on success
+ */
+ int (*copy)(void *src, void *dst);
+
+/* ---- trivial low level functions ---- */
+
+ /** set small constant
+ @param a Number to write to
+ @param n Source upto bits_per_digit (actually meant for very small constants)
+ @return CRYPT_OK on succcess
+ */
+ int (*set_int)(void *a, unsigned long n);
+
+ /** get small constant
+ @param a Number to read, only fetches upto bits_per_digit from the number
+ @return The lower bits_per_digit of the integer (unsigned)
+ */
+ unsigned long (*get_int)(void *a);
+
+ /** get digit n
+ @param a The number to read from
+ @param n The number of the digit to fetch
+ @return The bits_per_digit sized n'th digit of a
+ */
+ unsigned long (*get_digit)(void *a, int n);
+
+ /** Get the number of digits that represent the number
+ @param a The number to count
+ @return The number of digits used to represent the number
+ */
+ int (*get_digit_count)(void *a);
+
+ /** compare two integers
+ @param a The left side integer
+ @param b The right side integer
+ @return LTC_MP_LT if a < b, LTC_MP_GT if a > b and LTC_MP_EQ otherwise. (signed comparison)
+ */
+ int (*compare)(void *a, void *b);
+
+ /** compare against int
+ @param a The left side integer
+ @param b The right side integer (upto bits_per_digit)
+ @return LTC_MP_LT if a < b, LTC_MP_GT if a > b and LTC_MP_EQ otherwise. (signed comparison)
+ */
+ int (*compare_d)(void *a, unsigned long n);
+
+ /** Count the number of bits used to represent the integer
+ @param a The integer to count
+ @return The number of bits required to represent the integer
+ */
+ int (*count_bits)(void *a);
+
+ /** Count the number of LSB bits which are zero
+ @param a The integer to count
+ @return The number of contiguous zero LSB bits
+ */
+ int (*count_lsb_bits)(void *a);
+
+ /** Compute a power of two
+ @param a The integer to store the power in
+ @param n The power of two you want to store (a = 2^n)
+ @return CRYPT_OK on success
+ */
+ int (*twoexpt)(void *a, int n);
+
+/* ---- radix conversions ---- */
+
+ /** read ascii string
+ @param a The integer to store into
+ @param str The string to read
+ @param radix The radix the integer has been represented in (2-64)
+ @return CRYPT_OK on success
+ */
+ int (*read_radix)(void *a, const char *str, int radix);
+
+ /** write number to string
+ @param a The integer to store
+ @param str The destination for the string
+ @param radix The radix the integer is to be represented in (2-64)
+ @return CRYPT_OK on success
+ */
+ int (*write_radix)(void *a, char *str, int radix);
+
+ /** get size as unsigned char string
+ @param a The integer to get the size (when stored in array of octets)
+ @return The length of the integer
+ */
+ unsigned long (*unsigned_size)(void *a);
+
+ /** store an integer as an array of octets
+ @param src The integer to store
+ @param dst The buffer to store the integer in
+ @return CRYPT_OK on success
+ */
+ int (*unsigned_write)(void *src, unsigned char *dst);
+
+ /** read an array of octets and store as integer
+ @param dst The integer to load
+ @param src The array of octets
+ @param len The number of octets
+ @return CRYPT_OK on success
+ */
+ int (*unsigned_read)(void *dst, unsigned char *src, unsigned long len);
+
+/* ---- basic math ---- */
+
+ /** add two integers
+ @param a The first source integer
+ @param b The second source integer
+ @param c The destination of "a + b"
+ @return CRYPT_OK on success
+ */
+ int (*add)(void *a, void *b, void *c);
+
+
+ /** add two integers
+ @param a The first source integer
+ @param b The second source integer (single digit of upto bits_per_digit in length)
+ @param c The destination of "a + b"
+ @return CRYPT_OK on success
+ */
+ int (*addi)(void *a, unsigned long b, void *c);
+
+ /** subtract two integers
+ @param a The first source integer
+ @param b The second source integer
+ @param c The destination of "a - b"
+ @return CRYPT_OK on success
+ */
+ int (*sub)(void *a, void *b, void *c);
+
+ /** subtract two integers
+ @param a The first source integer
+ @param b The second source integer (single digit of upto bits_per_digit in length)
+ @param c The destination of "a - b"
+ @return CRYPT_OK on success
+ */
+ int (*subi)(void *a, unsigned long b, void *c);
+
+ /** multiply two integers
+ @param a The first source integer
+ @param b The second source integer (single digit of upto bits_per_digit in length)
+ @param c The destination of "a * b"
+ @return CRYPT_OK on success
+ */
+ int (*mul)(void *a, void *b, void *c);
+
+ /** multiply two integers
+ @param a The first source integer
+ @param b The second source integer (single digit of upto bits_per_digit in length)
+ @param c The destination of "a * b"
+ @return CRYPT_OK on success
+ */
+ int (*muli)(void *a, unsigned long b, void *c);
+
+ /** Square an integer
+ @param a The integer to square
+ @param b The destination
+ @return CRYPT_OK on success
+ */
+ int (*sqr)(void *a, void *b);
+
+ /** Divide an integer
+ @param a The dividend
+ @param b The divisor
+ @param c The quotient (can be NULL to signify don't care)
+ @param d The remainder (can be NULL to signify don't care)
+ @return CRYPT_OK on success
+ */
+ int (*mpdiv)(void *a, void *b, void *c, void *d);
+
+ /** divide by two
+ @param a The integer to divide (shift right)
+ @param b The destination
+ @return CRYPT_OK on success
+ */
+ int (*div_2)(void *a, void *b);
+
+ /** Get remainder (small value)
+ @param a The integer to reduce
+ @param b The modulus (upto bits_per_digit in length)
+ @param c The destination for the residue
+ @return CRYPT_OK on success
+ */
+ int (*modi)(void *a, unsigned long b, unsigned long *c);
+
+ /** gcd
+ @param a The first integer
+ @param b The second integer
+ @param c The destination for (a, b)
+ @return CRYPT_OK on success
+ */
+ int (*gcd)(void *a, void *b, void *c);
+
+ /** lcm
+ @param a The first integer
+ @param b The second integer
+ @param c The destination for [a, b]
+ @return CRYPT_OK on success
+ */
+ int (*lcm)(void *a, void *b, void *c);
+
+ /** Modular multiplication
+ @param a The first source
+ @param b The second source
+ @param c The modulus
+ @param d The destination (a*b mod c)
+ @return CRYPT_OK on success
+ */
+ int (*mulmod)(void *a, void *b, void *c, void *d);
+
+ /** Modular squaring
+ @param a The first source
+ @param b The modulus
+ @param c The destination (a*a mod b)
+ @return CRYPT_OK on success
+ */
+ int (*sqrmod)(void *a, void *b, void *c);
+
+ /** Modular inversion
+ @param a The value to invert
+ @param b The modulus
+ @param c The destination (1/a mod b)
+ @return CRYPT_OK on success
+ */
+ int (*invmod)(void *, void *, void *);
+
+/* ---- reduction ---- */
+
+ /** setup montgomery
+ @param a The modulus
+ @param b The destination for the reduction digit
+ @return CRYPT_OK on success
+ */
+ int (*montgomery_setup)(void *a, void **b);
+
+ /** get normalization value
+ @param a The destination for the normalization value
+ @param b The modulus
+ @return CRYPT_OK on success
+ */
+ int (*montgomery_normalization)(void *a, void *b);
+
+ /** reduce a number
+ @param a The number [and dest] to reduce
+ @param b The modulus
+ @param c The value "b" from montgomery_setup()
+ @return CRYPT_OK on success
+ */
+ int (*montgomery_reduce)(void *a, void *b, void *c);
+
+ /** clean up (frees memory)
+ @param a The value "b" from montgomery_setup()
+ @return CRYPT_OK on success
+ */
+ void (*montgomery_deinit)(void *a);
+
+/* ---- exponentiation ---- */
+
+ /** Modular exponentiation
+ @param a The base integer
+ @param b The power (can be negative) integer
+ @param c The modulus integer
+ @param d The destination
+ @return CRYPT_OK on success
+ */
+ int (*exptmod)(void *a, void *b, void *c, void *d);
+
+ /** Primality testing
+ @param a The integer to test
+ @param b The destination of the result (FP_YES if prime)
+ @return CRYPT_OK on success
+ */
+ int (*isprime)(void *a, int *b);
+
+/* ---- (optional) ecc point math ---- */
+
+ /** ECC GF(p) point multiplication (from the NIST curves)
+ @param k The integer to multiply the point by
+ @param G The point to multiply
+ @param R The destination for kG
+ @param modulus The modulus for the field
+ @param map Boolean indicated whether to map back to affine or not (can be ignored if you work in affine only)
+ @return CRYPT_OK on success
+ */
+ int (*ecc_ptmul)(void *k, ecc_point *G, ecc_point *R, void *modulus, int map);
+
+ /** ECC GF(p) point addition
+ @param P The first point
+ @param Q The second point
+ @param R The destination of P + Q
+ @param modulus The modulus
+ @param mp The "b" value from montgomery_setup()
+ @return CRYPT_OK on success
+ */
+ int (*ecc_ptadd)(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp);
+
+ /** ECC GF(p) point double
+ @param P The first point
+ @param R The destination of 2P
+ @param modulus The modulus
+ @param mp The "b" value from montgomery_setup()
+ @return CRYPT_OK on success
+ */
+ int (*ecc_ptdbl)(ecc_point *P, ecc_point *R, void *modulus, void *mp);
+
+ /** ECC mapping from projective to affine, currently uses (x,y,z) => (x/z^2, y/z^3, 1)
+ @param P The point to map
+ @param modulus The modulus
+ @param mp The "b" value from montgomery_setup()
+ @return CRYPT_OK on success
+ @remark The mapping can be different but keep in mind a ecc_point only has three
+ integers (x,y,z) so if you use a different mapping you have to make it fit.
+ */
+ int (*ecc_map)(ecc_point *P, void *modulus, void *mp);
+
+ /** Computes kA*A + kB*B = C using Shamir's Trick
+ @param A First point to multiply
+ @param kA What to multiple A by
+ @param B Second point to multiply
+ @param kB What to multiple B by
+ @param C [out] Destination point (can overlap with A or B
+ @param modulus Modulus for curve
+ @return CRYPT_OK on success
+ */
+ int (*ecc_mul2add)(ecc_point *A, void *kA,
+ ecc_point *B, void *kB,
+ ecc_point *C,
+ void *modulus);
+
+/* ---- (optional) rsa optimized math (for internal CRT) ---- */
+
+ /** RSA Key Generation
+ @param prng An active PRNG state
+ @param wprng The index of the PRNG desired
+ @param size The size of the modulus (key size) desired (octets)
+ @param e The "e" value (public key). e==65537 is a good choice
+ @param key [out] Destination of a newly created private key pair
+ @return CRYPT_OK if successful, upon error all allocated ram is freed
+ */
+ int (*rsa_keygen)(prng_state *prng, int wprng, int size, long e, rsa_key *key);
+
+
+ /** RSA exponentiation
+ @param in The octet array representing the base
+ @param inlen The length of the input
+ @param out The destination (to be stored in an octet array format)
+ @param outlen The length of the output buffer and the resulting size (zero padded to the size of the modulus)
+ @param which PK_PUBLIC for public RSA and PK_PRIVATE for private RSA
+ @param key The RSA key to use
+ @return CRYPT_OK on success
+ */
+ int (*rsa_me)(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen, int which,
+ rsa_key *key);
+} ltc_math_descriptor;
+
+extern ltc_math_descriptor ltc_mp;
+
+int ltc_init_multi(void **a, ...);
+void ltc_deinit_multi(void *a, ...);
+
+#ifdef LTM_DESC
+extern const ltc_math_descriptor ltm_desc;
+#endif
+
+#ifdef TFM_DESC
+extern const ltc_math_descriptor tfm_desc;
+#endif
+
+#ifdef GMP_DESC
+extern const ltc_math_descriptor gmp_desc;
+#endif
+
+#if !defined(DESC_DEF_ONLY) && defined(LTC_SOURCE)
+ #undef MP_DIGIT_BIT
+ #undef mp_iszero
+ #undef mp_isodd
+ #undef mp_tohex
+
+ #define MP_DIGIT_BIT ltc_mp.bits_per_digit
+
+/* some handy macros */
+ #define mp_init(a) ltc_mp.init(a)
+ #define mp_init_multi ltc_init_multi
+ #define mp_clear(a) ltc_mp.deinit(a)
+ #define mp_clear_multi ltc_deinit_multi
+ #define mp_init_copy(a, b) ltc_mp.init_copy(a, b)
+
+ #define mp_neg(a, b) ltc_mp.neg(a, b)
+ #define mp_copy(a, b) ltc_mp.copy(a, b)
+
+ #define mp_set(a, b) ltc_mp.set_int(a, b)
+ #define mp_set_int(a, b) ltc_mp.set_int(a, b)
+ #define mp_get_int(a) ltc_mp.get_int(a)
+ #define mp_get_digit(a, n) ltc_mp.get_digit(a, n)
+ #define mp_get_digit_count(a) ltc_mp.get_digit_count(a)
+ #define mp_cmp(a, b) ltc_mp.compare(a, b)
+ #define mp_cmp_d(a, b) ltc_mp.compare_d(a, b)
+ #define mp_count_bits(a) ltc_mp.count_bits(a)
+ #define mp_cnt_lsb(a) ltc_mp.count_lsb_bits(a)
+ #define mp_2expt(a, b) ltc_mp.twoexpt(a, b)
+
+ #define mp_read_radix(a, b, c) ltc_mp.read_radix(a, b, c)
+ #define mp_toradix(a, b, c) ltc_mp.write_radix(a, b, c)
+ #define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a)
+ #define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b)
+ #define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c)
+
+ #define mp_add(a, b, c) ltc_mp.add(a, b, c)
+ #define mp_add_d(a, b, c) ltc_mp.addi(a, b, c)
+ #define mp_sub(a, b, c) ltc_mp.sub(a, b, c)
+ #define mp_sub_d(a, b, c) ltc_mp.subi(a, b, c)
+ #define mp_mul(a, b, c) ltc_mp.mul(a, b, c)
+ #define mp_mul_d(a, b, c) ltc_mp.muli(a, b, c)
+ #define mp_sqr(a, b) ltc_mp.sqr(a, b)
+ #define mp_div(a, b, c, d) ltc_mp.mpdiv(a, b, c, d)
+ #define mp_div_2(a, b) ltc_mp.div_2(a, b)
+ #define mp_mod(a, b, c) ltc_mp.mpdiv(a, b, NULL, c)
+ #define mp_mod_d(a, b, c) ltc_mp.modi(a, b, c)
+ #define mp_gcd(a, b, c) ltc_mp.gcd(a, b, c)
+ #define mp_lcm(a, b, c) ltc_mp.lcm(a, b, c)
+
+ #define mp_mulmod(a, b, c, d) ltc_mp.mulmod(a, b, c, d)
+ #define mp_sqrmod(a, b, c) ltc_mp.sqrmod(a, b, c)
+ #define mp_invmod(a, b, c) ltc_mp.invmod(a, b, c)
+
+ #define mp_montgomery_setup(a, b) ltc_mp.montgomery_setup(a, b)
+ #define mp_montgomery_normalization(a, b) ltc_mp.montgomery_normalization(a, b)
+ #define mp_montgomery_reduce(a, b, c) ltc_mp.montgomery_reduce(a, b, c)
+ #define mp_montgomery_free(a) ltc_mp.montgomery_deinit(a)
+
+ #define mp_exptmod(a, b, c, d) ltc_mp.exptmod(a, b, c, d)
+ #define mp_prime_is_prime(a, b, c) ltc_mp.isprime(a, c)
+
+ #define mp_iszero(a) (mp_cmp_d(a, 0) == LTC_MP_EQ ? LTC_MP_YES : LTC_MP_NO)
+ #define mp_isodd(a) (mp_get_digit_count(a) > 0 ? (mp_get_digit(a, 0) & 1 ? LTC_MP_YES : LTC_MP_NO) : LTC_MP_NO)
+ #define mp_exch(a, b) do { void *ABC__tmp = a; a = b; b = ABC__tmp; } while (0);
+
+ #define mp_tohex(a, b) mp_toradix(a, b, 16)
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_math.h,v $ */
+/* $Revision: 1.44 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+/* ---- LTC_BASE64 Routines ---- */
+#ifdef LTC_BASE64
+int base64_encode(const unsigned char *in, unsigned long len,
+ unsigned char *out, unsigned long *outlen);
+
+int base64_decode(const unsigned char *in, unsigned long len,
+ unsigned char *out, unsigned long *outlen);
+#endif
+
+/* ---- MEM routines ---- */
+void zeromem(void *dst, size_t len);
+void burn_stack(unsigned long len);
+
+const char *error_to_string(int err);
+
+extern const char *crypt_build_settings;
+
+/* ---- HMM ---- */
+int crypt_fsa(void *mp, ...);
+
+/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_misc.h,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+/* Defines the LTC_ARGCHK macro used within the library */
+/* ARGTYPE is defined in mycrypt_cfg.h */
+#if ARGTYPE == 0
+
+ #include
+
+/* this is the default LibTomCrypt macro */
+void crypt_argchk(char *v, char *s, int d);
+
+ #define LTC_ARGCHK(x) if (!(x)) { crypt_argchk(#x, __FILE__, __LINE__); }
+ #define LTC_ARGCHKVD(x) LTC_ARGCHK(x)
+
+#elif ARGTYPE == 1
+
+/* fatal type of error */
+ #define LTC_ARGCHK(x) assert((x))
+ #define LTC_ARGCHKVD(x) LTC_ARGCHK(x)
+
+#elif ARGTYPE == 2
+
+ #define LTC_ARGCHK(x) if (!(x)) { fprintf(stderr, "\nwarning: ARGCHK failed at %s:%d\n", __FILE__, __LINE__); }
+ #define LTC_ARGCHKVD(x) LTC_ARGCHK(x)
+
+#elif ARGTYPE == 3
+
+ #define LTC_ARGCHK(x)
+ #define LTC_ARGCHKVD(x) LTC_ARGCHK(x)
+
+#elif ARGTYPE == 4
+
+ #define LTC_ARGCHK(x) if (!(x)) return CRYPT_INVALID_ARG;
+ #define LTC_ARGCHKVD(x) if (!(x)) return;
+#endif
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_argchk.h,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2006/08/27 20:50:21 $ */
+
+/* LTC_PKCS Header Info */
+
+/* ===> LTC_PKCS #1 -- RSA Cryptography <=== */
+#ifdef LTC_PKCS_1
+
+enum ltc_pkcs_1_v1_5_blocks {
+ LTC_LTC_PKCS_1_EMSA = 1, /* Block type 1 (LTC_PKCS #1 v1.5 signature padding) */
+ LTC_LTC_PKCS_1_EME = 2 /* Block type 2 (LTC_PKCS #1 v1.5 encryption padding) */
+};
+
+enum ltc_pkcs_1_paddings {
+ LTC_LTC_PKCS_1_V1_5 = 1, /* LTC_PKCS #1 v1.5 padding (\sa ltc_pkcs_1_v1_5_blocks) */
+ LTC_LTC_PKCS_1_OAEP = 2, /* LTC_PKCS #1 v2.0 encryption padding */
+ LTC_LTC_PKCS_1_PSS = 3 /* LTC_PKCS #1 v2.1 signature padding */
+};
+
+int pkcs_1_mgf1(int hash_idx,
+ const unsigned char *seed, unsigned long seedlen,
+ unsigned char *mask, unsigned long masklen);
+
+int pkcs_1_i2osp(void *n, unsigned long modulus_len, unsigned char *out);
+int pkcs_1_os2ip(void *n, unsigned char *in, unsigned long inlen);
+
+/* *** v1.5 padding */
+int pkcs_1_v1_5_encode(const unsigned char *msg,
+ unsigned long msglen,
+ int block_type,
+ unsigned long modulus_bitlen,
+ prng_state *prng,
+ int prng_idx,
+ unsigned char *out,
+ unsigned long *outlen);
+
+int pkcs_1_v1_5_decode(const unsigned char *msg,
+ unsigned long msglen,
+ int block_type,
+ unsigned long modulus_bitlen,
+ unsigned char *out,
+ unsigned long *outlen,
+ int *is_valid);
+
+/* *** v2.1 padding */
+int pkcs_1_oaep_encode(const unsigned char *msg, unsigned long msglen,
+ const unsigned char *lparam, unsigned long lparamlen,
+ unsigned long modulus_bitlen, prng_state *prng,
+ int prng_idx, int hash_idx,
+ unsigned char *out, unsigned long *outlen);
+
+int pkcs_1_oaep_decode(const unsigned char *msg, unsigned long msglen,
+ const unsigned char *lparam, unsigned long lparamlen,
+ unsigned long modulus_bitlen, int hash_idx,
+ unsigned char *out, unsigned long *outlen,
+ int *res);
+
+int pkcs_1_pss_encode(const unsigned char *msghash, unsigned long msghashlen,
+ unsigned long saltlen, prng_state *prng,
+ int prng_idx, int hash_idx,
+ unsigned long modulus_bitlen,
+ unsigned char *out, unsigned long *outlen);
+
+int pkcs_1_pss_decode(const unsigned char *msghash, unsigned long msghashlen,
+ const unsigned char *sig, unsigned long siglen,
+ unsigned long saltlen, int hash_idx,
+ unsigned long modulus_bitlen, int *res);
+#endif /* LTC_PKCS_1 */
+
+/* ===> LTC_PKCS #5 -- Password Based Cryptography <=== */
+#ifdef LTC_PKCS_5
+
+/* Algorithm #1 (old) */
+int pkcs_5_alg1(const unsigned char *password, unsigned long password_len,
+ const unsigned char *salt,
+ int iteration_count, int hash_idx,
+ unsigned char *out, unsigned long *outlen);
+
+/* Algorithm #2 (new) */
+int pkcs_5_alg2(const unsigned char *password, unsigned long password_len,
+ const unsigned char *salt, unsigned long salt_len,
+ int iteration_count, int hash_idx,
+ unsigned char *out, unsigned long *outlen);
+#endif /* LTC_PKCS_5 */
+
+/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_pkcs.h,v $ */
+/* $Revision: 1.8 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* TOMCRYPT_H_ */
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt.h,v $ */
+/* $Revision: 1.21 $ */
+/* $Date: 2006/12/16 19:34:05 $ */
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_argchk.c,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_cipher_descriptor.c
+ Stores the cipher descriptor table, Tom St Denis
+ */
+
+struct ltc_cipher_descriptor cipher_descriptor[TAB_SIZE] = {
+ { NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+LTC_MUTEX_GLOBAL(ltc_cipher_mutex)
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_cipher_descriptor.c,v $ */
+/* $Revision: 1.13 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_cipher_is_valid.c
+ Determine if cipher is valid, Tom St Denis
+ */
+
+/*
+ Test if a cipher index is valid
+ @param idx The index of the cipher to search for
+ @return CRYPT_OK if valid
+ */
+int cipher_is_valid(int idx) {
+ LTC_MUTEX_LOCK(<c_cipher_mutex);
+ if ((idx < 0) || (idx >= TAB_SIZE) || (cipher_descriptor[idx].name == NULL)) {
+ LTC_MUTEX_UNLOCK(<c_cipher_mutex);
+ return CRYPT_INVALID_CIPHER;
+ }
+ LTC_MUTEX_UNLOCK(<c_cipher_mutex);
+ return CRYPT_OK;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_cipher_is_valid.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_find_cipher.c
+ Find a cipher in the descriptor tables, Tom St Denis
+ */
+
+/**
+ Find a registered cipher by name
+ @param name The name of the cipher to look for
+ @return >= 0 if found, -1 if not present
+ */
+int find_cipher(const char *name) {
+ int x;
+
+ LTC_ARGCHK(name != NULL);
+ LTC_MUTEX_LOCK(<c_cipher_mutex);
+ for (x = 0; x < TAB_SIZE; x++) {
+ if ((cipher_descriptor[x].name != NULL) && !XSTRCMP(cipher_descriptor[x].name, name)) {
+ LTC_MUTEX_UNLOCK(<c_cipher_mutex);
+ return x;
+ }
+ }
+ LTC_MUTEX_UNLOCK(<c_cipher_mutex);
+ return -1;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_cipher.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_find_cipher_any.c
+ Find a cipher in the descriptor tables, Tom St Denis
+ */
+
+/**
+ Find a cipher flexibly. First by name then if not present by block and key size
+ @param name The name of the cipher desired
+ @param blocklen The minimum length of the block cipher desired (octets)
+ @param keylen The minimum length of the key size desired (octets)
+ @return >= 0 if found, -1 if not present
+ */
+int find_cipher_any(const char *name, int blocklen, int keylen) {
+ int x;
+
+ LTC_ARGCHK(name != NULL);
+
+ x = find_cipher(name);
+ if (x != -1) return x;
+
+ LTC_MUTEX_LOCK(<c_cipher_mutex);
+ for (x = 0; x < TAB_SIZE; x++) {
+ if (cipher_descriptor[x].name == NULL) {
+ continue;
+ }
+ if ((blocklen <= (int)cipher_descriptor[x].block_length) && (keylen <= (int)cipher_descriptor[x].max_key_length)) {
+ LTC_MUTEX_UNLOCK(<c_cipher_mutex);
+ return x;
+ }
+ }
+ LTC_MUTEX_UNLOCK(<c_cipher_mutex);
+ return -1;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_cipher_any.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_find_cipher_id.c
+ Find cipher by ID, Tom St Denis
+ */
+
+/**
+ Find a cipher by ID number
+ @param ID The ID (not same as index) of the cipher to find
+ @return >= 0 if found, -1 if not present
+ */
+int find_cipher_id(unsigned char ID) {
+ int x;
+
+ LTC_MUTEX_LOCK(<c_cipher_mutex);
+ for (x = 0; x < TAB_SIZE; x++) {
+ if (cipher_descriptor[x].ID == ID) {
+ x = (cipher_descriptor[x].name == NULL) ? -1 : x;
+ LTC_MUTEX_UNLOCK(<c_cipher_mutex);
+ return x;
+ }
+ }
+ LTC_MUTEX_UNLOCK(<c_cipher_mutex);
+ return -1;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_cipher_id.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_find_hash.c
+ Find a hash, Tom St Denis
+ */
+
+/**
+ Find a registered hash by name
+ @param name The name of the hash to look for
+ @return >= 0 if found, -1 if not present
+ */
+int find_hash(const char *name) {
+ int x;
+
+ LTC_ARGCHK(name != NULL);
+ LTC_MUTEX_LOCK(<c_hash_mutex);
+ for (x = 0; x < TAB_SIZE; x++) {
+ if ((hash_descriptor[x].name != NULL) && (XSTRCMP(hash_descriptor[x].name, name) == 0)) {
+ LTC_MUTEX_UNLOCK(<c_hash_mutex);
+ return x;
+ }
+ }
+ LTC_MUTEX_UNLOCK(<c_hash_mutex);
+ return -1;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_find_hash_any.c
+ Find a hash, Tom St Denis
+ */
+
+/**
+ Find a hash flexibly. First by name then if not present by digest size
+ @param name The name of the hash desired
+ @param digestlen The minimum length of the digest size (octets)
+ @return >= 0 if found, -1 if not present
+ */int find_hash_any(const char *name, int digestlen) {
+ int x, y, z;
+
+ LTC_ARGCHK(name != NULL);
+
+ x = find_hash(name);
+ if (x != -1) return x;
+
+ LTC_MUTEX_LOCK(<c_hash_mutex);
+ y = MAXBLOCKSIZE + 1;
+ z = -1;
+ for (x = 0; x < TAB_SIZE; x++) {
+ if (hash_descriptor[x].name == NULL) {
+ continue;
+ }
+ if (((int)hash_descriptor[x].hashsize >= digestlen) && ((int)hash_descriptor[x].hashsize < y)) {
+ z = x;
+ y = hash_descriptor[x].hashsize;
+ }
+ }
+ LTC_MUTEX_UNLOCK(<c_hash_mutex);
+ return z;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash_any.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_find_hash_id.c
+ Find hash by ID, Tom St Denis
+ */
+
+/**
+ Find a hash by ID number
+ @param ID The ID (not same as index) of the hash to find
+ @return >= 0 if found, -1 if not present
+ */
+int find_hash_id(unsigned char ID) {
+ int x;
+
+ LTC_MUTEX_LOCK(<c_hash_mutex);
+ for (x = 0; x < TAB_SIZE; x++) {
+ if (hash_descriptor[x].ID == ID) {
+ x = (hash_descriptor[x].name == NULL) ? -1 : x;
+ LTC_MUTEX_UNLOCK(<c_hash_mutex);
+ return x;
+ }
+ }
+ LTC_MUTEX_UNLOCK(<c_hash_mutex);
+ return -1;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash_id.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_find_hash_oid.c
+ Find a hash, Tom St Denis
+ */
+
+int find_hash_oid(const unsigned long *ID, unsigned long IDlen) {
+ int x;
+
+ LTC_ARGCHK(ID != NULL);
+ LTC_MUTEX_LOCK(<c_hash_mutex);
+ for (x = 0; x < TAB_SIZE; x++) {
+ if ((hash_descriptor[x].name != NULL) && (hash_descriptor[x].OIDlen == IDlen) && !XMEMCMP(hash_descriptor[x].OID, ID, sizeof(unsigned long) * IDlen)) {
+ LTC_MUTEX_UNLOCK(<c_hash_mutex);
+ return x;
+ }
+ }
+ LTC_MUTEX_UNLOCK(<c_hash_mutex);
+ return -1;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash_oid.c,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_find_prng.c
+ Find a PRNG, Tom St Denis
+ */
+
+/**
+ Find a registered PRNG by name
+ @param name The name of the PRNG to look for
+ @return >= 0 if found, -1 if not present
+ */
+int find_prng(const char *name) {
+ int x;
+
+ LTC_ARGCHK(name != NULL);
+ LTC_MUTEX_LOCK(<c_prng_mutex);
+ for (x = 0; x < TAB_SIZE; x++) {
+ if ((prng_descriptor[x].name != NULL) && (XSTRCMP(prng_descriptor[x].name, name) == 0)) {
+ LTC_MUTEX_UNLOCK(<c_prng_mutex);
+ return x;
+ }
+ }
+ LTC_MUTEX_UNLOCK(<c_prng_mutex);
+ return -1;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_prng.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+#include
+
+/**
+ @file crypt_fsa.c
+ LibTomCrypt FULL SPEED AHEAD!, Tom St Denis
+ */
+
+/* format is ltc_mp, cipher_desc, [cipher_desc], NULL, hash_desc, [hash_desc], NULL, prng_desc, [prng_desc], NULL */
+int crypt_fsa(void *mp, ...) {
+ int err;
+ va_list args;
+ void *p;
+
+ va_start(args, mp);
+ if (mp != NULL) {
+ XMEMCPY(<c_mp, mp, sizeof(ltc_mp));
+ }
+
+ while ((p = va_arg(args, void *)) != NULL) {
+ if ((err = register_cipher(p)) != CRYPT_OK) {
+ va_end(args);
+ return err;
+ }
+ }
+
+ while ((p = va_arg(args, void *)) != NULL) {
+ if ((err = register_hash(p)) != CRYPT_OK) {
+ va_end(args);
+ return err;
+ }
+ }
+
+ while ((p = va_arg(args, void *)) != NULL) {
+ if ((err = register_prng(p)) != CRYPT_OK) {
+ va_end(args);
+ return err;
+ }
+ }
+
+ va_end(args);
+ return CRYPT_OK;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_fsa.c,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_hash_descriptor.c
+ Stores the hash descriptor table, Tom St Denis
+ */
+
+struct ltc_hash_descriptor hash_descriptor[TAB_SIZE] = {
+ { NULL, 0, 0, 0, { 0 }, 0, NULL, NULL, NULL, NULL, NULL }
+};
+
+LTC_MUTEX_GLOBAL(ltc_hash_mutex)
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_hash_descriptor.c,v $ */
+/* $Revision: 1.10 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_hash_is_valid.c
+ Determine if hash is valid, Tom St Denis
+ */
+
+/*
+ Test if a hash index is valid
+ @param idx The index of the hash to search for
+ @return CRYPT_OK if valid
+ */
+int hash_is_valid(int idx) {
+ LTC_MUTEX_LOCK(<c_hash_mutex);
+ if ((idx < 0) || (idx >= TAB_SIZE) || (hash_descriptor[idx].name == NULL)) {
+ LTC_MUTEX_UNLOCK(<c_hash_mutex);
+ return CRYPT_INVALID_HASH;
+ }
+ LTC_MUTEX_UNLOCK(<c_hash_mutex);
+ return CRYPT_OK;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_hash_is_valid.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+ltc_math_descriptor ltc_mp;
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_prng_descriptor.c
+ Stores the PRNG descriptors, Tom St Denis
+ */
+struct ltc_prng_descriptor prng_descriptor[TAB_SIZE] = {
+ { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+LTC_MUTEX_GLOBAL(ltc_prng_mutex)
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_prng_descriptor.c,v $ */
+/* $Revision: 1.8 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_prng_is_valid.c
+ Determine if PRNG is valid, Tom St Denis
+ */
+
+/*
+ Test if a PRNG index is valid
+ @param idx The index of the PRNG to search for
+ @return CRYPT_OK if valid
+ */
+int prng_is_valid(int idx) {
+ LTC_MUTEX_LOCK(<c_prng_mutex);
+ if ((idx < 0) || (idx >= TAB_SIZE) || (prng_descriptor[idx].name == NULL)) {
+ LTC_MUTEX_UNLOCK(<c_prng_mutex);
+ return CRYPT_INVALID_PRNG;
+ }
+ LTC_MUTEX_UNLOCK(<c_prng_mutex);
+ return CRYPT_OK;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_prng_is_valid.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_register_cipher.c
+ Register a cipher, Tom St Denis
+ */
+
+/**
+ Register a cipher with the descriptor table
+ @param cipher The cipher you wish to register
+ @return value >= 0 if successfully added (or already present), -1 if unsuccessful
+ */
+int register_cipher(const struct ltc_cipher_descriptor *cipher) {
+ int x;
+
+ LTC_ARGCHK(cipher != NULL);
+
+ /* is it already registered? */
+ LTC_MUTEX_LOCK(<c_cipher_mutex);
+ for (x = 0; x < TAB_SIZE; x++) {
+ if ((cipher_descriptor[x].name != NULL) && (cipher_descriptor[x].ID == cipher->ID)) {
+ LTC_MUTEX_UNLOCK(<c_cipher_mutex);
+ return x;
+ }
+ }
+
+ /* find a blank spot */
+ for (x = 0; x < TAB_SIZE; x++) {
+ if (cipher_descriptor[x].name == NULL) {
+ XMEMCPY(&cipher_descriptor[x], cipher, sizeof(struct ltc_cipher_descriptor));
+ LTC_MUTEX_UNLOCK(<c_cipher_mutex);
+ return x;
+ }
+ }
+
+ /* no spot */
+ LTC_MUTEX_UNLOCK(<c_cipher_mutex);
+ return -1;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_register_cipher.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_register_hash.c
+ Register a HASH, Tom St Denis
+ */
+
+/**
+ Register a hash with the descriptor table
+ @param hash The hash you wish to register
+ @return value >= 0 if successfully added (or already present), -1 if unsuccessful
+ */
+int register_hash(const struct ltc_hash_descriptor *hash) {
+ int x;
+
+ LTC_ARGCHK(hash != NULL);
+
+ /* is it already registered? */
+ LTC_MUTEX_LOCK(<c_hash_mutex);
+ for (x = 0; x < TAB_SIZE; x++) {
+ if (XMEMCMP(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor)) == 0) {
+ LTC_MUTEX_UNLOCK(<c_hash_mutex);
+ return x;
+ }
+ }
+
+ /* find a blank spot */
+ for (x = 0; x < TAB_SIZE; x++) {
+ if (hash_descriptor[x].name == NULL) {
+ XMEMCPY(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor));
+ LTC_MUTEX_UNLOCK(<c_hash_mutex);
+ return x;
+ }
+ }
+
+ /* no spot */
+ LTC_MUTEX_UNLOCK(<c_hash_mutex);
+ return -1;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_register_hash.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_register_prng.c
+ Register a PRNG, Tom St Denis
+ */
+
+/**
+ Register a PRNG with the descriptor table
+ @param prng The PRNG you wish to register
+ @return value >= 0 if successfully added (or already present), -1 if unsuccessful
+ */
+int register_prng(const struct ltc_prng_descriptor *prng) {
+ int x;
+
+ LTC_ARGCHK(prng != NULL);
+
+ /* is it already registered? */
+ LTC_MUTEX_LOCK(<c_prng_mutex);
+ for (x = 0; x < TAB_SIZE; x++) {
+ if (XMEMCMP(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor)) == 0) {
+ LTC_MUTEX_UNLOCK(<c_prng_mutex);
+ return x;
+ }
+ }
+
+ /* find a blank spot */
+ for (x = 0; x < TAB_SIZE; x++) {
+ if (prng_descriptor[x].name == NULL) {
+ XMEMCPY(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor));
+ LTC_MUTEX_UNLOCK(<c_prng_mutex);
+ return x;
+ }
+ }
+
+ /* no spot */
+ LTC_MUTEX_UNLOCK(<c_prng_mutex);
+ return -1;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_register_prng.c,v $ */
+/* $Revision: 1.8 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_unregister_cipher.c
+ Unregister a cipher, Tom St Denis
+ */
+
+/**
+ Unregister a cipher from the descriptor table
+ @param cipher The cipher descriptor to remove
+ @return CRYPT_OK on success
+ */
+int unregister_cipher(const struct ltc_cipher_descriptor *cipher) {
+ int x;
+
+ LTC_ARGCHK(cipher != NULL);
+
+ /* is it already registered? */
+ LTC_MUTEX_LOCK(<c_cipher_mutex);
+ for (x = 0; x < TAB_SIZE; x++) {
+ if (XMEMCMP(&cipher_descriptor[x], cipher, sizeof(struct ltc_cipher_descriptor)) == 0) {
+ cipher_descriptor[x].name = NULL;
+ cipher_descriptor[x].ID = 255;
+ LTC_MUTEX_UNLOCK(<c_cipher_mutex);
+ return CRYPT_OK;
+ }
+ }
+ LTC_MUTEX_UNLOCK(<c_cipher_mutex);
+ return CRYPT_ERROR;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_unregister_cipher.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_unregister_hash.c
+ Unregister a hash, Tom St Denis
+ */
+
+/**
+ Unregister a hash from the descriptor table
+ @param hash The hash descriptor to remove
+ @return CRYPT_OK on success
+ */
+int unregister_hash(const struct ltc_hash_descriptor *hash) {
+ int x;
+
+ LTC_ARGCHK(hash != NULL);
+
+ /* is it already registered? */
+ LTC_MUTEX_LOCK(<c_hash_mutex);
+ for (x = 0; x < TAB_SIZE; x++) {
+ if (XMEMCMP(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor)) == 0) {
+ hash_descriptor[x].name = NULL;
+ LTC_MUTEX_UNLOCK(<c_hash_mutex);
+ return CRYPT_OK;
+ }
+ }
+ LTC_MUTEX_UNLOCK(<c_hash_mutex);
+ return CRYPT_ERROR;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_unregister_hash.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file crypt_unregister_prng.c
+ Unregister a PRNG, Tom St Denis
+ */
+
+/**
+ Unregister a PRNG from the descriptor table
+ @param prng The PRNG descriptor to remove
+ @return CRYPT_OK on success
+ */
+int unregister_prng(const struct ltc_prng_descriptor *prng) {
+ int x;
+
+ LTC_ARGCHK(prng != NULL);
+
+ /* is it already registered? */
+ LTC_MUTEX_LOCK(<c_prng_mutex);
+ for (x = 0; x < TAB_SIZE; x++) {
+ if (XMEMCMP(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor)) != 0) {
+ prng_descriptor[x].name = NULL;
+ LTC_MUTEX_UNLOCK(<c_prng_mutex);
+ return CRYPT_OK;
+ }
+ }
+ LTC_MUTEX_UNLOCK(<c_prng_mutex);
+ return CRYPT_ERROR;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_unregister_prng.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_decode_bit_string.c
+ ASN.1 DER, encode a BIT STRING, Tom St Denis
+ */
+
+
+#ifdef LTC_DER
+
+/**
+ Store a BIT STRING
+ @param in The DER encoded BIT STRING
+ @param inlen The size of the DER BIT STRING
+ @param out [out] The array of bits stored (one per char)
+ @param outlen [in/out] The number of bits stored
+ @return CRYPT_OK if successful
+ */
+int der_decode_bit_string(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen) {
+ unsigned long dlen, blen, x, y;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* packet must be at least 4 bytes */
+ if (inlen < 4) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* check for 0x03 */
+ if ((in[0] & 0x1F) != 0x03) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* offset in the data */
+ x = 1;
+
+ /* get the length of the data */
+ if (in[x] & 0x80) {
+ /* long format get number of length bytes */
+ y = in[x++] & 0x7F;
+
+ /* invalid if 0 or > 2 */
+ if ((y == 0) || (y > 2)) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* read the data len */
+ dlen = 0;
+ while (y--) {
+ dlen = (dlen << 8) | (unsigned long)in[x++];
+ }
+ } else {
+ /* short format */
+ dlen = in[x++] & 0x7F;
+ }
+
+ /* is the data len too long or too short? */
+ if ((dlen == 0) || (dlen + x > inlen)) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* get padding count */
+ blen = ((dlen - 1) << 3) - (in[x++] & 7);
+
+ /* too many bits? */
+ if (blen > *outlen) {
+ *outlen = blen;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ /* decode/store the bits */
+ for (y = 0; y < blen; y++) {
+ out[y] = (in[x] & (1 << (7 - (y & 7)))) ? 1 : 0;
+ if ((y & 7) == 7) {
+ ++x;
+ }
+ }
+
+ /* we done */
+ *outlen = blen;
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/bit/der_decode_bit_string.c,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_decode_boolean.c
+ ASN.1 DER, decode a BOOLEAN, Tom St Denis
+ */
+
+
+#ifdef LTC_DER
+
+/**
+ Read a BOOLEAN
+ @param in The destination for the DER encoded BOOLEAN
+ @param inlen The size of the DER BOOLEAN
+ @param out [out] The boolean to decode
+ @return CRYPT_OK if successful
+ */
+int der_decode_boolean(const unsigned char *in, unsigned long inlen,
+ int *out) {
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+
+ if ((inlen != 3) || (in[0] != 0x01) || (in[1] != 0x01) || ((in[2] != 0x00) && (in[2] != 0xFF))) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ *out = (in[2] == 0xFF) ? 1 : 0;
+
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/boolean/der_decode_boolean.c,v $ */
+/* $Revision: 1.2 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_decode_choice.c
+ ASN.1 DER, decode a CHOICE, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/**
+ Decode a CHOICE
+ @param in The DER encoded input
+ @param inlen [in/out] The size of the input and resulting size of read type
+ @param list The list of items to decode
+ @param outlen The number of items in the list
+ @return CRYPT_OK on success
+ */
+int der_decode_choice(const unsigned char *in, unsigned long *inlen,
+ ltc_asn1_list *list, unsigned long outlen) {
+ unsigned long size, x, z;
+ void *data;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(inlen != NULL);
+ LTC_ARGCHK(list != NULL);
+
+ /* get blk size */
+ if (*inlen < 2) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* set all of the "used" flags to zero */
+ for (x = 0; x < outlen; x++) {
+ list[x].used = 0;
+ }
+
+ /* now scan until we have a winner */
+ for (x = 0; x < outlen; x++) {
+ size = list[x].size;
+ data = list[x].data;
+
+ switch (list[x].type) {
+ case LTC_ASN1_INTEGER:
+ if (der_decode_integer(in, *inlen, data) == CRYPT_OK) {
+ if (der_length_integer(data, &z) == CRYPT_OK) {
+ list[x].used = 1;
+ *inlen = z;
+ return CRYPT_OK;
+ }
+ }
+ break;
+
+ case LTC_ASN1_SHORT_INTEGER:
+ if (der_decode_short_integer(in, *inlen, data) == CRYPT_OK) {
+ if (der_length_short_integer(size, &z) == CRYPT_OK) {
+ list[x].used = 1;
+ *inlen = z;
+ return CRYPT_OK;
+ }
+ }
+ break;
+
+ case LTC_ASN1_BIT_STRING:
+ if (der_decode_bit_string(in, *inlen, data, &size) == CRYPT_OK) {
+ if (der_length_bit_string(size, &z) == CRYPT_OK) {
+ list[x].used = 1;
+ list[x].size = size;
+ *inlen = z;
+ return CRYPT_OK;
+ }
+ }
+ break;
+
+ case LTC_ASN1_OCTET_STRING:
+ if (der_decode_octet_string(in, *inlen, data, &size) == CRYPT_OK) {
+ if (der_length_octet_string(size, &z) == CRYPT_OK) {
+ list[x].used = 1;
+ list[x].size = size;
+ *inlen = z;
+ return CRYPT_OK;
+ }
+ }
+ break;
+
+ case LTC_ASN1_NULL:
+ if ((*inlen == 2) && (in[x] == 0x05) && (in[x + 1] == 0x00)) {
+ *inlen = 2;
+ list[x].used = 1;
+ return CRYPT_OK;
+ }
+ break;
+
+ case LTC_ASN1_OBJECT_IDENTIFIER:
+ if (der_decode_object_identifier(in, *inlen, data, &size) == CRYPT_OK) {
+ if (der_length_object_identifier(data, size, &z) == CRYPT_OK) {
+ list[x].used = 1;
+ list[x].size = size;
+ *inlen = z;
+ return CRYPT_OK;
+ }
+ }
+ break;
+
+ case LTC_ASN1_IA5_STRING:
+ if (der_decode_ia5_string(in, *inlen, data, &size) == CRYPT_OK) {
+ if (der_length_ia5_string(data, size, &z) == CRYPT_OK) {
+ list[x].used = 1;
+ list[x].size = size;
+ *inlen = z;
+ return CRYPT_OK;
+ }
+ }
+ break;
+
+
+ case LTC_ASN1_PRINTABLE_STRING:
+ if (der_decode_printable_string(in, *inlen, data, &size) == CRYPT_OK) {
+ if (der_length_printable_string(data, size, &z) == CRYPT_OK) {
+ list[x].used = 1;
+ list[x].size = size;
+ *inlen = z;
+ return CRYPT_OK;
+ }
+ }
+ break;
+
+ case LTC_ASN1_UTF8_STRING:
+ if (der_decode_utf8_string(in, *inlen, data, &size) == CRYPT_OK) {
+ if (der_length_utf8_string(data, size, &z) == CRYPT_OK) {
+ list[x].used = 1;
+ list[x].size = size;
+ *inlen = z;
+ return CRYPT_OK;
+ }
+ }
+ break;
+
+ case LTC_ASN1_UTCTIME:
+ z = *inlen;
+ if (der_decode_utctime(in, &z, data) == CRYPT_OK) {
+ list[x].used = 1;
+ *inlen = z;
+ return CRYPT_OK;
+ }
+ break;
+
+ case LTC_ASN1_SET:
+ case LTC_ASN1_SETOF:
+ case LTC_ASN1_SEQUENCE:
+ if (der_decode_sequence(in, *inlen, data, size) == CRYPT_OK) {
+ if (der_length_sequence(data, size, &z) == CRYPT_OK) {
+ list[x].used = 1;
+ *inlen = z;
+ return CRYPT_OK;
+ }
+ }
+ break;
+
+ default:
+ return CRYPT_INVALID_ARG;
+ }
+ }
+
+ return CRYPT_INVALID_PACKET;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/choice/der_decode_choice.c,v $ */
+/* $Revision: 1.9 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_decode_ia5_string.c
+ ASN.1 DER, encode a IA5 STRING, Tom St Denis
+ */
+
+
+#ifdef LTC_DER
+
+/**
+ Store a IA5 STRING
+ @param in The DER encoded IA5 STRING
+ @param inlen The size of the DER IA5 STRING
+ @param out [out] The array of octets stored (one per char)
+ @param outlen [in/out] The number of octets stored
+ @return CRYPT_OK if successful
+ */
+int der_decode_ia5_string(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen) {
+ unsigned long x, y, len;
+ int t;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* must have header at least */
+ if (inlen < 2) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* check for 0x16 */
+ if ((in[0] & 0x1F) != 0x16) {
+ return CRYPT_INVALID_PACKET;
+ }
+ x = 1;
+
+ /* decode the length */
+ if (in[x] & 0x80) {
+ /* valid # of bytes in length are 1,2,3 */
+ y = in[x] & 0x7F;
+ if ((y == 0) || (y > 3) || ((x + y) > inlen)) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* read the length in */
+ len = 0;
+ ++x;
+ while (y--) {
+ len = (len << 8) | in[x++];
+ }
+ } else {
+ len = in[x++] & 0x7F;
+ }
+
+ /* is it too long? */
+ if (len > *outlen) {
+ *outlen = len;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ if (len + x > inlen) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* read the data */
+ for (y = 0; y < len; y++) {
+ t = der_ia5_value_decode(in[x++]);
+ if (t == -1) {
+ return CRYPT_INVALID_ARG;
+ }
+ out[y] = t;
+ }
+
+ *outlen = y;
+
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/ia5/der_decode_ia5_string.c,v $ */
+/* $Revision: 1.4 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_decode_integer.c
+ ASN.1 DER, decode an integer, Tom St Denis
+ */
+
+
+#ifdef LTC_DER
+
+/**
+ Read a mp_int integer
+ @param in The DER encoded data
+ @param inlen Size of DER encoded data
+ @param num The first mp_int to decode
+ @return CRYPT_OK if successful
+ */
+int der_decode_integer(const unsigned char *in, unsigned long inlen, void *num) {
+ unsigned long x, y, z;
+ int err;
+
+ LTC_ARGCHK(num != NULL);
+ LTC_ARGCHK(in != NULL);
+
+ /* min DER INTEGER is 0x02 01 00 == 0 */
+ if (inlen < (1 + 1 + 1)) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* ok expect 0x02 when we AND with 0001 1111 [1F] */
+ x = 0;
+ if ((in[x++] & 0x1F) != 0x02) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* now decode the len stuff */
+ z = in[x++];
+
+ if ((z & 0x80) == 0x00) {
+ /* short form */
+
+ /* will it overflow? */
+ if (x + z > inlen) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* no so read it */
+ if ((err = mp_read_unsigned_bin(num, (unsigned char *)in + x, z)) != CRYPT_OK) {
+ return err;
+ }
+ } else {
+ /* long form */
+ z &= 0x7F;
+
+ /* will number of length bytes overflow? (or > 4) */
+ if (((x + z) > inlen) || (z > 4) || (z == 0)) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* now read it in */
+ y = 0;
+ while (z--) {
+ y = ((unsigned long)(in[x++])) | (y << 8);
+ }
+
+ /* now will reading y bytes overrun? */
+ if ((x + y) > inlen) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* no so read it */
+ if ((err = mp_read_unsigned_bin(num, (unsigned char *)in + x, y)) != CRYPT_OK) {
+ return err;
+ }
+ }
+
+ /* see if it's negative */
+ if (in[x] & 0x80) {
+ void *tmp;
+ if (mp_init(&tmp) != CRYPT_OK) {
+ return CRYPT_MEM;
+ }
+
+ if ((mp_2expt(tmp, mp_count_bits(num)) != CRYPT_OK) || (mp_sub(num, tmp, num) != CRYPT_OK)) {
+ mp_clear(tmp);
+ return CRYPT_MEM;
+ }
+ mp_clear(tmp);
+ }
+
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/integer/der_decode_integer.c,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_decode_object_identifier.c
+ ASN.1 DER, Decode Object Identifier, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/**
+ Decode OID data and store the array of integers in words
+ @param in The OID DER encoded data
+ @param inlen The length of the OID data
+ @param words [out] The destination of the OID words
+ @param outlen [in/out] The number of OID words
+ @return CRYPT_OK if successful
+ */
+int der_decode_object_identifier(const unsigned char *in, unsigned long inlen,
+ unsigned long *words, unsigned long *outlen) {
+ unsigned long x, y, t, len;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(words != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* header is at least 3 bytes */
+ if (inlen < 3) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* must be room for at least two words */
+ if (*outlen < 2) {
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ /* decode the packet header */
+ x = 0;
+ if ((in[x++] & 0x1F) != 0x06) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* get the length */
+ if (in[x] < 128) {
+ len = in[x++];
+ } else {
+ if ((in[x] < 0x81) || (in[x] > 0x82)) {
+ return CRYPT_INVALID_PACKET;
+ }
+ y = in[x++] & 0x7F;
+ len = 0;
+ while (y--) {
+ len = (len << 8) | (unsigned long)in[x++];
+ }
+ }
+
+ if ((len < 1) || ((len + x) > inlen)) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* decode words */
+ y = 0;
+ t = 0;
+ while (len--) {
+ t = (t << 7) | (in[x] & 0x7F);
+ if (!(in[x++] & 0x80)) {
+ /* store t */
+ if (y >= *outlen) {
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+ if (y == 0) {
+ words[0] = t / 40;
+ words[1] = t % 40;
+ y = 2;
+ } else {
+ words[y++] = t;
+ }
+ t = 0;
+ }
+ }
+
+ *outlen = y;
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/object_identifier/der_decode_object_identifier.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_decode_octet_string.c
+ ASN.1 DER, encode a OCTET STRING, Tom St Denis
+ */
+
+
+#ifdef LTC_DER
+
+/**
+ Store a OCTET STRING
+ @param in The DER encoded OCTET STRING
+ @param inlen The size of the DER OCTET STRING
+ @param out [out] The array of octets stored (one per char)
+ @param outlen [in/out] The number of octets stored
+ @return CRYPT_OK if successful
+ */
+int der_decode_octet_string(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen) {
+ unsigned long x, y, len;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* must have header at least */
+ if (inlen < 2) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* check for 0x04 */
+ if ((in[0] & 0x1F) != 0x04) {
+ return CRYPT_INVALID_PACKET;
+ }
+ x = 1;
+
+ /* decode the length */
+ if (in[x] & 0x80) {
+ /* valid # of bytes in length are 1,2,3 */
+ y = in[x] & 0x7F;
+ if ((y == 0) || (y > 3) || ((x + y) > inlen)) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* read the length in */
+ len = 0;
+ ++x;
+ while (y--) {
+ len = (len << 8) | in[x++];
+ }
+ } else {
+ len = in[x++] & 0x7F;
+ }
+
+ /* is it too long? */
+ if (len > *outlen) {
+ *outlen = len;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ if (len + x > inlen) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* read the data */
+ for (y = 0; y < len; y++) {
+ out[y] = in[x++];
+ }
+
+ *outlen = y;
+
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/octet/der_decode_octet_string.c,v $ */
+/* $Revision: 1.4 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_decode_printable_string.c
+ ASN.1 DER, encode a printable STRING, Tom St Denis
+ */
+
+
+#ifdef LTC_DER
+
+/**
+ Store a printable STRING
+ @param in The DER encoded printable STRING
+ @param inlen The size of the DER printable STRING
+ @param out [out] The array of octets stored (one per char)
+ @param outlen [in/out] The number of octets stored
+ @return CRYPT_OK if successful
+ */
+int der_decode_printable_string(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen) {
+ unsigned long x, y, len;
+ int t;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* must have header at least */
+ if (inlen < 2) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* check for 0x13 */
+ if ((in[0] & 0x1F) != 0x13) {
+ return CRYPT_INVALID_PACKET;
+ }
+ x = 1;
+
+ /* decode the length */
+ if (in[x] & 0x80) {
+ /* valid # of bytes in length are 1,2,3 */
+ y = in[x] & 0x7F;
+ if ((y == 0) || (y > 3) || ((x + y) > inlen)) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* read the length in */
+ len = 0;
+ ++x;
+ while (y--) {
+ len = (len << 8) | in[x++];
+ }
+ } else {
+ len = in[x++] & 0x7F;
+ }
+
+ /* is it too long? */
+ if (len > *outlen) {
+ *outlen = len;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ if (len + x > inlen) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* read the data */
+ for (y = 0; y < len; y++) {
+ t = der_printable_value_decode(in[x++]);
+ if (t == -1) {
+ return CRYPT_INVALID_ARG;
+ }
+ out[y] = t;
+ }
+
+ *outlen = y;
+
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/printable_string/der_decode_printable_string.c,v $ */
+/* $Revision: 1.4 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+#include
+
+
+/**
+ @file der_decode_sequence_ex.c
+ ASN.1 DER, decode a SEQUENCE, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/**
+ Decode a SEQUENCE
+ @param in The DER encoded input
+ @param inlen The size of the input
+ @param list The list of items to decode
+ @param outlen The number of items in the list
+ @param ordered Search an unordered or ordered list
+ @return CRYPT_OK on success
+ */
+int der_decode_sequence_ex(const unsigned char *in, unsigned long inlen,
+ ltc_asn1_list *list, unsigned long outlen, int ordered) {
+ int err, type;
+ unsigned long size, x, y, z, i, blksize;
+ void *data;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(list != NULL);
+
+ /* get blk size */
+ if (inlen < 2) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* sequence type? We allow 0x30 SEQUENCE and 0x31 SET since fundamentally they're the same structure */
+ x = 0;
+ if ((in[x] != 0x30) && (in[x] != 0x31)) {
+ return CRYPT_INVALID_PACKET;
+ }
+ ++x;
+
+ if (in[x] < 128) {
+ blksize = in[x++];
+ } else if (in[x] & 0x80) {
+ if ((in[x] < 0x81) || (in[x] > 0x83)) {
+ return CRYPT_INVALID_PACKET;
+ }
+ y = in[x++] & 0x7F;
+
+ /* would reading the len bytes overrun? */
+ if (x + y > inlen) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* read len */
+ blksize = 0;
+ while (y--) {
+ blksize = (blksize << 8) | (unsigned long)in[x++];
+ }
+ }
+
+ /* would this blksize overflow? */
+ if (x + blksize > inlen) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* mark all as unused */
+ for (i = 0; i < outlen; i++) {
+ list[i].used = 0;
+ }
+
+ /* ok read data */
+ inlen = blksize;
+ for (i = 0; i < outlen; i++) {
+ z = 0;
+ type = list[i].type;
+ size = list[i].size;
+ data = list[i].data;
+ if (!ordered && (list[i].used == 1)) {
+ continue;
+ }
+
+ if (type == LTC_ASN1_EOL) {
+ break;
+ }
+
+ switch (type) {
+ case LTC_ASN1_BOOLEAN:
+ z = inlen;
+ if ((err = der_decode_boolean(in + x, z, ((int *)data))) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ if ((err = der_length_boolean(&z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ break;
+
+ case LTC_ASN1_INTEGER:
+ z = inlen;
+ if ((err = der_decode_integer(in + x, z, data)) != CRYPT_OK) {
+ if (!ordered) {
+ continue;
+ }
+ goto LBL_ERR;
+ }
+ if ((err = der_length_integer(data, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ break;
+
+ case LTC_ASN1_SHORT_INTEGER:
+ z = inlen;
+ if ((err = der_decode_short_integer(in + x, z, data)) != CRYPT_OK) {
+ if (!ordered) {
+ continue;
+ }
+ goto LBL_ERR;
+ }
+ if ((err = der_length_short_integer(((unsigned long *)data)[0], &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ break;
+
+ case LTC_ASN1_BIT_STRING:
+ z = inlen;
+ if ((err = der_decode_bit_string(in + x, z, data, &size)) != CRYPT_OK) {
+ if (!ordered) {
+ continue;
+ }
+ goto LBL_ERR;
+ }
+ list[i].size = size;
+ if ((err = der_length_bit_string(size, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ break;
+
+ case LTC_ASN1_OCTET_STRING:
+ z = inlen;
+ if ((err = der_decode_octet_string(in + x, z, data, &size)) != CRYPT_OK) {
+ if (!ordered) {
+ continue;
+ }
+ goto LBL_ERR;
+ }
+ list[i].size = size;
+ if ((err = der_length_octet_string(size, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ break;
+
+ case LTC_ASN1_NULL:
+ if ((inlen < 2) || (in[x] != 0x05) || (in[x + 1] != 0x00)) {
+ if (!ordered) {
+ continue;
+ }
+ err = CRYPT_INVALID_PACKET;
+ goto LBL_ERR;
+ }
+ z = 2;
+ break;
+
+ case LTC_ASN1_OBJECT_IDENTIFIER:
+ z = inlen;
+ if ((err = der_decode_object_identifier(in + x, z, data, &size)) != CRYPT_OK) {
+ if (!ordered) {
+ continue;
+ }
+ goto LBL_ERR;
+ }
+ list[i].size = size;
+ if ((err = der_length_object_identifier(data, size, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ break;
+
+ case LTC_ASN1_IA5_STRING:
+ z = inlen;
+ if ((err = der_decode_ia5_string(in + x, z, data, &size)) != CRYPT_OK) {
+ if (!ordered) {
+ continue;
+ }
+ goto LBL_ERR;
+ }
+ list[i].size = size;
+ if ((err = der_length_ia5_string(data, size, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ break;
+
+
+ case LTC_ASN1_PRINTABLE_STRING:
+ z = inlen;
+ if ((err = der_decode_printable_string(in + x, z, data, &size)) != CRYPT_OK) {
+ if (!ordered) {
+ continue;
+ }
+ goto LBL_ERR;
+ }
+ list[i].size = size;
+ if ((err = der_length_printable_string(data, size, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ break;
+
+ case LTC_ASN1_UTF8_STRING:
+ z = inlen;
+ if ((err = der_decode_utf8_string(in + x, z, data, &size)) != CRYPT_OK) {
+ if (!ordered) {
+ continue;
+ }
+ goto LBL_ERR;
+ }
+ list[i].size = size;
+ if ((err = der_length_utf8_string(data, size, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ break;
+
+ case LTC_ASN1_UTCTIME:
+ z = inlen;
+ if ((err = der_decode_utctime(in + x, &z, data)) != CRYPT_OK) {
+ if (!ordered) {
+ continue;
+ }
+ goto LBL_ERR;
+ }
+ break;
+
+ case LTC_ASN1_SET:
+ z = inlen;
+ if ((err = der_decode_set(in + x, z, data, size)) != CRYPT_OK) {
+ if (!ordered) {
+ continue;
+ }
+ goto LBL_ERR;
+ }
+ if ((err = der_length_sequence(data, size, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ break;
+
+ case LTC_ASN1_SETOF:
+ case LTC_ASN1_SEQUENCE:
+ /* detect if we have the right type */
+ if (((type == LTC_ASN1_SETOF) && ((in[x] & 0x3F) != 0x31)) || ((type == LTC_ASN1_SEQUENCE) && ((in[x] & 0x3F) != 0x30))) {
+ err = CRYPT_INVALID_PACKET;
+ goto LBL_ERR;
+ }
+
+ z = inlen;
+ if ((err = der_decode_sequence(in + x, z, data, size)) != CRYPT_OK) {
+ if (!ordered) {
+ continue;
+ }
+ goto LBL_ERR;
+ }
+ if ((err = der_length_sequence(data, size, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ break;
+
+
+ case LTC_ASN1_CHOICE:
+ z = inlen;
+ if ((err = der_decode_choice(in + x, &z, data, size)) != CRYPT_OK) {
+ if (!ordered) {
+ continue;
+ }
+ goto LBL_ERR;
+ }
+ break;
+
+ default:
+ err = CRYPT_INVALID_ARG;
+ goto LBL_ERR;
+ }
+ x += z;
+ inlen -= z;
+ list[i].used = 1;
+ if (!ordered) {
+ /* restart the decoder */
+ i = -1;
+ }
+ }
+
+ for (i = 0; i < outlen; i++) {
+ if (list[i].used == 0) {
+ err = CRYPT_INVALID_PACKET;
+ goto LBL_ERR;
+ }
+ }
+ err = CRYPT_OK;
+
+LBL_ERR:
+ return err;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_ex.c,v $ */
+/* $Revision: 1.16 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_decode_sequence_flexi.c
+ ASN.1 DER, decode an array of ASN.1 types with a flexi parser, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+static unsigned long fetch_length(const unsigned char *in, unsigned long inlen) {
+ unsigned long x, y, z;
+
+ y = 0;
+
+ /* skip type and read len */
+ if (inlen < 2) {
+ return 0xFFFFFFFF;
+ }
+ ++in;
+ ++y;
+
+ /* read len */
+ x = *in++;
+ ++y;
+
+ /* <128 means literal */
+ if (x < 128) {
+ return x + y;
+ }
+ x &= 0x7F; /* the lower 7 bits are the length of the length */
+ inlen -= 2;
+
+ /* len means len of len! */
+ if ((x == 0) || (x > 4) || (x > inlen)) {
+ return 0xFFFFFFFF;
+ }
+
+ y += x;
+ z = 0;
+ while (x--) {
+ z = (z << 8) | ((unsigned long)*in);
+ ++in;
+ }
+ return z + y;
+}
+
+/**
+ ASN.1 DER Flexi(ble) decoder will decode arbitrary DER packets and create a linked list of the decoded elements.
+ @param in The input buffer
+ @param inlen [in/out] The length of the input buffer and on output the amount of decoded data
+ @param out [out] A pointer to the linked list
+ @return CRYPT_OK on success.
+ */
+int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out) {
+ ltc_asn1_list *l;
+ unsigned long err, type, len, totlen, x, y;
+ void *realloc_tmp;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(inlen != NULL);
+ LTC_ARGCHK(out != NULL);
+
+ l = NULL;
+ totlen = 0;
+
+ /* scan the input and and get lengths and what not */
+ while (*inlen) {
+ /* read the type byte */
+ type = *in;
+
+ /* fetch length */
+ len = fetch_length(in, *inlen);
+ if (len > *inlen) {
+ err = CRYPT_INVALID_PACKET;
+ goto error;
+ }
+
+ /* alloc new link */
+ if (l == NULL) {
+ l = XCALLOC(1, sizeof(*l));
+ if (l == NULL) {
+ err = CRYPT_MEM;
+ goto error;
+ }
+ } else {
+ l->next = XCALLOC(1, sizeof(*l));
+ if (l->next == NULL) {
+ err = CRYPT_MEM;
+ goto error;
+ }
+ l->next->prev = l;
+ l = l->next;
+ }
+
+ /* now switch on type */
+ switch (type) {
+ case 0x01: /* BOOLEAN */
+ l->type = LTC_ASN1_BOOLEAN;
+ l->size = 1;
+ l->data = XCALLOC(1, sizeof(int));
+
+ if ((err = der_decode_boolean(in, *inlen, l->data)) != CRYPT_OK) {
+ goto error;
+ }
+
+ if ((err = der_length_boolean(&len)) != CRYPT_OK) {
+ goto error;
+ }
+ break;
+
+ case 0x02: /* INTEGER */
+ /* init field */
+ l->type = LTC_ASN1_INTEGER;
+ l->size = 1;
+ if ((err = mp_init(&l->data)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* decode field */
+ if ((err = der_decode_integer(in, *inlen, l->data)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* calc length of object */
+ if ((err = der_length_integer(l->data, &len)) != CRYPT_OK) {
+ goto error;
+ }
+ break;
+
+ case 0x03: /* BIT */
+ /* init field */
+ l->type = LTC_ASN1_BIT_STRING;
+ l->size = len * 8; /* *8 because we store decoded bits one per char and they are encoded 8 per char. */
+
+ if ((l->data = XCALLOC(1, l->size)) == NULL) {
+ err = CRYPT_MEM;
+ goto error;
+ }
+
+ if ((err = der_decode_bit_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
+ goto error;
+ }
+
+ if ((err = der_length_bit_string(l->size, &len)) != CRYPT_OK) {
+ goto error;
+ }
+ break;
+
+ case 0x04: /* OCTET */
+
+ /* init field */
+ l->type = LTC_ASN1_OCTET_STRING;
+ l->size = len;
+
+ if ((l->data = XCALLOC(1, l->size)) == NULL) {
+ err = CRYPT_MEM;
+ goto error;
+ }
+
+ if ((err = der_decode_octet_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
+ goto error;
+ }
+
+ if ((err = der_length_octet_string(l->size, &len)) != CRYPT_OK) {
+ goto error;
+ }
+ break;
+
+ case 0x05: /* NULL */
+
+ /* valid NULL is 0x05 0x00 */
+ if ((in[0] != 0x05) || (in[1] != 0x00)) {
+ err = CRYPT_INVALID_PACKET;
+ goto error;
+ }
+
+ /* simple to store ;-) */
+ l->type = LTC_ASN1_NULL;
+ l->data = NULL;
+ l->size = 0;
+ len = 2;
+
+ break;
+
+ case 0x06: /* OID */
+
+ /* init field */
+ l->type = LTC_ASN1_OBJECT_IDENTIFIER;
+ l->size = len;
+
+ if ((l->data = XCALLOC(len, sizeof(unsigned long))) == NULL) {
+ err = CRYPT_MEM;
+ goto error;
+ }
+
+ if ((err = der_decode_object_identifier(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
+ goto error;
+ }
+
+ if ((err = der_length_object_identifier(l->data, l->size, &len)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* resize it to save a bunch of mem */
+ if ((realloc_tmp = XREALLOC(l->data, l->size * sizeof(unsigned long))) == NULL) {
+ /* out of heap but this is not an error */
+ break;
+ }
+ l->data = realloc_tmp;
+ break;
+
+ case 0x0C: /* UTF8 */
+
+ /* init field */
+ l->type = LTC_ASN1_UTF8_STRING;
+ l->size = len;
+
+ if ((l->data = XCALLOC(l->size, sizeof(wchar_t))) == NULL) {
+ err = CRYPT_MEM;
+ goto error;
+ }
+
+ if ((err = der_decode_utf8_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
+ goto error;
+ }
+
+ if ((err = der_length_utf8_string(l->data, l->size, &len)) != CRYPT_OK) {
+ goto error;
+ }
+ break;
+
+ case 0x13: /* PRINTABLE */
+
+ /* init field */
+ l->type = LTC_ASN1_PRINTABLE_STRING;
+ l->size = len;
+
+ if ((l->data = XCALLOC(1, l->size)) == NULL) {
+ err = CRYPT_MEM;
+ goto error;
+ }
+
+ if ((err = der_decode_printable_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
+ goto error;
+ }
+
+ if ((err = der_length_printable_string(l->data, l->size, &len)) != CRYPT_OK) {
+ goto error;
+ }
+ break;
+
+ case 0x16: /* IA5 */
+
+ /* init field */
+ l->type = LTC_ASN1_IA5_STRING;
+ l->size = len;
+
+ if ((l->data = XCALLOC(1, l->size)) == NULL) {
+ err = CRYPT_MEM;
+ goto error;
+ }
+
+ if ((err = der_decode_ia5_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) {
+ goto error;
+ }
+
+ if ((err = der_length_ia5_string(l->data, l->size, &len)) != CRYPT_OK) {
+ goto error;
+ }
+ break;
+
+ case 0x17: /* UTC TIME */
+
+ /* init field */
+ l->type = LTC_ASN1_UTCTIME;
+ l->size = 1;
+
+ if ((l->data = XCALLOC(1, sizeof(ltc_utctime))) == NULL) {
+ err = CRYPT_MEM;
+ goto error;
+ }
+
+ len = *inlen;
+ if ((err = der_decode_utctime(in, &len, l->data)) != CRYPT_OK) {
+ goto error;
+ }
+
+ if ((err = der_length_utctime(l->data, &len)) != CRYPT_OK) {
+ goto error;
+ }
+ break;
+
+ case 0x30: /* SEQUENCE */
+ case 0x31: /* SET */
+
+ /* init field */
+ l->type = (type == 0x30) ? LTC_ASN1_SEQUENCE : LTC_ASN1_SET;
+
+ /* we have to decode the SEQUENCE header and get it's length */
+
+ /* move past type */
+ ++in;
+ --(*inlen);
+
+ /* read length byte */
+ x = *in++;
+ --(*inlen);
+
+ /* smallest SEQUENCE/SET header */
+ y = 2;
+
+ /* now if it's > 127 the next bytes are the length of the length */
+ if (x > 128) {
+ x &= 0x7F;
+ in += x;
+ *inlen -= x;
+
+ /* update sequence header len */
+ y += x;
+ }
+
+ /* Sequence elements go as child */
+ len = len - y;
+ if ((err = der_decode_sequence_flexi(in, &len, &(l->child))) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* len update */
+ totlen += y;
+
+ /* link them up y0 */
+ l->child->parent = l;
+
+ break;
+
+ default:
+ /* invalid byte ... this is a soft error */
+ /* remove link */
+ l = l->prev;
+ XFREE(l->next);
+ l->next = NULL;
+ goto outside;
+ }
+
+ /* advance pointers */
+ totlen += len;
+ in += len;
+ *inlen -= len;
+ }
+
+outside:
+
+ /* rewind l please */
+ while (l->prev != NULL || l->parent != NULL) {
+ if (l->parent != NULL) {
+ l = l->parent;
+ } else {
+ l = l->prev;
+ }
+ }
+
+ /* return */
+ *out = l;
+ *inlen = totlen;
+ return CRYPT_OK;
+
+error:
+ /* free list */
+ der_sequence_free(l);
+
+ return err;
+}
+#endif
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c,v $ */
+/* $Revision: 1.26 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+#include
+
+
+/**
+ @file der_decode_sequence_multi.c
+ ASN.1 DER, decode a SEQUENCE, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/**
+ Decode a SEQUENCE type using a VA list
+ @param in Input buffer
+ @param inlen Length of input in octets
+ @remark <...> is of the form (int, unsigned long, void*)
+ @return CRYPT_OK on success
+ */
+int der_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...) {
+ int err, type;
+ unsigned long size, x;
+ void *data;
+ va_list args;
+ ltc_asn1_list *list;
+
+ LTC_ARGCHK(in != NULL);
+
+ /* get size of output that will be required */
+ va_start(args, inlen);
+ x = 0;
+ for ( ; ; ) {
+ type = va_arg(args, int);
+ size = va_arg(args, unsigned long);
+ data = va_arg(args, void *);
+
+ if (type == LTC_ASN1_EOL) {
+ break;
+ }
+
+ switch (type) {
+ case LTC_ASN1_BOOLEAN:
+ case LTC_ASN1_INTEGER:
+ case LTC_ASN1_SHORT_INTEGER:
+ case LTC_ASN1_BIT_STRING:
+ case LTC_ASN1_OCTET_STRING:
+ case LTC_ASN1_NULL:
+ case LTC_ASN1_OBJECT_IDENTIFIER:
+ case LTC_ASN1_IA5_STRING:
+ case LTC_ASN1_PRINTABLE_STRING:
+ case LTC_ASN1_UTF8_STRING:
+ case LTC_ASN1_UTCTIME:
+ case LTC_ASN1_SET:
+ case LTC_ASN1_SETOF:
+ case LTC_ASN1_SEQUENCE:
+ case LTC_ASN1_CHOICE:
+ ++x;
+ break;
+
+ default:
+ va_end(args);
+ return CRYPT_INVALID_ARG;
+ }
+ }
+ va_end(args);
+
+ /* allocate structure for x elements */
+ if (x == 0) {
+ return CRYPT_NOP;
+ }
+
+ list = XCALLOC(x, sizeof(*list));
+ if (list == NULL) {
+ return CRYPT_MEM;
+ }
+
+ /* fill in the structure */
+ va_start(args, inlen);
+ x = 0;
+ for ( ; ; ) {
+ type = va_arg(args, int);
+ size = va_arg(args, unsigned long);
+ data = va_arg(args, void *);
+
+ if (type == LTC_ASN1_EOL) {
+ break;
+ }
+
+ switch (type) {
+ case LTC_ASN1_BOOLEAN:
+ case LTC_ASN1_INTEGER:
+ case LTC_ASN1_SHORT_INTEGER:
+ case LTC_ASN1_BIT_STRING:
+ case LTC_ASN1_OCTET_STRING:
+ case LTC_ASN1_NULL:
+ case LTC_ASN1_OBJECT_IDENTIFIER:
+ case LTC_ASN1_IA5_STRING:
+ case LTC_ASN1_PRINTABLE_STRING:
+ case LTC_ASN1_UTF8_STRING:
+ case LTC_ASN1_UTCTIME:
+ case LTC_ASN1_SEQUENCE:
+ case LTC_ASN1_SET:
+ case LTC_ASN1_SETOF:
+ case LTC_ASN1_CHOICE:
+ list[x].type = type;
+ list[x].size = size;
+ list[x++].data = data;
+ break;
+
+ default:
+ va_end(args);
+ err = CRYPT_INVALID_ARG;
+ goto LBL_ERR;
+ }
+ }
+ va_end(args);
+
+ err = der_decode_sequence(in, inlen, list, x);
+LBL_ERR:
+ XFREE(list);
+ return err;
+}
+#endif
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_multi.c,v $ */
+/* $Revision: 1.13 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_decode_short_integer.c
+ ASN.1 DER, decode an integer, Tom St Denis
+ */
+
+
+#ifdef LTC_DER
+
+/**
+ Read a short integer
+ @param in The DER encoded data
+ @param inlen Size of data
+ @param num [out] The integer to decode
+ @return CRYPT_OK if successful
+ */
+int der_decode_short_integer(const unsigned char *in, unsigned long inlen, unsigned long *num) {
+ unsigned long len, x, y;
+
+ LTC_ARGCHK(num != NULL);
+ LTC_ARGCHK(in != NULL);
+
+ /* check length */
+ if (inlen < 2) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* check header */
+ x = 0;
+ if ((in[x++] & 0x1F) != 0x02) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* get the packet len */
+ len = in[x++];
+
+ if (x + len > inlen) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* read number */
+ y = 0;
+ while (len--) {
+ y = (y << 8) | (unsigned long)in[x++];
+ }
+ *num = y;
+
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/short_integer/der_decode_short_integer.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_decode_utctime.c
+ ASN.1 DER, decode a UTCTIME, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+static int char_to_int(unsigned char x) {
+ switch (x) {
+ case '0':
+ return 0;
+
+ case '1':
+ return 1;
+
+ case '2':
+ return 2;
+
+ case '3':
+ return 3;
+
+ case '4':
+ return 4;
+
+ case '5':
+ return 5;
+
+ case '6':
+ return 6;
+
+ case '7':
+ return 7;
+
+ case '8':
+ return 8;
+
+ case '9':
+ return 9;
+ }
+ return 100;
+}
+
+ #define DECODE_V(y, max) \
+ y = char_to_int(buf[x]) * 10 + char_to_int(buf[x + 1]); \
+ if (y >= max) return CRYPT_INVALID_PACKET; \
+ x += 2;
+
+/**
+ Decodes a UTC time structure in DER format (reads all 6 valid encoding formats)
+ @param in Input buffer
+ @param inlen Length of input buffer in octets
+ @param out [out] Destination of UTC time structure
+ @return CRYPT_OK if successful
+ */
+int der_decode_utctime(const unsigned char *in, unsigned long *inlen,
+ ltc_utctime *out) {
+ unsigned char buf[32];
+ unsigned long x;
+ int y;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(inlen != NULL);
+ LTC_ARGCHK(out != NULL);
+
+ /* check header */
+ if ((*inlen < 2UL) || (in[1] >= sizeof(buf)) || ((in[1] + 2UL) > *inlen)) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* decode the string */
+ for (x = 0; x < in[1]; x++) {
+ y = der_ia5_value_decode(in[x + 2]);
+ if (y == -1) {
+ return CRYPT_INVALID_PACKET;
+ }
+ buf[x] = y;
+ }
+ *inlen = 2 + x;
+
+
+ /* possible encodings are
+ YYMMDDhhmmZ
+ YYMMDDhhmm+hh'mm'
+ YYMMDDhhmm-hh'mm'
+ YYMMDDhhmmssZ
+ YYMMDDhhmmss+hh'mm'
+ YYMMDDhhmmss-hh'mm'
+
+ So let's do a trivial decode upto [including] mm
+ */
+
+ x = 0;
+ DECODE_V(out->YY, 100);
+ DECODE_V(out->MM, 13);
+ DECODE_V(out->DD, 32);
+ DECODE_V(out->hh, 24);
+ DECODE_V(out->mm, 60);
+
+ /* clear timezone and seconds info */
+ out->off_dir = out->off_hh = out->off_mm = out->ss = 0;
+
+ /* now is it Z, +, - or 0-9 */
+ if (buf[x] == 'Z') {
+ return CRYPT_OK;
+ } else if ((buf[x] == '+') || (buf[x] == '-')) {
+ out->off_dir = (buf[x++] == '+') ? 0 : 1;
+ DECODE_V(out->off_hh, 24);
+ DECODE_V(out->off_mm, 60);
+ return CRYPT_OK;
+ }
+
+ /* decode seconds */
+ DECODE_V(out->ss, 60);
+
+ /* now is it Z, +, - */
+ if (buf[x] == 'Z') {
+ return CRYPT_OK;
+ } else if ((buf[x] == '+') || (buf[x] == '-')) {
+ out->off_dir = (buf[x++] == '+') ? 0 : 1;
+ DECODE_V(out->off_hh, 24);
+ DECODE_V(out->off_mm, 60);
+ return CRYPT_OK;
+ } else {
+ return CRYPT_INVALID_PACKET;
+ }
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utctime/der_decode_utctime.c,v $ */
+/* $Revision: 1.9 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_decode_utf8_string.c
+ ASN.1 DER, encode a UTF8 STRING, Tom St Denis
+ */
+
+
+#ifdef LTC_DER
+
+/**
+ Store a UTF8 STRING
+ @param in The DER encoded UTF8 STRING
+ @param inlen The size of the DER UTF8 STRING
+ @param out [out] The array of utf8s stored (one per char)
+ @param outlen [in/out] The number of utf8s stored
+ @return CRYPT_OK if successful
+ */
+int der_decode_utf8_string(const unsigned char *in, unsigned long inlen,
+ wchar_t *out, unsigned long *outlen) {
+ wchar_t tmp;
+ unsigned long x, y, z, len;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* must have header at least */
+ if (inlen < 2) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* check for 0x0C */
+ if ((in[0] & 0x1F) != 0x0C) {
+ return CRYPT_INVALID_PACKET;
+ }
+ x = 1;
+
+ /* decode the length */
+ if (in[x] & 0x80) {
+ /* valid # of bytes in length are 1,2,3 */
+ y = in[x] & 0x7F;
+ if ((y == 0) || (y > 3) || ((x + y) > inlen)) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* read the length in */
+ len = 0;
+ ++x;
+ while (y--) {
+ len = (len << 8) | in[x++];
+ }
+ } else {
+ len = in[x++] & 0x7F;
+ }
+
+ if (len + x > inlen) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* proceed to decode */
+ for (y = 0; x < inlen; ) {
+ /* get first byte */
+ tmp = in[x++];
+
+ /* count number of bytes */
+ for (z = 0; (tmp & 0x80) && (z <= 4); z++, tmp = (tmp << 1) & 0xFF);
+
+ if ((z > 4) || (x + (z - 1) > inlen)) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* decode, grab upper bits */
+ tmp >>= z;
+
+ /* grab remaining bytes */
+ if (z > 1) {
+ --z;
+ }
+ while (z-- != 0) {
+ if ((in[x] & 0xC0) != 0x80) {
+ return CRYPT_INVALID_PACKET;
+ }
+ tmp = (tmp << 6) | ((wchar_t)in[x++] & 0x3F);
+ }
+
+ if (y > *outlen) {
+ *outlen = y;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+ out[y++] = tmp;
+ }
+ *outlen = y;
+
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utf8/der_decode_utf8_string.c,v $ */
+/* $Revision: 1.8 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_encode_bit_string.c
+ ASN.1 DER, encode a BIT STRING, Tom St Denis
+ */
+
+
+#ifdef LTC_DER
+
+/**
+ Store a BIT STRING
+ @param in The array of bits to store (one per char)
+ @param inlen The number of bits tostore
+ @param out [out] The destination for the DER encoded BIT STRING
+ @param outlen [in/out] The max size and resulting size of the DER BIT STRING
+ @return CRYPT_OK if successful
+ */
+int der_encode_bit_string(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen) {
+ unsigned long len, x, y;
+ unsigned char buf;
+ int err;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* avoid overflows */
+ if ((err = der_length_bit_string(inlen, &len)) != CRYPT_OK) {
+ return err;
+ }
+
+ if (len > *outlen) {
+ *outlen = len;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ /* store header (include bit padding count in length) */
+ x = 0;
+ y = (inlen >> 3) + ((inlen & 7) ? 1 : 0) + 1;
+
+ out[x++] = 0x03;
+ if (y < 128) {
+ out[x++] = (unsigned char)y;
+ } else if (y < 256) {
+ out[x++] = 0x81;
+ out[x++] = (unsigned char)y;
+ } else if (y < 65536) {
+ out[x++] = 0x82;
+ out[x++] = (unsigned char)((y >> 8) & 255);
+ out[x++] = (unsigned char)(y & 255);
+ }
+
+ /* store number of zero padding bits */
+ out[x++] = (unsigned char)((8 - inlen) & 7);
+
+ /* store the bits in big endian format */
+ for (y = buf = 0; y < inlen; y++) {
+ buf |= (in[y] ? 1 : 0) << (7 - (y & 7));
+ if ((y & 7) == 7) {
+ out[x++] = buf;
+ buf = 0;
+ }
+ }
+ /* store last byte */
+ if (inlen & 7) {
+ out[x++] = buf;
+ }
+ *outlen = x;
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/bit/der_encode_bit_string.c,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_encode_boolean.c
+ ASN.1 DER, encode a BOOLEAN, Tom St Denis
+ */
+
+
+#ifdef LTC_DER
+
+/**
+ Store a BOOLEAN
+ @param in The boolean to encode
+ @param out [out] The destination for the DER encoded BOOLEAN
+ @param outlen [in/out] The max size and resulting size of the DER BOOLEAN
+ @return CRYPT_OK if successful
+ */
+int der_encode_boolean(int in,
+ unsigned char *out, unsigned long *outlen) {
+ LTC_ARGCHK(outlen != NULL);
+ LTC_ARGCHK(out != NULL);
+
+ if (*outlen < 3) {
+ *outlen = 3;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ *outlen = 3;
+ out[0] = 0x01;
+ out[1] = 0x01;
+ out[2] = in ? 0xFF : 0x00;
+
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/boolean/der_encode_boolean.c,v $ */
+/* $Revision: 1.4 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_encode_ia5_string.c
+ ASN.1 DER, encode a IA5 STRING, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/**
+ Store an IA5 STRING
+ @param in The array of IA5 to store (one per char)
+ @param inlen The number of IA5 to store
+ @param out [out] The destination for the DER encoded IA5 STRING
+ @param outlen [in/out] The max size and resulting size of the DER IA5 STRING
+ @return CRYPT_OK if successful
+ */
+int der_encode_ia5_string(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen) {
+ unsigned long x, y, len;
+ int err;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* get the size */
+ if ((err = der_length_ia5_string(in, inlen, &len)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* too big? */
+ if (len > *outlen) {
+ *outlen = len;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ /* encode the header+len */
+ x = 0;
+ out[x++] = 0x16;
+ if (inlen < 128) {
+ out[x++] = (unsigned char)inlen;
+ } else if (inlen < 256) {
+ out[x++] = 0x81;
+ out[x++] = (unsigned char)inlen;
+ } else if (inlen < 65536UL) {
+ out[x++] = 0x82;
+ out[x++] = (unsigned char)((inlen >> 8) & 255);
+ out[x++] = (unsigned char)(inlen & 255);
+ } else if (inlen < 16777216UL) {
+ out[x++] = 0x83;
+ out[x++] = (unsigned char)((inlen >> 16) & 255);
+ out[x++] = (unsigned char)((inlen >> 8) & 255);
+ out[x++] = (unsigned char)(inlen & 255);
+ } else {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* store octets */
+ for (y = 0; y < inlen; y++) {
+ out[x++] = der_ia5_char_encode(in[y]);
+ }
+
+ /* retun length */
+ *outlen = x;
+
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/ia5/der_encode_ia5_string.c,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_encode_integer.c
+ ASN.1 DER, encode an integer, Tom St Denis
+ */
+
+
+#ifdef LTC_DER
+
+/* Exports a positive bignum as DER format (upto 2^32 bytes in size) */
+
+/**
+ Store a mp_int integer
+ @param num The first mp_int to encode
+ @param out [out] The destination for the DER encoded integers
+ @param outlen [in/out] The max size and resulting size of the DER encoded integers
+ @return CRYPT_OK if successful
+ */
+int der_encode_integer(void *num, unsigned char *out, unsigned long *outlen) {
+ unsigned long tmplen, y;
+ int err, leading_zero;
+
+ LTC_ARGCHK(num != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* find out how big this will be */
+ if ((err = der_length_integer(num, &tmplen)) != CRYPT_OK) {
+ return err;
+ }
+
+ if (*outlen < tmplen) {
+ *outlen = tmplen;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ if (mp_cmp_d(num, 0) != LTC_MP_LT) {
+ /* we only need a leading zero if the msb of the first byte is one */
+ if (((mp_count_bits(num) & 7) == 0) || (mp_iszero(num) == LTC_MP_YES)) {
+ leading_zero = 1;
+ } else {
+ leading_zero = 0;
+ }
+
+ /* get length of num in bytes (plus 1 since we force the msbyte to zero) */
+ y = mp_unsigned_bin_size(num) + leading_zero;
+ } else {
+ leading_zero = 0;
+ y = mp_count_bits(num);
+ y = y + (8 - (y & 7));
+ y = y >> 3;
+ if (((mp_cnt_lsb(num) + 1) == mp_count_bits(num)) && ((mp_count_bits(num) & 7) == 0)) --y;
+ }
+
+ /* now store initial data */
+ *out++ = 0x02;
+ if (y < 128) {
+ /* short form */
+ *out++ = (unsigned char)y;
+ } else if (y < 256) {
+ *out++ = 0x81;
+ *out++ = (unsigned char)y;
+ } else if (y < 65536UL) {
+ *out++ = 0x82;
+ *out++ = (unsigned char)((y >> 8) & 255);
+ *out++ = (unsigned char)y;
+ } else if (y < 16777216UL) {
+ *out++ = 0x83;
+ *out++ = (unsigned char)((y >> 16) & 255);
+ *out++ = (unsigned char)((y >> 8) & 255);
+ *out++ = (unsigned char)y;
+ } else {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* now store msbyte of zero if num is non-zero */
+ if (leading_zero) {
+ *out++ = 0x00;
+ }
+
+ /* if it's not zero store it as big endian */
+ if (mp_cmp_d(num, 0) == LTC_MP_GT) {
+ /* now store the mpint */
+ if ((err = mp_to_unsigned_bin(num, out)) != CRYPT_OK) {
+ return err;
+ }
+ } else if (mp_iszero(num) != LTC_MP_YES) {
+ void *tmp;
+
+ /* negative */
+ if (mp_init(&tmp) != CRYPT_OK) {
+ return CRYPT_MEM;
+ }
+
+ /* 2^roundup and subtract */
+ y = mp_count_bits(num);
+ y = y + (8 - (y & 7));
+ if (((mp_cnt_lsb(num) + 1) == mp_count_bits(num)) && ((mp_count_bits(num) & 7) == 0)) y -= 8;
+ if ((mp_2expt(tmp, y) != CRYPT_OK) || (mp_add(tmp, num, tmp) != CRYPT_OK)) {
+ mp_clear(tmp);
+ return CRYPT_MEM;
+ }
+ if ((err = mp_to_unsigned_bin(tmp, out)) != CRYPT_OK) {
+ mp_clear(tmp);
+ return err;
+ }
+ mp_clear(tmp);
+ }
+
+ /* we good */
+ *outlen = tmplen;
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/integer/der_encode_integer.c,v $ */
+/* $Revision: 1.9 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_encode_object_identifier.c
+ ASN.1 DER, Encode Object Identifier, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/**
+ Encode an OID
+ @param words The words to encode (upto 32-bits each)
+ @param nwords The number of words in the OID
+ @param out [out] Destination of OID data
+ @param outlen [in/out] The max and resulting size of the OID
+ @return CRYPT_OK if successful
+ */
+int der_encode_object_identifier(unsigned long *words, unsigned long nwords,
+ unsigned char *out, unsigned long *outlen) {
+ unsigned long i, x, y, z, t, mask, wordbuf;
+ int err;
+
+ LTC_ARGCHK(words != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* check length */
+ if ((err = der_length_object_identifier(words, nwords, &x)) != CRYPT_OK) {
+ return err;
+ }
+ if (x > *outlen) {
+ *outlen = x;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ /* compute length to store OID data */
+ z = 0;
+ wordbuf = words[0] * 40 + words[1];
+ for (y = 1; y < nwords; y++) {
+ t = der_object_identifier_bits(wordbuf);
+ z += t / 7 + ((t % 7) ? 1 : 0) + (wordbuf == 0 ? 1 : 0);
+ if (y < nwords - 1) {
+ wordbuf = words[y + 1];
+ }
+ }
+
+ /* store header + length */
+ x = 0;
+ out[x++] = 0x06;
+ if (z < 128) {
+ out[x++] = (unsigned char)z;
+ } else if (z < 256) {
+ out[x++] = 0x81;
+ out[x++] = (unsigned char)z;
+ } else if (z < 65536UL) {
+ out[x++] = 0x82;
+ out[x++] = (unsigned char)((z >> 8) & 255);
+ out[x++] = (unsigned char)(z & 255);
+ } else {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* store first byte */
+ wordbuf = words[0] * 40 + words[1];
+ for (i = 1; i < nwords; i++) {
+ /* store 7 bit words in little endian */
+ t = wordbuf & 0xFFFFFFFF;
+ if (t) {
+ y = x;
+ mask = 0;
+ while (t) {
+ out[x++] = (unsigned char)((t & 0x7F) | mask);
+ t >>= 7;
+ mask |= 0x80; /* upper bit is set on all but the last byte */
+ }
+ /* now swap bytes y...x-1 */
+ z = x - 1;
+ while (y < z) {
+ t = out[y];
+ out[y] = out[z];
+ out[z] = (unsigned char)t;
+ ++y;
+ --z;
+ }
+ } else {
+ /* zero word */
+ out[x++] = 0x00;
+ }
+
+ if (i < nwords - 1) {
+ wordbuf = words[i + 1];
+ }
+ }
+
+ *outlen = x;
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/object_identifier/der_encode_object_identifier.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_encode_octet_string.c
+ ASN.1 DER, encode a OCTET STRING, Tom St Denis
+ */
+
+
+#ifdef LTC_DER
+
+/**
+ Store an OCTET STRING
+ @param in The array of OCTETS to store (one per char)
+ @param inlen The number of OCTETS to store
+ @param out [out] The destination for the DER encoded OCTET STRING
+ @param outlen [in/out] The max size and resulting size of the DER OCTET STRING
+ @return CRYPT_OK if successful
+ */
+int der_encode_octet_string(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen) {
+ unsigned long x, y, len;
+ int err;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* get the size */
+ if ((err = der_length_octet_string(inlen, &len)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* too big? */
+ if (len > *outlen) {
+ *outlen = len;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ /* encode the header+len */
+ x = 0;
+ out[x++] = 0x04;
+ if (inlen < 128) {
+ out[x++] = (unsigned char)inlen;
+ } else if (inlen < 256) {
+ out[x++] = 0x81;
+ out[x++] = (unsigned char)inlen;
+ } else if (inlen < 65536UL) {
+ out[x++] = 0x82;
+ out[x++] = (unsigned char)((inlen >> 8) & 255);
+ out[x++] = (unsigned char)(inlen & 255);
+ } else if (inlen < 16777216UL) {
+ out[x++] = 0x83;
+ out[x++] = (unsigned char)((inlen >> 16) & 255);
+ out[x++] = (unsigned char)((inlen >> 8) & 255);
+ out[x++] = (unsigned char)(inlen & 255);
+ } else {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* store octets */
+ for (y = 0; y < inlen; y++) {
+ out[x++] = in[y];
+ }
+
+ /* retun length */
+ *outlen = x;
+
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/octet/der_encode_octet_string.c,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_encode_printable_string.c
+ ASN.1 DER, encode a printable STRING, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/**
+ Store an printable STRING
+ @param in The array of printable to store (one per char)
+ @param inlen The number of printable to store
+ @param out [out] The destination for the DER encoded printable STRING
+ @param outlen [in/out] The max size and resulting size of the DER printable STRING
+ @return CRYPT_OK if successful
+ */
+int der_encode_printable_string(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen) {
+ unsigned long x, y, len;
+ int err;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* get the size */
+ if ((err = der_length_printable_string(in, inlen, &len)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* too big? */
+ if (len > *outlen) {
+ *outlen = len;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ /* encode the header+len */
+ x = 0;
+ out[x++] = 0x13;
+ if (inlen < 128) {
+ out[x++] = (unsigned char)inlen;
+ } else if (inlen < 256) {
+ out[x++] = 0x81;
+ out[x++] = (unsigned char)inlen;
+ } else if (inlen < 65536UL) {
+ out[x++] = 0x82;
+ out[x++] = (unsigned char)((inlen >> 8) & 255);
+ out[x++] = (unsigned char)(inlen & 255);
+ } else if (inlen < 16777216UL) {
+ out[x++] = 0x83;
+ out[x++] = (unsigned char)((inlen >> 16) & 255);
+ out[x++] = (unsigned char)((inlen >> 8) & 255);
+ out[x++] = (unsigned char)(inlen & 255);
+ } else {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* store octets */
+ for (y = 0; y < inlen; y++) {
+ out[x++] = der_printable_char_encode(in[y]);
+ }
+
+ /* retun length */
+ *outlen = x;
+
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/printable_string/der_encode_printable_string.c,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+#include
+
+
+/**
+ @file der_encode_sequence_ex.c
+ ASN.1 DER, encode a SEQUENCE, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/**
+ Encode a SEQUENCE
+ @param list The list of items to encode
+ @param inlen The number of items in the list
+ @param out [out] The destination
+ @param outlen [in/out] The size of the output
+ @param type_of LTC_ASN1_SEQUENCE or LTC_ASN1_SET/LTC_ASN1_SETOF
+ @return CRYPT_OK on success
+ */
+int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen, int type_of) {
+ int err, type;
+ unsigned long size, x, y, z, i;
+ void *data;
+
+ LTC_ARGCHK(list != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* get size of output that will be required */
+ y = 0;
+ for (i = 0; i < inlen; i++) {
+ type = list[i].type;
+ size = list[i].size;
+ data = list[i].data;
+
+ if (type == LTC_ASN1_EOL) {
+ break;
+ }
+
+ switch (type) {
+ case LTC_ASN1_BOOLEAN:
+ if ((err = der_length_boolean(&x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_INTEGER:
+ if ((err = der_length_integer(data, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_SHORT_INTEGER:
+ if ((err = der_length_short_integer(*((unsigned long *)data), &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_BIT_STRING:
+ if ((err = der_length_bit_string(size, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_OCTET_STRING:
+ if ((err = der_length_octet_string(size, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_NULL:
+ y += 2;
+ break;
+
+ case LTC_ASN1_OBJECT_IDENTIFIER:
+ if ((err = der_length_object_identifier(data, size, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_IA5_STRING:
+ if ((err = der_length_ia5_string(data, size, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_PRINTABLE_STRING:
+ if ((err = der_length_printable_string(data, size, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_UTF8_STRING:
+ if ((err = der_length_utf8_string(data, size, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_UTCTIME:
+ if ((err = der_length_utctime(data, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_SET:
+ case LTC_ASN1_SETOF:
+ case LTC_ASN1_SEQUENCE:
+ if ((err = der_length_sequence(data, size, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ default:
+ err = CRYPT_INVALID_ARG;
+ goto LBL_ERR;
+ }
+ }
+
+ /* calc header size */
+ z = y;
+ if (y < 128) {
+ y += 2;
+ } else if (y < 256) {
+ /* 0x30 0x81 LL */
+ y += 3;
+ } else if (y < 65536UL) {
+ /* 0x30 0x82 LL LL */
+ y += 4;
+ } else if (y < 16777216UL) {
+ /* 0x30 0x83 LL LL LL */
+ y += 5;
+ } else {
+ err = CRYPT_INVALID_ARG;
+ goto LBL_ERR;
+ }
+
+ /* too big ? */
+ if (*outlen < y) {
+ *outlen = y;
+ err = CRYPT_BUFFER_OVERFLOW;
+ goto LBL_ERR;
+ }
+
+ /* store header */
+ x = 0;
+ out[x++] = (type_of == LTC_ASN1_SEQUENCE) ? 0x30 : 0x31;
+
+ if (z < 128) {
+ out[x++] = (unsigned char)z;
+ } else if (z < 256) {
+ out[x++] = 0x81;
+ out[x++] = (unsigned char)z;
+ } else if (z < 65536UL) {
+ out[x++] = 0x82;
+ out[x++] = (unsigned char)((z >> 8UL) & 255);
+ out[x++] = (unsigned char)(z & 255);
+ } else if (z < 16777216UL) {
+ out[x++] = 0x83;
+ out[x++] = (unsigned char)((z >> 16UL) & 255);
+ out[x++] = (unsigned char)((z >> 8UL) & 255);
+ out[x++] = (unsigned char)(z & 255);
+ }
+
+ /* store data */
+ *outlen -= x;
+ for (i = 0; i < inlen; i++) {
+ type = list[i].type;
+ size = list[i].size;
+ data = list[i].data;
+
+ if (type == LTC_ASN1_EOL) {
+ break;
+ }
+
+ switch (type) {
+ case LTC_ASN1_BOOLEAN:
+ z = *outlen;
+ if ((err = der_encode_boolean(*((int *)data), out + x, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ x += z;
+ *outlen -= z;
+ break;
+
+ case LTC_ASN1_INTEGER:
+ z = *outlen;
+ if ((err = der_encode_integer(data, out + x, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ x += z;
+ *outlen -= z;
+ break;
+
+ case LTC_ASN1_SHORT_INTEGER:
+ z = *outlen;
+ if ((err = der_encode_short_integer(*((unsigned long *)data), out + x, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ x += z;
+ *outlen -= z;
+ break;
+
+ case LTC_ASN1_BIT_STRING:
+ z = *outlen;
+ if ((err = der_encode_bit_string(data, size, out + x, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ x += z;
+ *outlen -= z;
+ break;
+
+ case LTC_ASN1_OCTET_STRING:
+ z = *outlen;
+ if ((err = der_encode_octet_string(data, size, out + x, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ x += z;
+ *outlen -= z;
+ break;
+
+ case LTC_ASN1_NULL:
+ out[x++] = 0x05;
+ out[x++] = 0x00;
+ *outlen -= 2;
+ break;
+
+ case LTC_ASN1_OBJECT_IDENTIFIER:
+ z = *outlen;
+ if ((err = der_encode_object_identifier(data, size, out + x, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ x += z;
+ *outlen -= z;
+ break;
+
+ case LTC_ASN1_IA5_STRING:
+ z = *outlen;
+ if ((err = der_encode_ia5_string(data, size, out + x, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ x += z;
+ *outlen -= z;
+ break;
+
+ case LTC_ASN1_PRINTABLE_STRING:
+ z = *outlen;
+ if ((err = der_encode_printable_string(data, size, out + x, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ x += z;
+ *outlen -= z;
+ break;
+
+ case LTC_ASN1_UTF8_STRING:
+ z = *outlen;
+ if ((err = der_encode_utf8_string(data, size, out + x, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ x += z;
+ *outlen -= z;
+ break;
+
+ case LTC_ASN1_UTCTIME:
+ z = *outlen;
+ if ((err = der_encode_utctime(data, out + x, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ x += z;
+ *outlen -= z;
+ break;
+
+ case LTC_ASN1_SET:
+ z = *outlen;
+ if ((err = der_encode_set(data, size, out + x, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ x += z;
+ *outlen -= z;
+ break;
+
+ case LTC_ASN1_SETOF:
+ z = *outlen;
+ if ((err = der_encode_setof(data, size, out + x, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ x += z;
+ *outlen -= z;
+ break;
+
+ case LTC_ASN1_SEQUENCE:
+ z = *outlen;
+ if ((err = der_encode_sequence_ex(data, size, out + x, &z, type)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ x += z;
+ *outlen -= z;
+ break;
+
+ default:
+ err = CRYPT_INVALID_ARG;
+ goto LBL_ERR;
+ }
+ }
+ *outlen = x;
+ err = CRYPT_OK;
+
+LBL_ERR:
+ return err;
+}
+#endif
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+#include
+
+
+/**
+ @file der_encode_sequence_multi.c
+ ASN.1 DER, encode a SEQUENCE, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/**
+ Encode a SEQUENCE type using a VA list
+ @param out [out] Destination for data
+ @param outlen [in/out] Length of buffer and resulting length of output
+ @remark <...> is of the form (int, unsigned long, void*)
+ @return CRYPT_OK on success
+ */
+int der_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...) {
+ int err, type;
+ unsigned long size, x;
+ void *data;
+ va_list args;
+ ltc_asn1_list *list;
+
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* get size of output that will be required */
+ va_start(args, outlen);
+ x = 0;
+ for ( ; ; ) {
+ type = va_arg(args, int);
+ size = va_arg(args, unsigned long);
+ data = va_arg(args, void *);
+
+ if (type == LTC_ASN1_EOL) {
+ break;
+ }
+
+ switch (type) {
+ case LTC_ASN1_BOOLEAN:
+ case LTC_ASN1_INTEGER:
+ case LTC_ASN1_SHORT_INTEGER:
+ case LTC_ASN1_BIT_STRING:
+ case LTC_ASN1_OCTET_STRING:
+ case LTC_ASN1_NULL:
+ case LTC_ASN1_OBJECT_IDENTIFIER:
+ case LTC_ASN1_IA5_STRING:
+ case LTC_ASN1_PRINTABLE_STRING:
+ case LTC_ASN1_UTF8_STRING:
+ case LTC_ASN1_UTCTIME:
+ case LTC_ASN1_SEQUENCE:
+ case LTC_ASN1_SET:
+ case LTC_ASN1_SETOF:
+ ++x;
+ break;
+
+ default:
+ va_end(args);
+ return CRYPT_INVALID_ARG;
+ }
+ }
+ va_end(args);
+
+ /* allocate structure for x elements */
+ if (x == 0) {
+ return CRYPT_NOP;
+ }
+
+ list = XCALLOC(x, sizeof(*list));
+ if (list == NULL) {
+ return CRYPT_MEM;
+ }
+
+ /* fill in the structure */
+ va_start(args, outlen);
+ x = 0;
+ for ( ; ; ) {
+ type = va_arg(args, int);
+ size = va_arg(args, unsigned long);
+ data = va_arg(args, void *);
+
+ if (type == LTC_ASN1_EOL) {
+ break;
+ }
+
+ switch (type) {
+ case LTC_ASN1_BOOLEAN:
+ case LTC_ASN1_INTEGER:
+ case LTC_ASN1_SHORT_INTEGER:
+ case LTC_ASN1_BIT_STRING:
+ case LTC_ASN1_OCTET_STRING:
+ case LTC_ASN1_NULL:
+ case LTC_ASN1_OBJECT_IDENTIFIER:
+ case LTC_ASN1_IA5_STRING:
+ case LTC_ASN1_PRINTABLE_STRING:
+ case LTC_ASN1_UTF8_STRING:
+ case LTC_ASN1_UTCTIME:
+ case LTC_ASN1_SEQUENCE:
+ case LTC_ASN1_SET:
+ case LTC_ASN1_SETOF:
+ list[x].type = type;
+ list[x].size = size;
+ list[x++].data = data;
+ break;
+
+ default:
+ va_end(args);
+ err = CRYPT_INVALID_ARG;
+ goto LBL_ERR;
+ }
+ }
+ va_end(args);
+
+ err = der_encode_sequence(list, x, out, outlen);
+LBL_ERR:
+ XFREE(list);
+ return err;
+}
+#endif
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_encode_sequence_multi.c,v $ */
+/* $Revision: 1.12 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_encode_set.c
+ ASN.1 DER, Encode a SET, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/* LTC define to ASN.1 TAG */
+static int ltc_to_asn1(int v) {
+ switch (v) {
+ case LTC_ASN1_BOOLEAN:
+ return 0x01;
+
+ case LTC_ASN1_INTEGER:
+ case LTC_ASN1_SHORT_INTEGER:
+ return 0x02;
+
+ case LTC_ASN1_BIT_STRING:
+ return 0x03;
+
+ case LTC_ASN1_OCTET_STRING:
+ return 0x04;
+
+ case LTC_ASN1_NULL:
+ return 0x05;
+
+ case LTC_ASN1_OBJECT_IDENTIFIER:
+ return 0x06;
+
+ case LTC_ASN1_UTF8_STRING:
+ return 0x0C;
+
+ case LTC_ASN1_PRINTABLE_STRING:
+ return 0x13;
+
+ case LTC_ASN1_IA5_STRING:
+ return 0x16;
+
+ case LTC_ASN1_UTCTIME:
+ return 0x17;
+
+ case LTC_ASN1_SEQUENCE:
+ return 0x30;
+
+ case LTC_ASN1_SET:
+ case LTC_ASN1_SETOF:
+ return 0x31;
+
+ default:
+ return -1;
+ }
+}
+
+static int qsort_helper_set(const void *a, const void *b) {
+ ltc_asn1_list *A = (ltc_asn1_list *)a, *B = (ltc_asn1_list *)b;
+ int r;
+
+ r = ltc_to_asn1(A->type) - ltc_to_asn1(B->type);
+
+ /* for QSORT the order is UNDEFINED if they are "equal" which means it is NOT DETERMINISTIC. So we force it to be :-) */
+ if (r == 0) {
+ /* their order in the original list now determines the position */
+ return A->used - B->used;
+ } else {
+ return r;
+ }
+}
+
+/*
+ Encode a SET type
+ @param list The list of items to encode
+ @param inlen The number of items in the list
+ @param out [out] The destination
+ @param outlen [in/out] The size of the output
+ @return CRYPT_OK on success
+ */
+int der_encode_set(ltc_asn1_list *list, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen) {
+ ltc_asn1_list *copy;
+ unsigned long x;
+ int err;
+
+ /* make copy of list */
+ copy = XCALLOC(inlen, sizeof(*copy));
+ if (copy == NULL) {
+ return CRYPT_MEM;
+ }
+
+ /* fill in used member with index so we can fully sort it */
+ for (x = 0; x < inlen; x++) {
+ copy[x] = list[x];
+ copy[x].used = x;
+ }
+
+ /* sort it by the "type" field */
+ XQSORT(copy, inlen, sizeof(*copy), &qsort_helper_set);
+
+ /* call der_encode_sequence_ex() */
+ err = der_encode_sequence_ex(copy, inlen, out, outlen, LTC_ASN1_SET);
+
+ /* free list */
+ XFREE(copy);
+
+ return err;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/set/der_encode_set.c,v $ */
+/* $Revision: 1.12 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_encode_setof.c
+ ASN.1 DER, Encode SET OF, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+struct edge {
+ unsigned char *start;
+ unsigned long size;
+};
+
+static int qsort_helper(const void *a, const void *b) {
+ struct edge *A = (struct edge *)a, *B = (struct edge *)b;
+ int r;
+ unsigned long x;
+
+ /* compare min length */
+ r = XMEMCMP(A->start, B->start, MIN(A->size, B->size));
+
+ if ((r == 0) && (A->size != B->size)) {
+ if (A->size > B->size) {
+ for (x = B->size; x < A->size; x++) {
+ if (A->start[x]) {
+ return 1;
+ }
+ }
+ } else {
+ for (x = A->size; x < B->size; x++) {
+ if (B->start[x]) {
+ return -1;
+ }
+ }
+ }
+ }
+
+ return r;
+}
+
+/**
+ Encode a SETOF stucture
+ @param list The list of items to encode
+ @param inlen The number of items in the list
+ @param out [out] The destination
+ @param outlen [in/out] The size of the output
+ @return CRYPT_OK on success
+ */
+int der_encode_setof(ltc_asn1_list *list, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen) {
+ unsigned long x, y, z, hdrlen;
+ int err;
+ struct edge *edges;
+ unsigned char *ptr, *buf;
+
+ /* check that they're all the same type */
+ for (x = 1; x < inlen; x++) {
+ if (list[x].type != list[x - 1].type) {
+ return CRYPT_INVALID_ARG;
+ }
+ }
+
+ /* alloc buffer to store copy of output */
+ buf = XCALLOC(1, *outlen);
+ if (buf == NULL) {
+ return CRYPT_MEM;
+ }
+
+ /* encode list */
+ if ((err = der_encode_sequence_ex(list, inlen, buf, outlen, LTC_ASN1_SETOF)) != CRYPT_OK) {
+ XFREE(buf);
+ return err;
+ }
+
+ /* allocate edges */
+ edges = XCALLOC(inlen, sizeof(*edges));
+ if (edges == NULL) {
+ XFREE(buf);
+ return CRYPT_MEM;
+ }
+
+ /* skip header */
+ ptr = buf + 1;
+
+ /* now skip length data */
+ x = *ptr++;
+ if (x >= 0x80) {
+ ptr += (x & 0x7F);
+ }
+
+ /* get the size of the static header */
+ hdrlen = ((unsigned long)ptr) - ((unsigned long)buf);
+
+
+ /* scan for edges */
+ x = 0;
+ while (ptr < (buf + *outlen)) {
+ /* store start */
+ edges[x].start = ptr;
+
+ /* skip type */
+ z = 1;
+
+ /* parse length */
+ y = ptr[z++];
+ if (y < 128) {
+ edges[x].size = y;
+ } else {
+ y &= 0x7F;
+ edges[x].size = 0;
+ while (y--) {
+ edges[x].size = (edges[x].size << 8) | ((unsigned long)ptr[z++]);
+ }
+ }
+
+ /* skip content */
+ edges[x].size += z;
+ ptr += edges[x].size;
+ ++x;
+ }
+
+ /* sort based on contents (using edges) */
+ XQSORT(edges, inlen, sizeof(*edges), &qsort_helper);
+
+ /* copy static header */
+ XMEMCPY(out, buf, hdrlen);
+
+ /* copy+sort using edges+indecies to output from buffer */
+ for (y = hdrlen, x = 0; x < inlen; x++) {
+ XMEMCPY(out + y, edges[x].start, edges[x].size);
+ y += edges[x].size;
+ }
+
+ #ifdef LTC_CLEAN_STACK
+ zeromem(buf, *outlen);
+ #endif
+
+ /* free buffers */
+ XFREE(edges);
+ XFREE(buf);
+
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/set/der_encode_setof.c,v $ */
+/* $Revision: 1.12 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_encode_short_integer.c
+ ASN.1 DER, encode an integer, Tom St Denis
+ */
+
+
+#ifdef LTC_DER
+
+/**
+ Store a short integer in the range (0,2^32-1)
+ @param num The integer to encode
+ @param out [out] The destination for the DER encoded integers
+ @param outlen [in/out] The max size and resulting size of the DER encoded integers
+ @return CRYPT_OK if successful
+ */
+int der_encode_short_integer(unsigned long num, unsigned char *out, unsigned long *outlen) {
+ unsigned long len, x, y, z;
+ int err;
+
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* force to 32 bits */
+ num &= 0xFFFFFFFFUL;
+
+ /* find out how big this will be */
+ if ((err = der_length_short_integer(num, &len)) != CRYPT_OK) {
+ return err;
+ }
+
+ if (*outlen < len) {
+ *outlen = len;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ /* get len of output */
+ z = 0;
+ y = num;
+ while (y) {
+ ++z;
+ y >>= 8;
+ }
+
+ /* handle zero */
+ if (z == 0) {
+ z = 1;
+ }
+
+ /* see if msb is set */
+ z += (num & (1UL << ((z << 3) - 1))) ? 1 : 0;
+
+ /* adjust the number so the msB is non-zero */
+ for (x = 0; (z <= 4) && (x < (4 - z)); x++) {
+ num <<= 8;
+ }
+
+ /* store header */
+ x = 0;
+ out[x++] = 0x02;
+ out[x++] = (unsigned char)z;
+
+ /* if 31st bit is set output a leading zero and decrement count */
+ if (z == 5) {
+ out[x++] = 0;
+ --z;
+ }
+
+ /* store values */
+ for (y = 0; y < z; y++) {
+ out[x++] = (unsigned char)((num >> 24) & 0xFF);
+ num <<= 8;
+ }
+
+ /* we good */
+ *outlen = x;
+
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/short_integer/der_encode_short_integer.c,v $ */
+/* $Revision: 1.8 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_encode_utctime.c
+ ASN.1 DER, encode a UTCTIME, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+static const char baseten[] = "0123456789";
+
+ #define STORE_V(y) \
+ out[x++] = der_ia5_char_encode(baseten[(y / 10) % 10]); \
+ out[x++] = der_ia5_char_encode(baseten[y % 10]);
+
+/**
+ Encodes a UTC time structure in DER format
+ @param utctime The UTC time structure to encode
+ @param out The destination of the DER encoding of the UTC time structure
+ @param outlen [in/out] The length of the DER encoding
+ @return CRYPT_OK if successful
+ */
+int der_encode_utctime(ltc_utctime *utctime,
+ unsigned char *out, unsigned long *outlen) {
+ unsigned long x, tmplen;
+ int err;
+
+ LTC_ARGCHK(utctime != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ if ((err = der_length_utctime(utctime, &tmplen)) != CRYPT_OK) {
+ return err;
+ }
+ if (tmplen > *outlen) {
+ *outlen = tmplen;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ /* store header */
+ out[0] = 0x17;
+
+ /* store values */
+ x = 2;
+ STORE_V(utctime->YY);
+ STORE_V(utctime->MM);
+ STORE_V(utctime->DD);
+ STORE_V(utctime->hh);
+ STORE_V(utctime->mm);
+ STORE_V(utctime->ss);
+
+ if (utctime->off_mm || utctime->off_hh) {
+ out[x++] = der_ia5_char_encode(utctime->off_dir ? '-' : '+');
+ STORE_V(utctime->off_hh);
+ STORE_V(utctime->off_mm);
+ } else {
+ out[x++] = der_ia5_char_encode('Z');
+ }
+
+ /* store length */
+ out[1] = (unsigned char)(x - 2);
+
+ /* all good let's return */
+ *outlen = x;
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utctime/der_encode_utctime.c,v $ */
+/* $Revision: 1.10 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_encode_utf8_string.c
+ ASN.1 DER, encode a UTF8 STRING, Tom St Denis
+ */
+
+
+#ifdef LTC_DER
+
+/**
+ Store an UTF8 STRING
+ @param in The array of UTF8 to store (one per wchar_t)
+ @param inlen The number of UTF8 to store
+ @param out [out] The destination for the DER encoded UTF8 STRING
+ @param outlen [in/out] The max size and resulting size of the DER UTF8 STRING
+ @return CRYPT_OK if successful
+ */
+int der_encode_utf8_string(const wchar_t *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen) {
+ unsigned long x, y, len;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* get the size */
+ for (x = len = 0; x < inlen; x++) {
+ if ((in[x] < 0) || (in[x] > 0x1FFFF)) {
+ return CRYPT_INVALID_ARG;
+ }
+ len += der_utf8_charsize(in[x]);
+ }
+
+ if (len < 128) {
+ y = 2 + len;
+ } else if (len < 256) {
+ y = 3 + len;
+ } else if (len < 65536UL) {
+ y = 4 + len;
+ } else if (len < 16777216UL) {
+ y = 5 + len;
+ } else {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* too big? */
+ if (y > *outlen) {
+ *outlen = len;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ /* encode the header+len */
+ x = 0;
+ out[x++] = 0x0C;
+ if (len < 128) {
+ out[x++] = (unsigned char)len;
+ } else if (len < 256) {
+ out[x++] = 0x81;
+ out[x++] = (unsigned char)len;
+ } else if (len < 65536UL) {
+ out[x++] = 0x82;
+ out[x++] = (unsigned char)((len >> 8) & 255);
+ out[x++] = (unsigned char)(len & 255);
+ } else if (len < 16777216UL) {
+ out[x++] = 0x83;
+ out[x++] = (unsigned char)((len >> 16) & 255);
+ out[x++] = (unsigned char)((len >> 8) & 255);
+ out[x++] = (unsigned char)(len & 255);
+ } else {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* store UTF8 */
+ for (y = 0; y < inlen; y++) {
+ switch (der_utf8_charsize(in[y])) {
+ case 1:
+ out[x++] = (unsigned char)in[y];
+ break;
+
+ case 2:
+ out[x++] = 0xC0 | ((in[y] >> 6) & 0x1F);
+ out[x++] = 0x80 | (in[y] & 0x3F);
+ break;
+
+ case 3:
+ out[x++] = 0xE0 | ((in[y] >> 12) & 0x0F);
+ out[x++] = 0x80 | ((in[y] >> 6) & 0x3F);
+ out[x++] = 0x80 | (in[y] & 0x3F);
+ break;
+
+ case 4:
+ out[x++] = 0xF0 | ((in[y] >> 18) & 0x07);
+ out[x++] = 0x80 | ((in[y] >> 12) & 0x3F);
+ out[x++] = 0x80 | ((in[y] >> 6) & 0x3F);
+ out[x++] = 0x80 | (in[y] & 0x3F);
+ break;
+ }
+ }
+
+ /* retun length */
+ *outlen = x;
+
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utf8/der_encode_utf8_string.c,v $ */
+/* $Revision: 1.9 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_length_bit_string.c
+ ASN.1 DER, get length of BIT STRING, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/**
+ Gets length of DER encoding of BIT STRING
+ @param nbits The number of bits in the string to encode
+ @param outlen [out] The length of the DER encoding for the given string
+ @return CRYPT_OK if successful
+ */
+int der_length_bit_string(unsigned long nbits, unsigned long *outlen) {
+ unsigned long nbytes;
+
+ LTC_ARGCHK(outlen != NULL);
+
+ /* get the number of the bytes */
+ nbytes = (nbits >> 3) + ((nbits & 7) ? 1 : 0) + 1;
+
+ if (nbytes < 128) {
+ /* 03 LL PP DD DD DD ... */
+ *outlen = 2 + nbytes;
+ } else if (nbytes < 256) {
+ /* 03 81 LL PP DD DD DD ... */
+ *outlen = 3 + nbytes;
+ } else if (nbytes < 65536) {
+ /* 03 82 LL LL PP DD DD DD ... */
+ *outlen = 4 + nbytes;
+ } else {
+ return CRYPT_INVALID_ARG;
+ }
+
+ return CRYPT_OK;
+}
+#endif
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/bit/der_length_bit_string.c,v $ */
+/* $Revision: 1.3 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_length_boolean.c
+ ASN.1 DER, get length of a BOOLEAN, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/**
+ Gets length of DER encoding of a BOOLEAN
+ @param outlen [out] The length of the DER encoding
+ @return CRYPT_OK if successful
+ */
+int der_length_boolean(unsigned long *outlen) {
+ LTC_ARGCHK(outlen != NULL);
+ *outlen = 3;
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/boolean/der_length_boolean.c,v $ */
+/* $Revision: 1.3 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_length_ia5_string.c
+ ASN.1 DER, get length of IA5 STRING, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+static const struct {
+ int code, value;
+} ia5_table[] = {
+ { '\0', 0 },
+ { '\a', 7 },
+ { '\b', 8 },
+ { '\t', 9 },
+ { '\n', 10 },
+ { '\f', 12 },
+ { '\r', 13 },
+ { ' ', 32 },
+ { '!', 33 },
+ { '"', 34 },
+ { '#', 35 },
+ { '$', 36 },
+ { '%', 37 },
+ { '&', 38 },
+ { '\'', 39 },
+ { '(', 40 },
+ { ')', 41 },
+ { '*', 42 },
+ { '+', 43 },
+ { ',', 44 },
+ { '-', 45 },
+ { '.', 46 },
+ { '/', 47 },
+ { '0', 48 },
+ { '1', 49 },
+ { '2', 50 },
+ { '3', 51 },
+ { '4', 52 },
+ { '5', 53 },
+ { '6', 54 },
+ { '7', 55 },
+ { '8', 56 },
+ { '9', 57 },
+ { ':', 58 },
+ { ';', 59 },
+ { '<', 60 },
+ { '=', 61 },
+ { '>', 62 },
+ { '?', 63 },
+ { '@', 64 },
+ { 'A', 65 },
+ { 'B', 66 },
+ { 'C', 67 },
+ { 'D', 68 },
+ { 'E', 69 },
+ { 'F', 70 },
+ { 'G', 71 },
+ { 'H', 72 },
+ { 'I', 73 },
+ { 'J', 74 },
+ { 'K', 75 },
+ { 'L', 76 },
+ { 'M', 77 },
+ { 'N', 78 },
+ { 'O', 79 },
+ { 'P', 80 },
+ { 'Q', 81 },
+ { 'R', 82 },
+ { 'S', 83 },
+ { 'T', 84 },
+ { 'U', 85 },
+ { 'V', 86 },
+ { 'W', 87 },
+ { 'X', 88 },
+ { 'Y', 89 },
+ { 'Z', 90 },
+ { '[', 91 },
+ { '\\', 92 },
+ { ']', 93 },
+ { '^', 94 },
+ { '_', 95 },
+ { '`', 96 },
+ { 'a', 97 },
+ { 'b', 98 },
+ { 'c', 99 },
+ { 'd', 100 },
+ { 'e', 101 },
+ { 'f', 102 },
+ { 'g', 103 },
+ { 'h', 104 },
+ { 'i', 105 },
+ { 'j', 106 },
+ { 'k', 107 },
+ { 'l', 108 },
+ { 'm', 109 },
+ { 'n', 110 },
+ { 'o', 111 },
+ { 'p', 112 },
+ { 'q', 113 },
+ { 'r', 114 },
+ { 's', 115 },
+ { 't', 116 },
+ { 'u', 117 },
+ { 'v', 118 },
+ { 'w', 119 },
+ { 'x', 120 },
+ { 'y', 121 },
+ { 'z', 122 },
+ { '{', 123 },
+ { '|', 124 },
+ { '}', 125 },
+ { '~', 126 }
+};
+
+int der_ia5_char_encode(int c) {
+ int x;
+
+ for (x = 0; x < (int)(sizeof(ia5_table) / sizeof(ia5_table[0])); x++) {
+ if (ia5_table[x].code == c) {
+ return ia5_table[x].value;
+ }
+ }
+ return -1;
+}
+
+int der_ia5_value_decode(int v) {
+ int x;
+
+ for (x = 0; x < (int)(sizeof(ia5_table) / sizeof(ia5_table[0])); x++) {
+ if (ia5_table[x].value == v) {
+ return ia5_table[x].code;
+ }
+ }
+ return -1;
+}
+
+/**
+ Gets length of DER encoding of IA5 STRING
+ @param octets The values you want to encode
+ @param noctets The number of octets in the string to encode
+ @param outlen [out] The length of the DER encoding for the given string
+ @return CRYPT_OK if successful
+ */
+int der_length_ia5_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen) {
+ unsigned long x;
+
+ LTC_ARGCHK(outlen != NULL);
+ LTC_ARGCHK(octets != NULL);
+
+ /* scan string for validity */
+ for (x = 0; x < noctets; x++) {
+ if (der_ia5_char_encode(octets[x]) == -1) {
+ return CRYPT_INVALID_ARG;
+ }
+ }
+
+ if (noctets < 128) {
+ /* 16 LL DD DD DD ... */
+ *outlen = 2 + noctets;
+ } else if (noctets < 256) {
+ /* 16 81 LL DD DD DD ... */
+ *outlen = 3 + noctets;
+ } else if (noctets < 65536UL) {
+ /* 16 82 LL LL DD DD DD ... */
+ *outlen = 4 + noctets;
+ } else if (noctets < 16777216UL) {
+ /* 16 83 LL LL LL DD DD DD ... */
+ *outlen = 5 + noctets;
+ } else {
+ return CRYPT_INVALID_ARG;
+ }
+
+ return CRYPT_OK;
+}
+#endif
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/ia5/der_length_ia5_string.c,v $ */
+/* $Revision: 1.3 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_length_integer.c
+ ASN.1 DER, get length of encoding, Tom St Denis
+ */
+
+
+#ifdef LTC_DER
+
+/**
+ Gets length of DER encoding of num
+ @param num The int to get the size of
+ @param outlen [out] The length of the DER encoding for the given integer
+ @return CRYPT_OK if successful
+ */
+int der_length_integer(void *num, unsigned long *outlen) {
+ unsigned long z, len;
+ int leading_zero;
+
+ LTC_ARGCHK(num != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ if (mp_cmp_d(num, 0) != LTC_MP_LT) {
+ /* positive */
+
+ /* we only need a leading zero if the msb of the first byte is one */
+ if (((mp_count_bits(num) & 7) == 0) || (mp_iszero(num) == LTC_MP_YES)) {
+ leading_zero = 1;
+ } else {
+ leading_zero = 0;
+ }
+
+ /* size for bignum */
+ z = len = leading_zero + mp_unsigned_bin_size(num);
+ } else {
+ /* it's negative */
+ /* find power of 2 that is a multiple of eight and greater than count bits */
+ leading_zero = 0;
+ z = mp_count_bits(num);
+ z = z + (8 - (z & 7));
+ if (((mp_cnt_lsb(num) + 1) == mp_count_bits(num)) && ((mp_count_bits(num) & 7) == 0)) --z;
+ len = z = z >> 3;
+ }
+
+ /* now we need a length */
+ if (z < 128) {
+ /* short form */
+ ++len;
+ } else {
+ /* long form (relies on z != 0), assumes length bytes < 128 */
+ ++len;
+
+ while (z) {
+ ++len;
+ z >>= 8;
+ }
+ }
+
+ /* we need a 0x02 to indicate it's INTEGER */
+ ++len;
+
+ /* return length */
+ *outlen = len;
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/integer/der_length_integer.c,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_length_object_identifier.c
+ ASN.1 DER, get length of Object Identifier, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+unsigned long der_object_identifier_bits(unsigned long x) {
+ unsigned long c;
+
+ x &= 0xFFFFFFFF;
+ c = 0;
+ while (x) {
+ ++c;
+ x >>= 1;
+ }
+ return c;
+}
+
+/**
+ Gets length of DER encoding of Object Identifier
+ @param nwords The number of OID words
+ @param words The actual OID words to get the size of
+ @param outlen [out] The length of the DER encoding for the given string
+ @return CRYPT_OK if successful
+ */
+int der_length_object_identifier(unsigned long *words, unsigned long nwords, unsigned long *outlen) {
+ unsigned long y, z, t, wordbuf;
+
+ LTC_ARGCHK(words != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+
+ /* must be >= 2 words */
+ if (nwords < 2) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* word1 = 0,1,2,3 and word2 0..39 */
+ if ((words[0] > 3) || ((words[0] < 2) && (words[1] > 39))) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* leading word is the first two */
+ z = 0;
+ wordbuf = words[0] * 40 + words[1];
+ for (y = 1; y < nwords; y++) {
+ t = der_object_identifier_bits(wordbuf);
+ z += t / 7 + ((t % 7) ? 1 : 0) + (wordbuf == 0 ? 1 : 0);
+ if (y < nwords - 1) {
+ /* grab next word */
+ wordbuf = words[y + 1];
+ }
+ }
+
+ /* now depending on the length our length encoding changes */
+ if (z < 128) {
+ z += 2;
+ } else if (z < 256) {
+ z += 3;
+ } else if (z < 65536UL) {
+ z += 4;
+ } else {
+ return CRYPT_INVALID_ARG;
+ }
+
+ *outlen = z;
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/object_identifier/der_length_object_identifier.c,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_length_octet_string.c
+ ASN.1 DER, get length of OCTET STRING, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/**
+ Gets length of DER encoding of OCTET STRING
+ @param noctets The number of octets in the string to encode
+ @param outlen [out] The length of the DER encoding for the given string
+ @return CRYPT_OK if successful
+ */
+int der_length_octet_string(unsigned long noctets, unsigned long *outlen) {
+ LTC_ARGCHK(outlen != NULL);
+
+ if (noctets < 128) {
+ /* 04 LL DD DD DD ... */
+ *outlen = 2 + noctets;
+ } else if (noctets < 256) {
+ /* 04 81 LL DD DD DD ... */
+ *outlen = 3 + noctets;
+ } else if (noctets < 65536UL) {
+ /* 04 82 LL LL DD DD DD ... */
+ *outlen = 4 + noctets;
+ } else if (noctets < 16777216UL) {
+ /* 04 83 LL LL LL DD DD DD ... */
+ *outlen = 5 + noctets;
+ } else {
+ return CRYPT_INVALID_ARG;
+ }
+
+ return CRYPT_OK;
+}
+#endif
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/octet/der_length_octet_string.c,v $ */
+/* $Revision: 1.3 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_length_printable_string.c
+ ASN.1 DER, get length of Printable STRING, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+static const struct {
+ int code, value;
+} printable_table[] = {
+ { ' ', 32 },
+ { '\'', 39 },
+ { '(', 40 },
+ { ')', 41 },
+ { '+', 43 },
+ { ',', 44 },
+ { '-', 45 },
+ { '.', 46 },
+ { '/', 47 },
+ { '0', 48 },
+ { '1', 49 },
+ { '2', 50 },
+ { '3', 51 },
+ { '4', 52 },
+ { '5', 53 },
+ { '6', 54 },
+ { '7', 55 },
+ { '8', 56 },
+ { '9', 57 },
+ { ':', 58 },
+ { '=', 61 },
+ { '?', 63 },
+ { 'A', 65 },
+ { 'B', 66 },
+ { 'C', 67 },
+ { 'D', 68 },
+ { 'E', 69 },
+ { 'F', 70 },
+ { 'G', 71 },
+ { 'H', 72 },
+ { 'I', 73 },
+ { 'J', 74 },
+ { 'K', 75 },
+ { 'L', 76 },
+ { 'M', 77 },
+ { 'N', 78 },
+ { 'O', 79 },
+ { 'P', 80 },
+ { 'Q', 81 },
+ { 'R', 82 },
+ { 'S', 83 },
+ { 'T', 84 },
+ { 'U', 85 },
+ { 'V', 86 },
+ { 'W', 87 },
+ { 'X', 88 },
+ { 'Y', 89 },
+ { 'Z', 90 },
+ { 'a', 97 },
+ { 'b', 98 },
+ { 'c', 99 },
+ { 'd', 100 },
+ { 'e', 101 },
+ { 'f', 102 },
+ { 'g', 103 },
+ { 'h', 104 },
+ { 'i', 105 },
+ { 'j', 106 },
+ { 'k', 107 },
+ { 'l', 108 },
+ { 'm', 109 },
+ { 'n', 110 },
+ { 'o', 111 },
+ { 'p', 112 },
+ { 'q', 113 },
+ { 'r', 114 },
+ { 's', 115 },
+ { 't', 116 },
+ { 'u', 117 },
+ { 'v', 118 },
+ { 'w', 119 },
+ { 'x', 120 },
+ { 'y', 121 },
+ { 'z', 122 },
+};
+
+int der_printable_char_encode(int c) {
+ int x;
+
+ for (x = 0; x < (int)(sizeof(printable_table) / sizeof(printable_table[0])); x++) {
+ if (printable_table[x].code == c) {
+ return printable_table[x].value;
+ }
+ }
+ return -1;
+}
+
+int der_printable_value_decode(int v) {
+ int x;
+
+ for (x = 0; x < (int)(sizeof(printable_table) / sizeof(printable_table[0])); x++) {
+ if (printable_table[x].value == v) {
+ return printable_table[x].code;
+ }
+ }
+ return -1;
+}
+
+/**
+ Gets length of DER encoding of Printable STRING
+ @param octets The values you want to encode
+ @param noctets The number of octets in the string to encode
+ @param outlen [out] The length of the DER encoding for the given string
+ @return CRYPT_OK if successful
+ */
+int der_length_printable_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen) {
+ unsigned long x;
+
+ LTC_ARGCHK(outlen != NULL);
+ LTC_ARGCHK(octets != NULL);
+
+ /* scan string for validity */
+ for (x = 0; x < noctets; x++) {
+ if (der_printable_char_encode(octets[x]) == -1) {
+ return CRYPT_INVALID_ARG;
+ }
+ }
+
+ if (noctets < 128) {
+ /* 16 LL DD DD DD ... */
+ *outlen = 2 + noctets;
+ } else if (noctets < 256) {
+ /* 16 81 LL DD DD DD ... */
+ *outlen = 3 + noctets;
+ } else if (noctets < 65536UL) {
+ /* 16 82 LL LL DD DD DD ... */
+ *outlen = 4 + noctets;
+ } else if (noctets < 16777216UL) {
+ /* 16 83 LL LL LL DD DD DD ... */
+ *outlen = 5 + noctets;
+ } else {
+ return CRYPT_INVALID_ARG;
+ }
+
+ return CRYPT_OK;
+}
+#endif
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/printable_string/der_length_printable_string.c,v $ */
+/* $Revision: 1.3 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_length_sequence.c
+ ASN.1 DER, length a SEQUENCE, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/**
+ Get the length of a DER sequence
+ @param list The sequences of items in the SEQUENCE
+ @param inlen The number of items
+ @param outlen [out] The length required in octets to store it
+ @return CRYPT_OK on success
+ */
+int der_length_sequence(ltc_asn1_list *list, unsigned long inlen,
+ unsigned long *outlen) {
+ int err, type;
+ unsigned long size, x, y, z, i;
+ void *data;
+
+ LTC_ARGCHK(list != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* get size of output that will be required */
+ y = 0;
+ for (i = 0; i < inlen; i++) {
+ type = list[i].type;
+ size = list[i].size;
+ data = list[i].data;
+
+ if (type == LTC_ASN1_EOL) {
+ break;
+ }
+
+ switch (type) {
+ case LTC_ASN1_BOOLEAN:
+ if ((err = der_length_boolean(&x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_INTEGER:
+ if ((err = der_length_integer(data, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_SHORT_INTEGER:
+ if ((err = der_length_short_integer(*((unsigned long *)data), &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_BIT_STRING:
+ if ((err = der_length_bit_string(size, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_OCTET_STRING:
+ if ((err = der_length_octet_string(size, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_NULL:
+ y += 2;
+ break;
+
+ case LTC_ASN1_OBJECT_IDENTIFIER:
+ if ((err = der_length_object_identifier(data, size, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_IA5_STRING:
+ if ((err = der_length_ia5_string(data, size, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_PRINTABLE_STRING:
+ if ((err = der_length_printable_string(data, size, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_UTCTIME:
+ if ((err = der_length_utctime(data, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_UTF8_STRING:
+ if ((err = der_length_utf8_string(data, size, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+ case LTC_ASN1_SET:
+ case LTC_ASN1_SETOF:
+ case LTC_ASN1_SEQUENCE:
+ if ((err = der_length_sequence(data, size, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ y += x;
+ break;
+
+
+ default:
+ err = CRYPT_INVALID_ARG;
+ goto LBL_ERR;
+ }
+ }
+
+ /* calc header size */
+ z = y;
+ if (y < 128) {
+ y += 2;
+ } else if (y < 256) {
+ /* 0x30 0x81 LL */
+ y += 3;
+ } else if (y < 65536UL) {
+ /* 0x30 0x82 LL LL */
+ y += 4;
+ } else if (y < 16777216UL) {
+ /* 0x30 0x83 LL LL LL */
+ y += 5;
+ } else {
+ err = CRYPT_INVALID_ARG;
+ goto LBL_ERR;
+ }
+
+ /* store size */
+ *outlen = y;
+ err = CRYPT_OK;
+
+LBL_ERR:
+ return err;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_length_sequence.c,v $ */
+/* $Revision: 1.14 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_length_short_integer.c
+ ASN.1 DER, get length of encoding, Tom St Denis
+ */
+
+
+#ifdef LTC_DER
+
+/**
+ Gets length of DER encoding of num
+ @param num The integer to get the size of
+ @param outlen [out] The length of the DER encoding for the given integer
+ @return CRYPT_OK if successful
+ */
+int der_length_short_integer(unsigned long num, unsigned long *outlen) {
+ unsigned long z, y, len;
+
+ LTC_ARGCHK(outlen != NULL);
+
+ /* force to 32 bits */
+ num &= 0xFFFFFFFFUL;
+
+ /* get the number of bytes */
+ z = 0;
+ y = num;
+ while (y) {
+ ++z;
+ y >>= 8;
+ }
+
+ /* handle zero */
+ if (z == 0) {
+ z = 1;
+ }
+
+ /* we need a 0x02 to indicate it's INTEGER */
+ len = 1;
+
+ /* length byte */
+ ++len;
+
+ /* bytes in value */
+ len += z;
+
+ /* see if msb is set */
+ len += (num & (1UL << ((z << 3) - 1))) ? 1 : 0;
+
+ /* return length */
+ *outlen = len;
+
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/short_integer/der_length_short_integer.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_length_utctime.c
+ ASN.1 DER, get length of UTCTIME, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/**
+ Gets length of DER encoding of UTCTIME
+ @param utctime The UTC time structure to get the size of
+ @param outlen [out] The length of the DER encoding
+ @return CRYPT_OK if successful
+ */
+int der_length_utctime(ltc_utctime *utctime, unsigned long *outlen) {
+ LTC_ARGCHK(outlen != NULL);
+ LTC_ARGCHK(utctime != NULL);
+
+ if ((utctime->off_hh == 0) && (utctime->off_mm == 0)) {
+ /* we encode as YYMMDDhhmmssZ */
+ *outlen = 2 + 13;
+ } else {
+ /* we encode as YYMMDDhhmmss{+|-}hh'mm' */
+ *outlen = 2 + 17;
+ }
+
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utctime/der_length_utctime.c,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_length_utf8_string.c
+ ASN.1 DER, get length of UTF8 STRING, Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/** Return the size in bytes of a UTF-8 character
+ @param c The UTF-8 character to measure
+ @return The size in bytes
+ */
+unsigned long der_utf8_charsize(const wchar_t c) {
+ if (c <= 0x7F) {
+ return 1;
+ } else if (c <= 0x7FF) {
+ return 2;
+ } else if (c <= 0xFFFF) {
+ return 3;
+ } else {
+ return 4;
+ }
+}
+
+/**
+ Gets length of DER encoding of UTF8 STRING
+ @param in The characters to measure the length of
+ @param noctets The number of octets in the string to encode
+ @param outlen [out] The length of the DER encoding for the given string
+ @return CRYPT_OK if successful
+ */
+int der_length_utf8_string(const wchar_t *in, unsigned long noctets, unsigned long *outlen) {
+ unsigned long x, len;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ len = 0;
+ for (x = 0; x < noctets; x++) {
+ if ((in[x] < 0) || (in[x] > 0x10FFFF)) {
+ return CRYPT_INVALID_ARG;
+ }
+ len += der_utf8_charsize(in[x]);
+ }
+
+ if (len < 128) {
+ /* 0C LL DD DD DD ... */
+ *outlen = 2 + len;
+ } else if (len < 256) {
+ /* 0C 81 LL DD DD DD ... */
+ *outlen = 3 + len;
+ } else if (len < 65536UL) {
+ /* 0C 82 LL LL DD DD DD ... */
+ *outlen = 4 + len;
+ } else if (len < 16777216UL) {
+ /* 0C 83 LL LL LL DD DD DD ... */
+ *outlen = 5 + len;
+ } else {
+ return CRYPT_INVALID_ARG;
+ }
+
+ return CRYPT_OK;
+}
+#endif
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utf8/der_length_utf8_string.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file der_sequence_free.c
+ ASN.1 DER, free's a structure allocated by der_decode_sequence_flexi(), Tom St Denis
+ */
+
+#ifdef LTC_DER
+
+/**
+ Free memory allocated by der_decode_sequence_flexi()
+ @param in The list to free
+ */
+void der_sequence_free(ltc_asn1_list *in) {
+ ltc_asn1_list *l;
+
+ /* walk to the start of the chain */
+ while (in->prev != NULL || in->parent != NULL) {
+ if (in->parent != NULL) {
+ in = in->parent;
+ } else {
+ in = in->prev;
+ }
+ }
+
+ /* now walk the list and free stuff */
+ while (in != NULL) {
+ /* is there a child? */
+ if (in->child) {
+ /* disconnect */
+ in->child->parent = NULL;
+ der_sequence_free(in->child);
+ }
+
+ switch (in->type) {
+ case LTC_ASN1_SET:
+ case LTC_ASN1_SETOF:
+ case LTC_ASN1_SEQUENCE:
+ break;
+
+ case LTC_ASN1_INTEGER:
+ if (in->data != NULL) {
+ mp_clear(in->data);
+ }
+ break;
+
+ default:
+ if (in->data != NULL) {
+ XFREE(in->data);
+ }
+ }
+
+ /* move to next and free current */
+ l = in->next;
+ XFREE(in);
+ in = l;
+ }
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_sequence_free.c,v $ */
+/* $Revision: 1.4 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ecc.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+/* This holds the key settings. ***MUST*** be organized by size from smallest to largest. */
+const ltc_ecc_set_type ltc_ecc_sets[] = {
+ #ifdef ECC112
+ {
+ 14,
+ "SECP112R1",
+ "DB7C2ABF62E35E668076BEAD208B",
+ "659EF8BA043916EEDE8911702B22",
+ "DB7C2ABF62E35E7628DFAC6561C5",
+ "09487239995A5EE76B55F9C2F098",
+ "A89CE5AF8724C0A23E0E0FF77500"
+ },
+ #endif
+ #ifdef ECC128
+ {
+ 16,
+ "SECP128R1",
+ "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF",
+ "E87579C11079F43DD824993C2CEE5ED3",
+ "FFFFFFFE0000000075A30D1B9038A115",
+ "161FF7528B899B2D0C28607CA52C5B86",
+ "CF5AC8395BAFEB13C02DA292DDED7A83",
+ },
+ #endif
+ #ifdef ECC160
+ {
+ 20,
+ "SECP160R1",
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF",
+ "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45",
+ "0100000000000000000001F4C8F927AED3CA752257",
+ "4A96B5688EF573284664698968C38BB913CBFC82",
+ "23A628553168947D59DCC912042351377AC5FB32",
+ },
+ #endif
+ #ifdef ECC192
+ {
+ 24,
+ "ECC-192",
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF",
+ "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1",
+ "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831",
+ "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012",
+ "7192B95FFC8DA78631011ED6B24CDD573F977A11E794811",
+ },
+ #endif
+ #ifdef ECC224
+ {
+ 28,
+ "ECC-224",
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001",
+ "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4",
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D",
+ "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21",
+ "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34",
+ },
+ #endif
+ #ifdef ECC256
+ {
+ 32,
+ "ECC-256",
+ "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
+ "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",
+ "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
+ "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
+ "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
+ },
+ #endif
+ #ifdef ECC384
+ {
+ 48,
+ "ECC-384",
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
+ "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
+ "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
+ "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
+ },
+ #endif
+ #ifdef ECC521
+ {
+ 66,
+ "ECC-521",
+ "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+ "51953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00",
+ "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
+ "C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
+ "11839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
+ },
+ #endif
+ {
+ 0,
+ NULL, NULL, NULL, NULL, NULL, NULL
+ }
+};
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc.c,v $ */
+/* $Revision: 1.40 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ecc_ansi_x963_export.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+/** ECC X9.63 (Sec. 4.3.6) uncompressed export
+ @param key Key to export
+ @param out [out] destination of export
+ @param outlen [in/out] Length of destination and final output size
+ Return CRYPT_OK on success
+ */
+int ecc_ansi_x963_export(ecc_key *key, unsigned char *out, unsigned long *outlen) {
+ unsigned char buf[ECC_BUF_SIZE];
+ unsigned long numlen;
+
+ LTC_ARGCHK(key != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ if (ltc_ecc_is_valid_idx(key->idx) == 0) {
+ return CRYPT_INVALID_ARG;
+ }
+ numlen = key->dp->size;
+
+ if (*outlen < (1 + 2 * numlen)) {
+ *outlen = 1 + 2 * numlen;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ /* store byte 0x04 */
+ out[0] = 0x04;
+
+ /* pad and store x */
+ zeromem(buf, sizeof(buf));
+ mp_to_unsigned_bin(key->pubkey.x, buf + (numlen - mp_unsigned_bin_size(key->pubkey.x)));
+ XMEMCPY(out + 1, buf, numlen);
+
+ /* pad and store y */
+ zeromem(buf, sizeof(buf));
+ mp_to_unsigned_bin(key->pubkey.y, buf + (numlen - mp_unsigned_bin_size(key->pubkey.y)));
+ XMEMCPY(out + 1 + numlen, buf, numlen);
+
+ *outlen = 1 + 2 * numlen;
+ return CRYPT_OK;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_ansi_x963_export.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ecc_ansi_x963_import.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+/** Import an ANSI X9.63 format public key
+ @param in The input data to read
+ @param inlen The length of the input data
+ @param key [out] destination to store imported key \
+ */
+int ecc_ansi_x963_import(const unsigned char *in, unsigned long inlen, ecc_key *key) {
+ return ecc_ansi_x963_import_ex(in, inlen, key, NULL);
+}
+
+int ecc_ansi_x963_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, ltc_ecc_set_type *dp) {
+ int x, err;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(key != NULL);
+
+ /* must be odd */
+ if ((inlen & 1) == 0) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* init key */
+ if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK) {
+ return CRYPT_MEM;
+ }
+
+ /* check for 4, 6 or 7 */
+ if ((in[0] != 4) && (in[0] != 6) && (in[0] != 7)) {
+ err = CRYPT_INVALID_PACKET;
+ goto error;
+ }
+
+ /* read data */
+ if ((err = mp_read_unsigned_bin(key->pubkey.x, (unsigned char *)in + 1, (inlen - 1) >> 1)) != CRYPT_OK) {
+ goto error;
+ }
+
+ if ((err = mp_read_unsigned_bin(key->pubkey.y, (unsigned char *)in + 1 + ((inlen - 1) >> 1), (inlen - 1) >> 1)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) {
+ goto error;
+ }
+
+ if (dp == NULL) {
+ /* determine the idx */
+ for (x = 0; ltc_ecc_sets[x].size != 0; x++) {
+ if ((unsigned)ltc_ecc_sets[x].size >= ((inlen - 1) >> 1)) {
+ break;
+ }
+ }
+ if (ltc_ecc_sets[x].size == 0) {
+ err = CRYPT_INVALID_PACKET;
+ goto error;
+ }
+ /* set the idx */
+ key->idx = x;
+ key->dp = <c_ecc_sets[x];
+ } else {
+ if (((inlen - 1) >> 1) != (unsigned long)dp->size) {
+ err = CRYPT_INVALID_PACKET;
+ goto error;
+ }
+ key->idx = -1;
+ key->dp = dp;
+ }
+ key->type = PK_PUBLIC;
+
+ /* we're done */
+ return CRYPT_OK;
+error:
+ mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
+ return err;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_ansi_x963_import.c,v $ */
+/* $Revision: 1.11 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ecc_decrypt_key.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+/**
+ Decrypt an ECC encrypted key
+ @param in The ciphertext
+ @param inlen The length of the ciphertext (octets)
+ @param out [out] The plaintext
+ @param outlen [in/out] The max size and resulting size of the plaintext
+ @param key The corresponding private ECC key
+ @return CRYPT_OK if successful
+ */
+int ecc_decrypt_key(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ ecc_key *key) {
+ unsigned char *ecc_shared, *skey, *pub_expt;
+ unsigned long x, y, hashOID[32];
+ int hash, err;
+ ecc_key pubkey;
+ ltc_asn1_list decode[3];
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+ LTC_ARGCHK(key != NULL);
+
+ /* right key type? */
+ if (key->type != PK_PRIVATE) {
+ return CRYPT_PK_NOT_PRIVATE;
+ }
+
+ /* decode to find out hash */
+ LTC_SET_ASN1(decode, 0, LTC_ASN1_OBJECT_IDENTIFIER, hashOID, sizeof(hashOID) / sizeof(hashOID[0]));
+
+ if ((err = der_decode_sequence(in, inlen, decode, 1)) != CRYPT_OK) {
+ return err;
+ }
+
+ hash = find_hash_oid(hashOID, decode[0].size);
+ if (hash_is_valid(hash) != CRYPT_OK) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* we now have the hash! */
+
+ /* allocate memory */
+ pub_expt = XMALLOC(ECC_BUF_SIZE);
+ ecc_shared = XMALLOC(ECC_BUF_SIZE);
+ skey = XMALLOC(MAXBLOCKSIZE);
+ if ((pub_expt == NULL) || (ecc_shared == NULL) || (skey == NULL)) {
+ if (pub_expt != NULL) {
+ XFREE(pub_expt);
+ }
+ if (ecc_shared != NULL) {
+ XFREE(ecc_shared);
+ }
+ if (skey != NULL) {
+ XFREE(skey);
+ }
+ return CRYPT_MEM;
+ }
+ LTC_SET_ASN1(decode, 1, LTC_ASN1_OCTET_STRING, pub_expt, ECC_BUF_SIZE);
+ LTC_SET_ASN1(decode, 2, LTC_ASN1_OCTET_STRING, skey, MAXBLOCKSIZE);
+
+ /* read the structure in now */
+ if ((err = der_decode_sequence(in, inlen, decode, 3)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ /* import ECC key from packet */
+ if ((err = ecc_import(decode[1].data, decode[1].size, &pubkey)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ /* make shared key */
+ x = ECC_BUF_SIZE;
+ if ((err = ecc_shared_secret(key, &pubkey, ecc_shared, &x)) != CRYPT_OK) {
+ ecc_free(&pubkey);
+ goto LBL_ERR;
+ }
+ ecc_free(&pubkey);
+
+ y = MIN(ECC_BUF_SIZE, MAXBLOCKSIZE);
+ if ((err = hash_memory(hash, ecc_shared, x, ecc_shared, &y)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ /* ensure the hash of the shared secret is at least as big as the encrypt itself */
+ if (decode[2].size > y) {
+ err = CRYPT_INVALID_PACKET;
+ goto LBL_ERR;
+ }
+
+ /* avoid buffer overflow */
+ if (*outlen < decode[2].size) {
+ *outlen = decode[2].size;
+ err = CRYPT_BUFFER_OVERFLOW;
+ goto LBL_ERR;
+ }
+
+ /* Decrypt the key */
+ for (x = 0; x < decode[2].size; x++) {
+ out[x] = skey[x] ^ ecc_shared[x];
+ }
+ *outlen = x;
+
+ err = CRYPT_OK;
+LBL_ERR:
+ #ifdef LTC_CLEAN_STACK
+ zeromem(pub_expt, ECC_BUF_SIZE);
+ zeromem(ecc_shared, ECC_BUF_SIZE);
+ zeromem(skey, MAXBLOCKSIZE);
+ #endif
+
+ XFREE(pub_expt);
+ XFREE(ecc_shared);
+ XFREE(skey);
+
+ return err;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_decrypt_key.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ecc_encrypt_key.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+/**
+ Encrypt a symmetric key with ECC
+ @param in The symmetric key you want to encrypt
+ @param inlen The length of the key to encrypt (octets)
+ @param out [out] The destination for the ciphertext
+ @param outlen [in/out] The max size and resulting size of the ciphertext
+ @param prng An active PRNG state
+ @param wprng The index of the PRNG you wish to use
+ @param hash The index of the hash you want to use
+ @param key The ECC key you want to encrypt to
+ @return CRYPT_OK if successful
+ */
+int ecc_encrypt_key(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ prng_state *prng, int wprng, int hash,
+ ecc_key *key) {
+ unsigned char *pub_expt, *ecc_shared, *skey;
+ ecc_key pubkey;
+ unsigned long x, y, pubkeysize;
+ int err;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+ LTC_ARGCHK(key != NULL);
+
+ /* check that wprng/cipher/hash are not invalid */
+ if ((err = prng_is_valid(wprng)) != CRYPT_OK) {
+ return err;
+ }
+
+ if ((err = hash_is_valid(hash)) != CRYPT_OK) {
+ return err;
+ }
+
+ if (inlen > hash_descriptor[hash].hashsize) {
+ return CRYPT_INVALID_HASH;
+ }
+
+ /* make a random key and export the public copy */
+ if ((err = ecc_make_key_ex(prng, wprng, &pubkey, key->dp)) != CRYPT_OK) {
+ return err;
+ }
+
+ pub_expt = XMALLOC(ECC_BUF_SIZE);
+ ecc_shared = XMALLOC(ECC_BUF_SIZE);
+ skey = XMALLOC(MAXBLOCKSIZE);
+ if ((pub_expt == NULL) || (ecc_shared == NULL) || (skey == NULL)) {
+ if (pub_expt != NULL) {
+ XFREE(pub_expt);
+ }
+ if (ecc_shared != NULL) {
+ XFREE(ecc_shared);
+ }
+ if (skey != NULL) {
+ XFREE(skey);
+ }
+ ecc_free(&pubkey);
+ return CRYPT_MEM;
+ }
+
+ pubkeysize = ECC_BUF_SIZE;
+ if ((err = ecc_export(pub_expt, &pubkeysize, PK_PUBLIC, &pubkey)) != CRYPT_OK) {
+ ecc_free(&pubkey);
+ goto LBL_ERR;
+ }
+
+ /* make random key */
+ x = ECC_BUF_SIZE;
+ if ((err = ecc_shared_secret(&pubkey, key, ecc_shared, &x)) != CRYPT_OK) {
+ ecc_free(&pubkey);
+ goto LBL_ERR;
+ }
+ ecc_free(&pubkey);
+ y = MAXBLOCKSIZE;
+ if ((err = hash_memory(hash, ecc_shared, x, skey, &y)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ /* Encrypt key */
+ for (x = 0; x < inlen; x++) {
+ skey[x] ^= in[x];
+ }
+
+ err = der_encode_sequence_multi(out, outlen,
+ LTC_ASN1_OBJECT_IDENTIFIER, hash_descriptor[hash].OIDlen, hash_descriptor[hash].OID,
+ LTC_ASN1_OCTET_STRING, pubkeysize, pub_expt,
+ LTC_ASN1_OCTET_STRING, inlen, skey,
+ LTC_ASN1_EOL, 0UL, NULL);
+
+LBL_ERR:
+ #ifdef LTC_CLEAN_STACK
+ /* clean up */
+ zeromem(pub_expt, ECC_BUF_SIZE);
+ zeromem(ecc_shared, ECC_BUF_SIZE);
+ zeromem(skey, MAXBLOCKSIZE);
+ #endif
+
+ XFREE(skey);
+ XFREE(ecc_shared);
+ XFREE(pub_expt);
+
+ return err;
+}
+#endif
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_encrypt_key.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ecc_export.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+/**
+ Export an ECC key as a binary packet
+ @param out [out] Destination for the key
+ @param outlen [in/out] Max size and resulting size of the exported key
+ @param type The type of key you want to export (PK_PRIVATE or PK_PUBLIC)
+ @param key The key to export
+ @return CRYPT_OK if successful
+ */
+int ecc_export(unsigned char *out, unsigned long *outlen, int type, ecc_key *key) {
+ int err;
+ unsigned char flags[1];
+ unsigned long key_size;
+
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+ LTC_ARGCHK(key != NULL);
+
+ /* type valid? */
+ if ((key->type != PK_PRIVATE) && (type == PK_PRIVATE)) {
+ return CRYPT_PK_TYPE_MISMATCH;
+ }
+
+ if (ltc_ecc_is_valid_idx(key->idx) == 0) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* we store the NIST byte size */
+ key_size = key->dp->size;
+
+ if (type == PK_PRIVATE) {
+ flags[0] = 1;
+ err = der_encode_sequence_multi(out, outlen,
+ LTC_ASN1_BIT_STRING, 1UL, flags,
+ LTC_ASN1_SHORT_INTEGER, 1UL, &key_size,
+ LTC_ASN1_INTEGER, 1UL, key->pubkey.x,
+ LTC_ASN1_INTEGER, 1UL, key->pubkey.y,
+ LTC_ASN1_INTEGER, 1UL, key->k,
+ LTC_ASN1_EOL, 0UL, NULL);
+ } else {
+ flags[0] = 0;
+ err = der_encode_sequence_multi(out, outlen,
+ LTC_ASN1_BIT_STRING, 1UL, flags,
+ LTC_ASN1_SHORT_INTEGER, 1UL, &key_size,
+ LTC_ASN1_INTEGER, 1UL, key->pubkey.x,
+ LTC_ASN1_INTEGER, 1UL, key->pubkey.y,
+ LTC_ASN1_EOL, 0UL, NULL);
+ }
+
+ return err;
+}
+#endif
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_export.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ecc_free.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+/**
+ Free an ECC key from memory
+ @param key The key you wish to free
+ */
+void ecc_free(ecc_key *key) {
+ LTC_ARGCHKVD(key != NULL);
+ mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
+}
+#endif
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_free.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ecc_get_size.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+/**
+ Get the size of an ECC key
+ @param key The key to get the size of
+ @return The size (octets) of the key or INT_MAX on error
+ */
+int ecc_get_size(ecc_key *key) {
+ LTC_ARGCHK(key != NULL);
+ if (ltc_ecc_is_valid_idx(key->idx))
+ return key->dp->size;
+ else
+ return INT_MAX; /* large value known to cause it to fail when passed to ecc_make_key() */
+}
+#endif
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_get_size.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ecc_import.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+static int is_point(ecc_key *key) {
+ void *prime, *b, *t1, *t2;
+ int err;
+
+ if ((err = mp_init_multi(&prime, &b, &t1, &t2, NULL)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* load prime and b */
+ if ((err = mp_read_radix(prime, key->dp->prime, 16)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_read_radix(b, key->dp->B, 16)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* compute y^2 */
+ if ((err = mp_sqr(key->pubkey.y, t1)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* compute x^3 */
+ if ((err = mp_sqr(key->pubkey.x, t2)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_mod(t2, prime, t2)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_mul(key->pubkey.x, t2, t2)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* compute y^2 - x^3 */
+ if ((err = mp_sub(t1, t2, t1)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* compute y^2 - x^3 + 3x */
+ if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_mod(t1, prime, t1)) != CRYPT_OK) {
+ goto error;
+ }
+ while (mp_cmp_d(t1, 0) == LTC_MP_LT) {
+ if ((err = mp_add(t1, prime, t1)) != CRYPT_OK) {
+ goto error;
+ }
+ }
+ while (mp_cmp(t1, prime) != LTC_MP_LT) {
+ if ((err = mp_sub(t1, prime, t1)) != CRYPT_OK) {
+ goto error;
+ }
+ }
+
+ /* compare to b */
+ if (mp_cmp(t1, b) != LTC_MP_EQ) {
+ err = CRYPT_INVALID_PACKET;
+ } else {
+ err = CRYPT_OK;
+ }
+
+error:
+ mp_clear_multi(prime, b, t1, t2, NULL);
+ return err;
+}
+
+/**
+ Import an ECC key from a binary packet
+ @param in The packet to import
+ @param inlen The length of the packet
+ @param key [out] The destination of the import
+ @return CRYPT_OK if successful, upon error all allocated memory will be freed
+ */
+int ecc_import(const unsigned char *in, unsigned long inlen, ecc_key *key) {
+ return ecc_import_ex(in, inlen, key, NULL);
+}
+
+/**
+ Import an ECC key from a binary packet, using user supplied domain params rather than one of the NIST ones
+ @param in The packet to import
+ @param inlen The length of the packet
+ @param key [out] The destination of the import
+ @param dp pointer to user supplied params; must be the same as the params used when exporting
+ @return CRYPT_OK if successful, upon error all allocated memory will be freed
+ */
+int ecc_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_set_type *dp) {
+ unsigned long key_size;
+ unsigned char flags[1];
+ int err;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(key != NULL);
+ LTC_ARGCHK(ltc_mp.name != NULL);
+
+ /* init key */
+ if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK) {
+ return CRYPT_MEM;
+ }
+
+ /* find out what type of key it is */
+ if ((err = der_decode_sequence_multi(in, inlen,
+ LTC_ASN1_BIT_STRING, 1UL, &flags,
+ LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) {
+ goto done;
+ }
+
+
+ if (flags[0] == 1) {
+ /* private key */
+ key->type = PK_PRIVATE;
+ if ((err = der_decode_sequence_multi(in, inlen,
+ LTC_ASN1_BIT_STRING, 1UL, flags,
+ LTC_ASN1_SHORT_INTEGER, 1UL, &key_size,
+ LTC_ASN1_INTEGER, 1UL, key->pubkey.x,
+ LTC_ASN1_INTEGER, 1UL, key->pubkey.y,
+ LTC_ASN1_INTEGER, 1UL, key->k,
+ LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) {
+ goto done;
+ }
+ } else {
+ /* public key */
+ key->type = PK_PUBLIC;
+ if ((err = der_decode_sequence_multi(in, inlen,
+ LTC_ASN1_BIT_STRING, 1UL, flags,
+ LTC_ASN1_SHORT_INTEGER, 1UL, &key_size,
+ LTC_ASN1_INTEGER, 1UL, key->pubkey.x,
+ LTC_ASN1_INTEGER, 1UL, key->pubkey.y,
+ LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+
+ if (dp == NULL) {
+ /* find the idx */
+ for (key->idx = 0; ltc_ecc_sets[key->idx].size && (unsigned long)ltc_ecc_sets[key->idx].size != key_size; ++key->idx);
+ if (ltc_ecc_sets[key->idx].size == 0) {
+ err = CRYPT_INVALID_PACKET;
+ goto done;
+ }
+ key->dp = <c_ecc_sets[key->idx];
+ } else {
+ key->idx = -1;
+ key->dp = dp;
+ }
+ /* set z */
+ if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) {
+ goto done;
+ }
+
+ /* is it a point on the curve? */
+ if ((err = is_point(key)) != CRYPT_OK) {
+ goto done;
+ }
+
+ /* we're good */
+ return CRYPT_OK;
+done:
+ mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
+ return err;
+}
+#endif
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_import.c,v $ */
+/* $Revision: 1.13 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ecc_make_key.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+/**
+ Make a new ECC key
+ @param prng An active PRNG state
+ @param wprng The index of the PRNG you wish to use
+ @param keysize The keysize for the new key (in octets from 20 to 65 bytes)
+ @param key [out] Destination of the newly created key
+ @return CRYPT_OK if successful, upon error all allocated memory will be freed
+ */
+int ecc_make_key(prng_state *prng, int wprng, int keysize, ecc_key *key) {
+ int x, err;
+
+ /* find key size */
+ for (x = 0; (keysize > ltc_ecc_sets[x].size) && (ltc_ecc_sets[x].size != 0); x++);
+ keysize = ltc_ecc_sets[x].size;
+
+ if ((keysize > ECC_MAXSIZE) || (ltc_ecc_sets[x].size == 0)) {
+ return CRYPT_INVALID_KEYSIZE;
+ }
+ err = ecc_make_key_ex(prng, wprng, key, <c_ecc_sets[x]);
+ key->idx = x;
+ return err;
+}
+
+int ecc_make_key_ex(prng_state *prng, int wprng, ecc_key *key, const ltc_ecc_set_type *dp) {
+ int err;
+ ecc_point *base;
+ void *prime, *order;
+ unsigned char *buf;
+ int keysize;
+
+ LTC_ARGCHK(key != NULL);
+ LTC_ARGCHK(ltc_mp.name != NULL);
+ LTC_ARGCHK(dp != NULL);
+
+ /* good prng? */
+ if ((err = prng_is_valid(wprng)) != CRYPT_OK) {
+ return err;
+ }
+
+ key->idx = -1;
+ key->dp = dp;
+ keysize = dp->size;
+
+ /* allocate ram */
+ base = NULL;
+ buf = XMALLOC(ECC_MAXSIZE);
+ if (buf == NULL) {
+ return CRYPT_MEM;
+ }
+
+ /* make up random string */
+ if (prng_descriptor[wprng].read(buf, (unsigned long)keysize, prng) != (unsigned long)keysize) {
+ err = CRYPT_ERROR_READPRNG;
+ goto ERR_BUF;
+ }
+
+ /* setup the key variables */
+ if ((err = mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, &prime, &order, NULL)) != CRYPT_OK) {
+ goto ERR_BUF;
+ }
+ base = ltc_ecc_new_point();
+ if (base == NULL) {
+ err = CRYPT_MEM;
+ goto errkey;
+ }
+
+ /* read in the specs for this key */
+ if ((err = mp_read_radix(prime, (char *)key->dp->prime, 16)) != CRYPT_OK) {
+ goto errkey;
+ }
+ if ((err = mp_read_radix(order, (char *)key->dp->order, 16)) != CRYPT_OK) {
+ goto errkey;
+ }
+ if ((err = mp_read_radix(base->x, (char *)key->dp->Gx, 16)) != CRYPT_OK) {
+ goto errkey;
+ }
+ if ((err = mp_read_radix(base->y, (char *)key->dp->Gy, 16)) != CRYPT_OK) {
+ goto errkey;
+ }
+ if ((err = mp_set(base->z, 1)) != CRYPT_OK) {
+ goto errkey;
+ }
+ if ((err = mp_read_unsigned_bin(key->k, (unsigned char *)buf, keysize)) != CRYPT_OK) {
+ goto errkey;
+ }
+
+ /* the key should be smaller than the order of base point */
+ if (mp_cmp(key->k, order) != LTC_MP_LT) {
+ if ((err = mp_mod(key->k, order, key->k)) != CRYPT_OK) {
+ goto errkey;
+ }
+ }
+ /* make the public key */
+ if ((err = ltc_mp.ecc_ptmul(key->k, base, &key->pubkey, prime, 1)) != CRYPT_OK) {
+ goto errkey;
+ }
+ key->type = PK_PRIVATE;
+
+ /* free up ram */
+ err = CRYPT_OK;
+ goto cleanup;
+errkey:
+ mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
+cleanup:
+ ltc_ecc_del_point(base);
+ mp_clear_multi(prime, order, NULL);
+ERR_BUF:
+ #ifdef LTC_CLEAN_STACK
+ zeromem(buf, ECC_MAXSIZE);
+ #endif
+ XFREE(buf);
+ return err;
+}
+#endif
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_make_key.c,v $ */
+/* $Revision: 1.13 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ecc_shared_secret.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+/**
+ Create an ECC shared secret between two keys
+ @param private_key The private ECC key
+ @param public_key The public key
+ @param out [out] Destination of the shared secret (Conforms to EC-DH from ANSI X9.63)
+ @param outlen [in/out] The max size and resulting size of the shared secret
+ @return CRYPT_OK if successful
+ */
+int ecc_shared_secret(ecc_key *private_key, ecc_key *public_key,
+ unsigned char *out, unsigned long *outlen) {
+ unsigned long x;
+ ecc_point *result;
+ void *prime;
+ int err;
+
+ LTC_ARGCHK(private_key != NULL);
+ LTC_ARGCHK(public_key != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* type valid? */
+ if (private_key->type != PK_PRIVATE) {
+ return CRYPT_PK_NOT_PRIVATE;
+ }
+
+ if ((ltc_ecc_is_valid_idx(private_key->idx) == 0) || (ltc_ecc_is_valid_idx(public_key->idx) == 0)) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ if (XSTRCMP(private_key->dp->name, public_key->dp->name) != 0) {
+ return CRYPT_PK_TYPE_MISMATCH;
+ }
+
+ /* make new point */
+ result = ltc_ecc_new_point();
+ if (result == NULL) {
+ return CRYPT_MEM;
+ }
+
+ if ((err = mp_init(&prime)) != CRYPT_OK) {
+ ltc_ecc_del_point(result);
+ return err;
+ }
+
+ if ((err = mp_read_radix(prime, (char *)private_key->dp->prime, 16)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = ltc_mp.ecc_ptmul(private_key->k, &public_key->pubkey, result, prime, 1)) != CRYPT_OK) {
+ goto done;
+ }
+
+ x = (unsigned long)mp_unsigned_bin_size(prime);
+ if (*outlen < x) {
+ *outlen = x;
+ err = CRYPT_BUFFER_OVERFLOW;
+ goto done;
+ }
+ zeromem(out, x);
+ if ((err = mp_to_unsigned_bin(result->x, out + (x - mp_unsigned_bin_size(result->x)))) != CRYPT_OK) {
+ goto done;
+ }
+
+ err = CRYPT_OK;
+ *outlen = x;
+done:
+ mp_clear(prime);
+ ltc_ecc_del_point(result);
+ return err;
+}
+#endif
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_shared_secret.c,v $ */
+/* $Revision: 1.10 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ecc_sign_hash.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+/**
+ Sign a message digest
+ @param in The message digest to sign
+ @param inlen The length of the digest
+ @param out [out] The destination for the signature
+ @param outlen [in/out] The max size and resulting size of the signature
+ @param prng An active PRNG state
+ @param wprng The index of the PRNG you wish to use
+ @param key A private ECC key
+ @return CRYPT_OK if successful
+ */
+int ecc_sign_hash(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ prng_state *prng, int wprng, ecc_key *key) {
+ ecc_key pubkey;
+ void *r, *s, *e, *p;
+ int err;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+ LTC_ARGCHK(key != NULL);
+
+ /* is this a private key? */
+ if (key->type != PK_PRIVATE) {
+ return CRYPT_PK_NOT_PRIVATE;
+ }
+
+ /* is the IDX valid ? */
+ if (ltc_ecc_is_valid_idx(key->idx) != 1) {
+ return CRYPT_PK_INVALID_TYPE;
+ }
+
+ if ((err = prng_is_valid(wprng)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* get the hash and load it as a bignum into 'e' */
+ /* init the bignums */
+ if ((err = mp_init_multi(&r, &s, &p, &e, NULL)) != CRYPT_OK) {
+ return err;
+ }
+ if ((err = mp_read_radix(p, (char *)key->dp->order, 16)) != CRYPT_OK) {
+ goto errnokey;
+ }
+ if ((err = mp_read_unsigned_bin(e, (unsigned char *)in, (int)inlen)) != CRYPT_OK) {
+ goto errnokey;
+ }
+
+ /* make up a key and export the public copy */
+ for ( ; ; ) {
+ if ((err = ecc_make_key_ex(prng, wprng, &pubkey, key->dp)) != CRYPT_OK) {
+ goto errnokey;
+ }
+
+ /* find r = x1 mod n */
+ if ((err = mp_mod(pubkey.pubkey.x, p, r)) != CRYPT_OK) {
+ goto error;
+ }
+
+ if (mp_iszero(r) == LTC_MP_YES) {
+ ecc_free(&pubkey);
+ } else {
+ /* find s = (e + xr)/k */
+ if ((err = mp_invmod(pubkey.k, p, pubkey.k)) != CRYPT_OK) {
+ goto error;
+ } /* k = 1/k */
+ if ((err = mp_mulmod(key->k, r, p, s)) != CRYPT_OK) {
+ goto error;
+ } /* s = xr */
+ if ((err = mp_add(e, s, s)) != CRYPT_OK) {
+ goto error;
+ } /* s = e + xr */
+ if ((err = mp_mod(s, p, s)) != CRYPT_OK) {
+ goto error;
+ } /* s = e + xr */
+ if ((err = mp_mulmod(s, pubkey.k, p, s)) != CRYPT_OK) {
+ goto error;
+ } /* s = (e + xr)/k */
+ ecc_free(&pubkey);
+ if (mp_iszero(s) == LTC_MP_NO) {
+ break;
+ }
+ }
+ }
+
+ /* store as SEQUENCE { r, s -- integer } */
+ err = der_encode_sequence_multi(out, outlen,
+ LTC_ASN1_INTEGER, 1UL, r,
+ LTC_ASN1_INTEGER, 1UL, s,
+ LTC_ASN1_EOL, 0UL, NULL);
+ goto errnokey;
+error:
+ ecc_free(&pubkey);
+errnokey:
+ mp_clear_multi(r, s, p, e, NULL);
+ return err;
+}
+#endif
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_sign_hash.c,v $ */
+/* $Revision: 1.11 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ecc_sizes.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+void ecc_sizes(int *low, int *high) {
+ int i;
+
+ LTC_ARGCHKVD(low != NULL);
+ LTC_ARGCHKVD(high != NULL);
+
+ *low = INT_MAX;
+ *high = 0;
+ for (i = 0; ltc_ecc_sets[i].size != 0; i++) {
+ if (ltc_ecc_sets[i].size < *low) {
+ *low = ltc_ecc_sets[i].size;
+ }
+ if (ltc_ecc_sets[i].size > *high) {
+ *high = ltc_ecc_sets[i].size;
+ }
+ }
+}
+#endif
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_sizes.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ecc_test.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+/**
+ Perform on the ECC system
+ @return CRYPT_OK if successful
+ */
+int ecc_test(void) {
+ void *modulus, *order;
+ ecc_point *G, *GG;
+ int i, err, primality;
+
+ if ((err = mp_init_multi(&modulus, &order, NULL)) != CRYPT_OK) {
+ return err;
+ }
+
+ G = ltc_ecc_new_point();
+ GG = ltc_ecc_new_point();
+ if ((G == NULL) || (GG == NULL)) {
+ mp_clear_multi(modulus, order, NULL);
+ ltc_ecc_del_point(G);
+ ltc_ecc_del_point(GG);
+ return CRYPT_MEM;
+ }
+
+ for (i = 0; ltc_ecc_sets[i].size; i++) {
+ #if 0
+ printf("Testing %d\n", ltc_ecc_sets[i].size);
+ #endif
+ if ((err = mp_read_radix(modulus, (char *)ltc_ecc_sets[i].prime, 16)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_read_radix(order, (char *)ltc_ecc_sets[i].order, 16)) != CRYPT_OK) {
+ goto done;
+ }
+
+ /* is prime actually prime? */
+ if ((err = mp_prime_is_prime(modulus, 8, &primality)) != CRYPT_OK) {
+ goto done;
+ }
+ if (primality == 0) {
+ err = CRYPT_FAIL_TESTVECTOR;
+ goto done;
+ }
+
+ /* is order prime ? */
+ if ((err = mp_prime_is_prime(order, 8, &primality)) != CRYPT_OK) {
+ goto done;
+ }
+ if (primality == 0) {
+ err = CRYPT_FAIL_TESTVECTOR;
+ goto done;
+ }
+
+ if ((err = mp_read_radix(G->x, (char *)ltc_ecc_sets[i].Gx, 16)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_read_radix(G->y, (char *)ltc_ecc_sets[i].Gy, 16)) != CRYPT_OK) {
+ goto done;
+ }
+ mp_set(G->z, 1);
+
+ /* then we should have G == (order + 1)G */
+ if ((err = mp_add_d(order, 1, order)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = ltc_mp.ecc_ptmul(order, G, GG, modulus, 1)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((mp_cmp(G->x, GG->x) != LTC_MP_EQ) || (mp_cmp(G->y, GG->y) != LTC_MP_EQ)) {
+ err = CRYPT_FAIL_TESTVECTOR;
+ goto done;
+ }
+ }
+ err = CRYPT_OK;
+done:
+ ltc_ecc_del_point(GG);
+ ltc_ecc_del_point(G);
+ mp_clear_multi(order, modulus, NULL);
+ return err;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_test.c,v $ */
+/* $Revision: 1.12 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ecc_verify_hash.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+/* verify
+ *
+ * w = s^-1 mod n
+ * u1 = xw
+ * u2 = rw
+ * X = u1*G + u2*Q
+ * v = X_x1 mod n
+ * accept if v == r
+ */
+
+/**
+ Verify an ECC signature
+ @param sig The signature to verify
+ @param siglen The length of the signature (octets)
+ @param hash The hash (message digest) that was signed
+ @param hashlen The length of the hash (octets)
+ @param stat Result of signature, 1==valid, 0==invalid
+ @param key The corresponding public ECC key
+ @return CRYPT_OK if successful (even if the signature is not valid)
+ */
+int ecc_verify_hash(const unsigned char *sig, unsigned long siglen,
+ const unsigned char *hash, unsigned long hashlen,
+ int *stat, ecc_key *key) {
+ ecc_point *mG, *mQ;
+ void *r, *s, *v, *w, *u1, *u2, *e, *p, *m;
+ void *mp;
+ int err;
+
+ LTC_ARGCHK(sig != NULL);
+ LTC_ARGCHK(hash != NULL);
+ LTC_ARGCHK(stat != NULL);
+ LTC_ARGCHK(key != NULL);
+
+ /* default to invalid signature */
+ *stat = 0;
+ mp = NULL;
+
+ /* is the IDX valid ? */
+ if (ltc_ecc_is_valid_idx(key->idx) != 1) {
+ return CRYPT_PK_INVALID_TYPE;
+ }
+
+ /* allocate ints */
+ if ((err = mp_init_multi(&r, &s, &v, &w, &u1, &u2, &p, &e, &m, NULL)) != CRYPT_OK) {
+ return CRYPT_MEM;
+ }
+
+ /* allocate points */
+ mG = ltc_ecc_new_point();
+ mQ = ltc_ecc_new_point();
+ if ((mQ == NULL) || (mG == NULL)) {
+ err = CRYPT_MEM;
+ goto error;
+ }
+
+ /* parse header */
+ if ((err = der_decode_sequence_multi(sig, siglen,
+ LTC_ASN1_INTEGER, 1UL, r,
+ LTC_ASN1_INTEGER, 1UL, s,
+ LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* get the order */
+ if ((err = mp_read_radix(p, (char *)key->dp->order, 16)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* get the modulus */
+ if ((err = mp_read_radix(m, (char *)key->dp->prime, 16)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* check for zero */
+ if (mp_iszero(r) || mp_iszero(s) || (mp_cmp(r, p) != LTC_MP_LT) || (mp_cmp(s, p) != LTC_MP_LT)) {
+ err = CRYPT_INVALID_PACKET;
+ goto error;
+ }
+
+ /* read hash */
+ if ((err = mp_read_unsigned_bin(e, (unsigned char *)hash, (int)hashlen)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* w = s^-1 mod n */
+ if ((err = mp_invmod(s, p, w)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* u1 = ew */
+ if ((err = mp_mulmod(e, w, p, u1)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* u2 = rw */
+ if ((err = mp_mulmod(r, w, p, u2)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* find mG and mQ */
+ if ((err = mp_read_radix(mG->x, (char *)key->dp->Gx, 16)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_read_radix(mG->y, (char *)key->dp->Gy, 16)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_set(mG->z, 1)) != CRYPT_OK) {
+ goto error;
+ }
+
+ if ((err = mp_copy(key->pubkey.x, mQ->x)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_copy(key->pubkey.y, mQ->y)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_copy(key->pubkey.z, mQ->z)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* compute u1*mG + u2*mQ = mG */
+ if (ltc_mp.ecc_mul2add == NULL) {
+ if ((err = ltc_mp.ecc_ptmul(u1, mG, mG, m, 0)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = ltc_mp.ecc_ptmul(u2, mQ, mQ, m, 0)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* find the montgomery mp */
+ if ((err = mp_montgomery_setup(m, &mp)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* add them */
+ if ((err = ltc_mp.ecc_ptadd(mQ, mG, mG, m, mp)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* reduce */
+ if ((err = ltc_mp.ecc_map(mG, m, mp)) != CRYPT_OK) {
+ goto error;
+ }
+ } else {
+ /* use Shamir's trick to compute u1*mG + u2*mQ using half of the doubles */
+ if ((err = ltc_mp.ecc_mul2add(mG, u1, mQ, u2, mG, m)) != CRYPT_OK) {
+ goto error;
+ }
+ }
+
+ /* v = X_x1 mod n */
+ if ((err = mp_mod(mG->x, p, v)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* does v == r */
+ if (mp_cmp(v, r) == LTC_MP_EQ) {
+ *stat = 1;
+ }
+
+ /* clear up and return */
+ err = CRYPT_OK;
+error:
+ ltc_ecc_del_point(mG);
+ ltc_ecc_del_point(mQ);
+ mp_clear_multi(r, s, v, w, u1, u2, p, e, m, NULL);
+ if (mp != NULL) {
+ mp_montgomery_free(mp);
+ }
+ return err;
+}
+#endif
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_verify_hash.c,v $ */
+/* $Revision: 1.14 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+
+/**
+ @file error_to_string.c
+ Convert error codes to ASCII strings, Tom St Denis
+ */
+
+static const char * const err_2_str[] =
+{
+ "CRYPT_OK",
+ "CRYPT_ERROR",
+ "Non-fatal 'no-operation' requested.",
+
+ "Invalid keysize for block cipher.",
+ "Invalid number of rounds for block cipher.",
+ "Algorithm failed test vectors.",
+
+ "Buffer overflow.",
+ "Invalid input packet.",
+
+ "Invalid number of bits for a PRNG.",
+ "Error reading the PRNG.",
+
+ "Invalid cipher specified.",
+ "Invalid hash specified.",
+ "Invalid PRNG specified.",
+
+ "Out of memory.",
+
+ "Invalid PK key or key type specified for function.",
+ "A private PK key is required.",
+
+ "Invalid argument provided.",
+ "File Not Found",
+
+ "Invalid PK type.",
+ "Invalid PK system.",
+ "Duplicate PK key found on keyring.",
+ "Key not found in keyring.",
+ "Invalid sized parameter.",
+
+ "Invalid size for prime.",
+};
+
+/**
+ Convert an LTC error code to ASCII
+ @param err The error code
+ @return A pointer to the ASCII NUL terminated string for the error or "Invalid error code." if the err code was not valid.
+ */
+const char *error_to_string(int err) {
+ if ((err < 0) || (err >= (int)(sizeof(err_2_str) / sizeof(err_2_str[0])))) {
+ return "Invalid error code.";
+ } else {
+ return err_2_str[err];
+ }
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/error_to_string.c,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+#define DESC_DEF_ONLY
+
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/math/gmp_desc.c,v $ */
+/* $Revision: 1.16 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file hash_file.c
+ Hash a file, Tom St Denis
+ */
+
+/**
+ @param hash The index of the hash desired
+ @param fname The name of the file you wish to hash
+ @param out [out] The destination of the digest
+ @param outlen [in/out] The max size and resulting size of the message digest
+ @result CRYPT_OK if successful
+ */
+int hash_file(int hash, const char *fname, unsigned char *out, unsigned long *outlen) {
+#ifdef LTC_NO_FILE
+ return CRYPT_NOP;
+#else
+ FILE *in;
+ int err;
+ LTC_ARGCHK(fname != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ if ((err = hash_is_valid(hash)) != CRYPT_OK) {
+ return err;
+ }
+
+ in = fopen(fname, "rb");
+ if (in == NULL) {
+ return CRYPT_FILE_NOTFOUND;
+ }
+
+ err = hash_filehandle(hash, in, out, outlen);
+ if (fclose(in) != 0) {
+ return CRYPT_ERROR;
+ }
+
+ return err;
+#endif
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_file.c,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2006/12/28 01:27:23 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file hash_filehandle.c
+ Hash open files, Tom St Denis
+ */
+
+/**
+ Hash data from an open file handle.
+ @param hash The index of the hash you want to use
+ @param in The FILE* handle of the file you want to hash
+ @param out [out] The destination of the digest
+ @param outlen [in/out] The max size and resulting size of the digest
+ @result CRYPT_OK if successful
+ */
+int hash_filehandle(int hash, FILE *in, unsigned char *out, unsigned long *outlen) {
+#ifdef LTC_NO_FILE
+ return CRYPT_NOP;
+#else
+ hash_state md;
+ unsigned char buf[512];
+ size_t x;
+ int err;
+
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+ LTC_ARGCHK(in != NULL);
+
+ if ((err = hash_is_valid(hash)) != CRYPT_OK) {
+ return err;
+ }
+
+ if (*outlen < hash_descriptor[hash].hashsize) {
+ *outlen = hash_descriptor[hash].hashsize;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+ if ((err = hash_descriptor[hash].init(&md)) != CRYPT_OK) {
+ return err;
+ }
+
+ *outlen = hash_descriptor[hash].hashsize;
+ do {
+ x = fread(buf, 1, sizeof(buf), in);
+ if ((err = hash_descriptor[hash].process(&md, buf, x)) != CRYPT_OK) {
+ return err;
+ }
+ } while (x == sizeof(buf));
+ err = hash_descriptor[hash].done(&md, out);
+
+ #ifdef LTC_CLEAN_STACK
+ zeromem(buf, sizeof(buf));
+ #endif
+ return err;
+#endif
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_filehandle.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2006/12/28 01:27:23 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file hash_memory.c
+ Hash memory helper, Tom St Denis
+ */
+
+/**
+ Hash a block of memory and store the digest.
+ @param hash The index of the hash you wish to use
+ @param in The data you wish to hash
+ @param inlen The length of the data to hash (octets)
+ @param out [out] Where to store the digest
+ @param outlen [in/out] Max size and resulting size of the digest
+ @return CRYPT_OK if successful
+ */
+int hash_memory(int hash, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) {
+ hash_state *md;
+ int err;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ if ((err = hash_is_valid(hash)) != CRYPT_OK) {
+ return err;
+ }
+
+ if (*outlen < hash_descriptor[hash].hashsize) {
+ *outlen = hash_descriptor[hash].hashsize;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ md = XMALLOC(sizeof(hash_state));
+ if (md == NULL) {
+ return CRYPT_MEM;
+ }
+
+ if ((err = hash_descriptor[hash].init(md)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ if ((err = hash_descriptor[hash].process(md, in, inlen)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ err = hash_descriptor[hash].done(md, out);
+ *outlen = hash_descriptor[hash].hashsize;
+LBL_ERR:
+#ifdef LTC_CLEAN_STACK
+ zeromem(md, sizeof(hash_state));
+#endif
+ XFREE(md);
+
+ return err;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_memory.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2006/12/28 01:27:23 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+#include
+
+/**
+ @file hash_memory_multi.c
+ Hash (multiple buffers) memory helper, Tom St Denis
+ */
+
+/**
+ Hash multiple (non-adjacent) blocks of memory at once.
+ @param hash The index of the hash you wish to use
+ @param out [out] Where to store the digest
+ @param outlen [in/out] Max size and resulting size of the digest
+ @param in The data you wish to hash
+ @param inlen The length of the data to hash (octets)
+ @param ... tuples of (data,len) pairs to hash, terminated with a (NULL,x) (x=don't care)
+ @return CRYPT_OK if successful
+ */
+int hash_memory_multi(int hash, unsigned char *out, unsigned long *outlen,
+ const unsigned char *in, unsigned long inlen, ...) {
+ hash_state *md;
+ int err;
+ va_list args;
+ const unsigned char *curptr;
+ unsigned long curlen;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ if ((err = hash_is_valid(hash)) != CRYPT_OK) {
+ return err;
+ }
+
+ if (*outlen < hash_descriptor[hash].hashsize) {
+ *outlen = hash_descriptor[hash].hashsize;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ md = XMALLOC(sizeof(hash_state));
+ if (md == NULL) {
+ return CRYPT_MEM;
+ }
+
+ if ((err = hash_descriptor[hash].init(md)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ va_start(args, inlen);
+ curptr = in;
+ curlen = inlen;
+ for ( ; ; ) {
+ /* process buf */
+ if ((err = hash_descriptor[hash].process(md, curptr, curlen)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ /* step to next */
+ curptr = va_arg(args, const unsigned char *);
+ if (curptr == NULL) {
+ break;
+ }
+ curlen = va_arg(args, unsigned long);
+ }
+ err = hash_descriptor[hash].done(md, out);
+ *outlen = hash_descriptor[hash].hashsize;
+LBL_ERR:
+#ifdef LTC_CLEAN_STACK
+ zeromem(md, sizeof(hash_state));
+#endif
+ XFREE(md);
+ va_end(args);
+ return err;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_memory_multi.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2006/12/28 01:27:23 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ltc_ecc_is_valid_idx.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+/** Returns whether an ECC idx is valid or not
+ @param n The idx number to check
+ @return 1 if valid, 0 if not
+ */
+int ltc_ecc_is_valid_idx(int n) {
+ int x;
+
+ for (x = 0; ltc_ecc_sets[x].size != 0; x++);
+ /* -1 is a valid index --- indicating that the domain params were supplied by the user */
+ if ((n >= -1) && (n < x)) {
+ return 1;
+ }
+ return 0;
+}
+#endif
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_is_valid_idx.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ltc_ecc_map.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+/**
+ Map a projective jacbobian point back to affine space
+ @param P [in/out] The point to map
+ @param modulus The modulus of the field the ECC curve is in
+ @param mp The "b" value from montgomery_setup()
+ @return CRYPT_OK on success
+ */
+int ltc_ecc_map(ecc_point *P, void *modulus, void *mp) {
+ void *t1, *t2;
+ int err;
+
+ LTC_ARGCHK(P != NULL);
+ LTC_ARGCHK(modulus != NULL);
+ LTC_ARGCHK(mp != NULL);
+
+ if ((err = mp_init_multi(&t1, &t2, NULL)) != CRYPT_OK) {
+ return CRYPT_MEM;
+ }
+
+ /* first map z back to normal */
+ if ((err = mp_montgomery_reduce(P->z, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+
+ /* get 1/z */
+ if ((err = mp_invmod(P->z, modulus, t1)) != CRYPT_OK) {
+ goto done;
+ }
+
+ /* get 1/z^2 and 1/z^3 */
+ if ((err = mp_sqr(t1, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_mod(t2, modulus, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_mul(t1, t2, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_mod(t1, modulus, t1)) != CRYPT_OK) {
+ goto done;
+ }
+
+ /* multiply against x/y */
+ if ((err = mp_mul(P->x, t2, P->x)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(P->x, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_mul(P->y, t1, P->y)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(P->y, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_set(P->z, 1)) != CRYPT_OK) {
+ goto done;
+ }
+
+ err = CRYPT_OK;
+done:
+ mp_clear_multi(t1, t2, NULL);
+ return err;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_map.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ltc_ecc_mul2add.c
+ ECC Crypto, Shamir's Trick, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+ #ifdef LTC_ECC_SHAMIR
+
+/** Computes kA*A + kB*B = C using Shamir's Trick
+ @param A First point to multiply
+ @param kA What to multiple A by
+ @param B Second point to multiply
+ @param kB What to multiple B by
+ @param C [out] Destination point (can overlap with A or B
+ @param modulus Modulus for curve
+ @return CRYPT_OK on success
+ */
+int ltc_ecc_mul2add(ecc_point *A, void *kA,
+ ecc_point *B, void *kB,
+ ecc_point *C,
+ void *modulus) {
+ ecc_point *precomp[16];
+ unsigned bitbufA, bitbufB, lenA, lenB, len, x, y, nA, nB, nibble;
+ unsigned char *tA, *tB;
+ int err, first;
+ void *mp, *mu;
+
+ /* argchks */
+ LTC_ARGCHK(A != NULL);
+ LTC_ARGCHK(B != NULL);
+ LTC_ARGCHK(C != NULL);
+ LTC_ARGCHK(kA != NULL);
+ LTC_ARGCHK(kB != NULL);
+ LTC_ARGCHK(modulus != NULL);
+
+ /* allocate memory */
+ tA = XCALLOC(1, ECC_BUF_SIZE);
+ if (tA == NULL) {
+ return CRYPT_MEM;
+ }
+ tB = XCALLOC(1, ECC_BUF_SIZE);
+ if (tB == NULL) {
+ XFREE(tA);
+ return CRYPT_MEM;
+ }
+
+ /* get sizes */
+ lenA = mp_unsigned_bin_size(kA);
+ lenB = mp_unsigned_bin_size(kB);
+ len = MAX(lenA, lenB);
+
+ /* sanity check */
+ if ((lenA > ECC_BUF_SIZE) || (lenB > ECC_BUF_SIZE)) {
+ err = CRYPT_INVALID_ARG;
+ goto ERR_T;
+ }
+
+ /* extract and justify kA */
+ mp_to_unsigned_bin(kA, (len - lenA) + tA);
+
+ /* extract and justify kB */
+ mp_to_unsigned_bin(kB, (len - lenB) + tB);
+
+ /* allocate the table */
+ for (x = 0; x < 16; x++) {
+ precomp[x] = ltc_ecc_new_point();
+ if (precomp[x] == NULL) {
+ for (y = 0; y < x; ++y) {
+ ltc_ecc_del_point(precomp[y]);
+ }
+ err = CRYPT_MEM;
+ goto ERR_T;
+ }
+ }
+
+ /* init montgomery reduction */
+ if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) {
+ goto ERR_P;
+ }
+ if ((err = mp_init(&mu)) != CRYPT_OK) {
+ goto ERR_MP;
+ }
+ if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+
+ /* copy ones ... */
+ if ((err = mp_mulmod(A->x, mu, modulus, precomp[1]->x)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+ if ((err = mp_mulmod(A->y, mu, modulus, precomp[1]->y)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+ if ((err = mp_mulmod(A->z, mu, modulus, precomp[1]->z)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+
+ if ((err = mp_mulmod(B->x, mu, modulus, precomp[1 << 2]->x)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+ if ((err = mp_mulmod(B->y, mu, modulus, precomp[1 << 2]->y)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+ if ((err = mp_mulmod(B->z, mu, modulus, precomp[1 << 2]->z)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+
+ /* precomp [i,0](A + B) table */
+ if ((err = ltc_mp.ecc_ptdbl(precomp[1], precomp[2], modulus, mp)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+ if ((err = ltc_mp.ecc_ptadd(precomp[1], precomp[2], precomp[3], modulus, mp)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+
+ /* precomp [0,i](A + B) table */
+ if ((err = ltc_mp.ecc_ptdbl(precomp[1 << 2], precomp[2 << 2], modulus, mp)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+ if ((err = ltc_mp.ecc_ptadd(precomp[1 << 2], precomp[2 << 2], precomp[3 << 2], modulus, mp)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+
+ /* precomp [i,j](A + B) table (i != 0, j != 0) */
+ for (x = 1; x < 4; x++) {
+ for (y = 1; y < 4; y++) {
+ if ((err = ltc_mp.ecc_ptadd(precomp[x], precomp[(y << 2)], precomp[x + (y << 2)], modulus, mp)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+ }
+ }
+
+ nibble = 3;
+ first = 1;
+ bitbufA = tA[0];
+ bitbufB = tB[0];
+
+ /* for every byte of the multiplicands */
+ for (x = -1; ; ) {
+ /* grab a nibble */
+ if (++nibble == 4) {
+ ++x;
+ if (x == len) break;
+ bitbufA = tA[x];
+ bitbufB = tB[x];
+ nibble = 0;
+ }
+
+ /* extract two bits from both, shift/update */
+ nA = (bitbufA >> 6) & 0x03;
+ nB = (bitbufB >> 6) & 0x03;
+ bitbufA = (bitbufA << 2) & 0xFF;
+ bitbufB = (bitbufB << 2) & 0xFF;
+
+ /* if both zero, if first, continue */
+ if ((nA == 0) && (nB == 0) && (first == 1)) {
+ continue;
+ }
+
+ /* double twice, only if this isn't the first */
+ if (first == 0) {
+ /* double twice */
+ if ((err = ltc_mp.ecc_ptdbl(C, C, modulus, mp)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+ if ((err = ltc_mp.ecc_ptdbl(C, C, modulus, mp)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+ }
+
+ /* if not both zero */
+ if ((nA != 0) || (nB != 0)) {
+ if (first == 1) {
+ /* if first, copy from table */
+ first = 0;
+ if ((err = mp_copy(precomp[nA + (nB << 2)]->x, C->x)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+ if ((err = mp_copy(precomp[nA + (nB << 2)]->y, C->y)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+ if ((err = mp_copy(precomp[nA + (nB << 2)]->z, C->z)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+ } else {
+ /* if not first, add from table */
+ if ((err = ltc_mp.ecc_ptadd(C, precomp[nA + (nB << 2)], C, modulus, mp)) != CRYPT_OK) {
+ goto ERR_MU;
+ }
+ }
+ }
+ }
+
+ /* reduce to affine */
+ err = ltc_ecc_map(C, modulus, mp);
+
+ /* clean up */
+ERR_MU:
+ mp_clear(mu);
+ERR_MP:
+ mp_montgomery_free(mp);
+ERR_P:
+ for (x = 0; x < 16; x++) {
+ ltc_ecc_del_point(precomp[x]);
+ }
+ERR_T:
+ #ifdef LTC_CLEAN_STACK
+ zeromem(tA, ECC_BUF_SIZE);
+ zeromem(tB, ECC_BUF_SIZE);
+ #endif
+ XFREE(tA);
+ XFREE(tB);
+
+ return err;
+}
+ #endif
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_mul2add.c,v $ */
+/* $Revision: 1.8 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ltc_ecc_mulmod.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+ #ifndef LTC_ECC_TIMING_RESISTANT
+
+/* size of sliding window, don't change this! */
+ #define WINSIZE 4
+
+/**
+ Perform a point multiplication
+ @param k The scalar to multiply by
+ @param G The base point
+ @param R [out] Destination for kG
+ @param modulus The modulus of the field the ECC curve is in
+ @param map Boolean whether to map back to affine or not (1==map, 0 == leave in projective)
+ @return CRYPT_OK on success
+ */
+int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map) {
+ ecc_point *tG, *M[8];
+ int i, j, err;
+ void *mu, *mp;
+ unsigned long buf;
+ int first, bitbuf, bitcpy, bitcnt, mode, digidx;
+
+ LTC_ARGCHK(k != NULL);
+ LTC_ARGCHK(G != NULL);
+ LTC_ARGCHK(R != NULL);
+ LTC_ARGCHK(modulus != NULL);
+
+ /* init montgomery reduction */
+ if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) {
+ return err;
+ }
+ if ((err = mp_init(&mu)) != CRYPT_OK) {
+ mp_montgomery_free(mp);
+ return err;
+ }
+ if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) {
+ mp_montgomery_free(mp);
+ mp_clear(mu);
+ return err;
+ }
+
+ /* alloc ram for window temps */
+ for (i = 0; i < 8; i++) {
+ M[i] = ltc_ecc_new_point();
+ if (M[i] == NULL) {
+ for (j = 0; j < i; j++) {
+ ltc_ecc_del_point(M[j]);
+ }
+ mp_montgomery_free(mp);
+ mp_clear(mu);
+ return CRYPT_MEM;
+ }
+ }
+
+ /* make a copy of G incase R==G */
+ tG = ltc_ecc_new_point();
+ if (tG == NULL) {
+ err = CRYPT_MEM;
+ goto done;
+ }
+
+ /* tG = G and convert to montgomery */
+ if (mp_cmp_d(mu, 1) == LTC_MP_EQ) {
+ if ((err = mp_copy(G->x, tG->x)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_copy(G->y, tG->y)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_copy(G->z, tG->z)) != CRYPT_OK) {
+ goto done;
+ }
+ } else {
+ if ((err = mp_mulmod(G->x, mu, modulus, tG->x)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_mulmod(G->y, mu, modulus, tG->y)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_mulmod(G->z, mu, modulus, tG->z)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ mp_clear(mu);
+ mu = NULL;
+
+ /* calc the M tab, which holds kG for k==8..15 */
+ /* M[0] == 8G */
+ if ((err = ltc_mp.ecc_ptdbl(tG, M[0], modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = ltc_mp.ecc_ptdbl(M[0], M[0], modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = ltc_mp.ecc_ptdbl(M[0], M[0], modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+
+ /* now find (8+k)G for k=1..7 */
+ for (j = 9; j < 16; j++) {
+ if ((err = ltc_mp.ecc_ptadd(M[j - 9], tG, M[j - 8], modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+
+ /* setup sliding window */
+ mode = 0;
+ bitcnt = 1;
+ buf = 0;
+ digidx = mp_get_digit_count(k) - 1;
+ bitcpy = bitbuf = 0;
+ first = 1;
+
+ /* perform ops */
+ for ( ; ; ) {
+ /* grab next digit as required */
+ if (--bitcnt == 0) {
+ if (digidx == -1) {
+ break;
+ }
+ buf = mp_get_digit(k, digidx);
+ bitcnt = (int)ltc_mp.bits_per_digit;
+ --digidx;
+ }
+
+ /* grab the next msb from the ltiplicand */
+ i = (buf >> (ltc_mp.bits_per_digit - 1)) & 1;
+ buf <<= 1;
+
+ /* skip leading zero bits */
+ if ((mode == 0) && (i == 0)) {
+ continue;
+ }
+
+ /* if the bit is zero and mode == 1 then we double */
+ if ((mode == 1) && (i == 0)) {
+ if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ continue;
+ }
+
+ /* else we add it to the window */
+ bitbuf |= (i << (WINSIZE - ++bitcpy));
+ mode = 2;
+
+ if (bitcpy == WINSIZE) {
+ /* if this is the first window we do a simple copy */
+ if (first == 1) {
+ /* R = kG [k = first window] */
+ if ((err = mp_copy(M[bitbuf - 8]->x, R->x)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_copy(M[bitbuf - 8]->y, R->y)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_copy(M[bitbuf - 8]->z, R->z)) != CRYPT_OK) {
+ goto done;
+ }
+ first = 0;
+ } else {
+ /* normal window */
+ /* ok window is filled so double as required and add */
+ /* double first */
+ for (j = 0; j < WINSIZE; j++) {
+ if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+
+ /* then add, bitbuf will be 8..15 [8..2^WINSIZE] guaranteed */
+ if ((err = ltc_mp.ecc_ptadd(R, M[bitbuf - 8], R, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ /* empty window and reset */
+ bitcpy = bitbuf = 0;
+ mode = 1;
+ }
+ }
+
+ /* if bits remain then double/add */
+ if ((mode == 2) && (bitcpy > 0)) {
+ /* double then add */
+ for (j = 0; j < bitcpy; j++) {
+ /* only double if we have had at least one add first */
+ if (first == 0) {
+ if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+
+ bitbuf <<= 1;
+ if ((bitbuf & (1 << WINSIZE)) != 0) {
+ if (first == 1) {
+ /* first add, so copy */
+ if ((err = mp_copy(tG->x, R->x)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_copy(tG->y, R->y)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_copy(tG->z, R->z)) != CRYPT_OK) {
+ goto done;
+ }
+ first = 0;
+ } else {
+ /* then add */
+ if ((err = ltc_mp.ecc_ptadd(R, tG, R, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ }
+ }
+ }
+
+ /* map R back from projective space */
+ if (map) {
+ err = ltc_ecc_map(R, modulus, mp);
+ } else {
+ err = CRYPT_OK;
+ }
+done:
+ if (mu != NULL) {
+ mp_clear(mu);
+ }
+ mp_montgomery_free(mp);
+ ltc_ecc_del_point(tG);
+ for (i = 0; i < 8; i++) {
+ ltc_ecc_del_point(M[i]);
+ }
+ return err;
+}
+ #endif
+
+ #undef WINSIZE
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_mulmod.c,v $ */
+/* $Revision: 1.26 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ltc_ecc_mulmod_timing.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+ #ifdef LTC_ECC_TIMING_RESISTANT
+
+/**
+ Perform a point multiplication (timing resistant)
+ @param k The scalar to multiply by
+ @param G The base point
+ @param R [out] Destination for kG
+ @param modulus The modulus of the field the ECC curve is in
+ @param map Boolean whether to map back to affine or not (1==map, 0 == leave in projective)
+ @return CRYPT_OK on success
+ */
+int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map) {
+ ecc_point *tG, *M[3];
+ int i, j, err;
+ void *mu, *mp;
+ unsigned long buf;
+ int first, bitbuf, bitcpy, bitcnt, mode, digidx;
+
+ LTC_ARGCHK(k != NULL);
+ LTC_ARGCHK(G != NULL);
+ LTC_ARGCHK(R != NULL);
+ LTC_ARGCHK(modulus != NULL);
+
+ /* init montgomery reduction */
+ if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) {
+ return err;
+ }
+ if ((err = mp_init(&mu)) != CRYPT_OK) {
+ mp_montgomery_free(mp);
+ return err;
+ }
+ if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) {
+ mp_clear(mu);
+ mp_montgomery_free(mp);
+ return err;
+ }
+
+ /* alloc ram for window temps */
+ for (i = 0; i < 3; i++) {
+ M[i] = ltc_ecc_new_point();
+ if (M[i] == NULL) {
+ for (j = 0; j < i; j++) {
+ ltc_ecc_del_point(M[j]);
+ }
+ mp_clear(mu);
+ mp_montgomery_free(mp);
+ return CRYPT_MEM;
+ }
+ }
+
+ /* make a copy of G incase R==G */
+ tG = ltc_ecc_new_point();
+ if (tG == NULL) {
+ err = CRYPT_MEM;
+ goto done;
+ }
+
+ /* tG = G and convert to montgomery */
+ if ((err = mp_mulmod(G->x, mu, modulus, tG->x)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_mulmod(G->y, mu, modulus, tG->y)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_mulmod(G->z, mu, modulus, tG->z)) != CRYPT_OK) {
+ goto done;
+ }
+ mp_clear(mu);
+ mu = NULL;
+
+ /* calc the M tab */
+ /* M[0] == G */
+ if ((err = mp_copy(tG->x, M[0]->x)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_copy(tG->y, M[0]->y)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_copy(tG->z, M[0]->z)) != CRYPT_OK) {
+ goto done;
+ }
+ /* M[1] == 2G */
+ if ((err = ltc_mp.ecc_ptdbl(tG, M[1], modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+
+ /* setup sliding window */
+ mode = 0;
+ bitcnt = 1;
+ buf = 0;
+ digidx = mp_get_digit_count(k) - 1;
+ bitcpy = bitbuf = 0;
+ first = 1;
+
+ /* perform ops */
+ for ( ; ; ) {
+ /* grab next digit as required */
+ if (--bitcnt == 0) {
+ if (digidx == -1) {
+ break;
+ }
+ buf = mp_get_digit(k, digidx);
+ bitcnt = (int)MP_DIGIT_BIT;
+ --digidx;
+ }
+
+ /* grab the next msb from the ltiplicand */
+ i = (buf >> (MP_DIGIT_BIT - 1)) & 1;
+ buf <<= 1;
+
+ if ((mode == 0) && (i == 0)) {
+ /* dummy operations */
+ if ((err = ltc_mp.ecc_ptadd(M[0], M[1], M[2], modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = ltc_mp.ecc_ptdbl(M[1], M[2], modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ continue;
+ }
+
+ if ((mode == 0) && (i == 1)) {
+ mode = 1;
+ /* dummy operations */
+ if ((err = ltc_mp.ecc_ptadd(M[0], M[1], M[2], modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = ltc_mp.ecc_ptdbl(M[1], M[2], modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ continue;
+ }
+
+ if ((err = ltc_mp.ecc_ptadd(M[0], M[1], M[i ^ 1], modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = ltc_mp.ecc_ptdbl(M[i], M[i], modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+
+ /* copy result out */
+ if ((err = mp_copy(M[0]->x, R->x)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_copy(M[0]->y, R->y)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_copy(M[0]->z, R->z)) != CRYPT_OK) {
+ goto done;
+ }
+
+ /* map R back from projective space */
+ if (map) {
+ err = ltc_ecc_map(R, modulus, mp);
+ } else {
+ err = CRYPT_OK;
+ }
+done:
+ if (mu != NULL) {
+ mp_clear(mu);
+ }
+ mp_montgomery_free(mp);
+ ltc_ecc_del_point(tG);
+ for (i = 0; i < 3; i++) {
+ ltc_ecc_del_point(M[i]);
+ }
+ return err;
+}
+ #endif
+#endif
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_mulmod_timing.c,v $ */
+/* $Revision: 1.13 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ltc_ecc_points.c
+ ECC Crypto, Tom St Denis
+ */
+
+#ifdef LTC_MECC
+
+/**
+ Allocate a new ECC point
+ @return A newly allocated point or NULL on error
+ */
+ecc_point *ltc_ecc_new_point(void) {
+ ecc_point *p;
+
+ p = XCALLOC(1, sizeof(*p));
+ if (p == NULL) {
+ return NULL;
+ }
+ if (mp_init_multi(&p->x, &p->y, &p->z, NULL) != CRYPT_OK) {
+ XFREE(p);
+ return NULL;
+ }
+ return p;
+}
+
+/** Free an ECC point from memory
+ @param p The point to free
+ */
+void ltc_ecc_del_point(ecc_point *p) {
+ /* prevents free'ing null arguments */
+ if (p != NULL) {
+ mp_clear_multi(p->x, p->y, p->z, NULL); /* note: p->z may be NULL but that's ok with this function anyways */
+ XFREE(p);
+ }
+}
+#endif
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_points.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ltc_ecc_projective_add_point.c
+ ECC Crypto, Tom St Denis
+ */
+
+#if defined(LTC_MECC) && (!defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC))
+
+/**
+ Add two ECC points
+ @param P The point to add
+ @param Q The point to add
+ @param R [out] The destination of the double
+ @param modulus The modulus of the field the ECC curve is in
+ @param mp The "b" value from montgomery_setup()
+ @return CRYPT_OK on success
+ */
+int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp) {
+ void *t1, *t2, *x, *y, *z;
+ int err;
+
+ LTC_ARGCHK(P != NULL);
+ LTC_ARGCHK(Q != NULL);
+ LTC_ARGCHK(R != NULL);
+ LTC_ARGCHK(modulus != NULL);
+ LTC_ARGCHK(mp != NULL);
+
+ if ((err = mp_init_multi(&t1, &t2, &x, &y, &z, NULL)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* should we dbl instead? */
+ if ((err = mp_sub(modulus, Q->y, t1)) != CRYPT_OK) {
+ goto done;
+ }
+
+ if ((mp_cmp(P->x, Q->x) == LTC_MP_EQ) &&
+ ((Q->z != NULL) && (mp_cmp(P->z, Q->z) == LTC_MP_EQ)) &&
+ ((mp_cmp(P->y, Q->y) == LTC_MP_EQ) || (mp_cmp(P->y, t1) == LTC_MP_EQ))) {
+ mp_clear_multi(t1, t2, x, y, z, NULL);
+ return ltc_ecc_projective_dbl_point(P, R, modulus, mp);
+ }
+
+ if ((err = mp_copy(P->x, x)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_copy(P->y, y)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_copy(P->z, z)) != CRYPT_OK) {
+ goto done;
+ }
+
+ /* if Z is one then these are no-operations */
+ if (Q->z != NULL) {
+ /* T1 = Z' * Z' */
+ if ((err = mp_sqr(Q->z, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* X = X * T1 */
+ if ((err = mp_mul(t1, x, x)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* T1 = Z' * T1 */
+ if ((err = mp_mul(Q->z, t1, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* Y = Y * T1 */
+ if ((err = mp_mul(t1, y, y)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(y, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+
+ /* T1 = Z*Z */
+ if ((err = mp_sqr(z, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* T2 = X' * T1 */
+ if ((err = mp_mul(Q->x, t1, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* T1 = Z * T1 */
+ if ((err = mp_mul(z, t1, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* T1 = Y' * T1 */
+ if ((err = mp_mul(Q->y, t1, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+
+ /* Y = Y - T1 */
+ if ((err = mp_sub(y, t1, y)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp_d(y, 0) == LTC_MP_LT) {
+ if ((err = mp_add(y, modulus, y)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ /* T1 = 2T1 */
+ if ((err = mp_add(t1, t1, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp(t1, modulus) != LTC_MP_LT) {
+ if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ /* T1 = Y + T1 */
+ if ((err = mp_add(t1, y, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp(t1, modulus) != LTC_MP_LT) {
+ if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ /* X = X - T2 */
+ if ((err = mp_sub(x, t2, x)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp_d(x, 0) == LTC_MP_LT) {
+ if ((err = mp_add(x, modulus, x)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ /* T2 = 2T2 */
+ if ((err = mp_add(t2, t2, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp(t2, modulus) != LTC_MP_LT) {
+ if ((err = mp_sub(t2, modulus, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ /* T2 = X + T2 */
+ if ((err = mp_add(t2, x, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp(t2, modulus) != LTC_MP_LT) {
+ if ((err = mp_sub(t2, modulus, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+
+ /* if Z' != 1 */
+ if (Q->z != NULL) {
+ /* Z = Z * Z' */
+ if ((err = mp_mul(z, Q->z, z)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(z, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+
+ /* Z = Z * X */
+ if ((err = mp_mul(z, x, z)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(z, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+
+ /* T1 = T1 * X */
+ if ((err = mp_mul(t1, x, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* X = X * X */
+ if ((err = mp_sqr(x, x)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* T2 = T2 * x */
+ if ((err = mp_mul(t2, x, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* T1 = T1 * X */
+ if ((err = mp_mul(t1, x, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+
+ /* X = Y*Y */
+ if ((err = mp_sqr(y, x)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* X = X - T2 */
+ if ((err = mp_sub(x, t2, x)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp_d(x, 0) == LTC_MP_LT) {
+ if ((err = mp_add(x, modulus, x)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+
+ /* T2 = T2 - X */
+ if ((err = mp_sub(t2, x, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp_d(t2, 0) == LTC_MP_LT) {
+ if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ /* T2 = T2 - X */
+ if ((err = mp_sub(t2, x, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp_d(t2, 0) == LTC_MP_LT) {
+ if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ /* T2 = T2 * Y */
+ if ((err = mp_mul(t2, y, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* Y = T2 - T1 */
+ if ((err = mp_sub(t2, t1, y)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp_d(y, 0) == LTC_MP_LT) {
+ if ((err = mp_add(y, modulus, y)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ /* Y = Y/2 */
+ if (mp_isodd(y)) {
+ if ((err = mp_add(y, modulus, y)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ if ((err = mp_div_2(y, y)) != CRYPT_OK) {
+ goto done;
+ }
+
+ if ((err = mp_copy(x, R->x)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_copy(y, R->y)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_copy(z, R->z)) != CRYPT_OK) {
+ goto done;
+ }
+
+ err = CRYPT_OK;
+done:
+ mp_clear_multi(t1, t2, x, y, z, NULL);
+ return err;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_projective_add_point.c,v $ */
+/* $Revision: 1.16 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b
+ *
+ * All curves taken from NIST recommendation paper of July 1999
+ * Available at http://csrc.nist.gov/cryptval/dss.htm
+ */
+
+
+/**
+ @file ltc_ecc_projective_dbl_point.c
+ ECC Crypto, Tom St Denis
+ */
+
+#if defined(LTC_MECC) && (!defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC))
+
+/**
+ Double an ECC point
+ @param P The point to double
+ @param R [out] The destination of the double
+ @param modulus The modulus of the field the ECC curve is in
+ @param mp The "b" value from montgomery_setup()
+ @return CRYPT_OK on success
+ */
+int ltc_ecc_projective_dbl_point(ecc_point *P, ecc_point *R, void *modulus, void *mp) {
+ void *t1, *t2;
+ int err;
+
+ LTC_ARGCHK(P != NULL);
+ LTC_ARGCHK(R != NULL);
+ LTC_ARGCHK(modulus != NULL);
+ LTC_ARGCHK(mp != NULL);
+
+ if ((err = mp_init_multi(&t1, &t2, NULL)) != CRYPT_OK) {
+ return err;
+ }
+
+ if (P != R) {
+ if ((err = mp_copy(P->x, R->x)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_copy(P->y, R->y)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_copy(P->z, R->z)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+
+ /* t1 = Z * Z */
+ if ((err = mp_sqr(R->z, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* Z = Y * Z */
+ if ((err = mp_mul(R->z, R->y, R->z)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(R->z, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* Z = 2Z */
+ if ((err = mp_add(R->z, R->z, R->z)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp(R->z, modulus) != LTC_MP_LT) {
+ if ((err = mp_sub(R->z, modulus, R->z)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+
+ /* T2 = X - T1 */
+ if ((err = mp_sub(R->x, t1, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp_d(t2, 0) == LTC_MP_LT) {
+ if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ /* T1 = X + T1 */
+ if ((err = mp_add(t1, R->x, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp(t1, modulus) != LTC_MP_LT) {
+ if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ /* T2 = T1 * T2 */
+ if ((err = mp_mul(t1, t2, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* T1 = 2T2 */
+ if ((err = mp_add(t2, t2, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp(t1, modulus) != LTC_MP_LT) {
+ if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ /* T1 = T1 + T2 */
+ if ((err = mp_add(t1, t2, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp(t1, modulus) != LTC_MP_LT) {
+ if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+
+ /* Y = 2Y */
+ if ((err = mp_add(R->y, R->y, R->y)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp(R->y, modulus) != LTC_MP_LT) {
+ if ((err = mp_sub(R->y, modulus, R->y)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ /* Y = Y * Y */
+ if ((err = mp_sqr(R->y, R->y)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* T2 = Y * Y */
+ if ((err = mp_sqr(R->y, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* T2 = T2/2 */
+ if (mp_isodd(t2)) {
+ if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ if ((err = mp_div_2(t2, t2)) != CRYPT_OK) {
+ goto done;
+ }
+ /* Y = Y * X */
+ if ((err = mp_mul(R->y, R->x, R->y)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+
+ /* X = T1 * T1 */
+ if ((err = mp_sqr(t1, R->x)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(R->x, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* X = X - Y */
+ if ((err = mp_sub(R->x, R->y, R->x)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp_d(R->x, 0) == LTC_MP_LT) {
+ if ((err = mp_add(R->x, modulus, R->x)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ /* X = X - Y */
+ if ((err = mp_sub(R->x, R->y, R->x)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp_d(R->x, 0) == LTC_MP_LT) {
+ if ((err = mp_add(R->x, modulus, R->x)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+
+ /* Y = Y - X */
+ if ((err = mp_sub(R->y, R->x, R->y)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp_d(R->y, 0) == LTC_MP_LT) {
+ if ((err = mp_add(R->y, modulus, R->y)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+ /* Y = Y * T1 */
+ if ((err = mp_mul(R->y, t1, R->y)) != CRYPT_OK) {
+ goto done;
+ }
+ if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK) {
+ goto done;
+ }
+ /* Y = Y - T2 */
+ if ((err = mp_sub(R->y, t2, R->y)) != CRYPT_OK) {
+ goto done;
+ }
+ if (mp_cmp_d(R->y, 0) == LTC_MP_LT) {
+ if ((err = mp_add(R->y, modulus, R->y)) != CRYPT_OK) {
+ goto done;
+ }
+ }
+
+ err = CRYPT_OK;
+done:
+ mp_clear_multi(t1, t2, NULL);
+ return err;
+}
+#endif
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_projective_dbl_point.c,v $ */
+/* $Revision: 1.11 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+#define DESC_DEF_ONLY
+
+#ifdef LTM_DESC
+
+#undef mp_init
+#undef mp_init_multi
+#undef mp_clear
+#undef mp_clear_multi
+#undef mp_init_copy
+#undef mp_neg
+#undef mp_copy
+#undef mp_set
+#undef mp_set_int
+#undef mp_get_int
+#undef mp_get_digit
+#undef mp_get_digit_count
+#undef mp_cmp
+#undef mp_cmp_d
+#undef mp_count_bits
+#undef mp_cnt_lsb
+#undef mp_2expt
+#undef mp_read_radix
+#undef mp_toradix
+#undef mp_unsigned_bin_size
+#undef mp_to_unsigned_bin
+#undef mp_read_unsigned_bin
+#undef mp_add
+#undef mp_add_d
+#undef mp_sub
+#undef mp_sub_d
+#undef mp_mul
+#undef mp_mul_d
+#undef mp_sqr
+#undef mp_div
+#undef mp_div_2
+#undef mp_mod
+#undef mp_mod_d
+#undef mp_gcd
+#undef mp_lcm
+#undef mp_mulmod
+#undef mp_sqrmod
+#undef mp_invmod
+#undef mp_montgomery_setup
+#undef mp_montgomery_normalization
+#undef mp_montgomery_reduce
+#undef mp_montgomery_free
+#undef mp_exptmod
+#undef mp_prime_is_prime
+#undef mp_iszero
+#undef mp_isodd
+#undef mp_exch
+#undef mp_tohex
+
+static const struct {
+ int mpi_code, ltc_code;
+} mpi_to_ltc_codes[] = {
+ { MP_OKAY, CRYPT_OK },
+ { MP_MEM, CRYPT_MEM },
+ { MP_VAL, CRYPT_INVALID_ARG },
+};
+
+/**
+ Convert a MPI error to a LTC error (Possibly the most powerful function ever! Oh wait... no)
+ @param err The error to convert
+ @return The equivalent LTC error code or CRYPT_ERROR if none found
+ */
+static int mpi_to_ltc_error(int err) {
+ int x;
+
+ for (x = 0; x < (int)(sizeof(mpi_to_ltc_codes) / sizeof(mpi_to_ltc_codes[0])); x++) {
+ if (err == mpi_to_ltc_codes[x].mpi_code) {
+ return mpi_to_ltc_codes[x].ltc_code;
+ }
+ }
+ return CRYPT_ERROR;
+}
+
+static int init(void **a) {
+ int err;
+
+ LTC_ARGCHK(a != NULL);
+
+ *a = XCALLOC(1, sizeof(mp_int));
+ if (*a == NULL) {
+ return CRYPT_MEM;
+ }
+ if ((err = mpi_to_ltc_error(mp_init(*a))) != CRYPT_OK) {
+ XFREE(*a);
+ }
+ return err;
+}
+
+static void deinit(void *a) {
+ LTC_ARGCHKVD(a != NULL);
+ mp_clear(a);
+ XFREE(a);
+}
+
+static int neg(void *a, void *b) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ return mpi_to_ltc_error(mp_neg(a, b));
+}
+
+static int copy(void *a, void *b) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ return mpi_to_ltc_error(mp_copy(a, b));
+}
+
+static int init_copy(void **a, void *b) {
+ if (init(a) != CRYPT_OK) {
+ return CRYPT_MEM;
+ }
+ return copy(b, *a);
+}
+
+/* ---- trivial ---- */
+static int set_int(void *a, unsigned long b) {
+ LTC_ARGCHK(a != NULL);
+ return mpi_to_ltc_error(mp_set_int(a, b));
+}
+
+static unsigned long get_int(void *a) {
+ LTC_ARGCHK(a != NULL);
+ return mp_get_int(a);
+}
+
+static unsigned long get_digit(void *a, int n) {
+ mp_int *A;
+
+ LTC_ARGCHK(a != NULL);
+ A = a;
+ return (n >= A->used || n < 0) ? 0 : A->dp[n];
+}
+
+static int get_digit_count(void *a) {
+ mp_int *A;
+
+ LTC_ARGCHK(a != NULL);
+ A = a;
+ return A->used;
+}
+
+static int compare(void *a, void *b) {
+ int ret;
+
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ ret = mp_cmp(a, b);
+ switch (ret) {
+ case MP_LT:
+ return LTC_MP_LT;
+
+ case MP_EQ:
+ return LTC_MP_EQ;
+
+ case MP_GT:
+ return LTC_MP_GT;
+ }
+ return 0;
+}
+
+static int compare_d(void *a, unsigned long b) {
+ int ret;
+
+ LTC_ARGCHK(a != NULL);
+ ret = mp_cmp_d(a, b);
+ switch (ret) {
+ case MP_LT:
+ return LTC_MP_LT;
+
+ case MP_EQ:
+ return LTC_MP_EQ;
+
+ case MP_GT:
+ return LTC_MP_GT;
+ }
+ return 0;
+}
+
+static int count_bits(void *a) {
+ LTC_ARGCHK(a != NULL);
+ return mp_count_bits(a);
+}
+
+static int count_lsb_bits(void *a) {
+ LTC_ARGCHK(a != NULL);
+ return mp_cnt_lsb(a);
+}
+
+static int twoexpt(void *a, int n) {
+ LTC_ARGCHK(a != NULL);
+ return mpi_to_ltc_error(mp_2expt(a, n));
+}
+
+/* ---- conversions ---- */
+
+/* read ascii string */
+static int read_radix(void *a, const char *b, int radix) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ return mpi_to_ltc_error(mp_read_radix(a, b, radix));
+}
+
+/* write one */
+static int write_radix(void *a, char *b, int radix) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ return mpi_to_ltc_error(mp_toradix(a, b, radix));
+}
+
+/* get size as unsigned char string */
+static unsigned long unsigned_size(void *a) {
+ LTC_ARGCHK(a != NULL);
+ return mp_unsigned_bin_size(a);
+}
+
+/* store */
+static int unsigned_write(void *a, unsigned char *b) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ return mpi_to_ltc_error(mp_to_unsigned_bin(a, b));
+}
+
+/* read */
+static int unsigned_read(void *a, unsigned char *b, unsigned long len) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ return mpi_to_ltc_error(mp_read_unsigned_bin(a, b, len));
+}
+
+/* add */
+static int add(void *a, void *b, void *c) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ LTC_ARGCHK(c != NULL);
+ return mpi_to_ltc_error(mp_add(a, b, c));
+}
+
+static int addi(void *a, unsigned long b, void *c) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(c != NULL);
+ return mpi_to_ltc_error(mp_add_d(a, b, c));
+}
+
+/* sub */
+static int sub(void *a, void *b, void *c) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ LTC_ARGCHK(c != NULL);
+ return mpi_to_ltc_error(mp_sub(a, b, c));
+}
+
+static int subi(void *a, unsigned long b, void *c) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(c != NULL);
+ return mpi_to_ltc_error(mp_sub_d(a, b, c));
+}
+
+/* mul */
+static int mul(void *a, void *b, void *c) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ LTC_ARGCHK(c != NULL);
+ return mpi_to_ltc_error(mp_mul(a, b, c));
+}
+
+static int muli(void *a, unsigned long b, void *c) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(c != NULL);
+ return mpi_to_ltc_error(mp_mul_d(a, b, c));
+}
+
+/* sqr */
+static int sqr(void *a, void *b) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ return mpi_to_ltc_error(mp_sqr(a, b));
+}
+
+/* div */
+static int divide(void *a, void *b, void *c, void *d) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ return mpi_to_ltc_error(mp_div(a, b, c, d));
+}
+
+static int div_2(void *a, void *b) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ return mpi_to_ltc_error(mp_div_2(a, b));
+}
+
+/* modi */
+static int modi(void *a, unsigned long b, unsigned long *c) {
+ mp_digit tmp;
+ int err;
+
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(c != NULL);
+
+ if ((err = mpi_to_ltc_error(mp_mod_d(a, b, &tmp))) != CRYPT_OK) {
+ return err;
+ }
+ *c = tmp;
+ return CRYPT_OK;
+}
+
+/* gcd */
+static int gcd(void *a, void *b, void *c) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ LTC_ARGCHK(c != NULL);
+ return mpi_to_ltc_error(mp_gcd(a, b, c));
+}
+
+/* lcm */
+static int lcm(void *a, void *b, void *c) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ LTC_ARGCHK(c != NULL);
+ return mpi_to_ltc_error(mp_lcm(a, b, c));
+}
+
+static int mulmod(void *a, void *b, void *c, void *d) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ LTC_ARGCHK(c != NULL);
+ LTC_ARGCHK(d != NULL);
+ return mpi_to_ltc_error(mp_mulmod(a, b, c, d));
+}
+
+static int sqrmod(void *a, void *b, void *c) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ LTC_ARGCHK(c != NULL);
+ return mpi_to_ltc_error(mp_sqrmod(a, b, c));
+}
+
+/* invmod */
+static int invmod(void *a, void *b, void *c) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ LTC_ARGCHK(c != NULL);
+ return mpi_to_ltc_error(mp_invmod(a, b, c));
+}
+
+/* setup */
+static int montgomery_setup(void *a, void **b) {
+ int err;
+
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ *b = XCALLOC(1, sizeof(mp_digit));
+ if (*b == NULL) {
+ return CRYPT_MEM;
+ }
+ if ((err = mpi_to_ltc_error(mp_montgomery_setup(a, (mp_digit *)*b))) != CRYPT_OK) {
+ XFREE(*b);
+ }
+ return err;
+}
+
+/* get normalization value */
+static int montgomery_normalization(void *a, void *b) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ return mpi_to_ltc_error(mp_montgomery_calc_normalization(a, b));
+}
+
+/* reduce */
+static int montgomery_reduce(void *a, void *b, void *c) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ LTC_ARGCHK(c != NULL);
+ return mpi_to_ltc_error(mp_montgomery_reduce(a, b, *((mp_digit *)c)));
+}
+
+/* clean up */
+static void montgomery_deinit(void *a) {
+ XFREE(a);
+}
+
+static int exptmod(void *a, void *b, void *c, void *d) {
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ LTC_ARGCHK(c != NULL);
+ LTC_ARGCHK(d != NULL);
+ return mpi_to_ltc_error(mp_exptmod(a, b, c, d));
+}
+
+static int isprime(void *a, int *b) {
+ int err;
+
+ LTC_ARGCHK(a != NULL);
+ LTC_ARGCHK(b != NULL);
+ err = mpi_to_ltc_error(mp_prime_is_prime(a, 8, b));
+ *b = (*b == MP_YES) ? LTC_MP_YES : LTC_MP_NO;
+ return err;
+}
+
+const ltc_math_descriptor ltm_desc = {
+ "LibTomMath",
+ (int)DIGIT_BIT,
+
+ &init,
+ &init_copy,
+ &deinit,
+
+ &neg,
+ ©,
+
+ &set_int,
+ &get_int,
+ &get_digit,
+ &get_digit_count,
+ &compare,
+ &compare_d,
+ &count_bits,
+ &count_lsb_bits,
+ &twoexpt,
+
+ &read_radix,
+ &write_radix,
+ &unsigned_size,
+ &unsigned_write,
+ &unsigned_read,
+
+ &add,
+ &addi,
+ &sub,
+ &subi,
+ &mul,
+ &muli,
+ &sqr,
+ ÷,
+ &div_2,
+ &modi,
+ &gcd,
+ &lcm,
+
+ &mulmod,
+ &sqrmod,
+ &invmod,
+
+ &montgomery_setup,
+ &montgomery_normalization,
+ &montgomery_reduce,
+ &montgomery_deinit,
+
+ &exptmod,
+ &isprime,
+
+ #ifdef LTC_MECC
+ #ifdef LTC_MECC_FP
+ <c_ecc_fp_mulmod,
+ #else
+ <c_ecc_mulmod,
+ #endif
+ <c_ecc_projective_add_point,
+ <c_ecc_projective_dbl_point,
+ <c_ecc_map,
+ #ifdef LTC_ECC_SHAMIR
+ #ifdef LTC_MECC_FP
+ <c_ecc_fp_mul2add,
+ #else
+ <c_ecc_mul2add,
+ #endif /* LTC_MECC_FP */
+ #else
+ NULL,
+ #endif /* LTC_ECC_SHAMIR */
+ #else
+ NULL, NULL,NULL, NULL, NULL,
+ #endif /* LTC_MECC */
+
+ #ifdef LTC_MRSA
+ &rsa_make_key,
+ &rsa_exptmod,
+ #else
+ NULL, NULL
+ #endif
+};
+
+ #define mp_init(a) ltc_mp.init(a)
+ #define mp_init_multi ltc_init_multi
+ #define mp_clear(a) ltc_mp.deinit(a)
+ #define mp_clear_multi ltc_deinit_multi
+ #define mp_init_copy(a, b) ltc_mp.init_copy(a, b)
+
+ #define mp_neg(a, b) ltc_mp.neg(a, b)
+ #define mp_copy(a, b) ltc_mp.copy(a, b)
+
+ #define mp_set(a, b) ltc_mp.set_int(a, b)
+ #define mp_set_int(a, b) ltc_mp.set_int(a, b)
+ #define mp_get_int(a) ltc_mp.get_int(a)
+ #define mp_get_digit(a, n) ltc_mp.get_digit(a, n)
+ #define mp_get_digit_count(a) ltc_mp.get_digit_count(a)
+ #define mp_cmp(a, b) ltc_mp.compare(a, b)
+ #define mp_cmp_d(a, b) ltc_mp.compare_d(a, b)
+ #define mp_count_bits(a) ltc_mp.count_bits(a)
+ #define mp_cnt_lsb(a) ltc_mp.count_lsb_bits(a)
+ #define mp_2expt(a, b) ltc_mp.twoexpt(a, b)
+
+ #define mp_read_radix(a, b, c) ltc_mp.read_radix(a, b, c)
+ #define mp_toradix(a, b, c) ltc_mp.write_radix(a, b, c)
+ #define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a)
+ #define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b)
+ #define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c)
+
+ #define mp_add(a, b, c) ltc_mp.add(a, b, c)
+ #define mp_add_d(a, b, c) ltc_mp.addi(a, b, c)
+ #define mp_sub(a, b, c) ltc_mp.sub(a, b, c)
+ #define mp_sub_d(a, b, c) ltc_mp.subi(a, b, c)
+ #define mp_mul(a, b, c) ltc_mp.mul(a, b, c)
+ #define mp_mul_d(a, b, c) ltc_mp.muli(a, b, c)
+ #define mp_sqr(a, b) ltc_mp.sqr(a, b)
+ #define mp_div(a, b, c, d) ltc_mp.mpdiv(a, b, c, d)
+ #define mp_div_2(a, b) ltc_mp.div_2(a, b)
+ #define mp_mod(a, b, c) ltc_mp.mpdiv(a, b, NULL, c)
+ #define mp_mod_d(a, b, c) ltc_mp.modi(a, b, c)
+ #define mp_gcd(a, b, c) ltc_mp.gcd(a, b, c)
+ #define mp_lcm(a, b, c) ltc_mp.lcm(a, b, c)
+
+ #define mp_mulmod(a, b, c, d) ltc_mp.mulmod(a, b, c, d)
+ #define mp_sqrmod(a, b, c) ltc_mp.sqrmod(a, b, c)
+ #define mp_invmod(a, b, c) ltc_mp.invmod(a, b, c)
+
+ #define mp_montgomery_setup(a, b) ltc_mp.montgomery_setup(a, b)
+ #define mp_montgomery_normalization(a, b) ltc_mp.montgomery_normalization(a, b)
+ #define mp_montgomery_reduce(a, b, c) ltc_mp.montgomery_reduce(a, b, c)
+ #define mp_montgomery_free(a) ltc_mp.montgomery_deinit(a)
+
+ #define mp_exptmod(a, b, c, d) ltc_mp.exptmod(a, b, c, d)
+ #define mp_prime_is_prime(a, b, c) ltc_mp.isprime(a, c)
+
+ #define mp_iszero(a) (mp_cmp_d(a, 0) == LTC_MP_EQ ? LTC_MP_YES : LTC_MP_NO)
+ #define mp_isodd(a) (mp_get_digit_count(a) > 0 ? (mp_get_digit(a, 0) & 1 ? LTC_MP_YES : LTC_MP_NO) : LTC_MP_NO)
+ #define mp_exch(a, b) do { void *ABC__tmp = a; a = b; b = ABC__tmp; } while (0);
+
+ #define mp_tohex(a, b) mp_toradix(a, b, 16)
+
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/math/ltm_desc.c,v $ */
+/* $Revision: 1.31 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+#ifdef MPI
+#include
+
+int ltc_init_multi(void **a, ...) {
+ void **cur = a;
+ int np = 0;
+ va_list args;
+
+ va_start(args, a);
+ while (cur != NULL) {
+ if (mp_init(cur) != CRYPT_OK) {
+ /* failed */
+ va_list clean_list;
+
+ va_start(clean_list, a);
+ cur = a;
+ while (np--) {
+ mp_clear(*cur);
+ cur = va_arg(clean_list, void **);
+ }
+ va_end(clean_list);
+ return CRYPT_MEM;
+ }
+ ++np;
+ cur = va_arg(args, void **);
+ }
+ va_end(args);
+ return CRYPT_OK;
+}
+
+void ltc_deinit_multi(void *a, ...) {
+ void *cur = a;
+ va_list args;
+
+ va_start(args, a);
+ while (cur != NULL) {
+ mp_clear(cur);
+ cur = va_arg(args, void *);
+ }
+ va_end(args);
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/math/multi.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2006/12/28 01:27:23 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file pkcs_1_i2osp.c
+ Integer to Octet I2OSP, Tom St Denis
+ */
+
+#ifdef LTC_PKCS_1
+
+/* always stores the same # of bytes, pads with leading zero bytes
+ as required
+ */
+
+/**
+ LTC_PKCS #1 Integer to binary
+ @param n The integer to store
+ @param modulus_len The length of the RSA modulus
+ @param out [out] The destination for the integer
+ @return CRYPT_OK if successful
+ */
+int pkcs_1_i2osp(void *n, unsigned long modulus_len, unsigned char *out) {
+ unsigned long size;
+
+ size = mp_unsigned_bin_size(n);
+
+ if (size > modulus_len) {
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ /* store it */
+ zeromem(out, modulus_len);
+ return mp_to_unsigned_bin(n, out + (modulus_len - size));
+}
+#endif /* LTC_PKCS_1 */
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_i2osp.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file pkcs_1_mgf1.c
+ The Mask Generation Function (MGF1) for LTC_PKCS #1, Tom St Denis
+ */
+
+#ifdef LTC_PKCS_1
+
+/**
+ Perform LTC_PKCS #1 MGF1 (internal)
+ @param seed The seed for MGF1
+ @param seedlen The length of the seed
+ @param hash_idx The index of the hash desired
+ @param mask [out] The destination
+ @param masklen The length of the mask desired
+ @return CRYPT_OK if successful
+ */
+int pkcs_1_mgf1(int hash_idx,
+ const unsigned char *seed, unsigned long seedlen,
+ unsigned char *mask, unsigned long masklen) {
+ unsigned long hLen, x;
+ ulong32 counter;
+ int err;
+ hash_state *md;
+ unsigned char *buf;
+
+ LTC_ARGCHK(seed != NULL);
+ LTC_ARGCHK(mask != NULL);
+
+ /* ensure valid hash */
+ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* get hash output size */
+ hLen = hash_descriptor[hash_idx].hashsize;
+
+ /* allocate memory */
+ md = XMALLOC(sizeof(hash_state));
+ buf = XMALLOC(hLen);
+ if ((md == NULL) || (buf == NULL)) {
+ if (md != NULL) {
+ XFREE(md);
+ }
+ if (buf != NULL) {
+ XFREE(buf);
+ }
+ return CRYPT_MEM;
+ }
+
+ /* start counter */
+ counter = 0;
+
+ while (masklen > 0) {
+ /* handle counter */
+ STORE32H(counter, buf);
+ ++counter;
+
+ /* get hash of seed || counter */
+ if ((err = hash_descriptor[hash_idx].init(md)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ if ((err = hash_descriptor[hash_idx].process(md, seed, seedlen)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ if ((err = hash_descriptor[hash_idx].process(md, buf, 4)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ if ((err = hash_descriptor[hash_idx].done(md, buf)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ /* store it */
+ for (x = 0; x < hLen && masklen > 0; x++, masklen--) {
+ *mask++ = buf[x];
+ }
+ }
+
+ err = CRYPT_OK;
+LBL_ERR:
+ #ifdef LTC_CLEAN_STACK
+ zeromem(buf, hLen);
+ zeromem(md, sizeof(hash_state));
+ #endif
+
+ XFREE(buf);
+ XFREE(md);
+
+ return err;
+}
+#endif /* LTC_PKCS_1 */
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c,v $ */
+/* $Revision: 1.8 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file pkcs_1_oaep_decode.c
+ OAEP Padding for LTC_PKCS #1, Tom St Denis
+ */
+
+#ifdef LTC_PKCS_1
+
+/**
+ LTC_PKCS #1 v2.00 OAEP decode
+ @param msg The encoded data to decode
+ @param msglen The length of the encoded data (octets)
+ @param lparam The session or system data (can be NULL)
+ @param lparamlen The length of the lparam
+ @param modulus_bitlen The bit length of the RSA modulus
+ @param hash_idx The index of the hash desired
+ @param out [out] Destination of decoding
+ @param outlen [in/out] The max size and resulting size of the decoding
+ @param res [out] Result of decoding, 1==valid, 0==invalid
+ @return CRYPT_OK if successful (even if invalid)
+ */
+int pkcs_1_oaep_decode(const unsigned char *msg, unsigned long msglen,
+ const unsigned char *lparam, unsigned long lparamlen,
+ unsigned long modulus_bitlen, int hash_idx,
+ unsigned char *out, unsigned long *outlen,
+ int *res) {
+ unsigned char *DB, *seed, *mask;
+ unsigned long hLen, x, y, modulus_len;
+ int err;
+
+ LTC_ARGCHK(msg != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+ LTC_ARGCHK(res != NULL);
+
+ /* default to invalid packet */
+ *res = 0;
+
+ /* test valid hash */
+ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
+ return err;
+ }
+ hLen = hash_descriptor[hash_idx].hashsize;
+ modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0);
+
+ /* test hash/message size */
+ if ((2 * hLen >= (modulus_len - 2)) || (msglen != modulus_len)) {
+ return CRYPT_PK_INVALID_SIZE;
+ }
+
+ /* allocate ram for DB/mask/salt of size modulus_len */
+ DB = XMALLOC(modulus_len);
+ mask = XMALLOC(modulus_len);
+ seed = XMALLOC(hLen);
+ if ((DB == NULL) || (mask == NULL) || (seed == NULL)) {
+ if (DB != NULL) {
+ XFREE(DB);
+ }
+ if (mask != NULL) {
+ XFREE(mask);
+ }
+ if (seed != NULL) {
+ XFREE(seed);
+ }
+ return CRYPT_MEM;
+ }
+
+ /* ok so it's now in the form
+
+ 0x00 || maskedseed || maskedDB
+
+ 1 || hLen || modulus_len - hLen - 1
+
+ */
+
+ /* must have leading 0x00 byte */
+ if (msg[0] != 0x00) {
+ err = CRYPT_OK;
+ goto LBL_ERR;
+ }
+
+ /* now read the masked seed */
+ x = 1;
+ XMEMCPY(seed, msg + x, hLen);
+ x += hLen;
+
+ /* now read the masked DB */
+ XMEMCPY(DB, msg + x, modulus_len - hLen - 1);
+ x += modulus_len - hLen - 1;
+
+ /* compute MGF1 of maskedDB (hLen) */
+ if ((err = pkcs_1_mgf1(hash_idx, DB, modulus_len - hLen - 1, mask, hLen)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ /* XOR against seed */
+ for (y = 0; y < hLen; y++) {
+ seed[y] ^= mask[y];
+ }
+
+ /* compute MGF1 of seed (k - hlen - 1) */
+ if ((err = pkcs_1_mgf1(hash_idx, seed, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ /* xor against DB */
+ for (y = 0; y < (modulus_len - hLen - 1); y++) {
+ DB[y] ^= mask[y];
+ }
+
+ /* now DB == lhash || PS || 0x01 || M, PS == k - mlen - 2hlen - 2 zeroes */
+
+ /* compute lhash and store it in seed [reuse temps!] */
+ x = modulus_len;
+ if (lparam != NULL) {
+ if ((err = hash_memory(hash_idx, lparam, lparamlen, seed, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ } else {
+ /* can't pass hash_memory a NULL so use DB with zero length */
+ if ((err = hash_memory(hash_idx, DB, 0, seed, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* compare the lhash'es */
+ if (XMEMCMP(seed, DB, hLen) != 0) {
+ err = CRYPT_OK;
+ goto LBL_ERR;
+ }
+
+ /* now zeroes before a 0x01 */
+ for (x = hLen; x < (modulus_len - hLen - 1) && DB[x] == 0x00; x++) {
+ /* step... */
+ }
+
+ /* error out if wasn't 0x01 */
+ if ((x == (modulus_len - hLen - 1)) || (DB[x] != 0x01)) {
+ err = CRYPT_INVALID_PACKET;
+ goto LBL_ERR;
+ }
+
+ /* rest is the message (and skip 0x01) */
+ if ((modulus_len - hLen - 1 - ++x) > *outlen) {
+ *outlen = modulus_len - hLen - 1 - x;
+ err = CRYPT_BUFFER_OVERFLOW;
+ goto LBL_ERR;
+ }
+
+ /* copy message */
+ *outlen = modulus_len - hLen - 1 - x;
+ XMEMCPY(out, DB + x, modulus_len - hLen - 1 - x);
+ x += modulus_len - hLen - 1;
+
+ /* valid packet */
+ *res = 1;
+
+ err = CRYPT_OK;
+LBL_ERR:
+ #ifdef LTC_CLEAN_STACK
+ zeromem(DB, modulus_len);
+ zeromem(seed, hLen);
+ zeromem(mask, modulus_len);
+ #endif
+
+ XFREE(seed);
+ XFREE(mask);
+ XFREE(DB);
+
+ return err;
+}
+#endif /* LTC_PKCS_1 */
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c,v $ */
+/* $Revision: 1.13 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file pkcs_1_oaep_encode.c
+ OAEP Padding for LTC_PKCS #1, Tom St Denis
+ */
+
+#ifdef LTC_PKCS_1
+
+/**
+ LTC_PKCS #1 v2.00 OAEP encode
+ @param msg The data to encode
+ @param msglen The length of the data to encode (octets)
+ @param lparam A session or system parameter (can be NULL)
+ @param lparamlen The length of the lparam data
+ @param modulus_bitlen The bit length of the RSA modulus
+ @param prng An active PRNG state
+ @param prng_idx The index of the PRNG desired
+ @param hash_idx The index of the hash desired
+ @param out [out] The destination for the encoded data
+ @param outlen [in/out] The max size and resulting size of the encoded data
+ @return CRYPT_OK if successful
+ */
+int pkcs_1_oaep_encode(const unsigned char *msg, unsigned long msglen,
+ const unsigned char *lparam, unsigned long lparamlen,
+ unsigned long modulus_bitlen, prng_state *prng,
+ int prng_idx, int hash_idx,
+ unsigned char *out, unsigned long *outlen) {
+ unsigned char *DB, *seed, *mask;
+ unsigned long hLen, x, y, modulus_len;
+ int err;
+
+ LTC_ARGCHK(msg != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* test valid hash */
+ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* valid prng */
+ if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) {
+ return err;
+ }
+
+ hLen = hash_descriptor[hash_idx].hashsize;
+ modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0);
+
+ /* test message size */
+ if ((2 * hLen >= (modulus_len - 2)) || (msglen > (modulus_len - 2 * hLen - 2))) {
+ return CRYPT_PK_INVALID_SIZE;
+ }
+
+ /* allocate ram for DB/mask/salt of size modulus_len */
+ DB = XMALLOC(modulus_len);
+ mask = XMALLOC(modulus_len);
+ seed = XMALLOC(hLen);
+ if ((DB == NULL) || (mask == NULL) || (seed == NULL)) {
+ if (DB != NULL) {
+ XFREE(DB);
+ }
+ if (mask != NULL) {
+ XFREE(mask);
+ }
+ if (seed != NULL) {
+ XFREE(seed);
+ }
+ return CRYPT_MEM;
+ }
+
+ /* get lhash */
+ /* DB == lhash || PS || 0x01 || M, PS == k - mlen - 2hlen - 2 zeroes */
+ x = modulus_len;
+ if (lparam != NULL) {
+ if ((err = hash_memory(hash_idx, lparam, lparamlen, DB, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ } else {
+ /* can't pass hash_memory a NULL so use DB with zero length */
+ if ((err = hash_memory(hash_idx, DB, 0, DB, &x)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* append PS then 0x01 (to lhash) */
+ x = hLen;
+ y = modulus_len - msglen - 2 * hLen - 2;
+ XMEMSET(DB + x, 0, y);
+ x += y;
+
+ /* 0x01 byte */
+ DB[x++] = 0x01;
+
+ /* message (length = msglen) */
+ XMEMCPY(DB + x, msg, msglen);
+ x += msglen;
+
+ /* now choose a random seed */
+ if (prng_descriptor[prng_idx].read(seed, hLen, prng) != hLen) {
+ err = CRYPT_ERROR_READPRNG;
+ goto LBL_ERR;
+ }
+
+ /* compute MGF1 of seed (k - hlen - 1) */
+ if ((err = pkcs_1_mgf1(hash_idx, seed, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ /* xor against DB */
+ for (y = 0; y < (modulus_len - hLen - 1); y++) {
+ DB[y] ^= mask[y];
+ }
+
+ /* compute MGF1 of maskedDB (hLen) */
+ if ((err = pkcs_1_mgf1(hash_idx, DB, modulus_len - hLen - 1, mask, hLen)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ /* XOR against seed */
+ for (y = 0; y < hLen; y++) {
+ seed[y] ^= mask[y];
+ }
+
+ /* create string of length modulus_len */
+ if (*outlen < modulus_len) {
+ *outlen = modulus_len;
+ err = CRYPT_BUFFER_OVERFLOW;
+ goto LBL_ERR;
+ }
+
+ /* start output which is 0x00 || maskedSeed || maskedDB */
+ x = 0;
+ out[x++] = 0x00;
+ XMEMCPY(out + x, seed, hLen);
+ x += hLen;
+ XMEMCPY(out + x, DB, modulus_len - hLen - 1);
+ x += modulus_len - hLen - 1;
+
+ *outlen = x;
+
+ err = CRYPT_OK;
+LBL_ERR:
+ #ifdef LTC_CLEAN_STACK
+ zeromem(DB, modulus_len);
+ zeromem(seed, hLen);
+ zeromem(mask, modulus_len);
+ #endif
+
+ XFREE(seed);
+ XFREE(mask);
+ XFREE(DB);
+
+ return err;
+}
+#endif /* LTC_PKCS_1 */
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_encode.c,v $ */
+/* $Revision: 1.9 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file pkcs_1_os2ip.c
+ Octet to Integer OS2IP, Tom St Denis
+ */
+#ifdef LTC_PKCS_1
+
+/**
+ Read a binary string into an mp_int
+ @param n [out] The mp_int destination
+ @param in The binary string to read
+ @param inlen The length of the binary string
+ @return CRYPT_OK if successful
+ */
+int pkcs_1_os2ip(void *n, unsigned char *in, unsigned long inlen) {
+ return mp_read_unsigned_bin(n, in, inlen);
+}
+#endif /* LTC_PKCS_1 */
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_os2ip.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file pkcs_1_pss_decode.c
+ LTC_PKCS #1 PSS Signature Padding, Tom St Denis
+ */
+
+#ifdef LTC_PKCS_1
+
+/**
+ LTC_PKCS #1 v2.00 PSS decode
+ @param msghash The hash to verify
+ @param msghashlen The length of the hash (octets)
+ @param sig The signature data (encoded data)
+ @param siglen The length of the signature data (octets)
+ @param saltlen The length of the salt used (octets)
+ @param hash_idx The index of the hash desired
+ @param modulus_bitlen The bit length of the RSA modulus
+ @param res [out] The result of the comparison, 1==valid, 0==invalid
+ @return CRYPT_OK if successful (even if the comparison failed)
+ */
+int pkcs_1_pss_decode(const unsigned char *msghash, unsigned long msghashlen,
+ const unsigned char *sig, unsigned long siglen,
+ unsigned long saltlen, int hash_idx,
+ unsigned long modulus_bitlen, int *res) {
+ unsigned char *DB, *mask, *salt, *hash;
+ unsigned long x, y, hLen, modulus_len;
+ int err;
+ hash_state md;
+
+ LTC_ARGCHK(msghash != NULL);
+ LTC_ARGCHK(res != NULL);
+
+ /* default to invalid */
+ *res = 0;
+
+ /* ensure hash is valid */
+ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
+ return err;
+ }
+
+ hLen = hash_descriptor[hash_idx].hashsize;
+ modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0);
+
+ /* check sizes */
+ if ((saltlen > modulus_len) ||
+ (modulus_len < hLen + saltlen + 2) || (siglen != modulus_len)) {
+ return CRYPT_PK_INVALID_SIZE;
+ }
+
+ /* allocate ram for DB/mask/salt/hash of size modulus_len */
+ DB = XMALLOC(modulus_len);
+ mask = XMALLOC(modulus_len);
+ salt = XMALLOC(modulus_len);
+ hash = XMALLOC(modulus_len);
+ if ((DB == NULL) || (mask == NULL) || (salt == NULL) || (hash == NULL)) {
+ if (DB != NULL) {
+ XFREE(DB);
+ }
+ if (mask != NULL) {
+ XFREE(mask);
+ }
+ if (salt != NULL) {
+ XFREE(salt);
+ }
+ if (hash != NULL) {
+ XFREE(hash);
+ }
+ return CRYPT_MEM;
+ }
+
+ /* ensure the 0xBC byte */
+ if (sig[siglen - 1] != 0xBC) {
+ err = CRYPT_INVALID_PACKET;
+ goto LBL_ERR;
+ }
+
+ /* copy out the DB */
+ x = 0;
+ XMEMCPY(DB, sig + x, modulus_len - hLen - 1);
+ x += modulus_len - hLen - 1;
+
+ /* copy out the hash */
+ XMEMCPY(hash, sig + x, hLen);
+ x += hLen;
+
+ /* check the MSB */
+ if ((sig[0] & ~(0xFF >> ((modulus_len << 3) - (modulus_bitlen - 1)))) != 0) {
+ err = CRYPT_INVALID_PACKET;
+ goto LBL_ERR;
+ }
+
+ /* generate mask of length modulus_len - hLen - 1 from hash */
+ if ((err = pkcs_1_mgf1(hash_idx, hash, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ /* xor against DB */
+ for (y = 0; y < (modulus_len - hLen - 1); y++) {
+ DB[y] ^= mask[y];
+ }
+
+ /* now clear the first byte [make sure smaller than modulus] */
+ DB[0] &= 0xFF >> ((modulus_len << 3) - (modulus_bitlen - 1));
+
+ /* DB = PS || 0x01 || salt, PS == modulus_len - saltlen - hLen - 2 zero bytes */
+
+ /* check for zeroes and 0x01 */
+ for (x = 0; x < modulus_len - saltlen - hLen - 2; x++) {
+ if (DB[x] != 0x00) {
+ err = CRYPT_INVALID_PACKET;
+ goto LBL_ERR;
+ }
+ }
+
+ /* check for the 0x01 */
+ if (DB[x++] != 0x01) {
+ err = CRYPT_INVALID_PACKET;
+ goto LBL_ERR;
+ }
+
+ /* M = (eight) 0x00 || msghash || salt, mask = H(M) */
+ if ((err = hash_descriptor[hash_idx].init(&md)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ zeromem(mask, 8);
+ if ((err = hash_descriptor[hash_idx].process(&md, mask, 8)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ if ((err = hash_descriptor[hash_idx].process(&md, msghash, msghashlen)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ if ((err = hash_descriptor[hash_idx].process(&md, DB + x, saltlen)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ if ((err = hash_descriptor[hash_idx].done(&md, mask)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ /* mask == hash means valid signature */
+ if (XMEMCMP(mask, hash, hLen) == 0) {
+ *res = 1;
+ }
+
+ err = CRYPT_OK;
+LBL_ERR:
+ #ifdef LTC_CLEAN_STACK
+ zeromem(DB, modulus_len);
+ zeromem(mask, modulus_len);
+ zeromem(salt, modulus_len);
+ zeromem(hash, modulus_len);
+ #endif
+
+ XFREE(hash);
+ XFREE(salt);
+ XFREE(mask);
+ XFREE(DB);
+
+ return err;
+}
+#endif /* LTC_PKCS_1 */
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c,v $ */
+/* $Revision: 1.11 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file pkcs_1_pss_encode.c
+ LTC_PKCS #1 PSS Signature Padding, Tom St Denis
+ */
+
+#ifdef LTC_PKCS_1
+
+/**
+ LTC_PKCS #1 v2.00 Signature Encoding
+ @param msghash The hash to encode
+ @param msghashlen The length of the hash (octets)
+ @param saltlen The length of the salt desired (octets)
+ @param prng An active PRNG context
+ @param prng_idx The index of the PRNG desired
+ @param hash_idx The index of the hash desired
+ @param modulus_bitlen The bit length of the RSA modulus
+ @param out [out] The destination of the encoding
+ @param outlen [in/out] The max size and resulting size of the encoded data
+ @return CRYPT_OK if successful
+ */
+int pkcs_1_pss_encode(const unsigned char *msghash, unsigned long msghashlen,
+ unsigned long saltlen, prng_state *prng,
+ int prng_idx, int hash_idx,
+ unsigned long modulus_bitlen,
+ unsigned char *out, unsigned long *outlen) {
+ unsigned char *DB, *mask, *salt, *hash;
+ unsigned long x, y, hLen, modulus_len;
+ int err;
+ hash_state md;
+
+ LTC_ARGCHK(msghash != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* ensure hash and PRNG are valid */
+ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
+ return err;
+ }
+ if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) {
+ return err;
+ }
+
+ hLen = hash_descriptor[hash_idx].hashsize;
+ modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0);
+
+ /* check sizes */
+ if ((saltlen > modulus_len) || (modulus_len < hLen + saltlen + 2)) {
+ return CRYPT_PK_INVALID_SIZE;
+ }
+
+ /* allocate ram for DB/mask/salt/hash of size modulus_len */
+ DB = XMALLOC(modulus_len);
+ mask = XMALLOC(modulus_len);
+ salt = XMALLOC(modulus_len);
+ hash = XMALLOC(modulus_len);
+ if ((DB == NULL) || (mask == NULL) || (salt == NULL) || (hash == NULL)) {
+ if (DB != NULL) {
+ XFREE(DB);
+ }
+ if (mask != NULL) {
+ XFREE(mask);
+ }
+ if (salt != NULL) {
+ XFREE(salt);
+ }
+ if (hash != NULL) {
+ XFREE(hash);
+ }
+ return CRYPT_MEM;
+ }
+
+
+ /* generate random salt */
+ if (saltlen > 0) {
+ if (prng_descriptor[prng_idx].read(salt, saltlen, prng) != saltlen) {
+ err = CRYPT_ERROR_READPRNG;
+ goto LBL_ERR;
+ }
+ }
+
+ /* M = (eight) 0x00 || msghash || salt, hash = H(M) */
+ if ((err = hash_descriptor[hash_idx].init(&md)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ zeromem(DB, 8);
+ if ((err = hash_descriptor[hash_idx].process(&md, DB, 8)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ if ((err = hash_descriptor[hash_idx].process(&md, msghash, msghashlen)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ if ((err = hash_descriptor[hash_idx].process(&md, salt, saltlen)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ if ((err = hash_descriptor[hash_idx].done(&md, hash)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ /* generate DB = PS || 0x01 || salt, PS == modulus_len - saltlen - hLen - 2 zero bytes */
+ x = 0;
+ XMEMSET(DB + x, 0, modulus_len - saltlen - hLen - 2);
+ x += modulus_len - saltlen - hLen - 2;
+ DB[x++] = 0x01;
+ XMEMCPY(DB + x, salt, saltlen);
+ x += saltlen;
+
+ /* generate mask of length modulus_len - hLen - 1 from hash */
+ if ((err = pkcs_1_mgf1(hash_idx, hash, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ /* xor against DB */
+ for (y = 0; y < (modulus_len - hLen - 1); y++) {
+ DB[y] ^= mask[y];
+ }
+
+ /* output is DB || hash || 0xBC */
+ if (*outlen < modulus_len) {
+ *outlen = modulus_len;
+ err = CRYPT_BUFFER_OVERFLOW;
+ goto LBL_ERR;
+ }
+
+ /* DB len = modulus_len - hLen - 1 */
+ y = 0;
+ XMEMCPY(out + y, DB, modulus_len - hLen - 1);
+ y += modulus_len - hLen - 1;
+
+ /* hash */
+ XMEMCPY(out + y, hash, hLen);
+ y += hLen;
+
+ /* 0xBC */
+ out[y] = 0xBC;
+
+ /* now clear the 8*modulus_len - modulus_bitlen most significant bits */
+ out[0] &= 0xFF >> ((modulus_len << 3) - (modulus_bitlen - 1));
+
+ /* store output size */
+ *outlen = modulus_len;
+ err = CRYPT_OK;
+LBL_ERR:
+ #ifdef LTC_CLEAN_STACK
+ zeromem(DB, modulus_len);
+ zeromem(mask, modulus_len);
+ zeromem(salt, modulus_len);
+ zeromem(hash, modulus_len);
+ #endif
+
+ XFREE(hash);
+ XFREE(salt);
+ XFREE(mask);
+ XFREE(DB);
+
+ return err;
+}
+#endif /* LTC_PKCS_1 */
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_encode.c,v $ */
+/* $Revision: 1.9 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/** @file pkcs_1_v1_5_decode.c
+ *
+ * LTC_PKCS #1 v1.5 Padding. (Andreas Lange)
+ */
+
+#ifdef LTC_PKCS_1
+
+/** @brief LTC_PKCS #1 v1.5 decode.
+ *
+ * @param msg The encoded data to decode
+ * @param msglen The length of the encoded data (octets)
+ * @param block_type Block type to use in padding (\sa ltc_pkcs_1_v1_5_blocks)
+ * @param modulus_bitlen The bit length of the RSA modulus
+ * @param out [out] Destination of decoding
+ * @param outlen [in/out] The max size and resulting size of the decoding
+ * @param is_valid [out] Boolean whether the padding was valid
+ *
+ * @return CRYPT_OK if successful (even if invalid)
+ */
+int pkcs_1_v1_5_decode(const unsigned char *msg,
+ unsigned long msglen,
+ int block_type,
+ unsigned long modulus_bitlen,
+ unsigned char *out,
+ unsigned long *outlen,
+ int *is_valid) {
+ unsigned long modulus_len, ps_len, i;
+ int result;
+
+ /* default to invalid packet */
+ *is_valid = 0;
+
+ modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0);
+
+ /* test message size */
+
+ if ((msglen > modulus_len) || (modulus_len < 11)) {
+ return CRYPT_PK_INVALID_SIZE;
+ }
+
+ /* separate encoded message */
+
+ if ((msg[0] != 0x00) || (msg[1] != (unsigned char)block_type)) {
+ result = CRYPT_INVALID_PACKET;
+ goto bail;
+ }
+
+ if (block_type == LTC_LTC_PKCS_1_EME) {
+ for (i = 2; i < modulus_len; i++) {
+ /* separator */
+ if (msg[i] == 0x00) {
+ break;
+ }
+ }
+ ps_len = i++ - 2;
+
+ if ((i >= modulus_len) || (ps_len < 8)) {
+ /* There was no octet with hexadecimal value 0x00 to separate ps from m,
+ * or the length of ps is less than 8 octets.
+ */
+ result = CRYPT_INVALID_PACKET;
+ goto bail;
+ }
+ } else {
+ for (i = 2; i < modulus_len - 1; i++) {
+ if (msg[i] != 0xFF) {
+ break;
+ }
+ }
+
+ /* separator check */
+ if (msg[i] != 0) {
+ /* There was no octet with hexadecimal value 0x00 to separate ps from m. */
+ result = CRYPT_INVALID_PACKET;
+ goto bail;
+ }
+
+ ps_len = i - 2;
+ }
+
+ if (*outlen < (msglen - (2 + ps_len + 1))) {
+ *outlen = msglen - (2 + ps_len + 1);
+ result = CRYPT_BUFFER_OVERFLOW;
+ goto bail;
+ }
+
+ *outlen = (msglen - (2 + ps_len + 1));
+ XMEMCPY(out, &msg[2 + ps_len + 1], *outlen);
+
+ /* valid packet */
+ *is_valid = 1;
+ result = CRYPT_OK;
+bail:
+ return result;
+} /* pkcs_1_v1_5_decode */
+#endif /* #ifdef LTC_PKCS_1 */
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/*! \file pkcs_1_v1_5_encode.c
+ *
+ * LTC_PKCS #1 v1.5 Padding (Andreas Lange)
+ */
+
+#ifdef LTC_PKCS_1
+
+/*! \brief LTC_PKCS #1 v1.5 encode.
+ *
+ * \param msg The data to encode
+ * \param msglen The length of the data to encode (octets)
+ * \param block_type Block type to use in padding (\sa ltc_pkcs_1_v1_5_blocks)
+ * \param modulus_bitlen The bit length of the RSA modulus
+ * \param prng An active PRNG state (only for LTC_LTC_PKCS_1_EME)
+ * \param prng_idx The index of the PRNG desired (only for LTC_LTC_PKCS_1_EME)
+ * \param out [out] The destination for the encoded data
+ * \param outlen [in/out] The max size and resulting size of the encoded data
+ *
+ * \return CRYPT_OK if successful
+ */
+int pkcs_1_v1_5_encode(const unsigned char *msg,
+ unsigned long msglen,
+ int block_type,
+ unsigned long modulus_bitlen,
+ prng_state *prng,
+ int prng_idx,
+ unsigned char *out,
+ unsigned long *outlen) {
+ unsigned long modulus_len, ps_len, i;
+ unsigned char *ps;
+ int result;
+
+ /* valid block_type? */
+ if ((block_type != LTC_LTC_PKCS_1_EMSA) &&
+ (block_type != LTC_LTC_PKCS_1_EME)) {
+ return CRYPT_PK_INVALID_PADDING;
+ }
+
+ if (block_type == LTC_LTC_PKCS_1_EME) { /* encryption padding, we need a valid PRNG */
+ if ((result = prng_is_valid(prng_idx)) != CRYPT_OK) {
+ return result;
+ }
+ }
+
+ modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0);
+
+ /* test message size */
+ if ((msglen + 11) > modulus_len) {
+ return CRYPT_PK_INVALID_SIZE;
+ }
+
+ if (*outlen < modulus_len) {
+ *outlen = modulus_len;
+ result = CRYPT_BUFFER_OVERFLOW;
+ goto bail;
+ }
+
+ /* generate an octets string PS */
+ ps = &out[2];
+ ps_len = modulus_len - msglen - 3;
+
+ if (block_type == LTC_LTC_PKCS_1_EME) {
+ /* now choose a random ps */
+ if (prng_descriptor[prng_idx].read(ps, ps_len, prng) != ps_len) {
+ result = CRYPT_ERROR_READPRNG;
+ goto bail;
+ }
+
+ /* transform zero bytes (if any) to non-zero random bytes */
+ for (i = 0; i < ps_len; i++) {
+ while (ps[i] == 0) {
+ if (prng_descriptor[prng_idx].read(&ps[i], 1, prng) != 1) {
+ result = CRYPT_ERROR_READPRNG;
+ goto bail;
+ }
+ }
+ }
+ } else {
+ XMEMSET(ps, 0xFF, ps_len);
+ }
+
+ /* create string of length modulus_len */
+ out[0] = 0x00;
+ out[1] = (unsigned char)block_type;/* block_type 1 or 2 */
+ out[2 + ps_len] = 0x00;
+ XMEMCPY(&out[2 + ps_len + 1], msg, msglen);
+ *outlen = modulus_len;
+
+ result = CRYPT_OK;
+bail:
+ return result;
+} /* pkcs_1_v1_5_encode */
+#endif /* #ifdef LTC_PKCS_1 */
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_encode.c,v $ */
+/* $Revision: 1.4 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file rand_prime.c
+ Generate a random prime, Tom St Denis
+ */
+
+#define USE_BBS 1
+
+int rand_prime(void *N, long len, prng_state *prng, int wprng) {
+ int err, res, type;
+ unsigned char *buf;
+
+ LTC_ARGCHK(N != NULL);
+
+ /* get type */
+ if (len < 0) {
+ type = USE_BBS;
+ len = -len;
+ } else {
+ type = 0;
+ }
+
+ /* allow sizes between 2 and 512 bytes for a prime size */
+ if ((len < 2) || (len > 512)) {
+ return CRYPT_INVALID_PRIME_SIZE;
+ }
+
+ /* valid PRNG? Better be! */
+ if ((err = prng_is_valid(wprng)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* allocate buffer to work with */
+ buf = XCALLOC(1, len);
+ if (buf == NULL) {
+ return CRYPT_MEM;
+ }
+
+ do {
+ /* generate value */
+ if (prng_descriptor[wprng].read(buf, len, prng) != (unsigned long)len) {
+ XFREE(buf);
+ return CRYPT_ERROR_READPRNG;
+ }
+
+ /* munge bits */
+ buf[0] |= 0x80 | 0x40;
+ buf[len - 1] |= 0x01 | ((type & USE_BBS) ? 0x02 : 0x00);
+
+ /* load value */
+ if ((err = mp_read_unsigned_bin(N, buf, len)) != CRYPT_OK) {
+ XFREE(buf);
+ return err;
+ }
+
+ /* test */
+ if ((err = mp_prime_is_prime(N, 8, &res)) != CRYPT_OK) {
+ XFREE(buf);
+ return err;
+ }
+ } while (res == LTC_MP_NO);
+
+#ifdef LTC_CLEAN_STACK
+ zeromem(buf, len);
+#endif
+
+ XFREE(buf);
+ return CRYPT_OK;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/math/rand_prime.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2006/12/28 01:27:23 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file rng_get_bytes.c
+ portable way to get secure random bits to feed a PRNG (Tom St Denis)
+ */
+
+#ifdef LTC_DEVRANDOM
+/* on *NIX read /dev/random */
+static unsigned long rng_nix(unsigned char *buf, unsigned long len,
+ void (*callback)(void)) {
+ #ifdef LTC_NO_FILE
+ return 0;
+ #else
+ FILE *f;
+ unsigned long x;
+ #ifdef TRY_URANDOM_FIRST
+ f = fopen("/dev/urandom", "rb");
+ if (f == NULL)
+ #endif /* TRY_URANDOM_FIRST */
+ f = fopen("/dev/random", "rb");
+
+ if (f == NULL) {
+ return 0;
+ }
+
+ /* disable buffering */
+ if (setvbuf(f, NULL, _IONBF, 0) != 0) {
+ fclose(f);
+ return 0;
+ }
+
+ x = (unsigned long)fread(buf, 1, (size_t)len, f);
+ fclose(f);
+ return x;
+ #endif /* LTC_NO_FILE */
+}
+#endif /* LTC_DEVRANDOM */
+
+/* on ANSI C platforms with 100 < CLOCKS_PER_SEC < 10000 */
+#if defined(CLOCKS_PER_SEC) && !defined(WINCE)
+
+ #define ANSI_RNG
+
+static unsigned long rng_ansic(unsigned char *buf, unsigned long len,
+ void (*callback)(void)) {
+ clock_t t1;
+ int l, acc, bits, a, b;
+
+ if ((XCLOCKS_PER_SEC < 100) || (XCLOCKS_PER_SEC > 10000)) {
+ return 0;
+ }
+
+ l = len;
+ bits = 8;
+ acc = a = b = 0;
+ while (len--) {
+ if (callback != NULL) callback();
+ while (bits--) {
+ do {
+ t1 = XCLOCK();
+ while (t1 == XCLOCK()) a ^= 1;
+ t1 = XCLOCK();
+ while (t1 == XCLOCK()) b ^= 1;
+ } while (a == b);
+ acc = (acc << 1) | a;
+ }
+ *buf++ = acc;
+ acc = 0;
+ bits = 8;
+ }
+ acc = bits = a = b = 0;
+ return l;
+}
+#endif
+
+/* Try the Microsoft CSP */
+#if defined(WIN32) || defined(WINCE)
+ #define _WIN32_WINNT 0x0400
+ #ifdef WINCE
+ #define UNDER_CE
+ #define ARM
+ #endif
+#include
+#include
+
+static unsigned long rng_win32(unsigned char *buf, unsigned long len,
+ void (*callback)(void)) {
+ HCRYPTPROV hProv = 0;
+
+ if (!CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL,
+ (CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)) &&
+ !CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET))
+ return 0;
+
+ if (CryptGenRandom(hProv, len, buf) == TRUE) {
+ CryptReleaseContext(hProv, 0);
+ return len;
+ } else {
+ CryptReleaseContext(hProv, 0);
+ return 0;
+ }
+}
+#endif /* WIN32 */
+
+/**
+ Read the system RNG
+ @param out Destination
+ @param outlen Length desired (octets)
+ @param callback Pointer to void function to act as "callback" when RNG is slow. This can be NULL
+ @return Number of octets read
+ */
+unsigned long rng_get_bytes(unsigned char *out, unsigned long outlen,
+ void (*callback)(void)) {
+ for (unsigned long i = 0; i < outlen; i++)
+ out[i] = rand() & 0xff;
+ return outlen;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/prngs/rng_get_bytes.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file rng_make_prng.c
+ portable way to get secure random bits to feed a PRNG (Tom St Denis)
+ */
+
+/**
+ Create a PRNG from a RNG
+ @param bits Number of bits of entropy desired (64 ... 1024)
+ @param wprng Index of which PRNG to setup
+ @param prng [out] PRNG state to initialize
+ @param callback A pointer to a void function for when the RNG is slow, this can be NULL
+ @return CRYPT_OK if successful
+ */
+int rng_make_prng(int bits, int wprng, prng_state *prng,
+ void (*callback)(void)) {
+ unsigned char buf[256];
+ int err;
+
+ LTC_ARGCHK(prng != NULL);
+
+ /* check parameter */
+ if ((err = prng_is_valid(wprng)) != CRYPT_OK) {
+ return err;
+ }
+
+ if ((bits < 64) || (bits > 1024)) {
+ return CRYPT_INVALID_PRNGSIZE;
+ }
+
+ if ((err = prng_descriptor[wprng].start(prng)) != CRYPT_OK) {
+ return err;
+ }
+
+ bits = ((bits / 8) + ((bits & 7) != 0 ? 1 : 0)) * 2;
+ if (rng_get_bytes(buf, (unsigned long)bits, callback) != (unsigned long)bits) {
+ return CRYPT_ERROR_READPRNG;
+ }
+
+ if ((err = prng_descriptor[wprng].add_entropy(buf, (unsigned long)bits, prng)) != CRYPT_OK) {
+ return err;
+ }
+
+ if ((err = prng_descriptor[wprng].ready(prng)) != CRYPT_OK) {
+ return err;
+ }
+
+#ifdef LTC_CLEAN_STACK
+ zeromem(buf, sizeof(buf));
+#endif
+ return CRYPT_OK;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/prngs/rng_make_prng.c,v $ */
+/* $Revision: 1.5 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file rsa_decrypt_key.c
+ RSA LTC_PKCS #1 Decryption, Tom St Denis and Andreas Lange
+ */
+
+#ifdef LTC_MRSA
+
+/**
+ LTC_PKCS #1 decrypt then v1.5 or OAEP depad
+ @param in The ciphertext
+ @param inlen The length of the ciphertext (octets)
+ @param out [out] The plaintext
+ @param outlen [in/out] The max size and resulting size of the plaintext (octets)
+ @param lparam The system "lparam" value
+ @param lparamlen The length of the lparam value (octets)
+ @param hash_idx The index of the hash desired
+ @param padding Type of padding (LTC_LTC_PKCS_1_OAEP or LTC_LTC_PKCS_1_V1_5)
+ @param stat [out] Result of the decryption, 1==valid, 0==invalid
+ @param key The corresponding private RSA key
+ @return CRYPT_OK if succcessul (even if invalid)
+ */
+int rsa_decrypt_key_ex(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ const unsigned char *lparam, unsigned long lparamlen,
+ int hash_idx, int padding,
+ int *stat, rsa_key *key) {
+ unsigned long modulus_bitlen, modulus_bytelen, x;
+ int err;
+ unsigned char *tmp;
+
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+ LTC_ARGCHK(key != NULL);
+ LTC_ARGCHK(stat != NULL);
+
+ /* default to invalid */
+ *stat = 0;
+
+ /* valid padding? */
+
+ if ((padding != LTC_LTC_PKCS_1_V1_5) &&
+ (padding != LTC_LTC_PKCS_1_OAEP)) {
+ return CRYPT_PK_INVALID_PADDING;
+ }
+
+ if (padding == LTC_LTC_PKCS_1_OAEP) {
+ /* valid hash ? */
+ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
+ return err;
+ }
+ }
+
+ /* get modulus len in bits */
+ modulus_bitlen = mp_count_bits((key->N));
+
+ /* outlen must be at least the size of the modulus */
+ modulus_bytelen = mp_unsigned_bin_size((key->N));
+ if (modulus_bytelen != inlen) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* allocate ram */
+ tmp = XMALLOC(inlen);
+ if (tmp == NULL) {
+ return CRYPT_MEM;
+ }
+
+ /* rsa decode the packet */
+ x = inlen;
+ if ((err = ltc_mp.rsa_me(in, inlen, tmp, &x, PK_PRIVATE, key)) != CRYPT_OK) {
+ XFREE(tmp);
+ return err;
+ }
+
+ if (padding == LTC_LTC_PKCS_1_OAEP) {
+ /* now OAEP decode the packet */
+ err = pkcs_1_oaep_decode(tmp, x, lparam, lparamlen, modulus_bitlen, hash_idx,
+ out, outlen, stat);
+ } else {
+ /* now LTC_PKCS #1 v1.5 depad the packet */
+ err = pkcs_1_v1_5_decode(tmp, x, LTC_LTC_PKCS_1_EME, modulus_bitlen, out, outlen, stat);
+ }
+
+ XFREE(tmp);
+ return err;
+}
+#endif /* LTC_MRSA */
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_decrypt_key.c,v $ */
+/* $Revision: 1.10 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file rsa_encrypt_key.c
+ RSA LTC_PKCS #1 encryption, Tom St Denis and Andreas Lange
+ */
+
+#ifdef LTC_MRSA
+
+/**
+ (LTC_PKCS #1 v2.0) OAEP pad then encrypt
+ @param in The plaintext
+ @param inlen The length of the plaintext (octets)
+ @param out [out] The ciphertext
+ @param outlen [in/out] The max size and resulting size of the ciphertext
+ @param lparam The system "lparam" for the encryption
+ @param lparamlen The length of lparam (octets)
+ @param prng An active PRNG
+ @param prng_idx The index of the desired prng
+ @param hash_idx The index of the desired hash
+ @param padding Type of padding (LTC_LTC_PKCS_1_OAEP or LTC_LTC_PKCS_1_V1_5)
+ @param key The RSA key to encrypt to
+ @return CRYPT_OK if successful
+ */
+int rsa_encrypt_key_ex(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ const unsigned char *lparam, unsigned long lparamlen,
+ prng_state *prng, int prng_idx, int hash_idx, int padding, rsa_key *key) {
+ unsigned long modulus_bitlen, modulus_bytelen, x;
+ int err;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+ LTC_ARGCHK(key != NULL);
+
+ /* valid padding? */
+ if ((padding != LTC_LTC_PKCS_1_V1_5) &&
+ (padding != LTC_LTC_PKCS_1_OAEP)) {
+ return CRYPT_PK_INVALID_PADDING;
+ }
+
+ /* valid prng? */
+ if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) {
+ return err;
+ }
+
+ if (padding == LTC_LTC_PKCS_1_OAEP) {
+ /* valid hash? */
+ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
+ return err;
+ }
+ }
+
+ /* get modulus len in bits */
+ modulus_bitlen = mp_count_bits((key->N));
+
+ /* outlen must be at least the size of the modulus */
+ modulus_bytelen = mp_unsigned_bin_size((key->N));
+ if (modulus_bytelen > *outlen) {
+ *outlen = modulus_bytelen;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ if (padding == LTC_LTC_PKCS_1_OAEP) {
+ /* OAEP pad the key */
+ x = *outlen;
+ if ((err = pkcs_1_oaep_encode(in, inlen, lparam,
+ lparamlen, modulus_bitlen, prng, prng_idx, hash_idx,
+ out, &x)) != CRYPT_OK) {
+ return err;
+ }
+ } else {
+ /* LTC_PKCS #1 v1.5 pad the key */
+ x = *outlen;
+ if ((err = pkcs_1_v1_5_encode(in, inlen, LTC_LTC_PKCS_1_EME,
+ modulus_bitlen, prng, prng_idx,
+ out, &x)) != CRYPT_OK) {
+ return err;
+ }
+ }
+
+ /* rsa exptmod the OAEP or LTC_PKCS #1 v1.5 pad */
+ return ltc_mp.rsa_me(out, x, out, outlen, PK_PUBLIC, key);
+}
+#endif /* LTC_MRSA */
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_encrypt_key.c,v $ */
+/* $Revision: 1.10 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file rsa_exptmod.c
+ RSA LTC_PKCS exptmod, Tom St Denis
+ */
+
+#ifdef LTC_MRSA
+
+/**
+ Compute an RSA modular exponentiation
+ @param in The input data to send into RSA
+ @param inlen The length of the input (octets)
+ @param out [out] The destination
+ @param outlen [in/out] The max size and resulting size of the output
+ @param which Which exponent to use, e.g. PK_PRIVATE or PK_PUBLIC
+ @param key The RSA key to use
+ @return CRYPT_OK if successful
+ */
+int rsa_exptmod(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen, int which,
+ rsa_key *key) {
+ void *tmp, *tmpa, *tmpb;
+ unsigned long x;
+ int err;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+ LTC_ARGCHK(key != NULL);
+
+ /* is the key of the right type for the operation? */
+ if ((which == PK_PRIVATE) && (key->type != PK_PRIVATE)) {
+ return CRYPT_PK_NOT_PRIVATE;
+ }
+
+ /* must be a private or public operation */
+ if ((which != PK_PRIVATE) && (which != PK_PUBLIC)) {
+ return CRYPT_PK_INVALID_TYPE;
+ }
+
+ /* init and copy into tmp */
+ if ((err = mp_init_multi(&tmp, &tmpa, &tmpb, NULL)) != CRYPT_OK) {
+ return err;
+ }
+ if ((err = mp_read_unsigned_bin(tmp, (unsigned char *)in, (int)inlen)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* sanity check on the input */
+ if (mp_cmp(key->N, tmp) == LTC_MP_LT) {
+ err = CRYPT_PK_INVALID_SIZE;
+ goto error;
+ }
+
+ /* are we using the private exponent and is the key optimized? */
+ if (which == PK_PRIVATE) {
+ /* tmpa = tmp^dP mod p */
+ if ((err = mp_exptmod(tmp, key->dP, key->p, tmpa)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* tmpb = tmp^dQ mod q */
+ if ((err = mp_exptmod(tmp, key->dQ, key->q, tmpb)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* tmp = (tmpa - tmpb) * qInv (mod p) */
+ if ((err = mp_sub(tmpa, tmpb, tmp)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_mulmod(tmp, key->qP, key->p, tmp)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* tmp = tmpb + q * tmp */
+ if ((err = mp_mul(tmp, key->q, tmp)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_add(tmp, tmpb, tmp)) != CRYPT_OK) {
+ goto error;
+ }
+ } else {
+ /* exptmod it */
+ if ((err = mp_exptmod(tmp, key->e, key->N, tmp)) != CRYPT_OK) {
+ goto error;
+ }
+ }
+
+ /* read it back */
+ x = (unsigned long)mp_unsigned_bin_size(key->N);
+ if (x > *outlen) {
+ *outlen = x;
+ err = CRYPT_BUFFER_OVERFLOW;
+ goto error;
+ }
+
+ /* this should never happen ... */
+ if (mp_unsigned_bin_size(tmp) > mp_unsigned_bin_size(key->N)) {
+ err = CRYPT_ERROR;
+ goto error;
+ }
+ *outlen = x;
+
+ /* convert it */
+ zeromem(out, x);
+ if ((err = mp_to_unsigned_bin(tmp, out + (x - mp_unsigned_bin_size(tmp)))) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* clean up and return */
+ err = CRYPT_OK;
+error:
+ mp_clear_multi(tmp, tmpa, tmpb, NULL);
+ return err;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_exptmod.c,v $ */
+/* $Revision: 1.18 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file rsa_free.c
+ Free an RSA key, Tom St Denis
+ */
+
+#ifdef LTC_MRSA
+
+/**
+ Free an RSA key from memory
+ @param key The RSA key to free
+ */
+void rsa_free(rsa_key *key) {
+ LTC_ARGCHKVD(key != NULL);
+ mp_clear_multi(key->e, key->d, key->N, key->dQ, key->dP, key->qP, key->p, key->q, NULL);
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_free.c,v $ */
+/* $Revision: 1.10 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file rsa_import.c
+ Import a LTC_PKCS RSA key, Tom St Denis
+ */
+
+#ifdef LTC_MRSA
+
+/**
+ Import an RSAPublicKey or RSAPrivateKey [two-prime only, only support >= 1024-bit keys, defined in LTC_PKCS #1 v2.1]
+ @param in The packet to import from
+ @param inlen It's length (octets)
+ @param key [out] Destination for newly imported key
+ @return CRYPT_OK if successful, upon error allocated memory is freed
+ */
+int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key) {
+ int err;
+ void *zero;
+ unsigned char *tmpbuf;
+ unsigned long t, x, y, z, tmpoid[16];
+ ltc_asn1_list ssl_pubkey_hashoid[2];
+ ltc_asn1_list ssl_pubkey[2];
+ ltc_mp = ltm_desc;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(key != NULL);
+ LTC_ARGCHK(ltc_mp.name != NULL);
+
+ /* init key */
+ if ((err = mp_init_multi(&key->e, &key->d, &key->N, &key->dQ,
+ &key->dP, &key->qP, &key->p, &key->q, NULL)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* see if the OpenSSL DER format RSA public key will work */
+ tmpbuf = XCALLOC(1, MAX_RSA_SIZE * 8);
+ if (tmpbuf == NULL) {
+ err = CRYPT_MEM;
+ goto LBL_ERR;
+ }
+
+ /* this includes the internal hash ID and optional params (NULL in this case) */
+ LTC_SET_ASN1(ssl_pubkey_hashoid, 0, LTC_ASN1_OBJECT_IDENTIFIER, tmpoid, sizeof(tmpoid) / sizeof(tmpoid[0]));
+ LTC_SET_ASN1(ssl_pubkey_hashoid, 1, LTC_ASN1_NULL, NULL, 0);
+
+ /* the actual format of the SSL DER key is odd, it stores a RSAPublicKey in a **BIT** string ... so we have to extract it
+ then proceed to convert bit to octet
+ */
+ LTC_SET_ASN1(ssl_pubkey, 0, LTC_ASN1_SEQUENCE, &ssl_pubkey_hashoid, 2);
+ LTC_SET_ASN1(ssl_pubkey, 1, LTC_ASN1_BIT_STRING, tmpbuf, MAX_RSA_SIZE * 8);
+
+ if (der_decode_sequence(in, inlen,
+ ssl_pubkey, 2UL) == CRYPT_OK) {
+ /* ok now we have to reassemble the BIT STRING to an OCTET STRING. Thanks OpenSSL... */
+ for (t = y = z = x = 0; x < ssl_pubkey[1].size; x++) {
+ y = (y << 1) | tmpbuf[x];
+ if (++z == 8) {
+ tmpbuf[t++] = (unsigned char)y;
+ y = 0;
+ z = 0;
+ }
+ }
+
+ /* now it should be SEQUENCE { INTEGER, INTEGER } */
+ if ((err = der_decode_sequence_multi(tmpbuf, t,
+ LTC_ASN1_INTEGER, 1UL, key->N,
+ LTC_ASN1_INTEGER, 1UL, key->e,
+ LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) {
+ XFREE(tmpbuf);
+ goto LBL_ERR;
+ }
+ XFREE(tmpbuf);
+ key->type = PK_PUBLIC;
+ return CRYPT_OK;
+ }
+ XFREE(tmpbuf);
+
+ /* not SSL public key, try to match against LTC_PKCS #1 standards */
+ if ((err = der_decode_sequence_multi(in, inlen,
+ LTC_ASN1_INTEGER, 1UL, key->N,
+ LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ if (mp_cmp_d(key->N, 0) == LTC_MP_EQ) {
+ if ((err = mp_init(&zero)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ /* it's a private key */
+ if ((err = der_decode_sequence_multi(in, inlen,
+ LTC_ASN1_INTEGER, 1UL, zero,
+ LTC_ASN1_INTEGER, 1UL, key->N,
+ LTC_ASN1_INTEGER, 1UL, key->e,
+ LTC_ASN1_INTEGER, 1UL, key->d,
+ LTC_ASN1_INTEGER, 1UL, key->p,
+ LTC_ASN1_INTEGER, 1UL, key->q,
+ LTC_ASN1_INTEGER, 1UL, key->dP,
+ LTC_ASN1_INTEGER, 1UL, key->dQ,
+ LTC_ASN1_INTEGER, 1UL, key->qP,
+ LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) {
+ mp_clear(zero);
+ goto LBL_ERR;
+ }
+ mp_clear(zero);
+ key->type = PK_PRIVATE;
+ } else if (mp_cmp_d(key->N, 1) == LTC_MP_EQ) {
+ /* we don't support multi-prime RSA */
+ err = CRYPT_PK_INVALID_TYPE;
+ goto LBL_ERR;
+ } else {
+ /* it's a public key and we lack e */
+ if ((err = der_decode_sequence_multi(in, inlen,
+ LTC_ASN1_INTEGER, 1UL, key->N,
+ LTC_ASN1_INTEGER, 1UL, key->e,
+ LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ key->type = PK_PUBLIC;
+ }
+ return CRYPT_OK;
+LBL_ERR:
+ mp_clear_multi(key->d, key->e, key->N, key->dQ, key->dP, key->qP, key->p, key->q, NULL);
+ return err;
+}
+#endif /* LTC_MRSA */
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_import.c,v $ */
+/* $Revision: 1.23 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file rsa_make_key.c
+ RSA key generation, Tom St Denis
+ */
+
+#ifdef LTC_MRSA
+
+/**
+ Create an RSA key
+ @param prng An active PRNG state
+ @param wprng The index of the PRNG desired
+ @param size The size of the modulus (key size) desired (octets)
+ @param e The "e" value (public key). e==65537 is a good choice
+ @param key [out] Destination of a newly created private key pair
+ @return CRYPT_OK if successful, upon error all allocated ram is freed
+ */
+int rsa_make_key(prng_state *prng, int wprng, int size, long e, rsa_key *key) {
+ void *p, *q, *tmp1, *tmp2, *tmp3;
+ int err;
+
+ LTC_ARGCHK(ltc_mp.name != NULL);
+ LTC_ARGCHK(key != NULL);
+
+ if ((size < (MIN_RSA_SIZE / 8)) || (size > (MAX_RSA_SIZE / 8))) {
+ return CRYPT_INVALID_KEYSIZE;
+ }
+
+ if ((e < 3) || ((e & 1) == 0)) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ if ((err = prng_is_valid(wprng)) != CRYPT_OK) {
+ return err;
+ }
+
+ if ((err = mp_init_multi(&p, &q, &tmp1, &tmp2, &tmp3, NULL)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* make primes p and q (optimization provided by Wayne Scott) */
+ if ((err = mp_set_int(tmp3, e)) != CRYPT_OK) {
+ goto errkey;
+ } /* tmp3 = e */
+
+ /* make prime "p" */
+ do {
+ if ((err = rand_prime(p, size / 2, prng, wprng)) != CRYPT_OK) {
+ goto errkey;
+ }
+ if ((err = mp_sub_d(p, 1, tmp1)) != CRYPT_OK) {
+ goto errkey;
+ } /* tmp1 = p-1 */
+ if ((err = mp_gcd(tmp1, tmp3, tmp2)) != CRYPT_OK) {
+ goto errkey;
+ } /* tmp2 = gcd(p-1, e) */
+ } while (mp_cmp_d(tmp2, 1) != 0); /* while e divides p-1 */
+
+ /* make prime "q" */
+ do {
+ if ((err = rand_prime(q, size / 2, prng, wprng)) != CRYPT_OK) {
+ goto errkey;
+ }
+ if ((err = mp_sub_d(q, 1, tmp1)) != CRYPT_OK) {
+ goto errkey;
+ } /* tmp1 = q-1 */
+ if ((err = mp_gcd(tmp1, tmp3, tmp2)) != CRYPT_OK) {
+ goto errkey;
+ } /* tmp2 = gcd(q-1, e) */
+ } while (mp_cmp_d(tmp2, 1) != 0); /* while e divides q-1 */
+
+ /* tmp1 = lcm(p-1, q-1) */
+ if ((err = mp_sub_d(p, 1, tmp2)) != CRYPT_OK) {
+ goto errkey;
+ } /* tmp2 = p-1 */
+ /* tmp1 = q-1 (previous do/while loop) */
+ if ((err = mp_lcm(tmp1, tmp2, tmp1)) != CRYPT_OK) {
+ goto errkey;
+ } /* tmp1 = lcm(p-1, q-1) */
+
+ /* make key */
+ if ((err = mp_init_multi(&key->e, &key->d, &key->N, &key->dQ, &key->dP, &key->qP, &key->p, &key->q, NULL)) != CRYPT_OK) {
+ goto errkey;
+ }
+
+ if ((err = mp_set_int(key->e, e)) != CRYPT_OK) {
+ goto errkey;
+ } /* key->e = e */
+ if ((err = mp_invmod(key->e, tmp1, key->d)) != CRYPT_OK) {
+ goto errkey;
+ } /* key->d = 1/e mod lcm(p-1,q-1) */
+ if ((err = mp_mul(p, q, key->N)) != CRYPT_OK) {
+ goto errkey;
+ } /* key->N = pq */
+
+ /* optimize for CRT now */
+ /* find d mod q-1 and d mod p-1 */
+ if ((err = mp_sub_d(p, 1, tmp1)) != CRYPT_OK) {
+ goto errkey;
+ } /* tmp1 = q-1 */
+ if ((err = mp_sub_d(q, 1, tmp2)) != CRYPT_OK) {
+ goto errkey;
+ } /* tmp2 = p-1 */
+ if ((err = mp_mod(key->d, tmp1, key->dP)) != CRYPT_OK) {
+ goto errkey;
+ } /* dP = d mod p-1 */
+ if ((err = mp_mod(key->d, tmp2, key->dQ)) != CRYPT_OK) {
+ goto errkey;
+ } /* dQ = d mod q-1 */
+ if ((err = mp_invmod(q, p, key->qP)) != CRYPT_OK) {
+ goto errkey;
+ } /* qP = 1/q mod p */
+
+ if ((err = mp_copy(p, key->p)) != CRYPT_OK) {
+ goto errkey;
+ }
+ if ((err = mp_copy(q, key->q)) != CRYPT_OK) {
+ goto errkey;
+ }
+
+ /* set key type (in this case it's CRT optimized) */
+ key->type = PK_PRIVATE;
+
+ /* return ok and free temps */
+ err = CRYPT_OK;
+ goto cleanup;
+errkey:
+ mp_clear_multi(key->d, key->e, key->N, key->dQ, key->dP, key->qP, key->p, key->q, NULL);
+cleanup:
+ mp_clear_multi(tmp3, tmp2, tmp1, p, q, NULL);
+ return err;
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_make_key.c,v $ */
+/* $Revision: 1.16 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file rsa_sign_hash.c
+ RSA LTC_PKCS #1 v1.5 and v2 PSS sign hash, Tom St Denis and Andreas Lange
+ */
+
+#ifdef LTC_MRSA
+
+/**
+ LTC_PKCS #1 pad then sign
+ @param in The hash to sign
+ @param inlen The length of the hash to sign (octets)
+ @param out [out] The signature
+ @param outlen [in/out] The max size and resulting size of the signature
+ @param padding Type of padding (LTC_LTC_PKCS_1_PSS or LTC_LTC_PKCS_1_V1_5)
+ @param prng An active PRNG state
+ @param prng_idx The index of the PRNG desired
+ @param hash_idx The index of the hash desired
+ @param saltlen The length of the salt desired (octets)
+ @param key The private RSA key to use
+ @return CRYPT_OK if successful
+ */
+int rsa_sign_hash_ex(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ int padding,
+ prng_state *prng, int prng_idx,
+ int hash_idx, unsigned long saltlen,
+ rsa_key *key) {
+ unsigned long modulus_bitlen, modulus_bytelen, x, y;
+ int err;
+
+ LTC_ARGCHK(in != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+ LTC_ARGCHK(key != NULL);
+
+ /* valid padding? */
+ if ((padding != LTC_LTC_PKCS_1_V1_5) && (padding != LTC_LTC_PKCS_1_PSS)) {
+ return CRYPT_PK_INVALID_PADDING;
+ }
+
+ hash_idx = register_hash(&sha256_desc);
+
+ if (padding == LTC_LTC_PKCS_1_PSS) {
+ /* valid prng and hash ? */
+ if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) {
+ return err;
+ }
+ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
+ return err;
+ }
+ }
+
+ /* get modulus len in bits */
+ modulus_bitlen = mp_count_bits((key->N));
+
+ /* outlen must be at least the size of the modulus */
+ modulus_bytelen = mp_unsigned_bin_size((key->N));
+ if (modulus_bytelen > *outlen) {
+ *outlen = modulus_bytelen;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+
+ if (padding == LTC_LTC_PKCS_1_PSS) {
+ /* PSS pad the key */
+ x = *outlen;
+ if ((err = pkcs_1_pss_encode(in, inlen, saltlen, prng, prng_idx,
+ hash_idx, modulus_bitlen, out, &x)) != CRYPT_OK) {
+ return err;
+ }
+ } else {
+ /* LTC_PKCS #1 v1.5 pad the hash */
+ unsigned char *tmpin;
+ ltc_asn1_list digestinfo[2], siginfo[2];
+
+ /* not all hashes have OIDs... so sad */
+ if (hash_descriptor[hash_idx].OIDlen == 0) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* construct the SEQUENCE
+ SEQUENCE {
+ SEQUENCE {hashoid OID
+ blah NULL
+ }
+ hash OCTET STRING
+ }
+ */
+ LTC_SET_ASN1(digestinfo, 0, LTC_ASN1_OBJECT_IDENTIFIER, hash_descriptor[hash_idx].OID, hash_descriptor[hash_idx].OIDlen);
+ LTC_SET_ASN1(digestinfo, 1, LTC_ASN1_NULL, NULL, 0);
+ LTC_SET_ASN1(siginfo, 0, LTC_ASN1_SEQUENCE, digestinfo, 2);
+ LTC_SET_ASN1(siginfo, 1, LTC_ASN1_OCTET_STRING, in, inlen);
+
+ /* allocate memory for the encoding */
+ y = mp_unsigned_bin_size(key->N);
+ tmpin = XMALLOC(y);
+ if (tmpin == NULL) {
+ return CRYPT_MEM;
+ }
+
+ if ((err = der_encode_sequence(siginfo, 2, tmpin, &y)) != CRYPT_OK) {
+ XFREE(tmpin);
+ return err;
+ }
+
+ x = *outlen;
+ if ((err = pkcs_1_v1_5_encode(tmpin, y, LTC_LTC_PKCS_1_EMSA,
+ modulus_bitlen, NULL, 0,
+ out, &x)) != CRYPT_OK) {
+ XFREE(tmpin);
+ return err;
+ }
+ XFREE(tmpin);
+ }
+
+ /* RSA encode it */
+ return ltc_mp.rsa_me(out, x, out, outlen, PK_PRIVATE, key);
+}
+#endif /* LTC_MRSA */
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_sign_hash.c,v $ */
+/* $Revision: 1.11 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file rsa_verify_hash.c
+ RSA LTC_PKCS #1 v1.5 or v2 PSS signature verification, Tom St Denis and Andreas Lange
+ */
+
+#ifdef LTC_MRSA
+
+/**
+ LTC_PKCS #1 de-sign then v1.5 or PSS depad
+ @param sig The signature data
+ @param siglen The length of the signature data (octets)
+ @param hash The hash of the message that was signed
+ @param hashlen The length of the hash of the message that was signed (octets)
+ @param padding Type of padding (LTC_LTC_PKCS_1_PSS or LTC_LTC_PKCS_1_V1_5)
+ @param hash_idx The index of the desired hash
+ @param saltlen The length of the salt used during signature
+ @param stat [out] The result of the signature comparison, 1==valid, 0==invalid
+ @param key The public RSA key corresponding to the key that performed the signature
+ @return CRYPT_OK on success (even if the signature is invalid)
+ */
+int rsa_verify_hash_ex(const unsigned char *sig, unsigned long siglen,
+ const unsigned char *hash, unsigned long hashlen,
+ int padding,
+ int hash_idx, unsigned long saltlen,
+ int *stat, rsa_key *key) {
+ unsigned long modulus_bitlen, modulus_bytelen, x;
+ int err;
+ unsigned char *tmpbuf;
+
+ LTC_ARGCHK(hash != NULL);
+ LTC_ARGCHK(sig != NULL);
+ LTC_ARGCHK(stat != NULL);
+ LTC_ARGCHK(key != NULL);
+
+ /* default to invalid */
+ *stat = 0;
+
+ /* valid padding? */
+
+ if ((padding != LTC_LTC_PKCS_1_V1_5) &&
+ (padding != LTC_LTC_PKCS_1_PSS)) {
+ return CRYPT_PK_INVALID_PADDING;
+ }
+
+ hash_idx = register_hash(&sha256_desc);
+
+ if (padding == LTC_LTC_PKCS_1_PSS) {
+ /* valid hash ? */
+ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
+ return err;
+ }
+ }
+
+ /* get modulus len in bits */
+ modulus_bitlen = mp_count_bits((key->N));
+
+ /* outlen must be at least the size of the modulus */
+ modulus_bytelen = mp_unsigned_bin_size((key->N));
+ if (modulus_bytelen != siglen) {
+ return CRYPT_INVALID_PACKET;
+ }
+
+ /* allocate temp buffer for decoded sig */
+ tmpbuf = XMALLOC(siglen);
+ if (tmpbuf == NULL) {
+ return CRYPT_MEM;
+ }
+
+ /* RSA decode it */
+ x = siglen;
+ if ((err = ltc_mp.rsa_me(sig, siglen, tmpbuf, &x, PK_PUBLIC, key)) != CRYPT_OK) {
+ XFREE(tmpbuf);
+ return err;
+ }
+
+ /* make sure the output is the right size */
+ if (x != siglen) {
+ XFREE(tmpbuf);
+ return CRYPT_INVALID_PACKET;
+ }
+
+ if (padding == LTC_LTC_PKCS_1_PSS) {
+ /* PSS decode and verify it */
+ err = pkcs_1_pss_decode(hash, hashlen, tmpbuf, x, saltlen, hash_idx, modulus_bitlen, stat);
+ } else {
+ /* LTC_PKCS #1 v1.5 decode it */
+ unsigned char *out;
+ unsigned long outlen, loid[16];
+ int decoded;
+ ltc_asn1_list digestinfo[2], siginfo[2];
+
+ /* not all hashes have OIDs... so sad */
+ if (hash_descriptor[hash_idx].OIDlen == 0) {
+ err = CRYPT_INVALID_ARG;
+ goto bail_2;
+ }
+
+ /* allocate temp buffer for decoded hash */
+ outlen = ((modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0)) - 3;
+ out = XMALLOC(outlen);
+ if (out == NULL) {
+ err = CRYPT_MEM;
+ goto bail_2;
+ }
+
+ if ((err = pkcs_1_v1_5_decode(tmpbuf, x, LTC_LTC_PKCS_1_EMSA, modulus_bitlen, out, &outlen, &decoded)) != CRYPT_OK) {
+ XFREE(out);
+ goto bail_2;
+ }
+
+ /* now we must decode out[0...outlen-1] using ASN.1, test the OID and then test the hash */
+
+ /* construct the SEQUENCE
+ SEQUENCE {
+ SEQUENCE {hashoid OID
+ blah NULL
+ }
+ hash OCTET STRING
+ }
+ */
+ LTC_SET_ASN1(digestinfo, 0, LTC_ASN1_OBJECT_IDENTIFIER, loid, sizeof(loid) / sizeof(loid[0]));
+ LTC_SET_ASN1(digestinfo, 1, LTC_ASN1_NULL, NULL, 0);
+ LTC_SET_ASN1(siginfo, 0, LTC_ASN1_SEQUENCE, digestinfo, 2);
+ LTC_SET_ASN1(siginfo, 1, LTC_ASN1_OCTET_STRING, tmpbuf, siglen);
+
+ if ((err = der_decode_sequence(out, outlen, siginfo, 2)) != CRYPT_OK) {
+ XFREE(out);
+ goto bail_2;
+ }
+
+ /* test OID */
+ if ((digestinfo[0].size == hash_descriptor[hash_idx].OIDlen) &&
+ (XMEMCMP(digestinfo[0].data, hash_descriptor[hash_idx].OID, sizeof(unsigned long) * hash_descriptor[hash_idx].OIDlen) == 0) &&
+ (siginfo[1].size == hashlen) &&
+ (XMEMCMP(siginfo[1].data, hash, hashlen) == 0)) {
+ *stat = 1;
+ }
+
+ #ifdef LTC_CLEAN_STACK
+ zeromem(out, outlen);
+ #endif
+ XFREE(out);
+ }
+
+bail_2:
+ #ifdef LTC_CLEAN_STACK
+ zeromem(tmpbuf, siglen);
+ #endif
+ XFREE(tmpbuf);
+ return err;
+}
+#endif /* LTC_MRSA */
+
+int rsa_create_signature(unsigned char *sig, unsigned long* siglen,
+ const unsigned char *hash, unsigned long hashlen,
+ rsa_key *key) {
+ return rsa_sign_hash_ex(hash, hashlen, sig, siglen, LTC_LTC_PKCS_1_V1_5, NULL, 0, 0, 0, key);
+}
+
+int rsa_verify_signature(const unsigned char *sig, unsigned long siglen,
+ const unsigned char *hash, unsigned long hashlen,
+ int* stat, rsa_key *key) {
+ return rsa_verify_hash_ex(sig, siglen, hash, hashlen, LTC_LTC_PKCS_1_V1_5, 0, 0, stat, key);
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_verify_hash.c,v $ */
+/* $Revision: 1.13 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file sprng.c
+ Secure PRNG, Tom St Denis
+ */
+
+/* A secure PRNG using the RNG functions. Basically this is a
+ * wrapper that allows you to use a secure RNG as a PRNG
+ * in the various other functions.
+ */
+
+#ifdef LTC_SPRNG
+
+const struct ltc_prng_descriptor sprng_desc =
+{
+ "sprng", 0,
+ &sprng_start,
+ &sprng_add_entropy,
+ &sprng_ready,
+ &sprng_read,
+ &sprng_done,
+ &sprng_export,
+ &sprng_import,
+ &sprng_test
+};
+
+/**
+ Start the PRNG
+ @param prng [out] The PRNG state to initialize
+ @return CRYPT_OK if successful
+ */
+int sprng_start(prng_state *prng) {
+ return CRYPT_OK;
+}
+
+/**
+ Add entropy to the PRNG state
+ @param in The data to add
+ @param inlen Length of the data to add
+ @param prng PRNG state to update
+ @return CRYPT_OK if successful
+ */
+int sprng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng) {
+ return CRYPT_OK;
+}
+
+/**
+ Make the PRNG ready to read from
+ @param prng The PRNG to make active
+ @return CRYPT_OK if successful
+ */
+int sprng_ready(prng_state *prng) {
+ return CRYPT_OK;
+}
+
+/**
+ Read from the PRNG
+ @param out Destination
+ @param outlen Length of output
+ @param prng The active PRNG to read from
+ @return Number of octets read
+ */
+unsigned long sprng_read(unsigned char *out, unsigned long outlen, prng_state *prng) {
+ LTC_ARGCHK(out != NULL);
+ return rng_get_bytes(out, outlen, NULL);
+}
+
+/**
+ Terminate the PRNG
+ @param prng The PRNG to terminate
+ @return CRYPT_OK if successful
+ */
+int sprng_done(prng_state *prng) {
+ return CRYPT_OK;
+}
+
+/**
+ Export the PRNG state
+ @param out [out] Destination
+ @param outlen [in/out] Max size and resulting size of the state
+ @param prng The PRNG to export
+ @return CRYPT_OK if successful
+ */
+int sprng_export(unsigned char *out, unsigned long *outlen, prng_state *prng) {
+ LTC_ARGCHK(outlen != NULL);
+
+ *outlen = 0;
+ return CRYPT_OK;
+}
+
+/**
+ Import a PRNG state
+ @param in The PRNG state
+ @param inlen Size of the state
+ @param prng The PRNG to import
+ @return CRYPT_OK if successful
+ */
+int sprng_import(const unsigned char *in, unsigned long inlen, prng_state *prng) {
+ return CRYPT_OK;
+}
+
+/**
+ PRNG self-test
+ @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
+ */
+int sprng_test(void) {
+ return CRYPT_OK;
+}
+#endif
+
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/prngs/sprng.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file zeromem.c
+ Zero a block of memory, Tom St Denis
+ */
+
+/**
+ Zero a block of memory
+ @param out The destination of the area to zero
+ @param outlen The length of the area to zero (octets)
+ */
+void zeromem(void *out, size_t outlen) {
+ unsigned char *mem = out;
+
+ LTC_ARGCHKVD(out != NULL);
+ while (outlen-- > 0) {
+ *mem++ = 0;
+ }
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/misc/zeromem.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file sha1.c
+ LTC_SHA1 code by Tom St Denis
+ */
+
+
+#ifdef LTC_SHA1
+
+const struct ltc_hash_descriptor sha1_desc =
+{
+ "sha1",
+ 2,
+ 20,
+ 64,
+
+ /* OID */
+ { 1, 3, 14, 3, 2, 26, },
+ 6,
+
+ &sha1_init,
+ &sha1_process,
+ &sha1_done,
+ &sha1_test,
+ NULL
+};
+
+ #define F0(x, y, z) (z ^ (x & (y ^ z)))
+ #define F1(x, y, z) (x ^ y ^ z)
+ #define F2(x, y, z) ((x & y) | (z & (x | y)))
+ #define F3(x, y, z) (x ^ y ^ z)
+
+ #ifdef LTC_CLEAN_STACK
+static int _sha1_compress(hash_state *md, unsigned char *buf)
+ #else
+static int sha1_compress(hash_state *md, unsigned char *buf)
+ #endif
+{
+ ulong32 a, b, c, d, e, W[80], i;
+
+ #ifdef LTC_SMALL_CODE
+ ulong32 t;
+ #endif
+
+ /* copy the state into 512-bits into W[0..15] */
+ for (i = 0; i < 16; i++) {
+ LOAD32H(W[i], buf + (4 * i));
+ }
+
+ /* copy state */
+ a = md->sha1.state[0];
+ b = md->sha1.state[1];
+ c = md->sha1.state[2];
+ d = md->sha1.state[3];
+ e = md->sha1.state[4];
+
+ /* expand it */
+ for (i = 16; i < 80; i++) {
+ W[i] = ROL(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
+ }
+
+ /* compress */
+ /* round one */
+ #define FF0(a, b, c, d, e, i) e = (ROLc(a, 5) + F0(b, c, d) + e + W[i] + 0x5a827999UL); b = ROLc(b, 30);
+ #define FF1(a, b, c, d, e, i) e = (ROLc(a, 5) + F1(b, c, d) + e + W[i] + 0x6ed9eba1UL); b = ROLc(b, 30);
+ #define FF2(a, b, c, d, e, i) e = (ROLc(a, 5) + F2(b, c, d) + e + W[i] + 0x8f1bbcdcUL); b = ROLc(b, 30);
+ #define FF3(a, b, c, d, e, i) e = (ROLc(a, 5) + F3(b, c, d) + e + W[i] + 0xca62c1d6UL); b = ROLc(b, 30);
+
+ #ifdef LTC_SMALL_CODE
+ for (i = 0; i < 20; ) {
+ FF0(a, b, c, d, e, i++);
+ t = e;
+ e = d;
+ d = c;
+ c = b;
+ b = a;
+ a = t;
+ }
+
+ for ( ; i < 40; ) {
+ FF1(a, b, c, d, e, i++);
+ t = e;
+ e = d;
+ d = c;
+ c = b;
+ b = a;
+ a = t;
+ }
+
+ for ( ; i < 60; ) {
+ FF2(a, b, c, d, e, i++);
+ t = e;
+ e = d;
+ d = c;
+ c = b;
+ b = a;
+ a = t;
+ }
+
+ for ( ; i < 80; ) {
+ FF3(a, b, c, d, e, i++);
+ t = e;
+ e = d;
+ d = c;
+ c = b;
+ b = a;
+ a = t;
+ }
+
+ #else
+ for (i = 0; i < 20; ) {
+ FF0(a, b, c, d, e, i++);
+ FF0(e, a, b, c, d, i++);
+ FF0(d, e, a, b, c, i++);
+ FF0(c, d, e, a, b, i++);
+ FF0(b, c, d, e, a, i++);
+ }
+
+ /* round two */
+ for ( ; i < 40; ) {
+ FF1(a, b, c, d, e, i++);
+ FF1(e, a, b, c, d, i++);
+ FF1(d, e, a, b, c, i++);
+ FF1(c, d, e, a, b, i++);
+ FF1(b, c, d, e, a, i++);
+ }
+
+ /* round three */
+ for ( ; i < 60; ) {
+ FF2(a, b, c, d, e, i++);
+ FF2(e, a, b, c, d, i++);
+ FF2(d, e, a, b, c, i++);
+ FF2(c, d, e, a, b, i++);
+ FF2(b, c, d, e, a, i++);
+ }
+
+ /* round four */
+ for ( ; i < 80; ) {
+ FF3(a, b, c, d, e, i++);
+ FF3(e, a, b, c, d, i++);
+ FF3(d, e, a, b, c, i++);
+ FF3(c, d, e, a, b, i++);
+ FF3(b, c, d, e, a, i++);
+ }
+ #endif
+
+ #undef FF0
+ #undef FF1
+ #undef FF2
+ #undef FF3
+
+ /* store */
+ md->sha1.state[0] = md->sha1.state[0] + a;
+ md->sha1.state[1] = md->sha1.state[1] + b;
+ md->sha1.state[2] = md->sha1.state[2] + c;
+ md->sha1.state[3] = md->sha1.state[3] + d;
+ md->sha1.state[4] = md->sha1.state[4] + e;
+
+ return CRYPT_OK;
+}
+
+ #ifdef LTC_CLEAN_STACK
+static int sha1_compress(hash_state *md, unsigned char *buf) {
+ int err;
+
+ err = _sha1_compress(md, buf);
+ burn_stack(sizeof(ulong32) * 87);
+ return err;
+}
+ #endif
+
+/**
+ Initialize the hash state
+ @param md The hash state you wish to initialize
+ @return CRYPT_OK if successful
+ */
+int sha1_init(hash_state *md) {
+ LTC_ARGCHK(md != NULL);
+ md->sha1.state[0] = 0x67452301UL;
+ md->sha1.state[1] = 0xefcdab89UL;
+ md->sha1.state[2] = 0x98badcfeUL;
+ md->sha1.state[3] = 0x10325476UL;
+ md->sha1.state[4] = 0xc3d2e1f0UL;
+ md->sha1.curlen = 0;
+ md->sha1.length = 0;
+ return CRYPT_OK;
+}
+
+/**
+ Process a block of memory though the hash
+ @param md The hash state
+ @param in The data to hash
+ @param inlen The length of the data (octets)
+ @return CRYPT_OK if successful
+ */
+HASH_PROCESS(sha1_process, sha1_compress, sha1, 64)
+
+/**
+ Terminate the hash to get the digest
+ @param md The hash state
+ @param out [out] The destination of the hash (20 bytes)
+ @return CRYPT_OK if successful
+ */
+int sha1_done(hash_state *md, unsigned char *out) {
+ int i;
+
+ LTC_ARGCHK(md != NULL);
+ LTC_ARGCHK(out != NULL);
+
+ if (md->sha1.curlen >= sizeof(md->sha1.buf)) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* increase the length of the message */
+ md->sha1.length += md->sha1.curlen * 8;
+
+ /* append the '1' bit */
+ md->sha1.buf[md->sha1.curlen++] = (unsigned char)0x80;
+
+ /* if the length is currently above 56 bytes we append zeros
+ * then compress. Then we can fall back to padding zeros and length
+ * encoding like normal.
+ */
+ if (md->sha1.curlen > 56) {
+ while (md->sha1.curlen < 64) {
+ md->sha1.buf[md->sha1.curlen++] = (unsigned char)0;
+ }
+ sha1_compress(md, md->sha1.buf);
+ md->sha1.curlen = 0;
+ }
+
+ /* pad upto 56 bytes of zeroes */
+ while (md->sha1.curlen < 56) {
+ md->sha1.buf[md->sha1.curlen++] = (unsigned char)0;
+ }
+
+ /* store length */
+ STORE64H(md->sha1.length, md->sha1.buf + 56);
+ sha1_compress(md, md->sha1.buf);
+
+ /* copy output */
+ for (i = 0; i < 5; i++) {
+ STORE32H(md->sha1.state[i], out + (4 * i));
+ }
+ #ifdef LTC_CLEAN_STACK
+ zeromem(md, sizeof(hash_state));
+ #endif
+ return CRYPT_OK;
+}
+
+/**
+ Self-test the hash
+ @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
+ */
+int sha1_test(void) {
+ #ifndef LTC_TEST
+ return CRYPT_NOP;
+ #else
+ static const struct {
+ char *msg;
+ unsigned char hash[20];
+ } tests[] = {
+ { "abc",
+ { 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a,
+ 0xba, 0x3e, 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c,
+ 0x9c, 0xd0, 0xd8, 0x9d } },
+ { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E,
+ 0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5,
+ 0xE5, 0x46, 0x70, 0xF1 } }
+ };
+
+ int i;
+ unsigned char tmp[20];
+ hash_state md;
+
+ for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
+ sha1_init(&md);
+ sha1_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg));
+ sha1_done(&md, tmp);
+ if (XMEMCMP(tmp, tests[i].hash, 20) != 0) {
+ return CRYPT_FAIL_TESTVECTOR;
+ }
+ }
+ return CRYPT_OK;
+ #endif
+}
+#endif
+
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/hashes/sha1.c,v $ */
+/* $Revision: 1.10 $ */
+/* $Date: 2007/05/12 14:25:28 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file sha256.c
+ LTC_SHA256 by Tom St Denis
+*/
+
+#ifdef LTC_SHA256
+
+const struct ltc_hash_descriptor sha256_desc =
+{
+ "sha256",
+ 0,
+ 32,
+ 64,
+
+ /* OID */
+ { 2, 16, 840, 1, 101, 3, 4, 2, 1, },
+ 9,
+
+ &sha256_init,
+ &sha256_process,
+ &sha256_done,
+ &sha256_test,
+ NULL
+};
+
+#ifdef LTC_SMALL_CODE
+/* the K array */
+static const ulong32 K[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+ 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+ 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+ 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+ 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+ 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+ 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+ 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+ 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+ 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+#endif
+
+/* Various logical functions */
+#define Ch(x,y,z) (z ^ (x & (y ^ z)))
+#define Maj(x,y,z) (((x | y) & z) | (x & y))
+#define S(x, n) RORc((x),(n))
+#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n))
+#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25))
+#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3))
+#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10))
+
+/* compress 512-bits */
+#ifdef LTC_CLEAN_STACK
+static int _sha256_compress(hash_state * md, unsigned char *buf)
+#else
+static int sha256_compress(hash_state * md, unsigned char *buf)
+#endif
+{
+ ulong32 S[8], W[64], t0, t1;
+#ifdef LTC_SMALL_CODE
+ ulong32 t;
+#endif
+ int i;
+
+ /* copy state into S */
+ for (i = 0; i < 8; i++) {
+ S[i] = md->sha256.state[i];
+ }
+
+ /* copy the state into 512-bits into W[0..15] */
+ for (i = 0; i < 16; i++) {
+ LOAD32H(W[i], buf + (4*i));
+ }
+
+ /* fill W[16..63] */
+ for (i = 16; i < 64; i++) {
+ W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
+ }
+
+ /* Compress */
+#ifdef LTC_SMALL_CODE
+#define RND(a,b,c,d,e,f,g,h,i) \
+ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
+ t1 = Sigma0(a) + Maj(a, b, c); \
+ d += t0; \
+ h = t0 + t1;
+
+ for (i = 0; i < 64; ++i) {
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i);
+ t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
+ S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
+ }
+#else
+#define RND(a,b,c,d,e,f,g,h,i,ki) \
+ t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \
+ t1 = Sigma0(a) + Maj(a, b, c); \
+ d += t0; \
+ h = t0 + t1;
+
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5);
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174);
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da);
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967);
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85);
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070);
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3);
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2);
+
+#undef RND
+
+#endif
+
+ /* feedback */
+ for (i = 0; i < 8; i++) {
+ md->sha256.state[i] = md->sha256.state[i] + S[i];
+ }
+ return CRYPT_OK;
+}
+
+#ifdef LTC_CLEAN_STACK
+static int sha256_compress(hash_state * md, unsigned char *buf)
+{
+ int err;
+ err = _sha256_compress(md, buf);
+ burn_stack(sizeof(ulong32) * 74);
+ return err;
+}
+#endif
+
+/**
+ Initialize the hash state
+ @param md The hash state you wish to initialize
+ @return CRYPT_OK if successful
+*/
+int sha256_init(hash_state * md)
+{
+ LTC_ARGCHK(md != NULL);
+
+ md->sha256.curlen = 0;
+ md->sha256.length = 0;
+ md->sha256.state[0] = 0x6A09E667UL;
+ md->sha256.state[1] = 0xBB67AE85UL;
+ md->sha256.state[2] = 0x3C6EF372UL;
+ md->sha256.state[3] = 0xA54FF53AUL;
+ md->sha256.state[4] = 0x510E527FUL;
+ md->sha256.state[5] = 0x9B05688CUL;
+ md->sha256.state[6] = 0x1F83D9ABUL;
+ md->sha256.state[7] = 0x5BE0CD19UL;
+ return CRYPT_OK;
+}
+
+/**
+ Process a block of memory though the hash
+ @param md The hash state
+ @param in The data to hash
+ @param inlen The length of the data (octets)
+ @return CRYPT_OK if successful
+*/
+HASH_PROCESS(sha256_process, sha256_compress, sha256, 64)
+
+/**
+ Terminate the hash to get the digest
+ @param md The hash state
+ @param out [out] The destination of the hash (32 bytes)
+ @return CRYPT_OK if successful
+*/
+int sha256_done(hash_state * md, unsigned char *out)
+{
+ int i;
+
+ LTC_ARGCHK(md != NULL);
+ LTC_ARGCHK(out != NULL);
+
+ if (md->sha256.curlen >= sizeof(md->sha256.buf)) {
+ return CRYPT_INVALID_ARG;
+ }
+
+
+ /* increase the length of the message */
+ md->sha256.length += md->sha256.curlen * 8;
+
+ /* append the '1' bit */
+ md->sha256.buf[md->sha256.curlen++] = (unsigned char)0x80;
+
+ /* if the length is currently above 56 bytes we append zeros
+ * then compress. Then we can fall back to padding zeros and length
+ * encoding like normal.
+ */
+ if (md->sha256.curlen > 56) {
+ while (md->sha256.curlen < 64) {
+ md->sha256.buf[md->sha256.curlen++] = (unsigned char)0;
+ }
+ sha256_compress(md, md->sha256.buf);
+ md->sha256.curlen = 0;
+ }
+
+ /* pad upto 56 bytes of zeroes */
+ while (md->sha256.curlen < 56) {
+ md->sha256.buf[md->sha256.curlen++] = (unsigned char)0;
+ }
+
+ /* store length */
+ STORE64H(md->sha256.length, md->sha256.buf+56);
+ sha256_compress(md, md->sha256.buf);
+
+ /* copy output */
+ for (i = 0; i < 8; i++) {
+ STORE32H(md->sha256.state[i], out+(4*i));
+ }
+#ifdef LTC_CLEAN_STACK
+ zeromem(md, sizeof(hash_state));
+#endif
+ return CRYPT_OK;
+}
+
+/**
+ Self-test the hash
+ @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
+*/
+int sha256_test(void)
+{
+ #ifndef LTC_TEST
+ return CRYPT_NOP;
+ #else
+ static const struct {
+ char *msg;
+ unsigned char hash[32];
+ } tests[] = {
+ { "abc",
+ { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
+ 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
+ 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+ 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad }
+ },
+ { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ { 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,
+ 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
+ 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
+ 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 }
+ },
+ };
+
+ int i;
+ unsigned char tmp[32];
+ hash_state md;
+
+ for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
+ sha256_init(&md);
+ sha256_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg));
+ sha256_done(&md, tmp);
+ if (XMEMCMP(tmp, tests[i].hash, 32) != 0) {
+ return CRYPT_FAIL_TESTVECTOR;
+ }
+ }
+ return CRYPT_OK;
+ #endif
+}
+
+#endif
+
+
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+/**
+ @param sha384.c
+ LTC_SHA384 hash included in sha512.c, Tom St Denis
+*/
+
+
+
+#if defined(LTC_SHA384) && defined(LTC_SHA512)
+
+const struct ltc_hash_descriptor sha384_desc =
+{
+ "sha384",
+ 4,
+ 48,
+ 128,
+
+ /* OID */
+ { 2, 16, 840, 1, 101, 3, 4, 2, 2, },
+ 9,
+
+ &sha384_init,
+ &sha512_process,
+ &sha384_done,
+ &sha384_test,
+ NULL
+};
+
+/**
+ Initialize the hash state
+ @param md The hash state you wish to initialize
+ @return CRYPT_OK if successful
+*/
+int sha384_init(hash_state * md)
+{
+ LTC_ARGCHK(md != NULL);
+
+ md->sha512.curlen = 0;
+ md->sha512.length = 0;
+ md->sha512.state[0] = CONST64(0xcbbb9d5dc1059ed8);
+ md->sha512.state[1] = CONST64(0x629a292a367cd507);
+ md->sha512.state[2] = CONST64(0x9159015a3070dd17);
+ md->sha512.state[3] = CONST64(0x152fecd8f70e5939);
+ md->sha512.state[4] = CONST64(0x67332667ffc00b31);
+ md->sha512.state[5] = CONST64(0x8eb44a8768581511);
+ md->sha512.state[6] = CONST64(0xdb0c2e0d64f98fa7);
+ md->sha512.state[7] = CONST64(0x47b5481dbefa4fa4);
+ return CRYPT_OK;
+}
+
+/**
+ Terminate the hash to get the digest
+ @param md The hash state
+ @param out [out] The destination of the hash (48 bytes)
+ @return CRYPT_OK if successful
+*/
+int sha384_done(hash_state * md, unsigned char *out)
+{
+ unsigned char buf[64];
+
+ LTC_ARGCHK(md != NULL);
+ LTC_ARGCHK(out != NULL);
+
+ if (md->sha512.curlen >= sizeof(md->sha512.buf)) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ sha512_done(md, buf);
+ XMEMCPY(out, buf, 48);
+#ifdef LTC_CLEAN_STACK
+ zeromem(buf, sizeof(buf));
+#endif
+ return CRYPT_OK;
+}
+
+/**
+ Self-test the hash
+ @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
+*/
+int sha384_test(void)
+{
+ #ifndef LTC_TEST
+ return CRYPT_NOP;
+ #else
+ static const struct {
+ char *msg;
+ unsigned char hash[48];
+ } tests[] = {
+ { "abc",
+ { 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b,
+ 0xb5, 0xa0, 0x3d, 0x69, 0x9a, 0xc6, 0x50, 0x07,
+ 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63,
+ 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed,
+ 0x80, 0x86, 0x07, 0x2b, 0xa1, 0xe7, 0xcc, 0x23,
+ 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7 }
+ },
+ { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
+ { 0x09, 0x33, 0x0c, 0x33, 0xf7, 0x11, 0x47, 0xe8,
+ 0x3d, 0x19, 0x2f, 0xc7, 0x82, 0xcd, 0x1b, 0x47,
+ 0x53, 0x11, 0x1b, 0x17, 0x3b, 0x3b, 0x05, 0xd2,
+ 0x2f, 0xa0, 0x80, 0x86, 0xe3, 0xb0, 0xf7, 0x12,
+ 0xfc, 0xc7, 0xc7, 0x1a, 0x55, 0x7e, 0x2d, 0xb9,
+ 0x66, 0xc3, 0xe9, 0xfa, 0x91, 0x74, 0x60, 0x39 }
+ },
+ };
+
+ int i;
+ unsigned char tmp[48];
+ hash_state md;
+
+ for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
+ sha384_init(&md);
+ sha384_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg));
+ sha384_done(&md, tmp);
+ if (XMEMCMP(tmp, tests[i].hash, 48) != 0) {
+ return CRYPT_FAIL_TESTVECTOR;
+ }
+ }
+ return CRYPT_OK;
+ #endif
+}
+
+#endif /* defined(LTC_SHA384) && defined(LTC_SHA512) */
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @param sha512.c
+ LTC_SHA512 by Tom St Denis
+*/
+
+#ifdef LTC_SHA512
+
+const struct ltc_hash_descriptor sha512_desc =
+{
+ "sha512",
+ 5,
+ 64,
+ 128,
+
+ /* OID */
+ { 2, 16, 840, 1, 101, 3, 4, 2, 3, },
+ 9,
+
+ &sha512_init,
+ &sha512_process,
+ &sha512_done,
+ &sha512_test,
+ NULL
+};
+
+/* the K array */
+static const ulong64 K[80] = {
+CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd),
+CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc),
+CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019),
+CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118),
+CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe),
+CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2),
+CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1),
+CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694),
+CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3),
+CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65),
+CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483),
+CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5),
+CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210),
+CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4),
+CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725),
+CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70),
+CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926),
+CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df),
+CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8),
+CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b),
+CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001),
+CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30),
+CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910),
+CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8),
+CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53),
+CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8),
+CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb),
+CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3),
+CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60),
+CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec),
+CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9),
+CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b),
+CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207),
+CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178),
+CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6),
+CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b),
+CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493),
+CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c),
+CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a),
+CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817)
+};
+
+/* Various logical functions */
+#undef S
+#undef R
+#undef Sigma0
+#undef Sigma1
+#undef Gamma0
+#undef Gamma1
+
+#define Ch(x,y,z) (z ^ (x & (y ^ z)))
+#define Maj(x,y,z) (((x | y) & z) | (x & y))
+#define S(x, n) ROR64c(x, n)
+#define R(x, n) (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)n))
+#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39))
+#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
+#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))
+#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6))
+
+/* compress 1024-bits */
+#ifdef LTC_CLEAN_STACK
+static int _sha512_compress(hash_state * md, unsigned char *buf)
+#else
+static int sha512_compress(hash_state * md, unsigned char *buf)
+#endif
+{
+ ulong64 S[8], W[80], t0, t1;
+ int i;
+
+ /* copy state into S */
+ for (i = 0; i < 8; i++) {
+ S[i] = md->sha512.state[i];
+ }
+
+ /* copy the state into 1024-bits into W[0..15] */
+ for (i = 0; i < 16; i++) {
+ LOAD64H(W[i], buf + (8*i));
+ }
+
+ /* fill W[16..79] */
+ for (i = 16; i < 80; i++) {
+ W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
+ }
+
+ /* Compress */
+#ifdef LTC_SMALL_CODE
+ for (i = 0; i < 80; i++) {
+ t0 = S[7] + Sigma1(S[4]) + Ch(S[4], S[5], S[6]) + K[i] + W[i];
+ t1 = Sigma0(S[0]) + Maj(S[0], S[1], S[2]);
+ S[7] = S[6];
+ S[6] = S[5];
+ S[5] = S[4];
+ S[4] = S[3] + t0;
+ S[3] = S[2];
+ S[2] = S[1];
+ S[1] = S[0];
+ S[0] = t0 + t1;
+ }
+#else
+#define RND(a,b,c,d,e,f,g,h,i) \
+ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
+ t1 = Sigma0(a) + Maj(a, b, c); \
+ d += t0; \
+ h = t0 + t1;
+
+ for (i = 0; i < 80; i += 8) {
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7);
+ }
+#endif
+
+
+ /* feedback */
+ for (i = 0; i < 8; i++) {
+ md->sha512.state[i] = md->sha512.state[i] + S[i];
+ }
+
+ return CRYPT_OK;
+}
+
+/* compress 1024-bits */
+#ifdef LTC_CLEAN_STACK
+static int sha512_compress(hash_state * md, unsigned char *buf)
+{
+ int err;
+ err = _sha512_compress(md, buf);
+ burn_stack(sizeof(ulong64) * 90 + sizeof(int));
+ return err;
+}
+#endif
+
+/**
+ Initialize the hash state
+ @param md The hash state you wish to initialize
+ @return CRYPT_OK if successful
+*/
+int sha512_init(hash_state * md)
+{
+ LTC_ARGCHK(md != NULL);
+ md->sha512.curlen = 0;
+ md->sha512.length = 0;
+ md->sha512.state[0] = CONST64(0x6a09e667f3bcc908);
+ md->sha512.state[1] = CONST64(0xbb67ae8584caa73b);
+ md->sha512.state[2] = CONST64(0x3c6ef372fe94f82b);
+ md->sha512.state[3] = CONST64(0xa54ff53a5f1d36f1);
+ md->sha512.state[4] = CONST64(0x510e527fade682d1);
+ md->sha512.state[5] = CONST64(0x9b05688c2b3e6c1f);
+ md->sha512.state[6] = CONST64(0x1f83d9abfb41bd6b);
+ md->sha512.state[7] = CONST64(0x5be0cd19137e2179);
+ return CRYPT_OK;
+}
+
+/**
+ Process a block of memory though the hash
+ @param md The hash state
+ @param in The data to hash
+ @param inlen The length of the data (octets)
+ @return CRYPT_OK if successful
+*/
+HASH_PROCESS(sha512_process, sha512_compress, sha512, 128)
+
+/**
+ Terminate the hash to get the digest
+ @param md The hash state
+ @param out [out] The destination of the hash (64 bytes)
+ @return CRYPT_OK if successful
+*/
+int sha512_done(hash_state * md, unsigned char *out)
+{
+ int i;
+
+ LTC_ARGCHK(md != NULL);
+ LTC_ARGCHK(out != NULL);
+
+ if (md->sha512.curlen >= sizeof(md->sha512.buf)) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* increase the length of the message */
+ md->sha512.length += md->sha512.curlen * CONST64(8);
+
+ /* append the '1' bit */
+ md->sha512.buf[md->sha512.curlen++] = (unsigned char)0x80;
+
+ /* if the length is currently above 112 bytes we append zeros
+ * then compress. Then we can fall back to padding zeros and length
+ * encoding like normal.
+ */
+ if (md->sha512.curlen > 112) {
+ while (md->sha512.curlen < 128) {
+ md->sha512.buf[md->sha512.curlen++] = (unsigned char)0;
+ }
+ sha512_compress(md, md->sha512.buf);
+ md->sha512.curlen = 0;
+ }
+
+ /* pad upto 120 bytes of zeroes
+ * note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash
+ * > 2^64 bits of data... :-)
+ */
+ while (md->sha512.curlen < 120) {
+ md->sha512.buf[md->sha512.curlen++] = (unsigned char)0;
+ }
+
+ /* store length */
+ STORE64H(md->sha512.length, md->sha512.buf+120);
+ sha512_compress(md, md->sha512.buf);
+
+ /* copy output */
+ for (i = 0; i < 8; i++) {
+ STORE64H(md->sha512.state[i], out+(8*i));
+ }
+#ifdef LTC_CLEAN_STACK
+ zeromem(md, sizeof(hash_state));
+#endif
+ return CRYPT_OK;
+}
+
+/**
+ Self-test the hash
+ @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
+*/
+int sha512_test(void)
+{
+ #ifndef LTC_TEST
+ return CRYPT_NOP;
+ #else
+ static const struct {
+ char *msg;
+ unsigned char hash[64];
+ } tests[] = {
+ { "abc",
+ { 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba,
+ 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,
+ 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
+ 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,
+ 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8,
+ 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
+ 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
+ 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f }
+ },
+ { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
+ { 0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda,
+ 0x8c, 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f,
+ 0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1,
+ 0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18,
+ 0x50, 0x1d, 0x28, 0x9e, 0x49, 0x00, 0xf7, 0xe4,
+ 0x33, 0x1b, 0x99, 0xde, 0xc4, 0xb5, 0x43, 0x3a,
+ 0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26, 0x54,
+ 0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, 0x09 }
+ },
+ };
+
+ int i;
+ unsigned char tmp[64];
+ hash_state md;
+
+ for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
+ sha512_init(&md);
+ sha512_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg));
+ sha512_done(&md, tmp);
+ if (XMEMCMP(tmp, tests[i].hash, 64) != 0) {
+ return CRYPT_FAIL_TESTVECTOR;
+ }
+ }
+ return CRYPT_OK;
+ #endif
+}
+
+#endif
+
+
+
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file hmac_init.c
+ HMAC support, initialize state, Tom St Denis/Dobes Vandermeer
+*/
+
+#ifdef LTC_HMAC
+
+#define LTC_HMAC_BLOCKSIZE hash_descriptor[hash].blocksize
+
+/**
+ Initialize an HMAC context.
+ @param hmac The HMAC state
+ @param hash The index of the hash you want to use
+ @param key The secret key
+ @param keylen The length of the secret key (octets)
+ @return CRYPT_OK if successful
+*/
+int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned long keylen)
+{
+ unsigned char *buf;
+ unsigned long hashsize;
+ unsigned long i, z;
+ int err;
+
+ LTC_ARGCHK(hmac != NULL);
+ LTC_ARGCHK(key != NULL);
+
+ /* valid hash? */
+ if ((err = hash_is_valid(hash)) != CRYPT_OK) {
+ return err;
+ }
+ hmac->hash = hash;
+ hashsize = hash_descriptor[hash].hashsize;
+
+ /* valid key length? */
+ if (keylen == 0) {
+ return CRYPT_INVALID_KEYSIZE;
+ }
+
+ /* allocate ram for buf */
+ buf = XMALLOC(LTC_HMAC_BLOCKSIZE);
+ if (buf == NULL) {
+ return CRYPT_MEM;
+ }
+
+ /* allocate memory for key */
+ hmac->key = XMALLOC(LTC_HMAC_BLOCKSIZE);
+ if (hmac->key == NULL) {
+ XFREE(buf);
+ return CRYPT_MEM;
+ }
+
+ /* (1) make sure we have a large enough key */
+ if(keylen > LTC_HMAC_BLOCKSIZE) {
+ z = LTC_HMAC_BLOCKSIZE;
+ if ((err = hash_memory(hash, key, keylen, hmac->key, &z)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ keylen = hashsize;
+ } else {
+ XMEMCPY(hmac->key, key, (size_t)keylen);
+ }
+
+ if(keylen < LTC_HMAC_BLOCKSIZE) {
+ zeromem((hmac->key) + keylen, (size_t)(LTC_HMAC_BLOCKSIZE - keylen));
+ }
+
+ /* Create the initial vector for step (3) */
+ for(i=0; i < LTC_HMAC_BLOCKSIZE; i++) {
+ buf[i] = hmac->key[i] ^ 0x36;
+ }
+
+ /* Pre-pend that to the hash data */
+ if ((err = hash_descriptor[hash].init(&hmac->md)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ if ((err = hash_descriptor[hash].process(&hmac->md, buf, LTC_HMAC_BLOCKSIZE)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ goto done;
+LBL_ERR:
+ /* free the key since we failed */
+ XFREE(hmac->key);
+done:
+#ifdef LTC_CLEAN_STACK
+ zeromem(buf, LTC_HMAC_BLOCKSIZE);
+#endif
+
+ XFREE(buf);
+ return err;
+}
+
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file hmac_process.c
+ HMAC support, process data, Tom St Denis/Dobes Vandermeer
+*/
+
+#ifdef LTC_HMAC
+
+/**
+ Process data through HMAC
+ @param hmac The hmac state
+ @param in The data to send through HMAC
+ @param inlen The length of the data to HMAC (octets)
+ @return CRYPT_OK if successful
+*/
+int hmac_process(hmac_state *hmac, const unsigned char *in, unsigned long inlen)
+{
+ int err;
+ LTC_ARGCHK(hmac != NULL);
+ LTC_ARGCHK(in != NULL);
+ if ((err = hash_is_valid(hmac->hash)) != CRYPT_OK) {
+ return err;
+ }
+ return hash_descriptor[hmac->hash].process(&hmac->md, in, inlen);
+}
+
+#endif
+
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file hmac_done.c
+ HMAC support, terminate stream, Tom St Denis/Dobes Vandermeer
+*/
+
+#ifdef LTC_HMAC
+
+#define LTC_HMAC_BLOCKSIZE hash_descriptor[hash].blocksize
+
+/**
+ Terminate an HMAC session
+ @param hmac The HMAC state
+ @param out [out] The destination of the HMAC authentication tag
+ @param outlen [in/out] The max size and resulting size of the HMAC authentication tag
+ @return CRYPT_OK if successful
+*/
+int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen)
+{
+ unsigned char *buf, *isha;
+ unsigned long hashsize, i;
+ int hash, err;
+
+ LTC_ARGCHK(hmac != NULL);
+ LTC_ARGCHK(out != NULL);
+
+ /* test hash */
+ hash = hmac->hash;
+ if((err = hash_is_valid(hash)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* get the hash message digest size */
+ hashsize = hash_descriptor[hash].hashsize;
+
+ /* allocate buffers */
+ buf = XMALLOC(LTC_HMAC_BLOCKSIZE);
+ isha = XMALLOC(hashsize);
+ if (buf == NULL || isha == NULL) {
+ if (buf != NULL) {
+ XFREE(buf);
+ }
+ if (isha != NULL) {
+ XFREE(isha);
+ }
+ return CRYPT_MEM;
+ }
+
+ /* Get the hash of the first HMAC vector plus the data */
+ if ((err = hash_descriptor[hash].done(&hmac->md, isha)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ /* Create the second HMAC vector vector for step (3) */
+ for(i=0; i < LTC_HMAC_BLOCKSIZE; i++) {
+ buf[i] = hmac->key[i] ^ 0x5C;
+ }
+
+ /* Now calculate the "outer" hash for step (5), (6), and (7) */
+ if ((err = hash_descriptor[hash].init(&hmac->md)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ if ((err = hash_descriptor[hash].process(&hmac->md, buf, LTC_HMAC_BLOCKSIZE)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ if ((err = hash_descriptor[hash].process(&hmac->md, isha, hashsize)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+ if ((err = hash_descriptor[hash].done(&hmac->md, buf)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ /* copy to output */
+ for (i = 0; i < hashsize && i < *outlen; i++) {
+ out[i] = buf[i];
+ }
+ *outlen = i;
+
+ err = CRYPT_OK;
+LBL_ERR:
+ XFREE(hmac->key);
+#ifdef LTC_CLEAN_STACK
+ zeromem(isha, hashsize);
+ zeromem(buf, hashsize);
+ zeromem(hmac, sizeof(*hmac));
+#endif
+
+ XFREE(isha);
+ XFREE(buf);
+
+ return err;
+}
+
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+#define __LTC_AES_TAB_C__
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+/* The precomputed tables for AES */
+/*
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+Te4[x] = S [x].[01, 01, 01, 01];
+
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x].[01, 01, 01, 01];
+*/
+
+#ifdef __LTC_AES_TAB_C__
+
+/**
+ @file aes_tab.c
+ AES tables
+*/
+static const ulong32 TE0[256] = {
+ 0xc66363a5UL, 0xf87c7c84UL, 0xee777799UL, 0xf67b7b8dUL,
+ 0xfff2f20dUL, 0xd66b6bbdUL, 0xde6f6fb1UL, 0x91c5c554UL,
+ 0x60303050UL, 0x02010103UL, 0xce6767a9UL, 0x562b2b7dUL,
+ 0xe7fefe19UL, 0xb5d7d762UL, 0x4dababe6UL, 0xec76769aUL,
+ 0x8fcaca45UL, 0x1f82829dUL, 0x89c9c940UL, 0xfa7d7d87UL,
+ 0xeffafa15UL, 0xb25959ebUL, 0x8e4747c9UL, 0xfbf0f00bUL,
+ 0x41adadecUL, 0xb3d4d467UL, 0x5fa2a2fdUL, 0x45afafeaUL,
+ 0x239c9cbfUL, 0x53a4a4f7UL, 0xe4727296UL, 0x9bc0c05bUL,
+ 0x75b7b7c2UL, 0xe1fdfd1cUL, 0x3d9393aeUL, 0x4c26266aUL,
+ 0x6c36365aUL, 0x7e3f3f41UL, 0xf5f7f702UL, 0x83cccc4fUL,
+ 0x6834345cUL, 0x51a5a5f4UL, 0xd1e5e534UL, 0xf9f1f108UL,
+ 0xe2717193UL, 0xabd8d873UL, 0x62313153UL, 0x2a15153fUL,
+ 0x0804040cUL, 0x95c7c752UL, 0x46232365UL, 0x9dc3c35eUL,
+ 0x30181828UL, 0x379696a1UL, 0x0a05050fUL, 0x2f9a9ab5UL,
+ 0x0e070709UL, 0x24121236UL, 0x1b80809bUL, 0xdfe2e23dUL,
+ 0xcdebeb26UL, 0x4e272769UL, 0x7fb2b2cdUL, 0xea75759fUL,
+ 0x1209091bUL, 0x1d83839eUL, 0x582c2c74UL, 0x341a1a2eUL,
+ 0x361b1b2dUL, 0xdc6e6eb2UL, 0xb45a5aeeUL, 0x5ba0a0fbUL,
+ 0xa45252f6UL, 0x763b3b4dUL, 0xb7d6d661UL, 0x7db3b3ceUL,
+ 0x5229297bUL, 0xdde3e33eUL, 0x5e2f2f71UL, 0x13848497UL,
+ 0xa65353f5UL, 0xb9d1d168UL, 0x00000000UL, 0xc1eded2cUL,
+ 0x40202060UL, 0xe3fcfc1fUL, 0x79b1b1c8UL, 0xb65b5bedUL,
+ 0xd46a6abeUL, 0x8dcbcb46UL, 0x67bebed9UL, 0x7239394bUL,
+ 0x944a4adeUL, 0x984c4cd4UL, 0xb05858e8UL, 0x85cfcf4aUL,
+ 0xbbd0d06bUL, 0xc5efef2aUL, 0x4faaaae5UL, 0xedfbfb16UL,
+ 0x864343c5UL, 0x9a4d4dd7UL, 0x66333355UL, 0x11858594UL,
+ 0x8a4545cfUL, 0xe9f9f910UL, 0x04020206UL, 0xfe7f7f81UL,
+ 0xa05050f0UL, 0x783c3c44UL, 0x259f9fbaUL, 0x4ba8a8e3UL,
+ 0xa25151f3UL, 0x5da3a3feUL, 0x804040c0UL, 0x058f8f8aUL,
+ 0x3f9292adUL, 0x219d9dbcUL, 0x70383848UL, 0xf1f5f504UL,
+ 0x63bcbcdfUL, 0x77b6b6c1UL, 0xafdada75UL, 0x42212163UL,
+ 0x20101030UL, 0xe5ffff1aUL, 0xfdf3f30eUL, 0xbfd2d26dUL,
+ 0x81cdcd4cUL, 0x180c0c14UL, 0x26131335UL, 0xc3ecec2fUL,
+ 0xbe5f5fe1UL, 0x359797a2UL, 0x884444ccUL, 0x2e171739UL,
+ 0x93c4c457UL, 0x55a7a7f2UL, 0xfc7e7e82UL, 0x7a3d3d47UL,
+ 0xc86464acUL, 0xba5d5de7UL, 0x3219192bUL, 0xe6737395UL,
+ 0xc06060a0UL, 0x19818198UL, 0x9e4f4fd1UL, 0xa3dcdc7fUL,
+ 0x44222266UL, 0x542a2a7eUL, 0x3b9090abUL, 0x0b888883UL,
+ 0x8c4646caUL, 0xc7eeee29UL, 0x6bb8b8d3UL, 0x2814143cUL,
+ 0xa7dede79UL, 0xbc5e5ee2UL, 0x160b0b1dUL, 0xaddbdb76UL,
+ 0xdbe0e03bUL, 0x64323256UL, 0x743a3a4eUL, 0x140a0a1eUL,
+ 0x924949dbUL, 0x0c06060aUL, 0x4824246cUL, 0xb85c5ce4UL,
+ 0x9fc2c25dUL, 0xbdd3d36eUL, 0x43acacefUL, 0xc46262a6UL,
+ 0x399191a8UL, 0x319595a4UL, 0xd3e4e437UL, 0xf279798bUL,
+ 0xd5e7e732UL, 0x8bc8c843UL, 0x6e373759UL, 0xda6d6db7UL,
+ 0x018d8d8cUL, 0xb1d5d564UL, 0x9c4e4ed2UL, 0x49a9a9e0UL,
+ 0xd86c6cb4UL, 0xac5656faUL, 0xf3f4f407UL, 0xcfeaea25UL,
+ 0xca6565afUL, 0xf47a7a8eUL, 0x47aeaee9UL, 0x10080818UL,
+ 0x6fbabad5UL, 0xf0787888UL, 0x4a25256fUL, 0x5c2e2e72UL,
+ 0x381c1c24UL, 0x57a6a6f1UL, 0x73b4b4c7UL, 0x97c6c651UL,
+ 0xcbe8e823UL, 0xa1dddd7cUL, 0xe874749cUL, 0x3e1f1f21UL,
+ 0x964b4bddUL, 0x61bdbddcUL, 0x0d8b8b86UL, 0x0f8a8a85UL,
+ 0xe0707090UL, 0x7c3e3e42UL, 0x71b5b5c4UL, 0xcc6666aaUL,
+ 0x904848d8UL, 0x06030305UL, 0xf7f6f601UL, 0x1c0e0e12UL,
+ 0xc26161a3UL, 0x6a35355fUL, 0xae5757f9UL, 0x69b9b9d0UL,
+ 0x17868691UL, 0x99c1c158UL, 0x3a1d1d27UL, 0x279e9eb9UL,
+ 0xd9e1e138UL, 0xebf8f813UL, 0x2b9898b3UL, 0x22111133UL,
+ 0xd26969bbUL, 0xa9d9d970UL, 0x078e8e89UL, 0x339494a7UL,
+ 0x2d9b9bb6UL, 0x3c1e1e22UL, 0x15878792UL, 0xc9e9e920UL,
+ 0x87cece49UL, 0xaa5555ffUL, 0x50282878UL, 0xa5dfdf7aUL,
+ 0x038c8c8fUL, 0x59a1a1f8UL, 0x09898980UL, 0x1a0d0d17UL,
+ 0x65bfbfdaUL, 0xd7e6e631UL, 0x844242c6UL, 0xd06868b8UL,
+ 0x824141c3UL, 0x299999b0UL, 0x5a2d2d77UL, 0x1e0f0f11UL,
+ 0x7bb0b0cbUL, 0xa85454fcUL, 0x6dbbbbd6UL, 0x2c16163aUL,
+};
+
+#ifndef PELI_TAB
+static const ulong32 Te4[256] = {
+ 0x63636363UL, 0x7c7c7c7cUL, 0x77777777UL, 0x7b7b7b7bUL,
+ 0xf2f2f2f2UL, 0x6b6b6b6bUL, 0x6f6f6f6fUL, 0xc5c5c5c5UL,
+ 0x30303030UL, 0x01010101UL, 0x67676767UL, 0x2b2b2b2bUL,
+ 0xfefefefeUL, 0xd7d7d7d7UL, 0xababababUL, 0x76767676UL,
+ 0xcacacacaUL, 0x82828282UL, 0xc9c9c9c9UL, 0x7d7d7d7dUL,
+ 0xfafafafaUL, 0x59595959UL, 0x47474747UL, 0xf0f0f0f0UL,
+ 0xadadadadUL, 0xd4d4d4d4UL, 0xa2a2a2a2UL, 0xafafafafUL,
+ 0x9c9c9c9cUL, 0xa4a4a4a4UL, 0x72727272UL, 0xc0c0c0c0UL,
+ 0xb7b7b7b7UL, 0xfdfdfdfdUL, 0x93939393UL, 0x26262626UL,
+ 0x36363636UL, 0x3f3f3f3fUL, 0xf7f7f7f7UL, 0xccccccccUL,
+ 0x34343434UL, 0xa5a5a5a5UL, 0xe5e5e5e5UL, 0xf1f1f1f1UL,
+ 0x71717171UL, 0xd8d8d8d8UL, 0x31313131UL, 0x15151515UL,
+ 0x04040404UL, 0xc7c7c7c7UL, 0x23232323UL, 0xc3c3c3c3UL,
+ 0x18181818UL, 0x96969696UL, 0x05050505UL, 0x9a9a9a9aUL,
+ 0x07070707UL, 0x12121212UL, 0x80808080UL, 0xe2e2e2e2UL,
+ 0xebebebebUL, 0x27272727UL, 0xb2b2b2b2UL, 0x75757575UL,
+ 0x09090909UL, 0x83838383UL, 0x2c2c2c2cUL, 0x1a1a1a1aUL,
+ 0x1b1b1b1bUL, 0x6e6e6e6eUL, 0x5a5a5a5aUL, 0xa0a0a0a0UL,
+ 0x52525252UL, 0x3b3b3b3bUL, 0xd6d6d6d6UL, 0xb3b3b3b3UL,
+ 0x29292929UL, 0xe3e3e3e3UL, 0x2f2f2f2fUL, 0x84848484UL,
+ 0x53535353UL, 0xd1d1d1d1UL, 0x00000000UL, 0xededededUL,
+ 0x20202020UL, 0xfcfcfcfcUL, 0xb1b1b1b1UL, 0x5b5b5b5bUL,
+ 0x6a6a6a6aUL, 0xcbcbcbcbUL, 0xbebebebeUL, 0x39393939UL,
+ 0x4a4a4a4aUL, 0x4c4c4c4cUL, 0x58585858UL, 0xcfcfcfcfUL,
+ 0xd0d0d0d0UL, 0xefefefefUL, 0xaaaaaaaaUL, 0xfbfbfbfbUL,
+ 0x43434343UL, 0x4d4d4d4dUL, 0x33333333UL, 0x85858585UL,
+ 0x45454545UL, 0xf9f9f9f9UL, 0x02020202UL, 0x7f7f7f7fUL,
+ 0x50505050UL, 0x3c3c3c3cUL, 0x9f9f9f9fUL, 0xa8a8a8a8UL,
+ 0x51515151UL, 0xa3a3a3a3UL, 0x40404040UL, 0x8f8f8f8fUL,
+ 0x92929292UL, 0x9d9d9d9dUL, 0x38383838UL, 0xf5f5f5f5UL,
+ 0xbcbcbcbcUL, 0xb6b6b6b6UL, 0xdadadadaUL, 0x21212121UL,
+ 0x10101010UL, 0xffffffffUL, 0xf3f3f3f3UL, 0xd2d2d2d2UL,
+ 0xcdcdcdcdUL, 0x0c0c0c0cUL, 0x13131313UL, 0xececececUL,
+ 0x5f5f5f5fUL, 0x97979797UL, 0x44444444UL, 0x17171717UL,
+ 0xc4c4c4c4UL, 0xa7a7a7a7UL, 0x7e7e7e7eUL, 0x3d3d3d3dUL,
+ 0x64646464UL, 0x5d5d5d5dUL, 0x19191919UL, 0x73737373UL,
+ 0x60606060UL, 0x81818181UL, 0x4f4f4f4fUL, 0xdcdcdcdcUL,
+ 0x22222222UL, 0x2a2a2a2aUL, 0x90909090UL, 0x88888888UL,
+ 0x46464646UL, 0xeeeeeeeeUL, 0xb8b8b8b8UL, 0x14141414UL,
+ 0xdedededeUL, 0x5e5e5e5eUL, 0x0b0b0b0bUL, 0xdbdbdbdbUL,
+ 0xe0e0e0e0UL, 0x32323232UL, 0x3a3a3a3aUL, 0x0a0a0a0aUL,
+ 0x49494949UL, 0x06060606UL, 0x24242424UL, 0x5c5c5c5cUL,
+ 0xc2c2c2c2UL, 0xd3d3d3d3UL, 0xacacacacUL, 0x62626262UL,
+ 0x91919191UL, 0x95959595UL, 0xe4e4e4e4UL, 0x79797979UL,
+ 0xe7e7e7e7UL, 0xc8c8c8c8UL, 0x37373737UL, 0x6d6d6d6dUL,
+ 0x8d8d8d8dUL, 0xd5d5d5d5UL, 0x4e4e4e4eUL, 0xa9a9a9a9UL,
+ 0x6c6c6c6cUL, 0x56565656UL, 0xf4f4f4f4UL, 0xeaeaeaeaUL,
+ 0x65656565UL, 0x7a7a7a7aUL, 0xaeaeaeaeUL, 0x08080808UL,
+ 0xbabababaUL, 0x78787878UL, 0x25252525UL, 0x2e2e2e2eUL,
+ 0x1c1c1c1cUL, 0xa6a6a6a6UL, 0xb4b4b4b4UL, 0xc6c6c6c6UL,
+ 0xe8e8e8e8UL, 0xddddddddUL, 0x74747474UL, 0x1f1f1f1fUL,
+ 0x4b4b4b4bUL, 0xbdbdbdbdUL, 0x8b8b8b8bUL, 0x8a8a8a8aUL,
+ 0x70707070UL, 0x3e3e3e3eUL, 0xb5b5b5b5UL, 0x66666666UL,
+ 0x48484848UL, 0x03030303UL, 0xf6f6f6f6UL, 0x0e0e0e0eUL,
+ 0x61616161UL, 0x35353535UL, 0x57575757UL, 0xb9b9b9b9UL,
+ 0x86868686UL, 0xc1c1c1c1UL, 0x1d1d1d1dUL, 0x9e9e9e9eUL,
+ 0xe1e1e1e1UL, 0xf8f8f8f8UL, 0x98989898UL, 0x11111111UL,
+ 0x69696969UL, 0xd9d9d9d9UL, 0x8e8e8e8eUL, 0x94949494UL,
+ 0x9b9b9b9bUL, 0x1e1e1e1eUL, 0x87878787UL, 0xe9e9e9e9UL,
+ 0xcecececeUL, 0x55555555UL, 0x28282828UL, 0xdfdfdfdfUL,
+ 0x8c8c8c8cUL, 0xa1a1a1a1UL, 0x89898989UL, 0x0d0d0d0dUL,
+ 0xbfbfbfbfUL, 0xe6e6e6e6UL, 0x42424242UL, 0x68686868UL,
+ 0x41414141UL, 0x99999999UL, 0x2d2d2d2dUL, 0x0f0f0f0fUL,
+ 0xb0b0b0b0UL, 0x54545454UL, 0xbbbbbbbbUL, 0x16161616UL,
+};
+#endif
+
+#ifndef ENCRYPT_ONLY
+
+static const ulong32 TD0[256] = {
+ 0x51f4a750UL, 0x7e416553UL, 0x1a17a4c3UL, 0x3a275e96UL,
+ 0x3bab6bcbUL, 0x1f9d45f1UL, 0xacfa58abUL, 0x4be30393UL,
+ 0x2030fa55UL, 0xad766df6UL, 0x88cc7691UL, 0xf5024c25UL,
+ 0x4fe5d7fcUL, 0xc52acbd7UL, 0x26354480UL, 0xb562a38fUL,
+ 0xdeb15a49UL, 0x25ba1b67UL, 0x45ea0e98UL, 0x5dfec0e1UL,
+ 0xc32f7502UL, 0x814cf012UL, 0x8d4697a3UL, 0x6bd3f9c6UL,
+ 0x038f5fe7UL, 0x15929c95UL, 0xbf6d7aebUL, 0x955259daUL,
+ 0xd4be832dUL, 0x587421d3UL, 0x49e06929UL, 0x8ec9c844UL,
+ 0x75c2896aUL, 0xf48e7978UL, 0x99583e6bUL, 0x27b971ddUL,
+ 0xbee14fb6UL, 0xf088ad17UL, 0xc920ac66UL, 0x7dce3ab4UL,
+ 0x63df4a18UL, 0xe51a3182UL, 0x97513360UL, 0x62537f45UL,
+ 0xb16477e0UL, 0xbb6bae84UL, 0xfe81a01cUL, 0xf9082b94UL,
+ 0x70486858UL, 0x8f45fd19UL, 0x94de6c87UL, 0x527bf8b7UL,
+ 0xab73d323UL, 0x724b02e2UL, 0xe31f8f57UL, 0x6655ab2aUL,
+ 0xb2eb2807UL, 0x2fb5c203UL, 0x86c57b9aUL, 0xd33708a5UL,
+ 0x302887f2UL, 0x23bfa5b2UL, 0x02036abaUL, 0xed16825cUL,
+ 0x8acf1c2bUL, 0xa779b492UL, 0xf307f2f0UL, 0x4e69e2a1UL,
+ 0x65daf4cdUL, 0x0605bed5UL, 0xd134621fUL, 0xc4a6fe8aUL,
+ 0x342e539dUL, 0xa2f355a0UL, 0x058ae132UL, 0xa4f6eb75UL,
+ 0x0b83ec39UL, 0x4060efaaUL, 0x5e719f06UL, 0xbd6e1051UL,
+ 0x3e218af9UL, 0x96dd063dUL, 0xdd3e05aeUL, 0x4de6bd46UL,
+ 0x91548db5UL, 0x71c45d05UL, 0x0406d46fUL, 0x605015ffUL,
+ 0x1998fb24UL, 0xd6bde997UL, 0x894043ccUL, 0x67d99e77UL,
+ 0xb0e842bdUL, 0x07898b88UL, 0xe7195b38UL, 0x79c8eedbUL,
+ 0xa17c0a47UL, 0x7c420fe9UL, 0xf8841ec9UL, 0x00000000UL,
+ 0x09808683UL, 0x322bed48UL, 0x1e1170acUL, 0x6c5a724eUL,
+ 0xfd0efffbUL, 0x0f853856UL, 0x3daed51eUL, 0x362d3927UL,
+ 0x0a0fd964UL, 0x685ca621UL, 0x9b5b54d1UL, 0x24362e3aUL,
+ 0x0c0a67b1UL, 0x9357e70fUL, 0xb4ee96d2UL, 0x1b9b919eUL,
+ 0x80c0c54fUL, 0x61dc20a2UL, 0x5a774b69UL, 0x1c121a16UL,
+ 0xe293ba0aUL, 0xc0a02ae5UL, 0x3c22e043UL, 0x121b171dUL,
+ 0x0e090d0bUL, 0xf28bc7adUL, 0x2db6a8b9UL, 0x141ea9c8UL,
+ 0x57f11985UL, 0xaf75074cUL, 0xee99ddbbUL, 0xa37f60fdUL,
+ 0xf701269fUL, 0x5c72f5bcUL, 0x44663bc5UL, 0x5bfb7e34UL,
+ 0x8b432976UL, 0xcb23c6dcUL, 0xb6edfc68UL, 0xb8e4f163UL,
+ 0xd731dccaUL, 0x42638510UL, 0x13972240UL, 0x84c61120UL,
+ 0x854a247dUL, 0xd2bb3df8UL, 0xaef93211UL, 0xc729a16dUL,
+ 0x1d9e2f4bUL, 0xdcb230f3UL, 0x0d8652ecUL, 0x77c1e3d0UL,
+ 0x2bb3166cUL, 0xa970b999UL, 0x119448faUL, 0x47e96422UL,
+ 0xa8fc8cc4UL, 0xa0f03f1aUL, 0x567d2cd8UL, 0x223390efUL,
+ 0x87494ec7UL, 0xd938d1c1UL, 0x8ccaa2feUL, 0x98d40b36UL,
+ 0xa6f581cfUL, 0xa57ade28UL, 0xdab78e26UL, 0x3fadbfa4UL,
+ 0x2c3a9de4UL, 0x5078920dUL, 0x6a5fcc9bUL, 0x547e4662UL,
+ 0xf68d13c2UL, 0x90d8b8e8UL, 0x2e39f75eUL, 0x82c3aff5UL,
+ 0x9f5d80beUL, 0x69d0937cUL, 0x6fd52da9UL, 0xcf2512b3UL,
+ 0xc8ac993bUL, 0x10187da7UL, 0xe89c636eUL, 0xdb3bbb7bUL,
+ 0xcd267809UL, 0x6e5918f4UL, 0xec9ab701UL, 0x834f9aa8UL,
+ 0xe6956e65UL, 0xaaffe67eUL, 0x21bccf08UL, 0xef15e8e6UL,
+ 0xbae79bd9UL, 0x4a6f36ceUL, 0xea9f09d4UL, 0x29b07cd6UL,
+ 0x31a4b2afUL, 0x2a3f2331UL, 0xc6a59430UL, 0x35a266c0UL,
+ 0x744ebc37UL, 0xfc82caa6UL, 0xe090d0b0UL, 0x33a7d815UL,
+ 0xf104984aUL, 0x41ecdaf7UL, 0x7fcd500eUL, 0x1791f62fUL,
+ 0x764dd68dUL, 0x43efb04dUL, 0xccaa4d54UL, 0xe49604dfUL,
+ 0x9ed1b5e3UL, 0x4c6a881bUL, 0xc12c1fb8UL, 0x4665517fUL,
+ 0x9d5eea04UL, 0x018c355dUL, 0xfa877473UL, 0xfb0b412eUL,
+ 0xb3671d5aUL, 0x92dbd252UL, 0xe9105633UL, 0x6dd64713UL,
+ 0x9ad7618cUL, 0x37a10c7aUL, 0x59f8148eUL, 0xeb133c89UL,
+ 0xcea927eeUL, 0xb761c935UL, 0xe11ce5edUL, 0x7a47b13cUL,
+ 0x9cd2df59UL, 0x55f2733fUL, 0x1814ce79UL, 0x73c737bfUL,
+ 0x53f7cdeaUL, 0x5ffdaa5bUL, 0xdf3d6f14UL, 0x7844db86UL,
+ 0xcaaff381UL, 0xb968c43eUL, 0x3824342cUL, 0xc2a3405fUL,
+ 0x161dc372UL, 0xbce2250cUL, 0x283c498bUL, 0xff0d9541UL,
+ 0x39a80171UL, 0x080cb3deUL, 0xd8b4e49cUL, 0x6456c190UL,
+ 0x7bcb8461UL, 0xd532b670UL, 0x486c5c74UL, 0xd0b85742UL,
+};
+
+static const ulong32 Td4[256] = {
+ 0x52525252UL, 0x09090909UL, 0x6a6a6a6aUL, 0xd5d5d5d5UL,
+ 0x30303030UL, 0x36363636UL, 0xa5a5a5a5UL, 0x38383838UL,
+ 0xbfbfbfbfUL, 0x40404040UL, 0xa3a3a3a3UL, 0x9e9e9e9eUL,
+ 0x81818181UL, 0xf3f3f3f3UL, 0xd7d7d7d7UL, 0xfbfbfbfbUL,
+ 0x7c7c7c7cUL, 0xe3e3e3e3UL, 0x39393939UL, 0x82828282UL,
+ 0x9b9b9b9bUL, 0x2f2f2f2fUL, 0xffffffffUL, 0x87878787UL,
+ 0x34343434UL, 0x8e8e8e8eUL, 0x43434343UL, 0x44444444UL,
+ 0xc4c4c4c4UL, 0xdedededeUL, 0xe9e9e9e9UL, 0xcbcbcbcbUL,
+ 0x54545454UL, 0x7b7b7b7bUL, 0x94949494UL, 0x32323232UL,
+ 0xa6a6a6a6UL, 0xc2c2c2c2UL, 0x23232323UL, 0x3d3d3d3dUL,
+ 0xeeeeeeeeUL, 0x4c4c4c4cUL, 0x95959595UL, 0x0b0b0b0bUL,
+ 0x42424242UL, 0xfafafafaUL, 0xc3c3c3c3UL, 0x4e4e4e4eUL,
+ 0x08080808UL, 0x2e2e2e2eUL, 0xa1a1a1a1UL, 0x66666666UL,
+ 0x28282828UL, 0xd9d9d9d9UL, 0x24242424UL, 0xb2b2b2b2UL,
+ 0x76767676UL, 0x5b5b5b5bUL, 0xa2a2a2a2UL, 0x49494949UL,
+ 0x6d6d6d6dUL, 0x8b8b8b8bUL, 0xd1d1d1d1UL, 0x25252525UL,
+ 0x72727272UL, 0xf8f8f8f8UL, 0xf6f6f6f6UL, 0x64646464UL,
+ 0x86868686UL, 0x68686868UL, 0x98989898UL, 0x16161616UL,
+ 0xd4d4d4d4UL, 0xa4a4a4a4UL, 0x5c5c5c5cUL, 0xccccccccUL,
+ 0x5d5d5d5dUL, 0x65656565UL, 0xb6b6b6b6UL, 0x92929292UL,
+ 0x6c6c6c6cUL, 0x70707070UL, 0x48484848UL, 0x50505050UL,
+ 0xfdfdfdfdUL, 0xededededUL, 0xb9b9b9b9UL, 0xdadadadaUL,
+ 0x5e5e5e5eUL, 0x15151515UL, 0x46464646UL, 0x57575757UL,
+ 0xa7a7a7a7UL, 0x8d8d8d8dUL, 0x9d9d9d9dUL, 0x84848484UL,
+ 0x90909090UL, 0xd8d8d8d8UL, 0xababababUL, 0x00000000UL,
+ 0x8c8c8c8cUL, 0xbcbcbcbcUL, 0xd3d3d3d3UL, 0x0a0a0a0aUL,
+ 0xf7f7f7f7UL, 0xe4e4e4e4UL, 0x58585858UL, 0x05050505UL,
+ 0xb8b8b8b8UL, 0xb3b3b3b3UL, 0x45454545UL, 0x06060606UL,
+ 0xd0d0d0d0UL, 0x2c2c2c2cUL, 0x1e1e1e1eUL, 0x8f8f8f8fUL,
+ 0xcacacacaUL, 0x3f3f3f3fUL, 0x0f0f0f0fUL, 0x02020202UL,
+ 0xc1c1c1c1UL, 0xafafafafUL, 0xbdbdbdbdUL, 0x03030303UL,
+ 0x01010101UL, 0x13131313UL, 0x8a8a8a8aUL, 0x6b6b6b6bUL,
+ 0x3a3a3a3aUL, 0x91919191UL, 0x11111111UL, 0x41414141UL,
+ 0x4f4f4f4fUL, 0x67676767UL, 0xdcdcdcdcUL, 0xeaeaeaeaUL,
+ 0x97979797UL, 0xf2f2f2f2UL, 0xcfcfcfcfUL, 0xcecececeUL,
+ 0xf0f0f0f0UL, 0xb4b4b4b4UL, 0xe6e6e6e6UL, 0x73737373UL,
+ 0x96969696UL, 0xacacacacUL, 0x74747474UL, 0x22222222UL,
+ 0xe7e7e7e7UL, 0xadadadadUL, 0x35353535UL, 0x85858585UL,
+ 0xe2e2e2e2UL, 0xf9f9f9f9UL, 0x37373737UL, 0xe8e8e8e8UL,
+ 0x1c1c1c1cUL, 0x75757575UL, 0xdfdfdfdfUL, 0x6e6e6e6eUL,
+ 0x47474747UL, 0xf1f1f1f1UL, 0x1a1a1a1aUL, 0x71717171UL,
+ 0x1d1d1d1dUL, 0x29292929UL, 0xc5c5c5c5UL, 0x89898989UL,
+ 0x6f6f6f6fUL, 0xb7b7b7b7UL, 0x62626262UL, 0x0e0e0e0eUL,
+ 0xaaaaaaaaUL, 0x18181818UL, 0xbebebebeUL, 0x1b1b1b1bUL,
+ 0xfcfcfcfcUL, 0x56565656UL, 0x3e3e3e3eUL, 0x4b4b4b4bUL,
+ 0xc6c6c6c6UL, 0xd2d2d2d2UL, 0x79797979UL, 0x20202020UL,
+ 0x9a9a9a9aUL, 0xdbdbdbdbUL, 0xc0c0c0c0UL, 0xfefefefeUL,
+ 0x78787878UL, 0xcdcdcdcdUL, 0x5a5a5a5aUL, 0xf4f4f4f4UL,
+ 0x1f1f1f1fUL, 0xddddddddUL, 0xa8a8a8a8UL, 0x33333333UL,
+ 0x88888888UL, 0x07070707UL, 0xc7c7c7c7UL, 0x31313131UL,
+ 0xb1b1b1b1UL, 0x12121212UL, 0x10101010UL, 0x59595959UL,
+ 0x27272727UL, 0x80808080UL, 0xececececUL, 0x5f5f5f5fUL,
+ 0x60606060UL, 0x51515151UL, 0x7f7f7f7fUL, 0xa9a9a9a9UL,
+ 0x19191919UL, 0xb5b5b5b5UL, 0x4a4a4a4aUL, 0x0d0d0d0dUL,
+ 0x2d2d2d2dUL, 0xe5e5e5e5UL, 0x7a7a7a7aUL, 0x9f9f9f9fUL,
+ 0x93939393UL, 0xc9c9c9c9UL, 0x9c9c9c9cUL, 0xefefefefUL,
+ 0xa0a0a0a0UL, 0xe0e0e0e0UL, 0x3b3b3b3bUL, 0x4d4d4d4dUL,
+ 0xaeaeaeaeUL, 0x2a2a2a2aUL, 0xf5f5f5f5UL, 0xb0b0b0b0UL,
+ 0xc8c8c8c8UL, 0xebebebebUL, 0xbbbbbbbbUL, 0x3c3c3c3cUL,
+ 0x83838383UL, 0x53535353UL, 0x99999999UL, 0x61616161UL,
+ 0x17171717UL, 0x2b2b2b2bUL, 0x04040404UL, 0x7e7e7e7eUL,
+ 0xbabababaUL, 0x77777777UL, 0xd6d6d6d6UL, 0x26262626UL,
+ 0xe1e1e1e1UL, 0x69696969UL, 0x14141414UL, 0x63636363UL,
+ 0x55555555UL, 0x21212121UL, 0x0c0c0c0cUL, 0x7d7d7d7dUL,
+};
+
+#endif /* ENCRYPT_ONLY */
+
+#ifdef LTC_SMALL_CODE
+
+#define Te0(x) TE0[x]
+#define Te1(x) RORc(TE0[x], 8)
+#define Te2(x) RORc(TE0[x], 16)
+#define Te3(x) RORc(TE0[x], 24)
+
+#define Td0(x) TD0[x]
+#define Td1(x) RORc(TD0[x], 8)
+#define Td2(x) RORc(TD0[x], 16)
+#define Td3(x) RORc(TD0[x], 24)
+
+#define Te4_0 0x000000FF & Te4
+#define Te4_1 0x0000FF00 & Te4
+#define Te4_2 0x00FF0000 & Te4
+#define Te4_3 0xFF000000 & Te4
+
+#else
+
+#define Te0(x) TE0[x]
+#define Te1(x) TE1[x]
+#define Te2(x) TE2[x]
+#define Te3(x) TE3[x]
+
+#define Td0(x) TD0[x]
+#define Td1(x) TD1[x]
+#define Td2(x) TD2[x]
+#define Td3(x) TD3[x]
+
+static const ulong32 TE1[256] = {
+ 0xa5c66363UL, 0x84f87c7cUL, 0x99ee7777UL, 0x8df67b7bUL,
+ 0x0dfff2f2UL, 0xbdd66b6bUL, 0xb1de6f6fUL, 0x5491c5c5UL,
+ 0x50603030UL, 0x03020101UL, 0xa9ce6767UL, 0x7d562b2bUL,
+ 0x19e7fefeUL, 0x62b5d7d7UL, 0xe64dababUL, 0x9aec7676UL,
+ 0x458fcacaUL, 0x9d1f8282UL, 0x4089c9c9UL, 0x87fa7d7dUL,
+ 0x15effafaUL, 0xebb25959UL, 0xc98e4747UL, 0x0bfbf0f0UL,
+ 0xec41adadUL, 0x67b3d4d4UL, 0xfd5fa2a2UL, 0xea45afafUL,
+ 0xbf239c9cUL, 0xf753a4a4UL, 0x96e47272UL, 0x5b9bc0c0UL,
+ 0xc275b7b7UL, 0x1ce1fdfdUL, 0xae3d9393UL, 0x6a4c2626UL,
+ 0x5a6c3636UL, 0x417e3f3fUL, 0x02f5f7f7UL, 0x4f83ccccUL,
+ 0x5c683434UL, 0xf451a5a5UL, 0x34d1e5e5UL, 0x08f9f1f1UL,
+ 0x93e27171UL, 0x73abd8d8UL, 0x53623131UL, 0x3f2a1515UL,
+ 0x0c080404UL, 0x5295c7c7UL, 0x65462323UL, 0x5e9dc3c3UL,
+ 0x28301818UL, 0xa1379696UL, 0x0f0a0505UL, 0xb52f9a9aUL,
+ 0x090e0707UL, 0x36241212UL, 0x9b1b8080UL, 0x3ddfe2e2UL,
+ 0x26cdebebUL, 0x694e2727UL, 0xcd7fb2b2UL, 0x9fea7575UL,
+ 0x1b120909UL, 0x9e1d8383UL, 0x74582c2cUL, 0x2e341a1aUL,
+ 0x2d361b1bUL, 0xb2dc6e6eUL, 0xeeb45a5aUL, 0xfb5ba0a0UL,
+ 0xf6a45252UL, 0x4d763b3bUL, 0x61b7d6d6UL, 0xce7db3b3UL,
+ 0x7b522929UL, 0x3edde3e3UL, 0x715e2f2fUL, 0x97138484UL,
+ 0xf5a65353UL, 0x68b9d1d1UL, 0x00000000UL, 0x2cc1ededUL,
+ 0x60402020UL, 0x1fe3fcfcUL, 0xc879b1b1UL, 0xedb65b5bUL,
+ 0xbed46a6aUL, 0x468dcbcbUL, 0xd967bebeUL, 0x4b723939UL,
+ 0xde944a4aUL, 0xd4984c4cUL, 0xe8b05858UL, 0x4a85cfcfUL,
+ 0x6bbbd0d0UL, 0x2ac5efefUL, 0xe54faaaaUL, 0x16edfbfbUL,
+ 0xc5864343UL, 0xd79a4d4dUL, 0x55663333UL, 0x94118585UL,
+ 0xcf8a4545UL, 0x10e9f9f9UL, 0x06040202UL, 0x81fe7f7fUL,
+ 0xf0a05050UL, 0x44783c3cUL, 0xba259f9fUL, 0xe34ba8a8UL,
+ 0xf3a25151UL, 0xfe5da3a3UL, 0xc0804040UL, 0x8a058f8fUL,
+ 0xad3f9292UL, 0xbc219d9dUL, 0x48703838UL, 0x04f1f5f5UL,
+ 0xdf63bcbcUL, 0xc177b6b6UL, 0x75afdadaUL, 0x63422121UL,
+ 0x30201010UL, 0x1ae5ffffUL, 0x0efdf3f3UL, 0x6dbfd2d2UL,
+ 0x4c81cdcdUL, 0x14180c0cUL, 0x35261313UL, 0x2fc3ececUL,
+ 0xe1be5f5fUL, 0xa2359797UL, 0xcc884444UL, 0x392e1717UL,
+ 0x5793c4c4UL, 0xf255a7a7UL, 0x82fc7e7eUL, 0x477a3d3dUL,
+ 0xacc86464UL, 0xe7ba5d5dUL, 0x2b321919UL, 0x95e67373UL,
+ 0xa0c06060UL, 0x98198181UL, 0xd19e4f4fUL, 0x7fa3dcdcUL,
+ 0x66442222UL, 0x7e542a2aUL, 0xab3b9090UL, 0x830b8888UL,
+ 0xca8c4646UL, 0x29c7eeeeUL, 0xd36bb8b8UL, 0x3c281414UL,
+ 0x79a7dedeUL, 0xe2bc5e5eUL, 0x1d160b0bUL, 0x76addbdbUL,
+ 0x3bdbe0e0UL, 0x56643232UL, 0x4e743a3aUL, 0x1e140a0aUL,
+ 0xdb924949UL, 0x0a0c0606UL, 0x6c482424UL, 0xe4b85c5cUL,
+ 0x5d9fc2c2UL, 0x6ebdd3d3UL, 0xef43acacUL, 0xa6c46262UL,
+ 0xa8399191UL, 0xa4319595UL, 0x37d3e4e4UL, 0x8bf27979UL,
+ 0x32d5e7e7UL, 0x438bc8c8UL, 0x596e3737UL, 0xb7da6d6dUL,
+ 0x8c018d8dUL, 0x64b1d5d5UL, 0xd29c4e4eUL, 0xe049a9a9UL,
+ 0xb4d86c6cUL, 0xfaac5656UL, 0x07f3f4f4UL, 0x25cfeaeaUL,
+ 0xafca6565UL, 0x8ef47a7aUL, 0xe947aeaeUL, 0x18100808UL,
+ 0xd56fbabaUL, 0x88f07878UL, 0x6f4a2525UL, 0x725c2e2eUL,
+ 0x24381c1cUL, 0xf157a6a6UL, 0xc773b4b4UL, 0x5197c6c6UL,
+ 0x23cbe8e8UL, 0x7ca1ddddUL, 0x9ce87474UL, 0x213e1f1fUL,
+ 0xdd964b4bUL, 0xdc61bdbdUL, 0x860d8b8bUL, 0x850f8a8aUL,
+ 0x90e07070UL, 0x427c3e3eUL, 0xc471b5b5UL, 0xaacc6666UL,
+ 0xd8904848UL, 0x05060303UL, 0x01f7f6f6UL, 0x121c0e0eUL,
+ 0xa3c26161UL, 0x5f6a3535UL, 0xf9ae5757UL, 0xd069b9b9UL,
+ 0x91178686UL, 0x5899c1c1UL, 0x273a1d1dUL, 0xb9279e9eUL,
+ 0x38d9e1e1UL, 0x13ebf8f8UL, 0xb32b9898UL, 0x33221111UL,
+ 0xbbd26969UL, 0x70a9d9d9UL, 0x89078e8eUL, 0xa7339494UL,
+ 0xb62d9b9bUL, 0x223c1e1eUL, 0x92158787UL, 0x20c9e9e9UL,
+ 0x4987ceceUL, 0xffaa5555UL, 0x78502828UL, 0x7aa5dfdfUL,
+ 0x8f038c8cUL, 0xf859a1a1UL, 0x80098989UL, 0x171a0d0dUL,
+ 0xda65bfbfUL, 0x31d7e6e6UL, 0xc6844242UL, 0xb8d06868UL,
+ 0xc3824141UL, 0xb0299999UL, 0x775a2d2dUL, 0x111e0f0fUL,
+ 0xcb7bb0b0UL, 0xfca85454UL, 0xd66dbbbbUL, 0x3a2c1616UL,
+};
+static const ulong32 TE2[256] = {
+ 0x63a5c663UL, 0x7c84f87cUL, 0x7799ee77UL, 0x7b8df67bUL,
+ 0xf20dfff2UL, 0x6bbdd66bUL, 0x6fb1de6fUL, 0xc55491c5UL,
+ 0x30506030UL, 0x01030201UL, 0x67a9ce67UL, 0x2b7d562bUL,
+ 0xfe19e7feUL, 0xd762b5d7UL, 0xabe64dabUL, 0x769aec76UL,
+ 0xca458fcaUL, 0x829d1f82UL, 0xc94089c9UL, 0x7d87fa7dUL,
+ 0xfa15effaUL, 0x59ebb259UL, 0x47c98e47UL, 0xf00bfbf0UL,
+ 0xadec41adUL, 0xd467b3d4UL, 0xa2fd5fa2UL, 0xafea45afUL,
+ 0x9cbf239cUL, 0xa4f753a4UL, 0x7296e472UL, 0xc05b9bc0UL,
+ 0xb7c275b7UL, 0xfd1ce1fdUL, 0x93ae3d93UL, 0x266a4c26UL,
+ 0x365a6c36UL, 0x3f417e3fUL, 0xf702f5f7UL, 0xcc4f83ccUL,
+ 0x345c6834UL, 0xa5f451a5UL, 0xe534d1e5UL, 0xf108f9f1UL,
+ 0x7193e271UL, 0xd873abd8UL, 0x31536231UL, 0x153f2a15UL,
+ 0x040c0804UL, 0xc75295c7UL, 0x23654623UL, 0xc35e9dc3UL,
+ 0x18283018UL, 0x96a13796UL, 0x050f0a05UL, 0x9ab52f9aUL,
+ 0x07090e07UL, 0x12362412UL, 0x809b1b80UL, 0xe23ddfe2UL,
+ 0xeb26cdebUL, 0x27694e27UL, 0xb2cd7fb2UL, 0x759fea75UL,
+ 0x091b1209UL, 0x839e1d83UL, 0x2c74582cUL, 0x1a2e341aUL,
+ 0x1b2d361bUL, 0x6eb2dc6eUL, 0x5aeeb45aUL, 0xa0fb5ba0UL,
+ 0x52f6a452UL, 0x3b4d763bUL, 0xd661b7d6UL, 0xb3ce7db3UL,
+ 0x297b5229UL, 0xe33edde3UL, 0x2f715e2fUL, 0x84971384UL,
+ 0x53f5a653UL, 0xd168b9d1UL, 0x00000000UL, 0xed2cc1edUL,
+ 0x20604020UL, 0xfc1fe3fcUL, 0xb1c879b1UL, 0x5bedb65bUL,
+ 0x6abed46aUL, 0xcb468dcbUL, 0xbed967beUL, 0x394b7239UL,
+ 0x4ade944aUL, 0x4cd4984cUL, 0x58e8b058UL, 0xcf4a85cfUL,
+ 0xd06bbbd0UL, 0xef2ac5efUL, 0xaae54faaUL, 0xfb16edfbUL,
+ 0x43c58643UL, 0x4dd79a4dUL, 0x33556633UL, 0x85941185UL,
+ 0x45cf8a45UL, 0xf910e9f9UL, 0x02060402UL, 0x7f81fe7fUL,
+ 0x50f0a050UL, 0x3c44783cUL, 0x9fba259fUL, 0xa8e34ba8UL,
+ 0x51f3a251UL, 0xa3fe5da3UL, 0x40c08040UL, 0x8f8a058fUL,
+ 0x92ad3f92UL, 0x9dbc219dUL, 0x38487038UL, 0xf504f1f5UL,
+ 0xbcdf63bcUL, 0xb6c177b6UL, 0xda75afdaUL, 0x21634221UL,
+ 0x10302010UL, 0xff1ae5ffUL, 0xf30efdf3UL, 0xd26dbfd2UL,
+ 0xcd4c81cdUL, 0x0c14180cUL, 0x13352613UL, 0xec2fc3ecUL,
+ 0x5fe1be5fUL, 0x97a23597UL, 0x44cc8844UL, 0x17392e17UL,
+ 0xc45793c4UL, 0xa7f255a7UL, 0x7e82fc7eUL, 0x3d477a3dUL,
+ 0x64acc864UL, 0x5de7ba5dUL, 0x192b3219UL, 0x7395e673UL,
+ 0x60a0c060UL, 0x81981981UL, 0x4fd19e4fUL, 0xdc7fa3dcUL,
+ 0x22664422UL, 0x2a7e542aUL, 0x90ab3b90UL, 0x88830b88UL,
+ 0x46ca8c46UL, 0xee29c7eeUL, 0xb8d36bb8UL, 0x143c2814UL,
+ 0xde79a7deUL, 0x5ee2bc5eUL, 0x0b1d160bUL, 0xdb76addbUL,
+ 0xe03bdbe0UL, 0x32566432UL, 0x3a4e743aUL, 0x0a1e140aUL,
+ 0x49db9249UL, 0x060a0c06UL, 0x246c4824UL, 0x5ce4b85cUL,
+ 0xc25d9fc2UL, 0xd36ebdd3UL, 0xacef43acUL, 0x62a6c462UL,
+ 0x91a83991UL, 0x95a43195UL, 0xe437d3e4UL, 0x798bf279UL,
+ 0xe732d5e7UL, 0xc8438bc8UL, 0x37596e37UL, 0x6db7da6dUL,
+ 0x8d8c018dUL, 0xd564b1d5UL, 0x4ed29c4eUL, 0xa9e049a9UL,
+ 0x6cb4d86cUL, 0x56faac56UL, 0xf407f3f4UL, 0xea25cfeaUL,
+ 0x65afca65UL, 0x7a8ef47aUL, 0xaee947aeUL, 0x08181008UL,
+ 0xbad56fbaUL, 0x7888f078UL, 0x256f4a25UL, 0x2e725c2eUL,
+ 0x1c24381cUL, 0xa6f157a6UL, 0xb4c773b4UL, 0xc65197c6UL,
+ 0xe823cbe8UL, 0xdd7ca1ddUL, 0x749ce874UL, 0x1f213e1fUL,
+ 0x4bdd964bUL, 0xbddc61bdUL, 0x8b860d8bUL, 0x8a850f8aUL,
+ 0x7090e070UL, 0x3e427c3eUL, 0xb5c471b5UL, 0x66aacc66UL,
+ 0x48d89048UL, 0x03050603UL, 0xf601f7f6UL, 0x0e121c0eUL,
+ 0x61a3c261UL, 0x355f6a35UL, 0x57f9ae57UL, 0xb9d069b9UL,
+ 0x86911786UL, 0xc15899c1UL, 0x1d273a1dUL, 0x9eb9279eUL,
+ 0xe138d9e1UL, 0xf813ebf8UL, 0x98b32b98UL, 0x11332211UL,
+ 0x69bbd269UL, 0xd970a9d9UL, 0x8e89078eUL, 0x94a73394UL,
+ 0x9bb62d9bUL, 0x1e223c1eUL, 0x87921587UL, 0xe920c9e9UL,
+ 0xce4987ceUL, 0x55ffaa55UL, 0x28785028UL, 0xdf7aa5dfUL,
+ 0x8c8f038cUL, 0xa1f859a1UL, 0x89800989UL, 0x0d171a0dUL,
+ 0xbfda65bfUL, 0xe631d7e6UL, 0x42c68442UL, 0x68b8d068UL,
+ 0x41c38241UL, 0x99b02999UL, 0x2d775a2dUL, 0x0f111e0fUL,
+ 0xb0cb7bb0UL, 0x54fca854UL, 0xbbd66dbbUL, 0x163a2c16UL,
+};
+static const ulong32 TE3[256] = {
+
+ 0x6363a5c6UL, 0x7c7c84f8UL, 0x777799eeUL, 0x7b7b8df6UL,
+ 0xf2f20dffUL, 0x6b6bbdd6UL, 0x6f6fb1deUL, 0xc5c55491UL,
+ 0x30305060UL, 0x01010302UL, 0x6767a9ceUL, 0x2b2b7d56UL,
+ 0xfefe19e7UL, 0xd7d762b5UL, 0xababe64dUL, 0x76769aecUL,
+ 0xcaca458fUL, 0x82829d1fUL, 0xc9c94089UL, 0x7d7d87faUL,
+ 0xfafa15efUL, 0x5959ebb2UL, 0x4747c98eUL, 0xf0f00bfbUL,
+ 0xadadec41UL, 0xd4d467b3UL, 0xa2a2fd5fUL, 0xafafea45UL,
+ 0x9c9cbf23UL, 0xa4a4f753UL, 0x727296e4UL, 0xc0c05b9bUL,
+ 0xb7b7c275UL, 0xfdfd1ce1UL, 0x9393ae3dUL, 0x26266a4cUL,
+ 0x36365a6cUL, 0x3f3f417eUL, 0xf7f702f5UL, 0xcccc4f83UL,
+ 0x34345c68UL, 0xa5a5f451UL, 0xe5e534d1UL, 0xf1f108f9UL,
+ 0x717193e2UL, 0xd8d873abUL, 0x31315362UL, 0x15153f2aUL,
+ 0x04040c08UL, 0xc7c75295UL, 0x23236546UL, 0xc3c35e9dUL,
+ 0x18182830UL, 0x9696a137UL, 0x05050f0aUL, 0x9a9ab52fUL,
+ 0x0707090eUL, 0x12123624UL, 0x80809b1bUL, 0xe2e23ddfUL,
+ 0xebeb26cdUL, 0x2727694eUL, 0xb2b2cd7fUL, 0x75759feaUL,
+ 0x09091b12UL, 0x83839e1dUL, 0x2c2c7458UL, 0x1a1a2e34UL,
+ 0x1b1b2d36UL, 0x6e6eb2dcUL, 0x5a5aeeb4UL, 0xa0a0fb5bUL,
+ 0x5252f6a4UL, 0x3b3b4d76UL, 0xd6d661b7UL, 0xb3b3ce7dUL,
+ 0x29297b52UL, 0xe3e33eddUL, 0x2f2f715eUL, 0x84849713UL,
+ 0x5353f5a6UL, 0xd1d168b9UL, 0x00000000UL, 0xeded2cc1UL,
+ 0x20206040UL, 0xfcfc1fe3UL, 0xb1b1c879UL, 0x5b5bedb6UL,
+ 0x6a6abed4UL, 0xcbcb468dUL, 0xbebed967UL, 0x39394b72UL,
+ 0x4a4ade94UL, 0x4c4cd498UL, 0x5858e8b0UL, 0xcfcf4a85UL,
+ 0xd0d06bbbUL, 0xefef2ac5UL, 0xaaaae54fUL, 0xfbfb16edUL,
+ 0x4343c586UL, 0x4d4dd79aUL, 0x33335566UL, 0x85859411UL,
+ 0x4545cf8aUL, 0xf9f910e9UL, 0x02020604UL, 0x7f7f81feUL,
+ 0x5050f0a0UL, 0x3c3c4478UL, 0x9f9fba25UL, 0xa8a8e34bUL,
+ 0x5151f3a2UL, 0xa3a3fe5dUL, 0x4040c080UL, 0x8f8f8a05UL,
+ 0x9292ad3fUL, 0x9d9dbc21UL, 0x38384870UL, 0xf5f504f1UL,
+ 0xbcbcdf63UL, 0xb6b6c177UL, 0xdada75afUL, 0x21216342UL,
+ 0x10103020UL, 0xffff1ae5UL, 0xf3f30efdUL, 0xd2d26dbfUL,
+ 0xcdcd4c81UL, 0x0c0c1418UL, 0x13133526UL, 0xecec2fc3UL,
+ 0x5f5fe1beUL, 0x9797a235UL, 0x4444cc88UL, 0x1717392eUL,
+ 0xc4c45793UL, 0xa7a7f255UL, 0x7e7e82fcUL, 0x3d3d477aUL,
+ 0x6464acc8UL, 0x5d5de7baUL, 0x19192b32UL, 0x737395e6UL,
+ 0x6060a0c0UL, 0x81819819UL, 0x4f4fd19eUL, 0xdcdc7fa3UL,
+ 0x22226644UL, 0x2a2a7e54UL, 0x9090ab3bUL, 0x8888830bUL,
+ 0x4646ca8cUL, 0xeeee29c7UL, 0xb8b8d36bUL, 0x14143c28UL,
+ 0xdede79a7UL, 0x5e5ee2bcUL, 0x0b0b1d16UL, 0xdbdb76adUL,
+ 0xe0e03bdbUL, 0x32325664UL, 0x3a3a4e74UL, 0x0a0a1e14UL,
+ 0x4949db92UL, 0x06060a0cUL, 0x24246c48UL, 0x5c5ce4b8UL,
+ 0xc2c25d9fUL, 0xd3d36ebdUL, 0xacacef43UL, 0x6262a6c4UL,
+ 0x9191a839UL, 0x9595a431UL, 0xe4e437d3UL, 0x79798bf2UL,
+ 0xe7e732d5UL, 0xc8c8438bUL, 0x3737596eUL, 0x6d6db7daUL,
+ 0x8d8d8c01UL, 0xd5d564b1UL, 0x4e4ed29cUL, 0xa9a9e049UL,
+ 0x6c6cb4d8UL, 0x5656faacUL, 0xf4f407f3UL, 0xeaea25cfUL,
+ 0x6565afcaUL, 0x7a7a8ef4UL, 0xaeaee947UL, 0x08081810UL,
+ 0xbabad56fUL, 0x787888f0UL, 0x25256f4aUL, 0x2e2e725cUL,
+ 0x1c1c2438UL, 0xa6a6f157UL, 0xb4b4c773UL, 0xc6c65197UL,
+ 0xe8e823cbUL, 0xdddd7ca1UL, 0x74749ce8UL, 0x1f1f213eUL,
+ 0x4b4bdd96UL, 0xbdbddc61UL, 0x8b8b860dUL, 0x8a8a850fUL,
+ 0x707090e0UL, 0x3e3e427cUL, 0xb5b5c471UL, 0x6666aaccUL,
+ 0x4848d890UL, 0x03030506UL, 0xf6f601f7UL, 0x0e0e121cUL,
+ 0x6161a3c2UL, 0x35355f6aUL, 0x5757f9aeUL, 0xb9b9d069UL,
+ 0x86869117UL, 0xc1c15899UL, 0x1d1d273aUL, 0x9e9eb927UL,
+ 0xe1e138d9UL, 0xf8f813ebUL, 0x9898b32bUL, 0x11113322UL,
+ 0x6969bbd2UL, 0xd9d970a9UL, 0x8e8e8907UL, 0x9494a733UL,
+ 0x9b9bb62dUL, 0x1e1e223cUL, 0x87879215UL, 0xe9e920c9UL,
+ 0xcece4987UL, 0x5555ffaaUL, 0x28287850UL, 0xdfdf7aa5UL,
+ 0x8c8c8f03UL, 0xa1a1f859UL, 0x89898009UL, 0x0d0d171aUL,
+ 0xbfbfda65UL, 0xe6e631d7UL, 0x4242c684UL, 0x6868b8d0UL,
+ 0x4141c382UL, 0x9999b029UL, 0x2d2d775aUL, 0x0f0f111eUL,
+ 0xb0b0cb7bUL, 0x5454fca8UL, 0xbbbbd66dUL, 0x16163a2cUL,
+};
+
+#ifndef PELI_TAB
+static const ulong32 Te4_0[] = {
+0x00000063UL, 0x0000007cUL, 0x00000077UL, 0x0000007bUL, 0x000000f2UL, 0x0000006bUL, 0x0000006fUL, 0x000000c5UL,
+0x00000030UL, 0x00000001UL, 0x00000067UL, 0x0000002bUL, 0x000000feUL, 0x000000d7UL, 0x000000abUL, 0x00000076UL,
+0x000000caUL, 0x00000082UL, 0x000000c9UL, 0x0000007dUL, 0x000000faUL, 0x00000059UL, 0x00000047UL, 0x000000f0UL,
+0x000000adUL, 0x000000d4UL, 0x000000a2UL, 0x000000afUL, 0x0000009cUL, 0x000000a4UL, 0x00000072UL, 0x000000c0UL,
+0x000000b7UL, 0x000000fdUL, 0x00000093UL, 0x00000026UL, 0x00000036UL, 0x0000003fUL, 0x000000f7UL, 0x000000ccUL,
+0x00000034UL, 0x000000a5UL, 0x000000e5UL, 0x000000f1UL, 0x00000071UL, 0x000000d8UL, 0x00000031UL, 0x00000015UL,
+0x00000004UL, 0x000000c7UL, 0x00000023UL, 0x000000c3UL, 0x00000018UL, 0x00000096UL, 0x00000005UL, 0x0000009aUL,
+0x00000007UL, 0x00000012UL, 0x00000080UL, 0x000000e2UL, 0x000000ebUL, 0x00000027UL, 0x000000b2UL, 0x00000075UL,
+0x00000009UL, 0x00000083UL, 0x0000002cUL, 0x0000001aUL, 0x0000001bUL, 0x0000006eUL, 0x0000005aUL, 0x000000a0UL,
+0x00000052UL, 0x0000003bUL, 0x000000d6UL, 0x000000b3UL, 0x00000029UL, 0x000000e3UL, 0x0000002fUL, 0x00000084UL,
+0x00000053UL, 0x000000d1UL, 0x00000000UL, 0x000000edUL, 0x00000020UL, 0x000000fcUL, 0x000000b1UL, 0x0000005bUL,
+0x0000006aUL, 0x000000cbUL, 0x000000beUL, 0x00000039UL, 0x0000004aUL, 0x0000004cUL, 0x00000058UL, 0x000000cfUL,
+0x000000d0UL, 0x000000efUL, 0x000000aaUL, 0x000000fbUL, 0x00000043UL, 0x0000004dUL, 0x00000033UL, 0x00000085UL,
+0x00000045UL, 0x000000f9UL, 0x00000002UL, 0x0000007fUL, 0x00000050UL, 0x0000003cUL, 0x0000009fUL, 0x000000a8UL,
+0x00000051UL, 0x000000a3UL, 0x00000040UL, 0x0000008fUL, 0x00000092UL, 0x0000009dUL, 0x00000038UL, 0x000000f5UL,
+0x000000bcUL, 0x000000b6UL, 0x000000daUL, 0x00000021UL, 0x00000010UL, 0x000000ffUL, 0x000000f3UL, 0x000000d2UL,
+0x000000cdUL, 0x0000000cUL, 0x00000013UL, 0x000000ecUL, 0x0000005fUL, 0x00000097UL, 0x00000044UL, 0x00000017UL,
+0x000000c4UL, 0x000000a7UL, 0x0000007eUL, 0x0000003dUL, 0x00000064UL, 0x0000005dUL, 0x00000019UL, 0x00000073UL,
+0x00000060UL, 0x00000081UL, 0x0000004fUL, 0x000000dcUL, 0x00000022UL, 0x0000002aUL, 0x00000090UL, 0x00000088UL,
+0x00000046UL, 0x000000eeUL, 0x000000b8UL, 0x00000014UL, 0x000000deUL, 0x0000005eUL, 0x0000000bUL, 0x000000dbUL,
+0x000000e0UL, 0x00000032UL, 0x0000003aUL, 0x0000000aUL, 0x00000049UL, 0x00000006UL, 0x00000024UL, 0x0000005cUL,
+0x000000c2UL, 0x000000d3UL, 0x000000acUL, 0x00000062UL, 0x00000091UL, 0x00000095UL, 0x000000e4UL, 0x00000079UL,
+0x000000e7UL, 0x000000c8UL, 0x00000037UL, 0x0000006dUL, 0x0000008dUL, 0x000000d5UL, 0x0000004eUL, 0x000000a9UL,
+0x0000006cUL, 0x00000056UL, 0x000000f4UL, 0x000000eaUL, 0x00000065UL, 0x0000007aUL, 0x000000aeUL, 0x00000008UL,
+0x000000baUL, 0x00000078UL, 0x00000025UL, 0x0000002eUL, 0x0000001cUL, 0x000000a6UL, 0x000000b4UL, 0x000000c6UL,
+0x000000e8UL, 0x000000ddUL, 0x00000074UL, 0x0000001fUL, 0x0000004bUL, 0x000000bdUL, 0x0000008bUL, 0x0000008aUL,
+0x00000070UL, 0x0000003eUL, 0x000000b5UL, 0x00000066UL, 0x00000048UL, 0x00000003UL, 0x000000f6UL, 0x0000000eUL,
+0x00000061UL, 0x00000035UL, 0x00000057UL, 0x000000b9UL, 0x00000086UL, 0x000000c1UL, 0x0000001dUL, 0x0000009eUL,
+0x000000e1UL, 0x000000f8UL, 0x00000098UL, 0x00000011UL, 0x00000069UL, 0x000000d9UL, 0x0000008eUL, 0x00000094UL,
+0x0000009bUL, 0x0000001eUL, 0x00000087UL, 0x000000e9UL, 0x000000ceUL, 0x00000055UL, 0x00000028UL, 0x000000dfUL,
+0x0000008cUL, 0x000000a1UL, 0x00000089UL, 0x0000000dUL, 0x000000bfUL, 0x000000e6UL, 0x00000042UL, 0x00000068UL,
+0x00000041UL, 0x00000099UL, 0x0000002dUL, 0x0000000fUL, 0x000000b0UL, 0x00000054UL, 0x000000bbUL, 0x00000016UL
+};
+
+static const ulong32 Te4_1[] = {
+0x00006300UL, 0x00007c00UL, 0x00007700UL, 0x00007b00UL, 0x0000f200UL, 0x00006b00UL, 0x00006f00UL, 0x0000c500UL,
+0x00003000UL, 0x00000100UL, 0x00006700UL, 0x00002b00UL, 0x0000fe00UL, 0x0000d700UL, 0x0000ab00UL, 0x00007600UL,
+0x0000ca00UL, 0x00008200UL, 0x0000c900UL, 0x00007d00UL, 0x0000fa00UL, 0x00005900UL, 0x00004700UL, 0x0000f000UL,
+0x0000ad00UL, 0x0000d400UL, 0x0000a200UL, 0x0000af00UL, 0x00009c00UL, 0x0000a400UL, 0x00007200UL, 0x0000c000UL,
+0x0000b700UL, 0x0000fd00UL, 0x00009300UL, 0x00002600UL, 0x00003600UL, 0x00003f00UL, 0x0000f700UL, 0x0000cc00UL,
+0x00003400UL, 0x0000a500UL, 0x0000e500UL, 0x0000f100UL, 0x00007100UL, 0x0000d800UL, 0x00003100UL, 0x00001500UL,
+0x00000400UL, 0x0000c700UL, 0x00002300UL, 0x0000c300UL, 0x00001800UL, 0x00009600UL, 0x00000500UL, 0x00009a00UL,
+0x00000700UL, 0x00001200UL, 0x00008000UL, 0x0000e200UL, 0x0000eb00UL, 0x00002700UL, 0x0000b200UL, 0x00007500UL,
+0x00000900UL, 0x00008300UL, 0x00002c00UL, 0x00001a00UL, 0x00001b00UL, 0x00006e00UL, 0x00005a00UL, 0x0000a000UL,
+0x00005200UL, 0x00003b00UL, 0x0000d600UL, 0x0000b300UL, 0x00002900UL, 0x0000e300UL, 0x00002f00UL, 0x00008400UL,
+0x00005300UL, 0x0000d100UL, 0x00000000UL, 0x0000ed00UL, 0x00002000UL, 0x0000fc00UL, 0x0000b100UL, 0x00005b00UL,
+0x00006a00UL, 0x0000cb00UL, 0x0000be00UL, 0x00003900UL, 0x00004a00UL, 0x00004c00UL, 0x00005800UL, 0x0000cf00UL,
+0x0000d000UL, 0x0000ef00UL, 0x0000aa00UL, 0x0000fb00UL, 0x00004300UL, 0x00004d00UL, 0x00003300UL, 0x00008500UL,
+0x00004500UL, 0x0000f900UL, 0x00000200UL, 0x00007f00UL, 0x00005000UL, 0x00003c00UL, 0x00009f00UL, 0x0000a800UL,
+0x00005100UL, 0x0000a300UL, 0x00004000UL, 0x00008f00UL, 0x00009200UL, 0x00009d00UL, 0x00003800UL, 0x0000f500UL,
+0x0000bc00UL, 0x0000b600UL, 0x0000da00UL, 0x00002100UL, 0x00001000UL, 0x0000ff00UL, 0x0000f300UL, 0x0000d200UL,
+0x0000cd00UL, 0x00000c00UL, 0x00001300UL, 0x0000ec00UL, 0x00005f00UL, 0x00009700UL, 0x00004400UL, 0x00001700UL,
+0x0000c400UL, 0x0000a700UL, 0x00007e00UL, 0x00003d00UL, 0x00006400UL, 0x00005d00UL, 0x00001900UL, 0x00007300UL,
+0x00006000UL, 0x00008100UL, 0x00004f00UL, 0x0000dc00UL, 0x00002200UL, 0x00002a00UL, 0x00009000UL, 0x00008800UL,
+0x00004600UL, 0x0000ee00UL, 0x0000b800UL, 0x00001400UL, 0x0000de00UL, 0x00005e00UL, 0x00000b00UL, 0x0000db00UL,
+0x0000e000UL, 0x00003200UL, 0x00003a00UL, 0x00000a00UL, 0x00004900UL, 0x00000600UL, 0x00002400UL, 0x00005c00UL,
+0x0000c200UL, 0x0000d300UL, 0x0000ac00UL, 0x00006200UL, 0x00009100UL, 0x00009500UL, 0x0000e400UL, 0x00007900UL,
+0x0000e700UL, 0x0000c800UL, 0x00003700UL, 0x00006d00UL, 0x00008d00UL, 0x0000d500UL, 0x00004e00UL, 0x0000a900UL,
+0x00006c00UL, 0x00005600UL, 0x0000f400UL, 0x0000ea00UL, 0x00006500UL, 0x00007a00UL, 0x0000ae00UL, 0x00000800UL,
+0x0000ba00UL, 0x00007800UL, 0x00002500UL, 0x00002e00UL, 0x00001c00UL, 0x0000a600UL, 0x0000b400UL, 0x0000c600UL,
+0x0000e800UL, 0x0000dd00UL, 0x00007400UL, 0x00001f00UL, 0x00004b00UL, 0x0000bd00UL, 0x00008b00UL, 0x00008a00UL,
+0x00007000UL, 0x00003e00UL, 0x0000b500UL, 0x00006600UL, 0x00004800UL, 0x00000300UL, 0x0000f600UL, 0x00000e00UL,
+0x00006100UL, 0x00003500UL, 0x00005700UL, 0x0000b900UL, 0x00008600UL, 0x0000c100UL, 0x00001d00UL, 0x00009e00UL,
+0x0000e100UL, 0x0000f800UL, 0x00009800UL, 0x00001100UL, 0x00006900UL, 0x0000d900UL, 0x00008e00UL, 0x00009400UL,
+0x00009b00UL, 0x00001e00UL, 0x00008700UL, 0x0000e900UL, 0x0000ce00UL, 0x00005500UL, 0x00002800UL, 0x0000df00UL,
+0x00008c00UL, 0x0000a100UL, 0x00008900UL, 0x00000d00UL, 0x0000bf00UL, 0x0000e600UL, 0x00004200UL, 0x00006800UL,
+0x00004100UL, 0x00009900UL, 0x00002d00UL, 0x00000f00UL, 0x0000b000UL, 0x00005400UL, 0x0000bb00UL, 0x00001600UL
+};
+
+static const ulong32 Te4_2[] = {
+0x00630000UL, 0x007c0000UL, 0x00770000UL, 0x007b0000UL, 0x00f20000UL, 0x006b0000UL, 0x006f0000UL, 0x00c50000UL,
+0x00300000UL, 0x00010000UL, 0x00670000UL, 0x002b0000UL, 0x00fe0000UL, 0x00d70000UL, 0x00ab0000UL, 0x00760000UL,
+0x00ca0000UL, 0x00820000UL, 0x00c90000UL, 0x007d0000UL, 0x00fa0000UL, 0x00590000UL, 0x00470000UL, 0x00f00000UL,
+0x00ad0000UL, 0x00d40000UL, 0x00a20000UL, 0x00af0000UL, 0x009c0000UL, 0x00a40000UL, 0x00720000UL, 0x00c00000UL,
+0x00b70000UL, 0x00fd0000UL, 0x00930000UL, 0x00260000UL, 0x00360000UL, 0x003f0000UL, 0x00f70000UL, 0x00cc0000UL,
+0x00340000UL, 0x00a50000UL, 0x00e50000UL, 0x00f10000UL, 0x00710000UL, 0x00d80000UL, 0x00310000UL, 0x00150000UL,
+0x00040000UL, 0x00c70000UL, 0x00230000UL, 0x00c30000UL, 0x00180000UL, 0x00960000UL, 0x00050000UL, 0x009a0000UL,
+0x00070000UL, 0x00120000UL, 0x00800000UL, 0x00e20000UL, 0x00eb0000UL, 0x00270000UL, 0x00b20000UL, 0x00750000UL,
+0x00090000UL, 0x00830000UL, 0x002c0000UL, 0x001a0000UL, 0x001b0000UL, 0x006e0000UL, 0x005a0000UL, 0x00a00000UL,
+0x00520000UL, 0x003b0000UL, 0x00d60000UL, 0x00b30000UL, 0x00290000UL, 0x00e30000UL, 0x002f0000UL, 0x00840000UL,
+0x00530000UL, 0x00d10000UL, 0x00000000UL, 0x00ed0000UL, 0x00200000UL, 0x00fc0000UL, 0x00b10000UL, 0x005b0000UL,
+0x006a0000UL, 0x00cb0000UL, 0x00be0000UL, 0x00390000UL, 0x004a0000UL, 0x004c0000UL, 0x00580000UL, 0x00cf0000UL,
+0x00d00000UL, 0x00ef0000UL, 0x00aa0000UL, 0x00fb0000UL, 0x00430000UL, 0x004d0000UL, 0x00330000UL, 0x00850000UL,
+0x00450000UL, 0x00f90000UL, 0x00020000UL, 0x007f0000UL, 0x00500000UL, 0x003c0000UL, 0x009f0000UL, 0x00a80000UL,
+0x00510000UL, 0x00a30000UL, 0x00400000UL, 0x008f0000UL, 0x00920000UL, 0x009d0000UL, 0x00380000UL, 0x00f50000UL,
+0x00bc0000UL, 0x00b60000UL, 0x00da0000UL, 0x00210000UL, 0x00100000UL, 0x00ff0000UL, 0x00f30000UL, 0x00d20000UL,
+0x00cd0000UL, 0x000c0000UL, 0x00130000UL, 0x00ec0000UL, 0x005f0000UL, 0x00970000UL, 0x00440000UL, 0x00170000UL,
+0x00c40000UL, 0x00a70000UL, 0x007e0000UL, 0x003d0000UL, 0x00640000UL, 0x005d0000UL, 0x00190000UL, 0x00730000UL,
+0x00600000UL, 0x00810000UL, 0x004f0000UL, 0x00dc0000UL, 0x00220000UL, 0x002a0000UL, 0x00900000UL, 0x00880000UL,
+0x00460000UL, 0x00ee0000UL, 0x00b80000UL, 0x00140000UL, 0x00de0000UL, 0x005e0000UL, 0x000b0000UL, 0x00db0000UL,
+0x00e00000UL, 0x00320000UL, 0x003a0000UL, 0x000a0000UL, 0x00490000UL, 0x00060000UL, 0x00240000UL, 0x005c0000UL,
+0x00c20000UL, 0x00d30000UL, 0x00ac0000UL, 0x00620000UL, 0x00910000UL, 0x00950000UL, 0x00e40000UL, 0x00790000UL,
+0x00e70000UL, 0x00c80000UL, 0x00370000UL, 0x006d0000UL, 0x008d0000UL, 0x00d50000UL, 0x004e0000UL, 0x00a90000UL,
+0x006c0000UL, 0x00560000UL, 0x00f40000UL, 0x00ea0000UL, 0x00650000UL, 0x007a0000UL, 0x00ae0000UL, 0x00080000UL,
+0x00ba0000UL, 0x00780000UL, 0x00250000UL, 0x002e0000UL, 0x001c0000UL, 0x00a60000UL, 0x00b40000UL, 0x00c60000UL,
+0x00e80000UL, 0x00dd0000UL, 0x00740000UL, 0x001f0000UL, 0x004b0000UL, 0x00bd0000UL, 0x008b0000UL, 0x008a0000UL,
+0x00700000UL, 0x003e0000UL, 0x00b50000UL, 0x00660000UL, 0x00480000UL, 0x00030000UL, 0x00f60000UL, 0x000e0000UL,
+0x00610000UL, 0x00350000UL, 0x00570000UL, 0x00b90000UL, 0x00860000UL, 0x00c10000UL, 0x001d0000UL, 0x009e0000UL,
+0x00e10000UL, 0x00f80000UL, 0x00980000UL, 0x00110000UL, 0x00690000UL, 0x00d90000UL, 0x008e0000UL, 0x00940000UL,
+0x009b0000UL, 0x001e0000UL, 0x00870000UL, 0x00e90000UL, 0x00ce0000UL, 0x00550000UL, 0x00280000UL, 0x00df0000UL,
+0x008c0000UL, 0x00a10000UL, 0x00890000UL, 0x000d0000UL, 0x00bf0000UL, 0x00e60000UL, 0x00420000UL, 0x00680000UL,
+0x00410000UL, 0x00990000UL, 0x002d0000UL, 0x000f0000UL, 0x00b00000UL, 0x00540000UL, 0x00bb0000UL, 0x00160000UL
+};
+
+static const ulong32 Te4_3[] = {
+0x63000000UL, 0x7c000000UL, 0x77000000UL, 0x7b000000UL, 0xf2000000UL, 0x6b000000UL, 0x6f000000UL, 0xc5000000UL,
+0x30000000UL, 0x01000000UL, 0x67000000UL, 0x2b000000UL, 0xfe000000UL, 0xd7000000UL, 0xab000000UL, 0x76000000UL,
+0xca000000UL, 0x82000000UL, 0xc9000000UL, 0x7d000000UL, 0xfa000000UL, 0x59000000UL, 0x47000000UL, 0xf0000000UL,
+0xad000000UL, 0xd4000000UL, 0xa2000000UL, 0xaf000000UL, 0x9c000000UL, 0xa4000000UL, 0x72000000UL, 0xc0000000UL,
+0xb7000000UL, 0xfd000000UL, 0x93000000UL, 0x26000000UL, 0x36000000UL, 0x3f000000UL, 0xf7000000UL, 0xcc000000UL,
+0x34000000UL, 0xa5000000UL, 0xe5000000UL, 0xf1000000UL, 0x71000000UL, 0xd8000000UL, 0x31000000UL, 0x15000000UL,
+0x04000000UL, 0xc7000000UL, 0x23000000UL, 0xc3000000UL, 0x18000000UL, 0x96000000UL, 0x05000000UL, 0x9a000000UL,
+0x07000000UL, 0x12000000UL, 0x80000000UL, 0xe2000000UL, 0xeb000000UL, 0x27000000UL, 0xb2000000UL, 0x75000000UL,
+0x09000000UL, 0x83000000UL, 0x2c000000UL, 0x1a000000UL, 0x1b000000UL, 0x6e000000UL, 0x5a000000UL, 0xa0000000UL,
+0x52000000UL, 0x3b000000UL, 0xd6000000UL, 0xb3000000UL, 0x29000000UL, 0xe3000000UL, 0x2f000000UL, 0x84000000UL,
+0x53000000UL, 0xd1000000UL, 0x00000000UL, 0xed000000UL, 0x20000000UL, 0xfc000000UL, 0xb1000000UL, 0x5b000000UL,
+0x6a000000UL, 0xcb000000UL, 0xbe000000UL, 0x39000000UL, 0x4a000000UL, 0x4c000000UL, 0x58000000UL, 0xcf000000UL,
+0xd0000000UL, 0xef000000UL, 0xaa000000UL, 0xfb000000UL, 0x43000000UL, 0x4d000000UL, 0x33000000UL, 0x85000000UL,
+0x45000000UL, 0xf9000000UL, 0x02000000UL, 0x7f000000UL, 0x50000000UL, 0x3c000000UL, 0x9f000000UL, 0xa8000000UL,
+0x51000000UL, 0xa3000000UL, 0x40000000UL, 0x8f000000UL, 0x92000000UL, 0x9d000000UL, 0x38000000UL, 0xf5000000UL,
+0xbc000000UL, 0xb6000000UL, 0xda000000UL, 0x21000000UL, 0x10000000UL, 0xff000000UL, 0xf3000000UL, 0xd2000000UL,
+0xcd000000UL, 0x0c000000UL, 0x13000000UL, 0xec000000UL, 0x5f000000UL, 0x97000000UL, 0x44000000UL, 0x17000000UL,
+0xc4000000UL, 0xa7000000UL, 0x7e000000UL, 0x3d000000UL, 0x64000000UL, 0x5d000000UL, 0x19000000UL, 0x73000000UL,
+0x60000000UL, 0x81000000UL, 0x4f000000UL, 0xdc000000UL, 0x22000000UL, 0x2a000000UL, 0x90000000UL, 0x88000000UL,
+0x46000000UL, 0xee000000UL, 0xb8000000UL, 0x14000000UL, 0xde000000UL, 0x5e000000UL, 0x0b000000UL, 0xdb000000UL,
+0xe0000000UL, 0x32000000UL, 0x3a000000UL, 0x0a000000UL, 0x49000000UL, 0x06000000UL, 0x24000000UL, 0x5c000000UL,
+0xc2000000UL, 0xd3000000UL, 0xac000000UL, 0x62000000UL, 0x91000000UL, 0x95000000UL, 0xe4000000UL, 0x79000000UL,
+0xe7000000UL, 0xc8000000UL, 0x37000000UL, 0x6d000000UL, 0x8d000000UL, 0xd5000000UL, 0x4e000000UL, 0xa9000000UL,
+0x6c000000UL, 0x56000000UL, 0xf4000000UL, 0xea000000UL, 0x65000000UL, 0x7a000000UL, 0xae000000UL, 0x08000000UL,
+0xba000000UL, 0x78000000UL, 0x25000000UL, 0x2e000000UL, 0x1c000000UL, 0xa6000000UL, 0xb4000000UL, 0xc6000000UL,
+0xe8000000UL, 0xdd000000UL, 0x74000000UL, 0x1f000000UL, 0x4b000000UL, 0xbd000000UL, 0x8b000000UL, 0x8a000000UL,
+0x70000000UL, 0x3e000000UL, 0xb5000000UL, 0x66000000UL, 0x48000000UL, 0x03000000UL, 0xf6000000UL, 0x0e000000UL,
+0x61000000UL, 0x35000000UL, 0x57000000UL, 0xb9000000UL, 0x86000000UL, 0xc1000000UL, 0x1d000000UL, 0x9e000000UL,
+0xe1000000UL, 0xf8000000UL, 0x98000000UL, 0x11000000UL, 0x69000000UL, 0xd9000000UL, 0x8e000000UL, 0x94000000UL,
+0x9b000000UL, 0x1e000000UL, 0x87000000UL, 0xe9000000UL, 0xce000000UL, 0x55000000UL, 0x28000000UL, 0xdf000000UL,
+0x8c000000UL, 0xa1000000UL, 0x89000000UL, 0x0d000000UL, 0xbf000000UL, 0xe6000000UL, 0x42000000UL, 0x68000000UL,
+0x41000000UL, 0x99000000UL, 0x2d000000UL, 0x0f000000UL, 0xb0000000UL, 0x54000000UL, 0xbb000000UL, 0x16000000UL
+};
+#endif /* pelimac */
+
+#ifndef ENCRYPT_ONLY
+
+static const ulong32 TD1[256] = {
+ 0x5051f4a7UL, 0x537e4165UL, 0xc31a17a4UL, 0x963a275eUL,
+ 0xcb3bab6bUL, 0xf11f9d45UL, 0xabacfa58UL, 0x934be303UL,
+ 0x552030faUL, 0xf6ad766dUL, 0x9188cc76UL, 0x25f5024cUL,
+ 0xfc4fe5d7UL, 0xd7c52acbUL, 0x80263544UL, 0x8fb562a3UL,
+ 0x49deb15aUL, 0x6725ba1bUL, 0x9845ea0eUL, 0xe15dfec0UL,
+ 0x02c32f75UL, 0x12814cf0UL, 0xa38d4697UL, 0xc66bd3f9UL,
+ 0xe7038f5fUL, 0x9515929cUL, 0xebbf6d7aUL, 0xda955259UL,
+ 0x2dd4be83UL, 0xd3587421UL, 0x2949e069UL, 0x448ec9c8UL,
+ 0x6a75c289UL, 0x78f48e79UL, 0x6b99583eUL, 0xdd27b971UL,
+ 0xb6bee14fUL, 0x17f088adUL, 0x66c920acUL, 0xb47dce3aUL,
+ 0x1863df4aUL, 0x82e51a31UL, 0x60975133UL, 0x4562537fUL,
+ 0xe0b16477UL, 0x84bb6baeUL, 0x1cfe81a0UL, 0x94f9082bUL,
+ 0x58704868UL, 0x198f45fdUL, 0x8794de6cUL, 0xb7527bf8UL,
+ 0x23ab73d3UL, 0xe2724b02UL, 0x57e31f8fUL, 0x2a6655abUL,
+ 0x07b2eb28UL, 0x032fb5c2UL, 0x9a86c57bUL, 0xa5d33708UL,
+ 0xf2302887UL, 0xb223bfa5UL, 0xba02036aUL, 0x5ced1682UL,
+ 0x2b8acf1cUL, 0x92a779b4UL, 0xf0f307f2UL, 0xa14e69e2UL,
+ 0xcd65daf4UL, 0xd50605beUL, 0x1fd13462UL, 0x8ac4a6feUL,
+ 0x9d342e53UL, 0xa0a2f355UL, 0x32058ae1UL, 0x75a4f6ebUL,
+ 0x390b83ecUL, 0xaa4060efUL, 0x065e719fUL, 0x51bd6e10UL,
+ 0xf93e218aUL, 0x3d96dd06UL, 0xaedd3e05UL, 0x464de6bdUL,
+ 0xb591548dUL, 0x0571c45dUL, 0x6f0406d4UL, 0xff605015UL,
+ 0x241998fbUL, 0x97d6bde9UL, 0xcc894043UL, 0x7767d99eUL,
+ 0xbdb0e842UL, 0x8807898bUL, 0x38e7195bUL, 0xdb79c8eeUL,
+ 0x47a17c0aUL, 0xe97c420fUL, 0xc9f8841eUL, 0x00000000UL,
+ 0x83098086UL, 0x48322bedUL, 0xac1e1170UL, 0x4e6c5a72UL,
+ 0xfbfd0effUL, 0x560f8538UL, 0x1e3daed5UL, 0x27362d39UL,
+ 0x640a0fd9UL, 0x21685ca6UL, 0xd19b5b54UL, 0x3a24362eUL,
+ 0xb10c0a67UL, 0x0f9357e7UL, 0xd2b4ee96UL, 0x9e1b9b91UL,
+ 0x4f80c0c5UL, 0xa261dc20UL, 0x695a774bUL, 0x161c121aUL,
+ 0x0ae293baUL, 0xe5c0a02aUL, 0x433c22e0UL, 0x1d121b17UL,
+ 0x0b0e090dUL, 0xadf28bc7UL, 0xb92db6a8UL, 0xc8141ea9UL,
+ 0x8557f119UL, 0x4caf7507UL, 0xbbee99ddUL, 0xfda37f60UL,
+ 0x9ff70126UL, 0xbc5c72f5UL, 0xc544663bUL, 0x345bfb7eUL,
+ 0x768b4329UL, 0xdccb23c6UL, 0x68b6edfcUL, 0x63b8e4f1UL,
+ 0xcad731dcUL, 0x10426385UL, 0x40139722UL, 0x2084c611UL,
+ 0x7d854a24UL, 0xf8d2bb3dUL, 0x11aef932UL, 0x6dc729a1UL,
+ 0x4b1d9e2fUL, 0xf3dcb230UL, 0xec0d8652UL, 0xd077c1e3UL,
+ 0x6c2bb316UL, 0x99a970b9UL, 0xfa119448UL, 0x2247e964UL,
+ 0xc4a8fc8cUL, 0x1aa0f03fUL, 0xd8567d2cUL, 0xef223390UL,
+ 0xc787494eUL, 0xc1d938d1UL, 0xfe8ccaa2UL, 0x3698d40bUL,
+ 0xcfa6f581UL, 0x28a57adeUL, 0x26dab78eUL, 0xa43fadbfUL,
+ 0xe42c3a9dUL, 0x0d507892UL, 0x9b6a5fccUL, 0x62547e46UL,
+ 0xc2f68d13UL, 0xe890d8b8UL, 0x5e2e39f7UL, 0xf582c3afUL,
+ 0xbe9f5d80UL, 0x7c69d093UL, 0xa96fd52dUL, 0xb3cf2512UL,
+ 0x3bc8ac99UL, 0xa710187dUL, 0x6ee89c63UL, 0x7bdb3bbbUL,
+ 0x09cd2678UL, 0xf46e5918UL, 0x01ec9ab7UL, 0xa8834f9aUL,
+ 0x65e6956eUL, 0x7eaaffe6UL, 0x0821bccfUL, 0xe6ef15e8UL,
+ 0xd9bae79bUL, 0xce4a6f36UL, 0xd4ea9f09UL, 0xd629b07cUL,
+ 0xaf31a4b2UL, 0x312a3f23UL, 0x30c6a594UL, 0xc035a266UL,
+ 0x37744ebcUL, 0xa6fc82caUL, 0xb0e090d0UL, 0x1533a7d8UL,
+ 0x4af10498UL, 0xf741ecdaUL, 0x0e7fcd50UL, 0x2f1791f6UL,
+ 0x8d764dd6UL, 0x4d43efb0UL, 0x54ccaa4dUL, 0xdfe49604UL,
+ 0xe39ed1b5UL, 0x1b4c6a88UL, 0xb8c12c1fUL, 0x7f466551UL,
+ 0x049d5eeaUL, 0x5d018c35UL, 0x73fa8774UL, 0x2efb0b41UL,
+ 0x5ab3671dUL, 0x5292dbd2UL, 0x33e91056UL, 0x136dd647UL,
+ 0x8c9ad761UL, 0x7a37a10cUL, 0x8e59f814UL, 0x89eb133cUL,
+ 0xeecea927UL, 0x35b761c9UL, 0xede11ce5UL, 0x3c7a47b1UL,
+ 0x599cd2dfUL, 0x3f55f273UL, 0x791814ceUL, 0xbf73c737UL,
+ 0xea53f7cdUL, 0x5b5ffdaaUL, 0x14df3d6fUL, 0x867844dbUL,
+ 0x81caaff3UL, 0x3eb968c4UL, 0x2c382434UL, 0x5fc2a340UL,
+ 0x72161dc3UL, 0x0cbce225UL, 0x8b283c49UL, 0x41ff0d95UL,
+ 0x7139a801UL, 0xde080cb3UL, 0x9cd8b4e4UL, 0x906456c1UL,
+ 0x617bcb84UL, 0x70d532b6UL, 0x74486c5cUL, 0x42d0b857UL,
+};
+static const ulong32 TD2[256] = {
+ 0xa75051f4UL, 0x65537e41UL, 0xa4c31a17UL, 0x5e963a27UL,
+ 0x6bcb3babUL, 0x45f11f9dUL, 0x58abacfaUL, 0x03934be3UL,
+ 0xfa552030UL, 0x6df6ad76UL, 0x769188ccUL, 0x4c25f502UL,
+ 0xd7fc4fe5UL, 0xcbd7c52aUL, 0x44802635UL, 0xa38fb562UL,
+ 0x5a49deb1UL, 0x1b6725baUL, 0x0e9845eaUL, 0xc0e15dfeUL,
+ 0x7502c32fUL, 0xf012814cUL, 0x97a38d46UL, 0xf9c66bd3UL,
+ 0x5fe7038fUL, 0x9c951592UL, 0x7aebbf6dUL, 0x59da9552UL,
+ 0x832dd4beUL, 0x21d35874UL, 0x692949e0UL, 0xc8448ec9UL,
+ 0x896a75c2UL, 0x7978f48eUL, 0x3e6b9958UL, 0x71dd27b9UL,
+ 0x4fb6bee1UL, 0xad17f088UL, 0xac66c920UL, 0x3ab47dceUL,
+ 0x4a1863dfUL, 0x3182e51aUL, 0x33609751UL, 0x7f456253UL,
+ 0x77e0b164UL, 0xae84bb6bUL, 0xa01cfe81UL, 0x2b94f908UL,
+ 0x68587048UL, 0xfd198f45UL, 0x6c8794deUL, 0xf8b7527bUL,
+ 0xd323ab73UL, 0x02e2724bUL, 0x8f57e31fUL, 0xab2a6655UL,
+ 0x2807b2ebUL, 0xc2032fb5UL, 0x7b9a86c5UL, 0x08a5d337UL,
+ 0x87f23028UL, 0xa5b223bfUL, 0x6aba0203UL, 0x825ced16UL,
+ 0x1c2b8acfUL, 0xb492a779UL, 0xf2f0f307UL, 0xe2a14e69UL,
+ 0xf4cd65daUL, 0xbed50605UL, 0x621fd134UL, 0xfe8ac4a6UL,
+ 0x539d342eUL, 0x55a0a2f3UL, 0xe132058aUL, 0xeb75a4f6UL,
+ 0xec390b83UL, 0xefaa4060UL, 0x9f065e71UL, 0x1051bd6eUL,
+ 0x8af93e21UL, 0x063d96ddUL, 0x05aedd3eUL, 0xbd464de6UL,
+ 0x8db59154UL, 0x5d0571c4UL, 0xd46f0406UL, 0x15ff6050UL,
+ 0xfb241998UL, 0xe997d6bdUL, 0x43cc8940UL, 0x9e7767d9UL,
+ 0x42bdb0e8UL, 0x8b880789UL, 0x5b38e719UL, 0xeedb79c8UL,
+ 0x0a47a17cUL, 0x0fe97c42UL, 0x1ec9f884UL, 0x00000000UL,
+ 0x86830980UL, 0xed48322bUL, 0x70ac1e11UL, 0x724e6c5aUL,
+ 0xfffbfd0eUL, 0x38560f85UL, 0xd51e3daeUL, 0x3927362dUL,
+ 0xd9640a0fUL, 0xa621685cUL, 0x54d19b5bUL, 0x2e3a2436UL,
+ 0x67b10c0aUL, 0xe70f9357UL, 0x96d2b4eeUL, 0x919e1b9bUL,
+ 0xc54f80c0UL, 0x20a261dcUL, 0x4b695a77UL, 0x1a161c12UL,
+ 0xba0ae293UL, 0x2ae5c0a0UL, 0xe0433c22UL, 0x171d121bUL,
+ 0x0d0b0e09UL, 0xc7adf28bUL, 0xa8b92db6UL, 0xa9c8141eUL,
+ 0x198557f1UL, 0x074caf75UL, 0xddbbee99UL, 0x60fda37fUL,
+ 0x269ff701UL, 0xf5bc5c72UL, 0x3bc54466UL, 0x7e345bfbUL,
+ 0x29768b43UL, 0xc6dccb23UL, 0xfc68b6edUL, 0xf163b8e4UL,
+ 0xdccad731UL, 0x85104263UL, 0x22401397UL, 0x112084c6UL,
+ 0x247d854aUL, 0x3df8d2bbUL, 0x3211aef9UL, 0xa16dc729UL,
+ 0x2f4b1d9eUL, 0x30f3dcb2UL, 0x52ec0d86UL, 0xe3d077c1UL,
+ 0x166c2bb3UL, 0xb999a970UL, 0x48fa1194UL, 0x642247e9UL,
+ 0x8cc4a8fcUL, 0x3f1aa0f0UL, 0x2cd8567dUL, 0x90ef2233UL,
+ 0x4ec78749UL, 0xd1c1d938UL, 0xa2fe8ccaUL, 0x0b3698d4UL,
+ 0x81cfa6f5UL, 0xde28a57aUL, 0x8e26dab7UL, 0xbfa43fadUL,
+ 0x9de42c3aUL, 0x920d5078UL, 0xcc9b6a5fUL, 0x4662547eUL,
+ 0x13c2f68dUL, 0xb8e890d8UL, 0xf75e2e39UL, 0xaff582c3UL,
+ 0x80be9f5dUL, 0x937c69d0UL, 0x2da96fd5UL, 0x12b3cf25UL,
+ 0x993bc8acUL, 0x7da71018UL, 0x636ee89cUL, 0xbb7bdb3bUL,
+ 0x7809cd26UL, 0x18f46e59UL, 0xb701ec9aUL, 0x9aa8834fUL,
+ 0x6e65e695UL, 0xe67eaaffUL, 0xcf0821bcUL, 0xe8e6ef15UL,
+ 0x9bd9bae7UL, 0x36ce4a6fUL, 0x09d4ea9fUL, 0x7cd629b0UL,
+ 0xb2af31a4UL, 0x23312a3fUL, 0x9430c6a5UL, 0x66c035a2UL,
+ 0xbc37744eUL, 0xcaa6fc82UL, 0xd0b0e090UL, 0xd81533a7UL,
+ 0x984af104UL, 0xdaf741ecUL, 0x500e7fcdUL, 0xf62f1791UL,
+ 0xd68d764dUL, 0xb04d43efUL, 0x4d54ccaaUL, 0x04dfe496UL,
+ 0xb5e39ed1UL, 0x881b4c6aUL, 0x1fb8c12cUL, 0x517f4665UL,
+ 0xea049d5eUL, 0x355d018cUL, 0x7473fa87UL, 0x412efb0bUL,
+ 0x1d5ab367UL, 0xd25292dbUL, 0x5633e910UL, 0x47136dd6UL,
+ 0x618c9ad7UL, 0x0c7a37a1UL, 0x148e59f8UL, 0x3c89eb13UL,
+ 0x27eecea9UL, 0xc935b761UL, 0xe5ede11cUL, 0xb13c7a47UL,
+ 0xdf599cd2UL, 0x733f55f2UL, 0xce791814UL, 0x37bf73c7UL,
+ 0xcdea53f7UL, 0xaa5b5ffdUL, 0x6f14df3dUL, 0xdb867844UL,
+ 0xf381caafUL, 0xc43eb968UL, 0x342c3824UL, 0x405fc2a3UL,
+ 0xc372161dUL, 0x250cbce2UL, 0x498b283cUL, 0x9541ff0dUL,
+ 0x017139a8UL, 0xb3de080cUL, 0xe49cd8b4UL, 0xc1906456UL,
+ 0x84617bcbUL, 0xb670d532UL, 0x5c74486cUL, 0x5742d0b8UL,
+};
+static const ulong32 TD3[256] = {
+ 0xf4a75051UL, 0x4165537eUL, 0x17a4c31aUL, 0x275e963aUL,
+ 0xab6bcb3bUL, 0x9d45f11fUL, 0xfa58abacUL, 0xe303934bUL,
+ 0x30fa5520UL, 0x766df6adUL, 0xcc769188UL, 0x024c25f5UL,
+ 0xe5d7fc4fUL, 0x2acbd7c5UL, 0x35448026UL, 0x62a38fb5UL,
+ 0xb15a49deUL, 0xba1b6725UL, 0xea0e9845UL, 0xfec0e15dUL,
+ 0x2f7502c3UL, 0x4cf01281UL, 0x4697a38dUL, 0xd3f9c66bUL,
+ 0x8f5fe703UL, 0x929c9515UL, 0x6d7aebbfUL, 0x5259da95UL,
+ 0xbe832dd4UL, 0x7421d358UL, 0xe0692949UL, 0xc9c8448eUL,
+ 0xc2896a75UL, 0x8e7978f4UL, 0x583e6b99UL, 0xb971dd27UL,
+ 0xe14fb6beUL, 0x88ad17f0UL, 0x20ac66c9UL, 0xce3ab47dUL,
+ 0xdf4a1863UL, 0x1a3182e5UL, 0x51336097UL, 0x537f4562UL,
+ 0x6477e0b1UL, 0x6bae84bbUL, 0x81a01cfeUL, 0x082b94f9UL,
+ 0x48685870UL, 0x45fd198fUL, 0xde6c8794UL, 0x7bf8b752UL,
+ 0x73d323abUL, 0x4b02e272UL, 0x1f8f57e3UL, 0x55ab2a66UL,
+ 0xeb2807b2UL, 0xb5c2032fUL, 0xc57b9a86UL, 0x3708a5d3UL,
+ 0x2887f230UL, 0xbfa5b223UL, 0x036aba02UL, 0x16825cedUL,
+ 0xcf1c2b8aUL, 0x79b492a7UL, 0x07f2f0f3UL, 0x69e2a14eUL,
+ 0xdaf4cd65UL, 0x05bed506UL, 0x34621fd1UL, 0xa6fe8ac4UL,
+ 0x2e539d34UL, 0xf355a0a2UL, 0x8ae13205UL, 0xf6eb75a4UL,
+ 0x83ec390bUL, 0x60efaa40UL, 0x719f065eUL, 0x6e1051bdUL,
+ 0x218af93eUL, 0xdd063d96UL, 0x3e05aeddUL, 0xe6bd464dUL,
+ 0x548db591UL, 0xc45d0571UL, 0x06d46f04UL, 0x5015ff60UL,
+ 0x98fb2419UL, 0xbde997d6UL, 0x4043cc89UL, 0xd99e7767UL,
+ 0xe842bdb0UL, 0x898b8807UL, 0x195b38e7UL, 0xc8eedb79UL,
+ 0x7c0a47a1UL, 0x420fe97cUL, 0x841ec9f8UL, 0x00000000UL,
+ 0x80868309UL, 0x2bed4832UL, 0x1170ac1eUL, 0x5a724e6cUL,
+ 0x0efffbfdUL, 0x8538560fUL, 0xaed51e3dUL, 0x2d392736UL,
+ 0x0fd9640aUL, 0x5ca62168UL, 0x5b54d19bUL, 0x362e3a24UL,
+ 0x0a67b10cUL, 0x57e70f93UL, 0xee96d2b4UL, 0x9b919e1bUL,
+ 0xc0c54f80UL, 0xdc20a261UL, 0x774b695aUL, 0x121a161cUL,
+ 0x93ba0ae2UL, 0xa02ae5c0UL, 0x22e0433cUL, 0x1b171d12UL,
+ 0x090d0b0eUL, 0x8bc7adf2UL, 0xb6a8b92dUL, 0x1ea9c814UL,
+ 0xf1198557UL, 0x75074cafUL, 0x99ddbbeeUL, 0x7f60fda3UL,
+ 0x01269ff7UL, 0x72f5bc5cUL, 0x663bc544UL, 0xfb7e345bUL,
+ 0x4329768bUL, 0x23c6dccbUL, 0xedfc68b6UL, 0xe4f163b8UL,
+ 0x31dccad7UL, 0x63851042UL, 0x97224013UL, 0xc6112084UL,
+ 0x4a247d85UL, 0xbb3df8d2UL, 0xf93211aeUL, 0x29a16dc7UL,
+ 0x9e2f4b1dUL, 0xb230f3dcUL, 0x8652ec0dUL, 0xc1e3d077UL,
+ 0xb3166c2bUL, 0x70b999a9UL, 0x9448fa11UL, 0xe9642247UL,
+ 0xfc8cc4a8UL, 0xf03f1aa0UL, 0x7d2cd856UL, 0x3390ef22UL,
+ 0x494ec787UL, 0x38d1c1d9UL, 0xcaa2fe8cUL, 0xd40b3698UL,
+ 0xf581cfa6UL, 0x7ade28a5UL, 0xb78e26daUL, 0xadbfa43fUL,
+ 0x3a9de42cUL, 0x78920d50UL, 0x5fcc9b6aUL, 0x7e466254UL,
+ 0x8d13c2f6UL, 0xd8b8e890UL, 0x39f75e2eUL, 0xc3aff582UL,
+ 0x5d80be9fUL, 0xd0937c69UL, 0xd52da96fUL, 0x2512b3cfUL,
+ 0xac993bc8UL, 0x187da710UL, 0x9c636ee8UL, 0x3bbb7bdbUL,
+ 0x267809cdUL, 0x5918f46eUL, 0x9ab701ecUL, 0x4f9aa883UL,
+ 0x956e65e6UL, 0xffe67eaaUL, 0xbccf0821UL, 0x15e8e6efUL,
+ 0xe79bd9baUL, 0x6f36ce4aUL, 0x9f09d4eaUL, 0xb07cd629UL,
+ 0xa4b2af31UL, 0x3f23312aUL, 0xa59430c6UL, 0xa266c035UL,
+ 0x4ebc3774UL, 0x82caa6fcUL, 0x90d0b0e0UL, 0xa7d81533UL,
+ 0x04984af1UL, 0xecdaf741UL, 0xcd500e7fUL, 0x91f62f17UL,
+ 0x4dd68d76UL, 0xefb04d43UL, 0xaa4d54ccUL, 0x9604dfe4UL,
+ 0xd1b5e39eUL, 0x6a881b4cUL, 0x2c1fb8c1UL, 0x65517f46UL,
+ 0x5eea049dUL, 0x8c355d01UL, 0x877473faUL, 0x0b412efbUL,
+ 0x671d5ab3UL, 0xdbd25292UL, 0x105633e9UL, 0xd647136dUL,
+ 0xd7618c9aUL, 0xa10c7a37UL, 0xf8148e59UL, 0x133c89ebUL,
+ 0xa927eeceUL, 0x61c935b7UL, 0x1ce5ede1UL, 0x47b13c7aUL,
+ 0xd2df599cUL, 0xf2733f55UL, 0x14ce7918UL, 0xc737bf73UL,
+ 0xf7cdea53UL, 0xfdaa5b5fUL, 0x3d6f14dfUL, 0x44db8678UL,
+ 0xaff381caUL, 0x68c43eb9UL, 0x24342c38UL, 0xa3405fc2UL,
+ 0x1dc37216UL, 0xe2250cbcUL, 0x3c498b28UL, 0x0d9541ffUL,
+ 0xa8017139UL, 0x0cb3de08UL, 0xb4e49cd8UL, 0x56c19064UL,
+ 0xcb84617bUL, 0x32b670d5UL, 0x6c5c7448UL, 0xb85742d0UL,
+};
+
+static const ulong32 Tks0[] = {
+0x00000000UL, 0x0e090d0bUL, 0x1c121a16UL, 0x121b171dUL, 0x3824342cUL, 0x362d3927UL, 0x24362e3aUL, 0x2a3f2331UL,
+0x70486858UL, 0x7e416553UL, 0x6c5a724eUL, 0x62537f45UL, 0x486c5c74UL, 0x4665517fUL, 0x547e4662UL, 0x5a774b69UL,
+0xe090d0b0UL, 0xee99ddbbUL, 0xfc82caa6UL, 0xf28bc7adUL, 0xd8b4e49cUL, 0xd6bde997UL, 0xc4a6fe8aUL, 0xcaaff381UL,
+0x90d8b8e8UL, 0x9ed1b5e3UL, 0x8ccaa2feUL, 0x82c3aff5UL, 0xa8fc8cc4UL, 0xa6f581cfUL, 0xb4ee96d2UL, 0xbae79bd9UL,
+0xdb3bbb7bUL, 0xd532b670UL, 0xc729a16dUL, 0xc920ac66UL, 0xe31f8f57UL, 0xed16825cUL, 0xff0d9541UL, 0xf104984aUL,
+0xab73d323UL, 0xa57ade28UL, 0xb761c935UL, 0xb968c43eUL, 0x9357e70fUL, 0x9d5eea04UL, 0x8f45fd19UL, 0x814cf012UL,
+0x3bab6bcbUL, 0x35a266c0UL, 0x27b971ddUL, 0x29b07cd6UL, 0x038f5fe7UL, 0x0d8652ecUL, 0x1f9d45f1UL, 0x119448faUL,
+0x4be30393UL, 0x45ea0e98UL, 0x57f11985UL, 0x59f8148eUL, 0x73c737bfUL, 0x7dce3ab4UL, 0x6fd52da9UL, 0x61dc20a2UL,
+0xad766df6UL, 0xa37f60fdUL, 0xb16477e0UL, 0xbf6d7aebUL, 0x955259daUL, 0x9b5b54d1UL, 0x894043ccUL, 0x87494ec7UL,
+0xdd3e05aeUL, 0xd33708a5UL, 0xc12c1fb8UL, 0xcf2512b3UL, 0xe51a3182UL, 0xeb133c89UL, 0xf9082b94UL, 0xf701269fUL,
+0x4de6bd46UL, 0x43efb04dUL, 0x51f4a750UL, 0x5ffdaa5bUL, 0x75c2896aUL, 0x7bcb8461UL, 0x69d0937cUL, 0x67d99e77UL,
+0x3daed51eUL, 0x33a7d815UL, 0x21bccf08UL, 0x2fb5c203UL, 0x058ae132UL, 0x0b83ec39UL, 0x1998fb24UL, 0x1791f62fUL,
+0x764dd68dUL, 0x7844db86UL, 0x6a5fcc9bUL, 0x6456c190UL, 0x4e69e2a1UL, 0x4060efaaUL, 0x527bf8b7UL, 0x5c72f5bcUL,
+0x0605bed5UL, 0x080cb3deUL, 0x1a17a4c3UL, 0x141ea9c8UL, 0x3e218af9UL, 0x302887f2UL, 0x223390efUL, 0x2c3a9de4UL,
+0x96dd063dUL, 0x98d40b36UL, 0x8acf1c2bUL, 0x84c61120UL, 0xaef93211UL, 0xa0f03f1aUL, 0xb2eb2807UL, 0xbce2250cUL,
+0xe6956e65UL, 0xe89c636eUL, 0xfa877473UL, 0xf48e7978UL, 0xdeb15a49UL, 0xd0b85742UL, 0xc2a3405fUL, 0xccaa4d54UL,
+0x41ecdaf7UL, 0x4fe5d7fcUL, 0x5dfec0e1UL, 0x53f7cdeaUL, 0x79c8eedbUL, 0x77c1e3d0UL, 0x65daf4cdUL, 0x6bd3f9c6UL,
+0x31a4b2afUL, 0x3fadbfa4UL, 0x2db6a8b9UL, 0x23bfa5b2UL, 0x09808683UL, 0x07898b88UL, 0x15929c95UL, 0x1b9b919eUL,
+0xa17c0a47UL, 0xaf75074cUL, 0xbd6e1051UL, 0xb3671d5aUL, 0x99583e6bUL, 0x97513360UL, 0x854a247dUL, 0x8b432976UL,
+0xd134621fUL, 0xdf3d6f14UL, 0xcd267809UL, 0xc32f7502UL, 0xe9105633UL, 0xe7195b38UL, 0xf5024c25UL, 0xfb0b412eUL,
+0x9ad7618cUL, 0x94de6c87UL, 0x86c57b9aUL, 0x88cc7691UL, 0xa2f355a0UL, 0xacfa58abUL, 0xbee14fb6UL, 0xb0e842bdUL,
+0xea9f09d4UL, 0xe49604dfUL, 0xf68d13c2UL, 0xf8841ec9UL, 0xd2bb3df8UL, 0xdcb230f3UL, 0xcea927eeUL, 0xc0a02ae5UL,
+0x7a47b13cUL, 0x744ebc37UL, 0x6655ab2aUL, 0x685ca621UL, 0x42638510UL, 0x4c6a881bUL, 0x5e719f06UL, 0x5078920dUL,
+0x0a0fd964UL, 0x0406d46fUL, 0x161dc372UL, 0x1814ce79UL, 0x322bed48UL, 0x3c22e043UL, 0x2e39f75eUL, 0x2030fa55UL,
+0xec9ab701UL, 0xe293ba0aUL, 0xf088ad17UL, 0xfe81a01cUL, 0xd4be832dUL, 0xdab78e26UL, 0xc8ac993bUL, 0xc6a59430UL,
+0x9cd2df59UL, 0x92dbd252UL, 0x80c0c54fUL, 0x8ec9c844UL, 0xa4f6eb75UL, 0xaaffe67eUL, 0xb8e4f163UL, 0xb6edfc68UL,
+0x0c0a67b1UL, 0x02036abaUL, 0x10187da7UL, 0x1e1170acUL, 0x342e539dUL, 0x3a275e96UL, 0x283c498bUL, 0x26354480UL,
+0x7c420fe9UL, 0x724b02e2UL, 0x605015ffUL, 0x6e5918f4UL, 0x44663bc5UL, 0x4a6f36ceUL, 0x587421d3UL, 0x567d2cd8UL,
+0x37a10c7aUL, 0x39a80171UL, 0x2bb3166cUL, 0x25ba1b67UL, 0x0f853856UL, 0x018c355dUL, 0x13972240UL, 0x1d9e2f4bUL,
+0x47e96422UL, 0x49e06929UL, 0x5bfb7e34UL, 0x55f2733fUL, 0x7fcd500eUL, 0x71c45d05UL, 0x63df4a18UL, 0x6dd64713UL,
+0xd731dccaUL, 0xd938d1c1UL, 0xcb23c6dcUL, 0xc52acbd7UL, 0xef15e8e6UL, 0xe11ce5edUL, 0xf307f2f0UL, 0xfd0efffbUL,
+0xa779b492UL, 0xa970b999UL, 0xbb6bae84UL, 0xb562a38fUL, 0x9f5d80beUL, 0x91548db5UL, 0x834f9aa8UL, 0x8d4697a3UL
+};
+
+static const ulong32 Tks1[] = {
+0x00000000UL, 0x0b0e090dUL, 0x161c121aUL, 0x1d121b17UL, 0x2c382434UL, 0x27362d39UL, 0x3a24362eUL, 0x312a3f23UL,
+0x58704868UL, 0x537e4165UL, 0x4e6c5a72UL, 0x4562537fUL, 0x74486c5cUL, 0x7f466551UL, 0x62547e46UL, 0x695a774bUL,
+0xb0e090d0UL, 0xbbee99ddUL, 0xa6fc82caUL, 0xadf28bc7UL, 0x9cd8b4e4UL, 0x97d6bde9UL, 0x8ac4a6feUL, 0x81caaff3UL,
+0xe890d8b8UL, 0xe39ed1b5UL, 0xfe8ccaa2UL, 0xf582c3afUL, 0xc4a8fc8cUL, 0xcfa6f581UL, 0xd2b4ee96UL, 0xd9bae79bUL,
+0x7bdb3bbbUL, 0x70d532b6UL, 0x6dc729a1UL, 0x66c920acUL, 0x57e31f8fUL, 0x5ced1682UL, 0x41ff0d95UL, 0x4af10498UL,
+0x23ab73d3UL, 0x28a57adeUL, 0x35b761c9UL, 0x3eb968c4UL, 0x0f9357e7UL, 0x049d5eeaUL, 0x198f45fdUL, 0x12814cf0UL,
+0xcb3bab6bUL, 0xc035a266UL, 0xdd27b971UL, 0xd629b07cUL, 0xe7038f5fUL, 0xec0d8652UL, 0xf11f9d45UL, 0xfa119448UL,
+0x934be303UL, 0x9845ea0eUL, 0x8557f119UL, 0x8e59f814UL, 0xbf73c737UL, 0xb47dce3aUL, 0xa96fd52dUL, 0xa261dc20UL,
+0xf6ad766dUL, 0xfda37f60UL, 0xe0b16477UL, 0xebbf6d7aUL, 0xda955259UL, 0xd19b5b54UL, 0xcc894043UL, 0xc787494eUL,
+0xaedd3e05UL, 0xa5d33708UL, 0xb8c12c1fUL, 0xb3cf2512UL, 0x82e51a31UL, 0x89eb133cUL, 0x94f9082bUL, 0x9ff70126UL,
+0x464de6bdUL, 0x4d43efb0UL, 0x5051f4a7UL, 0x5b5ffdaaUL, 0x6a75c289UL, 0x617bcb84UL, 0x7c69d093UL, 0x7767d99eUL,
+0x1e3daed5UL, 0x1533a7d8UL, 0x0821bccfUL, 0x032fb5c2UL, 0x32058ae1UL, 0x390b83ecUL, 0x241998fbUL, 0x2f1791f6UL,
+0x8d764dd6UL, 0x867844dbUL, 0x9b6a5fccUL, 0x906456c1UL, 0xa14e69e2UL, 0xaa4060efUL, 0xb7527bf8UL, 0xbc5c72f5UL,
+0xd50605beUL, 0xde080cb3UL, 0xc31a17a4UL, 0xc8141ea9UL, 0xf93e218aUL, 0xf2302887UL, 0xef223390UL, 0xe42c3a9dUL,
+0x3d96dd06UL, 0x3698d40bUL, 0x2b8acf1cUL, 0x2084c611UL, 0x11aef932UL, 0x1aa0f03fUL, 0x07b2eb28UL, 0x0cbce225UL,
+0x65e6956eUL, 0x6ee89c63UL, 0x73fa8774UL, 0x78f48e79UL, 0x49deb15aUL, 0x42d0b857UL, 0x5fc2a340UL, 0x54ccaa4dUL,
+0xf741ecdaUL, 0xfc4fe5d7UL, 0xe15dfec0UL, 0xea53f7cdUL, 0xdb79c8eeUL, 0xd077c1e3UL, 0xcd65daf4UL, 0xc66bd3f9UL,
+0xaf31a4b2UL, 0xa43fadbfUL, 0xb92db6a8UL, 0xb223bfa5UL, 0x83098086UL, 0x8807898bUL, 0x9515929cUL, 0x9e1b9b91UL,
+0x47a17c0aUL, 0x4caf7507UL, 0x51bd6e10UL, 0x5ab3671dUL, 0x6b99583eUL, 0x60975133UL, 0x7d854a24UL, 0x768b4329UL,
+0x1fd13462UL, 0x14df3d6fUL, 0x09cd2678UL, 0x02c32f75UL, 0x33e91056UL, 0x38e7195bUL, 0x25f5024cUL, 0x2efb0b41UL,
+0x8c9ad761UL, 0x8794de6cUL, 0x9a86c57bUL, 0x9188cc76UL, 0xa0a2f355UL, 0xabacfa58UL, 0xb6bee14fUL, 0xbdb0e842UL,
+0xd4ea9f09UL, 0xdfe49604UL, 0xc2f68d13UL, 0xc9f8841eUL, 0xf8d2bb3dUL, 0xf3dcb230UL, 0xeecea927UL, 0xe5c0a02aUL,
+0x3c7a47b1UL, 0x37744ebcUL, 0x2a6655abUL, 0x21685ca6UL, 0x10426385UL, 0x1b4c6a88UL, 0x065e719fUL, 0x0d507892UL,
+0x640a0fd9UL, 0x6f0406d4UL, 0x72161dc3UL, 0x791814ceUL, 0x48322bedUL, 0x433c22e0UL, 0x5e2e39f7UL, 0x552030faUL,
+0x01ec9ab7UL, 0x0ae293baUL, 0x17f088adUL, 0x1cfe81a0UL, 0x2dd4be83UL, 0x26dab78eUL, 0x3bc8ac99UL, 0x30c6a594UL,
+0x599cd2dfUL, 0x5292dbd2UL, 0x4f80c0c5UL, 0x448ec9c8UL, 0x75a4f6ebUL, 0x7eaaffe6UL, 0x63b8e4f1UL, 0x68b6edfcUL,
+0xb10c0a67UL, 0xba02036aUL, 0xa710187dUL, 0xac1e1170UL, 0x9d342e53UL, 0x963a275eUL, 0x8b283c49UL, 0x80263544UL,
+0xe97c420fUL, 0xe2724b02UL, 0xff605015UL, 0xf46e5918UL, 0xc544663bUL, 0xce4a6f36UL, 0xd3587421UL, 0xd8567d2cUL,
+0x7a37a10cUL, 0x7139a801UL, 0x6c2bb316UL, 0x6725ba1bUL, 0x560f8538UL, 0x5d018c35UL, 0x40139722UL, 0x4b1d9e2fUL,
+0x2247e964UL, 0x2949e069UL, 0x345bfb7eUL, 0x3f55f273UL, 0x0e7fcd50UL, 0x0571c45dUL, 0x1863df4aUL, 0x136dd647UL,
+0xcad731dcUL, 0xc1d938d1UL, 0xdccb23c6UL, 0xd7c52acbUL, 0xe6ef15e8UL, 0xede11ce5UL, 0xf0f307f2UL, 0xfbfd0effUL,
+0x92a779b4UL, 0x99a970b9UL, 0x84bb6baeUL, 0x8fb562a3UL, 0xbe9f5d80UL, 0xb591548dUL, 0xa8834f9aUL, 0xa38d4697UL
+};
+
+static const ulong32 Tks2[] = {
+0x00000000UL, 0x0d0b0e09UL, 0x1a161c12UL, 0x171d121bUL, 0x342c3824UL, 0x3927362dUL, 0x2e3a2436UL, 0x23312a3fUL,
+0x68587048UL, 0x65537e41UL, 0x724e6c5aUL, 0x7f456253UL, 0x5c74486cUL, 0x517f4665UL, 0x4662547eUL, 0x4b695a77UL,
+0xd0b0e090UL, 0xddbbee99UL, 0xcaa6fc82UL, 0xc7adf28bUL, 0xe49cd8b4UL, 0xe997d6bdUL, 0xfe8ac4a6UL, 0xf381caafUL,
+0xb8e890d8UL, 0xb5e39ed1UL, 0xa2fe8ccaUL, 0xaff582c3UL, 0x8cc4a8fcUL, 0x81cfa6f5UL, 0x96d2b4eeUL, 0x9bd9bae7UL,
+0xbb7bdb3bUL, 0xb670d532UL, 0xa16dc729UL, 0xac66c920UL, 0x8f57e31fUL, 0x825ced16UL, 0x9541ff0dUL, 0x984af104UL,
+0xd323ab73UL, 0xde28a57aUL, 0xc935b761UL, 0xc43eb968UL, 0xe70f9357UL, 0xea049d5eUL, 0xfd198f45UL, 0xf012814cUL,
+0x6bcb3babUL, 0x66c035a2UL, 0x71dd27b9UL, 0x7cd629b0UL, 0x5fe7038fUL, 0x52ec0d86UL, 0x45f11f9dUL, 0x48fa1194UL,
+0x03934be3UL, 0x0e9845eaUL, 0x198557f1UL, 0x148e59f8UL, 0x37bf73c7UL, 0x3ab47dceUL, 0x2da96fd5UL, 0x20a261dcUL,
+0x6df6ad76UL, 0x60fda37fUL, 0x77e0b164UL, 0x7aebbf6dUL, 0x59da9552UL, 0x54d19b5bUL, 0x43cc8940UL, 0x4ec78749UL,
+0x05aedd3eUL, 0x08a5d337UL, 0x1fb8c12cUL, 0x12b3cf25UL, 0x3182e51aUL, 0x3c89eb13UL, 0x2b94f908UL, 0x269ff701UL,
+0xbd464de6UL, 0xb04d43efUL, 0xa75051f4UL, 0xaa5b5ffdUL, 0x896a75c2UL, 0x84617bcbUL, 0x937c69d0UL, 0x9e7767d9UL,
+0xd51e3daeUL, 0xd81533a7UL, 0xcf0821bcUL, 0xc2032fb5UL, 0xe132058aUL, 0xec390b83UL, 0xfb241998UL, 0xf62f1791UL,
+0xd68d764dUL, 0xdb867844UL, 0xcc9b6a5fUL, 0xc1906456UL, 0xe2a14e69UL, 0xefaa4060UL, 0xf8b7527bUL, 0xf5bc5c72UL,
+0xbed50605UL, 0xb3de080cUL, 0xa4c31a17UL, 0xa9c8141eUL, 0x8af93e21UL, 0x87f23028UL, 0x90ef2233UL, 0x9de42c3aUL,
+0x063d96ddUL, 0x0b3698d4UL, 0x1c2b8acfUL, 0x112084c6UL, 0x3211aef9UL, 0x3f1aa0f0UL, 0x2807b2ebUL, 0x250cbce2UL,
+0x6e65e695UL, 0x636ee89cUL, 0x7473fa87UL, 0x7978f48eUL, 0x5a49deb1UL, 0x5742d0b8UL, 0x405fc2a3UL, 0x4d54ccaaUL,
+0xdaf741ecUL, 0xd7fc4fe5UL, 0xc0e15dfeUL, 0xcdea53f7UL, 0xeedb79c8UL, 0xe3d077c1UL, 0xf4cd65daUL, 0xf9c66bd3UL,
+0xb2af31a4UL, 0xbfa43fadUL, 0xa8b92db6UL, 0xa5b223bfUL, 0x86830980UL, 0x8b880789UL, 0x9c951592UL, 0x919e1b9bUL,
+0x0a47a17cUL, 0x074caf75UL, 0x1051bd6eUL, 0x1d5ab367UL, 0x3e6b9958UL, 0x33609751UL, 0x247d854aUL, 0x29768b43UL,
+0x621fd134UL, 0x6f14df3dUL, 0x7809cd26UL, 0x7502c32fUL, 0x5633e910UL, 0x5b38e719UL, 0x4c25f502UL, 0x412efb0bUL,
+0x618c9ad7UL, 0x6c8794deUL, 0x7b9a86c5UL, 0x769188ccUL, 0x55a0a2f3UL, 0x58abacfaUL, 0x4fb6bee1UL, 0x42bdb0e8UL,
+0x09d4ea9fUL, 0x04dfe496UL, 0x13c2f68dUL, 0x1ec9f884UL, 0x3df8d2bbUL, 0x30f3dcb2UL, 0x27eecea9UL, 0x2ae5c0a0UL,
+0xb13c7a47UL, 0xbc37744eUL, 0xab2a6655UL, 0xa621685cUL, 0x85104263UL, 0x881b4c6aUL, 0x9f065e71UL, 0x920d5078UL,
+0xd9640a0fUL, 0xd46f0406UL, 0xc372161dUL, 0xce791814UL, 0xed48322bUL, 0xe0433c22UL, 0xf75e2e39UL, 0xfa552030UL,
+0xb701ec9aUL, 0xba0ae293UL, 0xad17f088UL, 0xa01cfe81UL, 0x832dd4beUL, 0x8e26dab7UL, 0x993bc8acUL, 0x9430c6a5UL,
+0xdf599cd2UL, 0xd25292dbUL, 0xc54f80c0UL, 0xc8448ec9UL, 0xeb75a4f6UL, 0xe67eaaffUL, 0xf163b8e4UL, 0xfc68b6edUL,
+0x67b10c0aUL, 0x6aba0203UL, 0x7da71018UL, 0x70ac1e11UL, 0x539d342eUL, 0x5e963a27UL, 0x498b283cUL, 0x44802635UL,
+0x0fe97c42UL, 0x02e2724bUL, 0x15ff6050UL, 0x18f46e59UL, 0x3bc54466UL, 0x36ce4a6fUL, 0x21d35874UL, 0x2cd8567dUL,
+0x0c7a37a1UL, 0x017139a8UL, 0x166c2bb3UL, 0x1b6725baUL, 0x38560f85UL, 0x355d018cUL, 0x22401397UL, 0x2f4b1d9eUL,
+0x642247e9UL, 0x692949e0UL, 0x7e345bfbUL, 0x733f55f2UL, 0x500e7fcdUL, 0x5d0571c4UL, 0x4a1863dfUL, 0x47136dd6UL,
+0xdccad731UL, 0xd1c1d938UL, 0xc6dccb23UL, 0xcbd7c52aUL, 0xe8e6ef15UL, 0xe5ede11cUL, 0xf2f0f307UL, 0xfffbfd0eUL,
+0xb492a779UL, 0xb999a970UL, 0xae84bb6bUL, 0xa38fb562UL, 0x80be9f5dUL, 0x8db59154UL, 0x9aa8834fUL, 0x97a38d46UL
+};
+
+static const ulong32 Tks3[] = {
+0x00000000UL, 0x090d0b0eUL, 0x121a161cUL, 0x1b171d12UL, 0x24342c38UL, 0x2d392736UL, 0x362e3a24UL, 0x3f23312aUL,
+0x48685870UL, 0x4165537eUL, 0x5a724e6cUL, 0x537f4562UL, 0x6c5c7448UL, 0x65517f46UL, 0x7e466254UL, 0x774b695aUL,
+0x90d0b0e0UL, 0x99ddbbeeUL, 0x82caa6fcUL, 0x8bc7adf2UL, 0xb4e49cd8UL, 0xbde997d6UL, 0xa6fe8ac4UL, 0xaff381caUL,
+0xd8b8e890UL, 0xd1b5e39eUL, 0xcaa2fe8cUL, 0xc3aff582UL, 0xfc8cc4a8UL, 0xf581cfa6UL, 0xee96d2b4UL, 0xe79bd9baUL,
+0x3bbb7bdbUL, 0x32b670d5UL, 0x29a16dc7UL, 0x20ac66c9UL, 0x1f8f57e3UL, 0x16825cedUL, 0x0d9541ffUL, 0x04984af1UL,
+0x73d323abUL, 0x7ade28a5UL, 0x61c935b7UL, 0x68c43eb9UL, 0x57e70f93UL, 0x5eea049dUL, 0x45fd198fUL, 0x4cf01281UL,
+0xab6bcb3bUL, 0xa266c035UL, 0xb971dd27UL, 0xb07cd629UL, 0x8f5fe703UL, 0x8652ec0dUL, 0x9d45f11fUL, 0x9448fa11UL,
+0xe303934bUL, 0xea0e9845UL, 0xf1198557UL, 0xf8148e59UL, 0xc737bf73UL, 0xce3ab47dUL, 0xd52da96fUL, 0xdc20a261UL,
+0x766df6adUL, 0x7f60fda3UL, 0x6477e0b1UL, 0x6d7aebbfUL, 0x5259da95UL, 0x5b54d19bUL, 0x4043cc89UL, 0x494ec787UL,
+0x3e05aeddUL, 0x3708a5d3UL, 0x2c1fb8c1UL, 0x2512b3cfUL, 0x1a3182e5UL, 0x133c89ebUL, 0x082b94f9UL, 0x01269ff7UL,
+0xe6bd464dUL, 0xefb04d43UL, 0xf4a75051UL, 0xfdaa5b5fUL, 0xc2896a75UL, 0xcb84617bUL, 0xd0937c69UL, 0xd99e7767UL,
+0xaed51e3dUL, 0xa7d81533UL, 0xbccf0821UL, 0xb5c2032fUL, 0x8ae13205UL, 0x83ec390bUL, 0x98fb2419UL, 0x91f62f17UL,
+0x4dd68d76UL, 0x44db8678UL, 0x5fcc9b6aUL, 0x56c19064UL, 0x69e2a14eUL, 0x60efaa40UL, 0x7bf8b752UL, 0x72f5bc5cUL,
+0x05bed506UL, 0x0cb3de08UL, 0x17a4c31aUL, 0x1ea9c814UL, 0x218af93eUL, 0x2887f230UL, 0x3390ef22UL, 0x3a9de42cUL,
+0xdd063d96UL, 0xd40b3698UL, 0xcf1c2b8aUL, 0xc6112084UL, 0xf93211aeUL, 0xf03f1aa0UL, 0xeb2807b2UL, 0xe2250cbcUL,
+0x956e65e6UL, 0x9c636ee8UL, 0x877473faUL, 0x8e7978f4UL, 0xb15a49deUL, 0xb85742d0UL, 0xa3405fc2UL, 0xaa4d54ccUL,
+0xecdaf741UL, 0xe5d7fc4fUL, 0xfec0e15dUL, 0xf7cdea53UL, 0xc8eedb79UL, 0xc1e3d077UL, 0xdaf4cd65UL, 0xd3f9c66bUL,
+0xa4b2af31UL, 0xadbfa43fUL, 0xb6a8b92dUL, 0xbfa5b223UL, 0x80868309UL, 0x898b8807UL, 0x929c9515UL, 0x9b919e1bUL,
+0x7c0a47a1UL, 0x75074cafUL, 0x6e1051bdUL, 0x671d5ab3UL, 0x583e6b99UL, 0x51336097UL, 0x4a247d85UL, 0x4329768bUL,
+0x34621fd1UL, 0x3d6f14dfUL, 0x267809cdUL, 0x2f7502c3UL, 0x105633e9UL, 0x195b38e7UL, 0x024c25f5UL, 0x0b412efbUL,
+0xd7618c9aUL, 0xde6c8794UL, 0xc57b9a86UL, 0xcc769188UL, 0xf355a0a2UL, 0xfa58abacUL, 0xe14fb6beUL, 0xe842bdb0UL,
+0x9f09d4eaUL, 0x9604dfe4UL, 0x8d13c2f6UL, 0x841ec9f8UL, 0xbb3df8d2UL, 0xb230f3dcUL, 0xa927eeceUL, 0xa02ae5c0UL,
+0x47b13c7aUL, 0x4ebc3774UL, 0x55ab2a66UL, 0x5ca62168UL, 0x63851042UL, 0x6a881b4cUL, 0x719f065eUL, 0x78920d50UL,
+0x0fd9640aUL, 0x06d46f04UL, 0x1dc37216UL, 0x14ce7918UL, 0x2bed4832UL, 0x22e0433cUL, 0x39f75e2eUL, 0x30fa5520UL,
+0x9ab701ecUL, 0x93ba0ae2UL, 0x88ad17f0UL, 0x81a01cfeUL, 0xbe832dd4UL, 0xb78e26daUL, 0xac993bc8UL, 0xa59430c6UL,
+0xd2df599cUL, 0xdbd25292UL, 0xc0c54f80UL, 0xc9c8448eUL, 0xf6eb75a4UL, 0xffe67eaaUL, 0xe4f163b8UL, 0xedfc68b6UL,
+0x0a67b10cUL, 0x036aba02UL, 0x187da710UL, 0x1170ac1eUL, 0x2e539d34UL, 0x275e963aUL, 0x3c498b28UL, 0x35448026UL,
+0x420fe97cUL, 0x4b02e272UL, 0x5015ff60UL, 0x5918f46eUL, 0x663bc544UL, 0x6f36ce4aUL, 0x7421d358UL, 0x7d2cd856UL,
+0xa10c7a37UL, 0xa8017139UL, 0xb3166c2bUL, 0xba1b6725UL, 0x8538560fUL, 0x8c355d01UL, 0x97224013UL, 0x9e2f4b1dUL,
+0xe9642247UL, 0xe0692949UL, 0xfb7e345bUL, 0xf2733f55UL, 0xcd500e7fUL, 0xc45d0571UL, 0xdf4a1863UL, 0xd647136dUL,
+0x31dccad7UL, 0x38d1c1d9UL, 0x23c6dccbUL, 0x2acbd7c5UL, 0x15e8e6efUL, 0x1ce5ede1UL, 0x07f2f0f3UL, 0x0efffbfdUL,
+0x79b492a7UL, 0x70b999a9UL, 0x6bae84bbUL, 0x62a38fb5UL, 0x5d80be9fUL, 0x548db591UL, 0x4f9aa883UL, 0x4697a38dUL
+};
+
+#endif /* ENCRYPT_ONLY */
+
+#endif /* SMALL CODE */
+
+static const ulong32 rcon[] = {
+ 0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL,
+ 0x10000000UL, 0x20000000UL, 0x40000000UL, 0x80000000UL,
+ 0x1B000000UL, 0x36000000UL, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+#endif /* __LTC_AES_TAB_C__ */
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/* AES implementation by Tom St Denis
+ *
+ * Derived from the Public Domain source code by
+
+---
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen
+ * @author Antoon Bosselaers
+ * @author Paulo Barreto
+---
+ */
+/**
+ @file aes.c
+ Implementation of AES
+*/
+
+
+
+#ifdef LTC_RIJNDAEL
+
+#ifndef ENCRYPT_ONLY
+
+#define SETUP rijndael_setup
+#define ECB_ENC rijndael_ecb_encrypt
+#define ECB_DEC rijndael_ecb_decrypt
+#define ECB_DONE rijndael_done
+#define ECB_TEST rijndael_test
+#define ECB_KS rijndael_keysize
+
+const struct ltc_cipher_descriptor rijndael_desc =
+{
+ "rijndael",
+ 6,
+ 16, 32, 16, 10,
+ SETUP, ECB_ENC, ECB_DEC, ECB_TEST, ECB_DONE, ECB_KS,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+const struct ltc_cipher_descriptor aes_desc =
+{
+ "aes",
+ 6,
+ 16, 32, 16, 10,
+ SETUP, ECB_ENC, ECB_DEC, ECB_TEST, ECB_DONE, ECB_KS,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+#else
+
+#define SETUP rijndael_enc_setup
+#define ECB_ENC rijndael_enc_ecb_encrypt
+#define ECB_KS rijndael_enc_keysize
+#define ECB_DONE rijndael_enc_done
+
+const struct ltc_cipher_descriptor rijndael_enc_desc =
+{
+ "rijndael",
+ 6,
+ 16, 32, 16, 10,
+ SETUP, ECB_ENC, NULL, NULL, ECB_DONE, ECB_KS,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+const struct ltc_cipher_descriptor aes_enc_desc =
+{
+ "aes",
+ 6,
+ 16, 32, 16, 10,
+ SETUP, ECB_ENC, NULL, NULL, ECB_DONE, ECB_KS,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+#endif
+
+#define __LTC_AES_TAB_C__
+
+
+static ulong32 setup_mix(ulong32 temp)
+{
+ return (Te4_3[byte(temp, 2)]) ^
+ (Te4_2[byte(temp, 1)]) ^
+ (Te4_1[byte(temp, 0)]) ^
+ (Te4_0[byte(temp, 3)]);
+}
+
+#ifndef ENCRYPT_ONLY
+#ifdef LTC_SMALL_CODE
+static ulong32 setup_mix2(ulong32 temp)
+{
+ return Td0(255 & Te4[byte(temp, 3)]) ^
+ Td1(255 & Te4[byte(temp, 2)]) ^
+ Td2(255 & Te4[byte(temp, 1)]) ^
+ Td3(255 & Te4[byte(temp, 0)]);
+}
+#endif
+#endif
+
+ /**
+ Initialize the AES (Rijndael) block cipher
+ @param key The symmetric key you wish to pass
+ @param keylen The key length in bytes
+ @param num_rounds The number of rounds desired (0 for default)
+ @param skey The key in as scheduled by this function.
+ @return CRYPT_OK if successful
+ */
+int SETUP(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey)
+{
+ int i;
+ ulong32 temp, *rk;
+#ifndef ENCRYPT_ONLY
+ ulong32 *rrk;
+#endif
+ LTC_ARGCHK(key != NULL);
+ LTC_ARGCHK(skey != NULL);
+
+ if (keylen != 16 && keylen != 24 && keylen != 32) {
+ return CRYPT_INVALID_KEYSIZE;
+ }
+
+ if (num_rounds != 0 && num_rounds != (10 + ((keylen/8)-2)*2)) {
+ return CRYPT_INVALID_ROUNDS;
+ }
+
+ skey->rijndael.Nr = 10 + ((keylen/8)-2)*2;
+
+ /* setup the forward key */
+ i = 0;
+ rk = skey->rijndael.eK;
+ LOAD32H(rk[0], key );
+ LOAD32H(rk[1], key + 4);
+ LOAD32H(rk[2], key + 8);
+ LOAD32H(rk[3], key + 12);
+ if (keylen == 16) {
+ for (;;) {
+ temp = rk[3];
+ rk[4] = rk[0] ^ setup_mix(temp) ^ rcon[i];
+ rk[5] = rk[1] ^ rk[4];
+ rk[6] = rk[2] ^ rk[5];
+ rk[7] = rk[3] ^ rk[6];
+ if (++i == 10) {
+ break;
+ }
+ rk += 4;
+ }
+ } else if (keylen == 24) {
+ LOAD32H(rk[4], key + 16);
+ LOAD32H(rk[5], key + 20);
+ for (;;) {
+ #ifdef _MSC_VER
+ temp = skey->rijndael.eK[rk - skey->rijndael.eK + 5];
+ #else
+ temp = rk[5];
+ #endif
+ rk[ 6] = rk[ 0] ^ setup_mix(temp) ^ rcon[i];
+ rk[ 7] = rk[ 1] ^ rk[ 6];
+ rk[ 8] = rk[ 2] ^ rk[ 7];
+ rk[ 9] = rk[ 3] ^ rk[ 8];
+ if (++i == 8) {
+ break;
+ }
+ rk[10] = rk[ 4] ^ rk[ 9];
+ rk[11] = rk[ 5] ^ rk[10];
+ rk += 6;
+ }
+ } else if (keylen == 32) {
+ LOAD32H(rk[4], key + 16);
+ LOAD32H(rk[5], key + 20);
+ LOAD32H(rk[6], key + 24);
+ LOAD32H(rk[7], key + 28);
+ for (;;) {
+ #ifdef _MSC_VER
+ temp = skey->rijndael.eK[rk - skey->rijndael.eK + 7];
+ #else
+ temp = rk[7];
+ #endif
+ rk[ 8] = rk[ 0] ^ setup_mix(temp) ^ rcon[i];
+ rk[ 9] = rk[ 1] ^ rk[ 8];
+ rk[10] = rk[ 2] ^ rk[ 9];
+ rk[11] = rk[ 3] ^ rk[10];
+ if (++i == 7) {
+ break;
+ }
+ temp = rk[11];
+ rk[12] = rk[ 4] ^ setup_mix(RORc(temp, 8));
+ rk[13] = rk[ 5] ^ rk[12];
+ rk[14] = rk[ 6] ^ rk[13];
+ rk[15] = rk[ 7] ^ rk[14];
+ rk += 8;
+ }
+ } else {
+ /* this can't happen */
+ /* coverity[dead_error_line] */
+ return CRYPT_ERROR;
+ }
+
+#ifndef ENCRYPT_ONLY
+ /* setup the inverse key now */
+ rk = skey->rijndael.dK;
+ rrk = skey->rijndael.eK + (28 + keylen) - 4;
+
+ /* apply the inverse MixColumn transform to all round keys but the first and the last: */
+ /* copy first */
+ *rk++ = *rrk++;
+ *rk++ = *rrk++;
+ *rk++ = *rrk++;
+ *rk = *rrk;
+ rk -= 3; rrk -= 3;
+
+ for (i = 1; i < skey->rijndael.Nr; i++) {
+ rrk -= 4;
+ rk += 4;
+ #ifdef LTC_SMALL_CODE
+ temp = rrk[0];
+ rk[0] = setup_mix2(temp);
+ temp = rrk[1];
+ rk[1] = setup_mix2(temp);
+ temp = rrk[2];
+ rk[2] = setup_mix2(temp);
+ temp = rrk[3];
+ rk[3] = setup_mix2(temp);
+ #else
+ temp = rrk[0];
+ rk[0] =
+ Tks0[byte(temp, 3)] ^
+ Tks1[byte(temp, 2)] ^
+ Tks2[byte(temp, 1)] ^
+ Tks3[byte(temp, 0)];
+ temp = rrk[1];
+ rk[1] =
+ Tks0[byte(temp, 3)] ^
+ Tks1[byte(temp, 2)] ^
+ Tks2[byte(temp, 1)] ^
+ Tks3[byte(temp, 0)];
+ temp = rrk[2];
+ rk[2] =
+ Tks0[byte(temp, 3)] ^
+ Tks1[byte(temp, 2)] ^
+ Tks2[byte(temp, 1)] ^
+ Tks3[byte(temp, 0)];
+ temp = rrk[3];
+ rk[3] =
+ Tks0[byte(temp, 3)] ^
+ Tks1[byte(temp, 2)] ^
+ Tks2[byte(temp, 1)] ^
+ Tks3[byte(temp, 0)];
+ #endif
+
+ }
+
+ /* copy last */
+ rrk -= 4;
+ rk += 4;
+ *rk++ = *rrk++;
+ *rk++ = *rrk++;
+ *rk++ = *rrk++;
+ *rk = *rrk;
+#endif /* ENCRYPT_ONLY */
+
+ return CRYPT_OK;
+}
+
+/**
+ Encrypts a block of text with AES
+ @param pt The input plaintext (16 bytes)
+ @param ct The output ciphertext (16 bytes)
+ @param skey The key as scheduled
+ @return CRYPT_OK if successful
+*/
+#ifdef LTC_CLEAN_STACK
+static int _rijndael_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey)
+#else
+int ECB_ENC(const unsigned char *pt, unsigned char *ct, symmetric_key *skey)
+#endif
+{
+ ulong32 s0, s1, s2, s3, t0, t1, t2, t3, *rk;
+ int Nr, r;
+
+ LTC_ARGCHK(pt != NULL);
+ LTC_ARGCHK(ct != NULL);
+ LTC_ARGCHK(skey != NULL);
+
+ Nr = skey->rijndael.Nr;
+ rk = skey->rijndael.eK;
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ LOAD32H(s0, pt ); s0 ^= rk[0];
+ LOAD32H(s1, pt + 4); s1 ^= rk[1];
+ LOAD32H(s2, pt + 8); s2 ^= rk[2];
+ LOAD32H(s3, pt + 12); s3 ^= rk[3];
+
+#ifdef LTC_SMALL_CODE
+
+ for (r = 0; ; r++) {
+ rk += 4;
+ t0 =
+ Te0(byte(s0, 3)) ^
+ Te1(byte(s1, 2)) ^
+ Te2(byte(s2, 1)) ^
+ Te3(byte(s3, 0)) ^
+ rk[0];
+ t1 =
+ Te0(byte(s1, 3)) ^
+ Te1(byte(s2, 2)) ^
+ Te2(byte(s3, 1)) ^
+ Te3(byte(s0, 0)) ^
+ rk[1];
+ t2 =
+ Te0(byte(s2, 3)) ^
+ Te1(byte(s3, 2)) ^
+ Te2(byte(s0, 1)) ^
+ Te3(byte(s1, 0)) ^
+ rk[2];
+ t3 =
+ Te0(byte(s3, 3)) ^
+ Te1(byte(s0, 2)) ^
+ Te2(byte(s1, 1)) ^
+ Te3(byte(s2, 0)) ^
+ rk[3];
+ if (r == Nr-2) {
+ break;
+ }
+ s0 = t0; s1 = t1; s2 = t2; s3 = t3;
+ }
+ rk += 4;
+
+#else
+
+ /*
+ * Nr - 1 full rounds:
+ */
+ r = Nr >> 1;
+ for (;;) {
+ t0 =
+ Te0(byte(s0, 3)) ^
+ Te1(byte(s1, 2)) ^
+ Te2(byte(s2, 1)) ^
+ Te3(byte(s3, 0)) ^
+ rk[4];
+ t1 =
+ Te0(byte(s1, 3)) ^
+ Te1(byte(s2, 2)) ^
+ Te2(byte(s3, 1)) ^
+ Te3(byte(s0, 0)) ^
+ rk[5];
+ t2 =
+ Te0(byte(s2, 3)) ^
+ Te1(byte(s3, 2)) ^
+ Te2(byte(s0, 1)) ^
+ Te3(byte(s1, 0)) ^
+ rk[6];
+ t3 =
+ Te0(byte(s3, 3)) ^
+ Te1(byte(s0, 2)) ^
+ Te2(byte(s1, 1)) ^
+ Te3(byte(s2, 0)) ^
+ rk[7];
+
+ rk += 8;
+ if (--r == 0) {
+ break;
+ }
+
+ s0 =
+ Te0(byte(t0, 3)) ^
+ Te1(byte(t1, 2)) ^
+ Te2(byte(t2, 1)) ^
+ Te3(byte(t3, 0)) ^
+ rk[0];
+ s1 =
+ Te0(byte(t1, 3)) ^
+ Te1(byte(t2, 2)) ^
+ Te2(byte(t3, 1)) ^
+ Te3(byte(t0, 0)) ^
+ rk[1];
+ s2 =
+ Te0(byte(t2, 3)) ^
+ Te1(byte(t3, 2)) ^
+ Te2(byte(t0, 1)) ^
+ Te3(byte(t1, 0)) ^
+ rk[2];
+ s3 =
+ Te0(byte(t3, 3)) ^
+ Te1(byte(t0, 2)) ^
+ Te2(byte(t1, 1)) ^
+ Te3(byte(t2, 0)) ^
+ rk[3];
+ }
+
+#endif
+
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 =
+ (Te4_3[byte(t0, 3)]) ^
+ (Te4_2[byte(t1, 2)]) ^
+ (Te4_1[byte(t2, 1)]) ^
+ (Te4_0[byte(t3, 0)]) ^
+ rk[0];
+ STORE32H(s0, ct);
+ s1 =
+ (Te4_3[byte(t1, 3)]) ^
+ (Te4_2[byte(t2, 2)]) ^
+ (Te4_1[byte(t3, 1)]) ^
+ (Te4_0[byte(t0, 0)]) ^
+ rk[1];
+ STORE32H(s1, ct+4);
+ s2 =
+ (Te4_3[byte(t2, 3)]) ^
+ (Te4_2[byte(t3, 2)]) ^
+ (Te4_1[byte(t0, 1)]) ^
+ (Te4_0[byte(t1, 0)]) ^
+ rk[2];
+ STORE32H(s2, ct+8);
+ s3 =
+ (Te4_3[byte(t3, 3)]) ^
+ (Te4_2[byte(t0, 2)]) ^
+ (Te4_1[byte(t1, 1)]) ^
+ (Te4_0[byte(t2, 0)]) ^
+ rk[3];
+ STORE32H(s3, ct+12);
+
+ return CRYPT_OK;
+}
+
+#ifdef LTC_CLEAN_STACK
+int ECB_ENC(const unsigned char *pt, unsigned char *ct, symmetric_key *skey)
+{
+ int err = _rijndael_ecb_encrypt(pt, ct, skey);
+ burn_stack(sizeof(unsigned long)*8 + sizeof(unsigned long*) + sizeof(int)*2);
+ return err;
+}
+#endif
+
+#ifndef ENCRYPT_ONLY
+
+/**
+ Decrypts a block of text with AES
+ @param ct The input ciphertext (16 bytes)
+ @param pt The output plaintext (16 bytes)
+ @param skey The key as scheduled
+ @return CRYPT_OK if successful
+*/
+#ifdef LTC_CLEAN_STACK
+static int _rijndael_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey)
+#else
+int ECB_DEC(const unsigned char *ct, unsigned char *pt, symmetric_key *skey)
+#endif
+{
+ ulong32 s0, s1, s2, s3, t0, t1, t2, t3, *rk;
+ int Nr, r;
+
+ LTC_ARGCHK(pt != NULL);
+ LTC_ARGCHK(ct != NULL);
+ LTC_ARGCHK(skey != NULL);
+
+ Nr = skey->rijndael.Nr;
+ rk = skey->rijndael.dK;
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ LOAD32H(s0, ct ); s0 ^= rk[0];
+ LOAD32H(s1, ct + 4); s1 ^= rk[1];
+ LOAD32H(s2, ct + 8); s2 ^= rk[2];
+ LOAD32H(s3, ct + 12); s3 ^= rk[3];
+
+#ifdef LTC_SMALL_CODE
+ for (r = 0; ; r++) {
+ rk += 4;
+ t0 =
+ Td0(byte(s0, 3)) ^
+ Td1(byte(s3, 2)) ^
+ Td2(byte(s2, 1)) ^
+ Td3(byte(s1, 0)) ^
+ rk[0];
+ t1 =
+ Td0(byte(s1, 3)) ^
+ Td1(byte(s0, 2)) ^
+ Td2(byte(s3, 1)) ^
+ Td3(byte(s2, 0)) ^
+ rk[1];
+ t2 =
+ Td0(byte(s2, 3)) ^
+ Td1(byte(s1, 2)) ^
+ Td2(byte(s0, 1)) ^
+ Td3(byte(s3, 0)) ^
+ rk[2];
+ t3 =
+ Td0(byte(s3, 3)) ^
+ Td1(byte(s2, 2)) ^
+ Td2(byte(s1, 1)) ^
+ Td3(byte(s0, 0)) ^
+ rk[3];
+ if (r == Nr-2) {
+ break;
+ }
+ s0 = t0; s1 = t1; s2 = t2; s3 = t3;
+ }
+ rk += 4;
+
+#else
+
+ /*
+ * Nr - 1 full rounds:
+ */
+ r = Nr >> 1;
+ for (;;) {
+
+ t0 =
+ Td0(byte(s0, 3)) ^
+ Td1(byte(s3, 2)) ^
+ Td2(byte(s2, 1)) ^
+ Td3(byte(s1, 0)) ^
+ rk[4];
+ t1 =
+ Td0(byte(s1, 3)) ^
+ Td1(byte(s0, 2)) ^
+ Td2(byte(s3, 1)) ^
+ Td3(byte(s2, 0)) ^
+ rk[5];
+ t2 =
+ Td0(byte(s2, 3)) ^
+ Td1(byte(s1, 2)) ^
+ Td2(byte(s0, 1)) ^
+ Td3(byte(s3, 0)) ^
+ rk[6];
+ t3 =
+ Td0(byte(s3, 3)) ^
+ Td1(byte(s2, 2)) ^
+ Td2(byte(s1, 1)) ^
+ Td3(byte(s0, 0)) ^
+ rk[7];
+
+ rk += 8;
+ if (--r == 0) {
+ break;
+ }
+
+
+ s0 =
+ Td0(byte(t0, 3)) ^
+ Td1(byte(t3, 2)) ^
+ Td2(byte(t2, 1)) ^
+ Td3(byte(t1, 0)) ^
+ rk[0];
+ s1 =
+ Td0(byte(t1, 3)) ^
+ Td1(byte(t0, 2)) ^
+ Td2(byte(t3, 1)) ^
+ Td3(byte(t2, 0)) ^
+ rk[1];
+ s2 =
+ Td0(byte(t2, 3)) ^
+ Td1(byte(t1, 2)) ^
+ Td2(byte(t0, 1)) ^
+ Td3(byte(t3, 0)) ^
+ rk[2];
+ s3 =
+ Td0(byte(t3, 3)) ^
+ Td1(byte(t2, 2)) ^
+ Td2(byte(t1, 1)) ^
+ Td3(byte(t0, 0)) ^
+ rk[3];
+ }
+#endif
+
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 =
+ (Td4[byte(t0, 3)] & 0xff000000) ^
+ (Td4[byte(t3, 2)] & 0x00ff0000) ^
+ (Td4[byte(t2, 1)] & 0x0000ff00) ^
+ (Td4[byte(t1, 0)] & 0x000000ff) ^
+ rk[0];
+ STORE32H(s0, pt);
+ s1 =
+ (Td4[byte(t1, 3)] & 0xff000000) ^
+ (Td4[byte(t0, 2)] & 0x00ff0000) ^
+ (Td4[byte(t3, 1)] & 0x0000ff00) ^
+ (Td4[byte(t2, 0)] & 0x000000ff) ^
+ rk[1];
+ STORE32H(s1, pt+4);
+ s2 =
+ (Td4[byte(t2, 3)] & 0xff000000) ^
+ (Td4[byte(t1, 2)] & 0x00ff0000) ^
+ (Td4[byte(t0, 1)] & 0x0000ff00) ^
+ (Td4[byte(t3, 0)] & 0x000000ff) ^
+ rk[2];
+ STORE32H(s2, pt+8);
+ s3 =
+ (Td4[byte(t3, 3)] & 0xff000000) ^
+ (Td4[byte(t2, 2)] & 0x00ff0000) ^
+ (Td4[byte(t1, 1)] & 0x0000ff00) ^
+ (Td4[byte(t0, 0)] & 0x000000ff) ^
+ rk[3];
+ STORE32H(s3, pt+12);
+
+ return CRYPT_OK;
+}
+
+
+#ifdef LTC_CLEAN_STACK
+int ECB_DEC(const unsigned char *ct, unsigned char *pt, symmetric_key *skey)
+{
+ int err = _rijndael_ecb_decrypt(ct, pt, skey);
+ burn_stack(sizeof(unsigned long)*8 + sizeof(unsigned long*) + sizeof(int)*2);
+ return err;
+}
+#endif
+
+/**
+ Performs a self-test of the AES block cipher
+ @return CRYPT_OK if functional, CRYPT_NOP if self-test has been disabled
+*/
+int ECB_TEST(void)
+{
+ #ifndef LTC_TEST
+ return CRYPT_NOP;
+ #else
+ int err;
+ static const struct {
+ int keylen;
+ unsigned char key[32], pt[16], ct[16];
+ } tests[] = {
+ { 16,
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+ { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff },
+ { 0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30,
+ 0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a }
+ }, {
+ 24,
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 },
+ { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff },
+ { 0xdd, 0xa9, 0x7c, 0xa4, 0x86, 0x4c, 0xdf, 0xe0,
+ 0x6e, 0xaf, 0x70, 0xa0, 0xec, 0x0d, 0x71, 0x91 }
+ }, {
+ 32,
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f },
+ { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff },
+ { 0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf,
+ 0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89 }
+ }
+ };
+
+ symmetric_key key;
+ unsigned char tmp[2][16];
+ int i, y;
+
+ for (i = 0; i < (int)(sizeof(tests)/sizeof(tests[0])); i++) {
+ zeromem(&key, sizeof(key));
+ if ((err = rijndael_setup(tests[i].key, tests[i].keylen, 0, &key)) != CRYPT_OK) {
+ return err;
+ }
+
+ rijndael_ecb_encrypt(tests[i].pt, tmp[0], &key);
+ rijndael_ecb_decrypt(tmp[0], tmp[1], &key);
+ if (XMEMCMP(tmp[0], tests[i].ct, 16) || XMEMCMP(tmp[1], tests[i].pt, 16)) {
+#if 0
+ printf("\n\nTest %d failed\n", i);
+ if (XMEMCMP(tmp[0], tests[i].ct, 16)) {
+ printf("CT: ");
+ for (i = 0; i < 16; i++) {
+ printf("%02x ", tmp[0][i]);
+ }
+ printf("\n");
+ } else {
+ printf("PT: ");
+ for (i = 0; i < 16; i++) {
+ printf("%02x ", tmp[1][i]);
+ }
+ printf("\n");
+ }
+#endif
+ return CRYPT_FAIL_TESTVECTOR;
+ }
+
+ /* now see if we can encrypt all zero bytes 1000 times, decrypt and come back where we started */
+ for (y = 0; y < 16; y++) tmp[0][y] = 0;
+ for (y = 0; y < 1000; y++) rijndael_ecb_encrypt(tmp[0], tmp[0], &key);
+ for (y = 0; y < 1000; y++) rijndael_ecb_decrypt(tmp[0], tmp[0], &key);
+ for (y = 0; y < 16; y++) if (tmp[0][y] != 0) return CRYPT_FAIL_TESTVECTOR;
+ }
+ return CRYPT_OK;
+ #endif
+}
+
+#endif /* ENCRYPT_ONLY */
+
+
+/** Terminate the context
+ @param skey The scheduled key
+*/
+void ECB_DONE(symmetric_key *skey)
+{
+ //LTC_UNUSED_PARAM(skey);
+}
+
+
+/**
+ Gets suitable key size
+ @param keysize [in/out] The length of the recommended key (in bytes). This function will store the suitable size back in this variable.
+ @return CRYPT_OK if the input key size is acceptable.
+*/
+int ECB_KS(int *keysize)
+{
+ LTC_ARGCHK(keysize != NULL);
+
+ if (*keysize < 16)
+ return CRYPT_INVALID_KEYSIZE;
+ if (*keysize < 24) {
+ *keysize = 16;
+ return CRYPT_OK;
+ } else if (*keysize < 32) {
+ *keysize = 24;
+ return CRYPT_OK;
+ } else {
+ *keysize = 32;
+ return CRYPT_OK;
+ }
+}
+
+#endif
+
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file cbc_decrypt.c
+ CBC implementation, encrypt block, Tom St Denis
+*/
+
+
+#ifdef LTC_CBC_MODE
+
+/**
+ CBC decrypt
+ @param ct Ciphertext
+ @param pt [out] Plaintext
+ @param len The number of bytes to process (must be multiple of block length)
+ @param cbc CBC state
+ @return CRYPT_OK if successful
+*/
+int cbc_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CBC *cbc)
+{
+ int x, err;
+ unsigned char tmp[16];
+#ifdef LTC_FAST
+ LTC_FAST_TYPE tmpy;
+#else
+ unsigned char tmpy;
+#endif
+
+ LTC_ARGCHK(pt != NULL);
+ LTC_ARGCHK(ct != NULL);
+ LTC_ARGCHK(cbc != NULL);
+
+ if ((err = cipher_is_valid(cbc->cipher)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* is blocklen valid? */
+ if (cbc->blocklen < 1 || cbc->blocklen > (int)sizeof(cbc->IV)) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ if (len % cbc->blocklen) {
+ return CRYPT_INVALID_ARG;
+ }
+#ifdef LTC_FAST
+ if (cbc->blocklen % sizeof(LTC_FAST_TYPE)) {
+ return CRYPT_INVALID_ARG;
+ }
+#endif
+
+ if (cipher_descriptor[cbc->cipher].accel_cbc_decrypt != NULL) {
+ return cipher_descriptor[cbc->cipher].accel_cbc_decrypt(ct, pt, len / cbc->blocklen, cbc->IV, &cbc->key);
+ } else {
+ while (len) {
+ /* decrypt */
+ if ((err = cipher_descriptor[cbc->cipher].ecb_decrypt(ct, tmp, &cbc->key)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* xor IV against plaintext */
+ #if defined(LTC_FAST)
+ for (x = 0; x < cbc->blocklen; x += sizeof(LTC_FAST_TYPE)) {
+ tmpy = *((LTC_FAST_TYPE*)((unsigned char *)cbc->IV + x)) ^ *((LTC_FAST_TYPE*)((unsigned char *)tmp + x));
+ *((LTC_FAST_TYPE*)((unsigned char *)cbc->IV + x)) = *((LTC_FAST_TYPE*)((unsigned char *)ct + x));
+ *((LTC_FAST_TYPE*)((unsigned char *)pt + x)) = tmpy;
+ }
+ #else
+ for (x = 0; x < cbc->blocklen; x++) {
+ tmpy = tmp[x] ^ cbc->IV[x];
+ cbc->IV[x] = ct[x];
+ pt[x] = tmpy;
+ }
+ #endif
+
+ ct += cbc->blocklen;
+ pt += cbc->blocklen;
+ len -= cbc->blocklen;
+ }
+ }
+ return CRYPT_OK;
+}
+
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file cbc_done.c
+ CBC implementation, finish chain, Tom St Denis
+*/
+
+#ifdef LTC_CBC_MODE
+
+/** Terminate the chain
+ @param cbc The CBC chain to terminate
+ @return CRYPT_OK on success
+*/
+int cbc_done(symmetric_CBC *cbc)
+{
+ int err;
+ LTC_ARGCHK(cbc != NULL);
+
+ if ((err = cipher_is_valid(cbc->cipher)) != CRYPT_OK) {
+ return err;
+ }
+ cipher_descriptor[cbc->cipher].done(&cbc->key);
+ return CRYPT_OK;
+}
+
+
+
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file cbc_encrypt.c
+ CBC implementation, encrypt block, Tom St Denis
+*/
+
+
+#ifdef LTC_CBC_MODE
+
+/**
+ CBC encrypt
+ @param pt Plaintext
+ @param ct [out] Ciphertext
+ @param len The number of bytes to process (must be multiple of block length)
+ @param cbc CBC state
+ @return CRYPT_OK if successful
+*/
+int cbc_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CBC *cbc)
+{
+ int x, err;
+
+ LTC_ARGCHK(pt != NULL);
+ LTC_ARGCHK(ct != NULL);
+ LTC_ARGCHK(cbc != NULL);
+
+ if ((err = cipher_is_valid(cbc->cipher)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* is blocklen valid? */
+ if (cbc->blocklen < 1 || cbc->blocklen > (int)sizeof(cbc->IV)) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ if (len % cbc->blocklen) {
+ return CRYPT_INVALID_ARG;
+ }
+#ifdef LTC_FAST
+ if (cbc->blocklen % sizeof(LTC_FAST_TYPE)) {
+ return CRYPT_INVALID_ARG;
+ }
+#endif
+
+ if (cipher_descriptor[cbc->cipher].accel_cbc_encrypt != NULL) {
+ return cipher_descriptor[cbc->cipher].accel_cbc_encrypt(pt, ct, len / cbc->blocklen, cbc->IV, &cbc->key);
+ } else {
+ while (len) {
+ /* xor IV against plaintext */
+ #if defined(LTC_FAST)
+ for (x = 0; x < cbc->blocklen; x += sizeof(LTC_FAST_TYPE)) {
+ *((LTC_FAST_TYPE*)((unsigned char *)cbc->IV + x)) ^= *((LTC_FAST_TYPE*)((unsigned char *)pt + x));
+ }
+ #else
+ for (x = 0; x < cbc->blocklen; x++) {
+ cbc->IV[x] ^= pt[x];
+ }
+ #endif
+
+ /* encrypt */
+ if ((err = cipher_descriptor[cbc->cipher].ecb_encrypt(cbc->IV, ct, &cbc->key)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* store IV [ciphertext] for a future block */
+ #if defined(LTC_FAST)
+ for (x = 0; x < cbc->blocklen; x += sizeof(LTC_FAST_TYPE)) {
+ *((LTC_FAST_TYPE*)((unsigned char *)cbc->IV + x)) = *((LTC_FAST_TYPE*)((unsigned char *)ct + x));
+ }
+ #else
+ for (x = 0; x < cbc->blocklen; x++) {
+ cbc->IV[x] = ct[x];
+ }
+ #endif
+
+ ct += cbc->blocklen;
+ pt += cbc->blocklen;
+ len -= cbc->blocklen;
+ }
+ }
+ return CRYPT_OK;
+}
+
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file cbc_getiv.c
+ CBC implementation, get IV, Tom St Denis
+*/
+
+#ifdef LTC_CBC_MODE
+
+/**
+ Get the current initial vector
+ @param IV [out] The destination of the initial vector
+ @param len [in/out] The max size and resulting size of the initial vector
+ @param cbc The CBC state
+ @return CRYPT_OK if successful
+*/
+int cbc_getiv(unsigned char *IV, unsigned long *len, symmetric_CBC *cbc)
+{
+ LTC_ARGCHK(IV != NULL);
+ LTC_ARGCHK(len != NULL);
+ LTC_ARGCHK(cbc != NULL);
+ if ((unsigned long)cbc->blocklen > *len) {
+ *len = cbc->blocklen;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+ XMEMCPY(IV, cbc->IV, cbc->blocklen);
+ *len = cbc->blocklen;
+
+ return CRYPT_OK;
+}
+
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file cbc_setiv.c
+ CBC implementation, set IV, Tom St Denis
+*/
+
+
+#ifdef LTC_CBC_MODE
+
+/**
+ Set an initial vector
+ @param IV The initial vector
+ @param len The length of the vector (in octets)
+ @param cbc The CBC state
+ @return CRYPT_OK if successful
+*/
+int cbc_setiv(const unsigned char *IV, unsigned long len, symmetric_CBC *cbc)
+{
+ LTC_ARGCHK(IV != NULL);
+ LTC_ARGCHK(cbc != NULL);
+ if (len != (unsigned long)cbc->blocklen) {
+ return CRYPT_INVALID_ARG;
+ }
+ XMEMCPY(cbc->IV, IV, len);
+ return CRYPT_OK;
+}
+
+#endif
+
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+/**
+ @file cbc_start.c
+ CBC implementation, start chain, Tom St Denis
+*/
+
+#ifdef LTC_CBC_MODE
+
+/**
+ Initialize a CBC context
+ @param cipher The index of the cipher desired
+ @param IV The initial vector
+ @param key The secret key
+ @param keylen The length of the secret key (octets)
+ @param num_rounds Number of rounds in the cipher desired (0 for default)
+ @param cbc The CBC state to initialize
+ @return CRYPT_OK if successful
+*/
+int cbc_start(int cipher, const unsigned char *IV, const unsigned char *key,
+ int keylen, int num_rounds, symmetric_CBC *cbc)
+{
+ int x, err;
+
+ LTC_ARGCHK(IV != NULL);
+ LTC_ARGCHK(key != NULL);
+ LTC_ARGCHK(cbc != NULL);
+
+ /* bad param? */
+ if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* setup cipher */
+ if ((err = cipher_descriptor[cipher].setup(key, keylen, num_rounds, &cbc->key)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* copy IV */
+ cbc->blocklen = cipher_descriptor[cipher].block_length;
+ cbc->cipher = cipher;
+ for (x = 0; x < cbc->blocklen; x++) {
+ cbc->IV[x] = IV[x];
+ }
+ return CRYPT_OK;
+}
+
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/**
+ @file gcm_add_iv.c
+ GCM implementation, add IV data to the state, by Tom St Denis
+*/
+
+
+#ifdef LTC_GCM_MODE
+
+/**
+ Add IV data to the GCM state
+ @param gcm The GCM state
+ @param IV The initial value data to add
+ @param IVlen The length of the IV
+ @return CRYPT_OK on success
+ */
+int gcm_add_iv(gcm_state *gcm,
+ const unsigned char *IV, unsigned long IVlen)
+{
+ unsigned long x, y;
+ int err;
+
+ LTC_ARGCHK(gcm != NULL);
+ if (IVlen > 0) {
+ LTC_ARGCHK(IV != NULL);
+ }
+
+ /* must be in IV mode */
+ if (gcm->mode != LTC_GCM_MODE_IV) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ if (gcm->buflen >= 16 || gcm->buflen < 0) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) {
+ return err;
+ }
+
+
+ /* trip the ivmode flag */
+ if (IVlen + gcm->buflen > 12) {
+ gcm->ivmode |= 1;
+ }
+
+ x = 0;
+#ifdef LTC_FAST
+ if (gcm->buflen == 0) {
+ for (x = 0; x < (IVlen & ~15); x += 16) {
+ for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) {
+ *((LTC_FAST_TYPE*)(&gcm->X[y])) ^= *((LTC_FAST_TYPE*)(&IV[x + y]));
+ }
+ gcm_mult_h(gcm, gcm->X);
+ gcm->totlen += 128;
+ }
+ IV += x;
+ }
+#endif
+
+ /* start adding IV data to the state */
+ for (; x < IVlen; x++) {
+ gcm->buf[gcm->buflen++] = *IV++;
+
+ if (gcm->buflen == 16) {
+ /* GF mult it */
+ for (y = 0; y < 16; y++) {
+ gcm->X[y] ^= gcm->buf[y];
+ }
+ gcm_mult_h(gcm, gcm->X);
+ gcm->buflen = 0;
+ gcm->totlen += 128;
+ }
+ }
+
+ return CRYPT_OK;
+}
+
+#endif
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_add_iv.c,v $ */
+/* $Revision: 1.9 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/**
+ @file gcm_done.c
+ GCM implementation, Terminate the stream, by Tom St Denis
+*/
+
+
+#ifdef LTC_GCM_MODE
+
+/**
+ Terminate a GCM stream
+ @param gcm The GCM state
+ @param tag [out] The destination for the MAC tag
+ @param taglen [in/out] The length of the MAC tag
+ @return CRYPT_OK on success
+ */
+int gcm_done(gcm_state *gcm,
+ unsigned char *tag, unsigned long *taglen)
+{
+ unsigned long x;
+ int err;
+
+ LTC_ARGCHK(gcm != NULL);
+ LTC_ARGCHK(tag != NULL);
+ LTC_ARGCHK(taglen != NULL);
+
+ if (gcm->buflen > 16 || gcm->buflen < 0) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) {
+ return err;
+ }
+
+
+ if (gcm->mode != LTC_GCM_MODE_TEXT) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* handle remaining ciphertext */
+ if (gcm->buflen) {
+ gcm->pttotlen += gcm->buflen * CONST64(8);
+ gcm_mult_h(gcm, gcm->X);
+ }
+
+ /* length */
+ STORE64H(gcm->totlen, gcm->buf);
+ STORE64H(gcm->pttotlen, gcm->buf+8);
+ for (x = 0; x < 16; x++) {
+ gcm->X[x] ^= gcm->buf[x];
+ }
+ gcm_mult_h(gcm, gcm->X);
+
+ /* encrypt original counter */
+ if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y_0, gcm->buf, &gcm->K)) != CRYPT_OK) {
+ return err;
+ }
+ for (x = 0; x < 16 && x < *taglen; x++) {
+ tag[x] = gcm->buf[x] ^ gcm->X[x];
+ }
+ *taglen = x;
+
+ cipher_descriptor[gcm->cipher].done(&gcm->K);
+
+ return CRYPT_OK;
+}
+
+#endif
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_done.c,v $ */
+/* $Revision: 1.11 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/**
+ @file gcm_init.c
+ GCM implementation, initialize state, by Tom St Denis
+*/
+
+
+#ifdef LTC_GCM_MODE
+
+/**
+ Initialize a GCM state
+ @param gcm The GCM state to initialize
+ @param cipher The index of the cipher to use
+ @param key The secret key
+ @param keylen The length of the secret key
+ @return CRYPT_OK on success
+ */
+int gcm_init(gcm_state *gcm, int cipher,
+ const unsigned char *key, int keylen)
+{
+ int err;
+ unsigned char B[16];
+#ifdef LTC_GCM_TABLES
+ int x, y, z, t;
+#endif
+
+ LTC_ARGCHK(gcm != NULL);
+ LTC_ARGCHK(key != NULL);
+
+#ifdef LTC_FAST
+ if (16 % sizeof(LTC_FAST_TYPE)) {
+ return CRYPT_INVALID_ARG;
+ }
+#endif
+
+ /* is cipher valid? */
+ if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
+ return err;
+ }
+ if (cipher_descriptor[cipher].block_length != 16) {
+ return CRYPT_INVALID_CIPHER;
+ }
+
+ /* schedule key */
+ if ((err = cipher_descriptor[cipher].setup(key, keylen, 0, &gcm->K)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* H = E(0) */
+ zeromem(B, 16);
+ if ((err = cipher_descriptor[cipher].ecb_encrypt(B, gcm->H, &gcm->K)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* setup state */
+ zeromem(gcm->buf, sizeof(gcm->buf));
+ zeromem(gcm->X, sizeof(gcm->X));
+ gcm->cipher = cipher;
+ gcm->mode = LTC_GCM_MODE_IV;
+ gcm->ivmode = 0;
+ gcm->buflen = 0;
+ gcm->totlen = 0;
+ gcm->pttotlen = 0;
+
+#ifdef LTC_GCM_TABLES
+ /* setup tables */
+
+ /* generate the first table as it has no shifting (from which we make the other tables) */
+ zeromem(B, 16);
+ for (y = 0; y < 256; y++) {
+ B[0] = y;
+ gcm_gf_mult(gcm->H, B, &gcm->PC[0][y][0]);
+ }
+
+ /* now generate the rest of the tables based the previous table */
+ for (x = 1; x < 16; x++) {
+ for (y = 0; y < 256; y++) {
+ /* now shift it right by 8 bits */
+ t = gcm->PC[x-1][y][15];
+ for (z = 15; z > 0; z--) {
+ gcm->PC[x][y][z] = gcm->PC[x-1][y][z-1];
+ }
+ gcm->PC[x][y][0] = gcm_shift_table[t<<1];
+ gcm->PC[x][y][1] ^= gcm_shift_table[(t<<1)+1];
+ }
+ }
+
+#endif
+
+ return CRYPT_OK;
+}
+
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_init.c,v $ */
+/* $Revision: 1.20 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/**
+ @file gcm_process.c
+ GCM implementation, process message data, by Tom St Denis
+*/
+
+
+#ifdef LTC_GCM_MODE
+
+/**
+ Process plaintext/ciphertext through GCM
+ @param gcm The GCM state
+ @param pt The plaintext
+ @param ptlen The plaintext length (ciphertext length is the same)
+ @param ct The ciphertext
+ @param direction Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT)
+ @return CRYPT_OK on success
+ */
+int gcm_process(gcm_state *gcm,
+ unsigned char *pt, unsigned long ptlen,
+ unsigned char *ct,
+ int direction)
+{
+ unsigned long x;
+ int y, err;
+ unsigned char b;
+
+ LTC_ARGCHK(gcm != NULL);
+ if (ptlen > 0) {
+ LTC_ARGCHK(pt != NULL);
+ LTC_ARGCHK(ct != NULL);
+ }
+
+ if (gcm->buflen > 16 || gcm->buflen < 0) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* in AAD mode? */
+ if (gcm->mode == LTC_GCM_MODE_AAD) {
+ /* let's process the AAD */
+ if (gcm->buflen) {
+ gcm->totlen += gcm->buflen * CONST64(8);
+ gcm_mult_h(gcm, gcm->X);
+ }
+
+ /* increment counter */
+ for (y = 15; y >= 12; y--) {
+ if (++gcm->Y[y] & 255) { break; }
+ }
+ /* encrypt the counter */
+ if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) {
+ return err;
+ }
+
+ gcm->buflen = 0;
+ gcm->mode = LTC_GCM_MODE_TEXT;
+ }
+
+ if (gcm->mode != LTC_GCM_MODE_TEXT) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ x = 0;
+#ifdef LTC_FAST
+ if (gcm->buflen == 0) {
+ if (direction == GCM_ENCRYPT) {
+ for (x = 0; x < (ptlen & ~15); x += 16) {
+ /* ctr encrypt */
+ for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) {
+ *((LTC_FAST_TYPE*)(&ct[x + y])) = *((LTC_FAST_TYPE*)(&pt[x+y])) ^ *((LTC_FAST_TYPE*)(&gcm->buf[y]));
+ *((LTC_FAST_TYPE*)(&gcm->X[y])) ^= *((LTC_FAST_TYPE*)(&ct[x+y]));
+ }
+ /* GMAC it */
+ gcm->pttotlen += 128;
+ gcm_mult_h(gcm, gcm->X);
+ /* increment counter */
+ for (y = 15; y >= 12; y--) {
+ if (++gcm->Y[y] & 255) { break; }
+ }
+ if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) {
+ return err;
+ }
+ }
+ } else {
+ for (x = 0; x < (ptlen & ~15); x += 16) {
+ /* ctr encrypt */
+ for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) {
+ *((LTC_FAST_TYPE*)(&gcm->X[y])) ^= *((LTC_FAST_TYPE*)(&ct[x+y]));
+ *((LTC_FAST_TYPE*)(&pt[x + y])) = *((LTC_FAST_TYPE*)(&ct[x+y])) ^ *((LTC_FAST_TYPE*)(&gcm->buf[y]));
+ }
+ /* GMAC it */
+ gcm->pttotlen += 128;
+ gcm_mult_h(gcm, gcm->X);
+ /* increment counter */
+ for (y = 15; y >= 12; y--) {
+ if (++gcm->Y[y] & 255) { break; }
+ }
+ if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) {
+ return err;
+ }
+ }
+ }
+ }
+#endif
+
+ /* process text */
+ for (; x < ptlen; x++) {
+ if (gcm->buflen == 16) {
+ gcm->pttotlen += 128;
+ gcm_mult_h(gcm, gcm->X);
+
+ /* increment counter */
+ for (y = 15; y >= 12; y--) {
+ if (++gcm->Y[y] & 255) { break; }
+ }
+ if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) {
+ return err;
+ }
+ gcm->buflen = 0;
+ }
+
+ if (direction == GCM_ENCRYPT) {
+ b = ct[x] = pt[x] ^ gcm->buf[gcm->buflen];
+ } else {
+ b = ct[x];
+ pt[x] = ct[x] ^ gcm->buf[gcm->buflen];
+ }
+ gcm->X[gcm->buflen++] ^= b;
+ }
+
+ return CRYPT_OK;
+}
+
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_process.c,v $ */
+/* $Revision: 1.16 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/**
+ @file gcm_mult_h.c
+ GCM implementation, do the GF mult, by Tom St Denis
+*/
+
+
+#if defined(LTC_GCM_MODE)
+/**
+ GCM multiply by H
+ @param gcm The GCM state which holds the H value
+ @param I The value to multiply H by
+ */
+void gcm_mult_h(gcm_state *gcm, unsigned char *I)
+{
+ unsigned char T[16];
+#ifdef LTC_GCM_TABLES
+ int x, y;
+#ifdef LTC_GCM_TABLES_SSE2
+ asm("movdqa (%0),%%xmm0"::"r"(&gcm->PC[0][I[0]][0]));
+ for (x = 1; x < 16; x++) {
+ asm("pxor (%0),%%xmm0"::"r"(&gcm->PC[x][I[x]][0]));
+ }
+ asm("movdqa %%xmm0,(%0)"::"r"(&T));
+#else
+ XMEMCPY(T, &gcm->PC[0][I[0]][0], 16);
+ for (x = 1; x < 16; x++) {
+#ifdef LTC_FAST
+ for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) {
+ *((LTC_FAST_TYPE *)(T + y)) ^= *((LTC_FAST_TYPE *)(&gcm->PC[x][I[x]][y]));
+ }
+#else
+ for (y = 0; y < 16; y++) {
+ T[y] ^= gcm->PC[x][I[x]][y];
+ }
+#endif /* LTC_FAST */
+ }
+#endif /* LTC_GCM_TABLES_SSE2 */
+#else
+ gcm_gf_mult(gcm->H, I, T);
+#endif
+ XMEMCPY(I, T, 16);
+}
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_mult_h.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/**
+ @file gcm_gf_mult.c
+ GCM implementation, do the GF mult, by Tom St Denis
+*/
+
+
+#if defined(LTC_GCM_TABLES) || defined(LRW_TABLES) || ((defined(LTC_GCM_MODE) || defined(LTC_GCM_MODE)) && defined(LTC_FAST))
+
+/* this is x*2^128 mod p(x) ... the results are 16 bytes each stored in a packed format. Since only the
+ * lower 16 bits are not zero'ed I removed the upper 14 bytes */
+const unsigned char gcm_shift_table[256*2] = {
+0x00, 0x00, 0x01, 0xc2, 0x03, 0x84, 0x02, 0x46, 0x07, 0x08, 0x06, 0xca, 0x04, 0x8c, 0x05, 0x4e,
+0x0e, 0x10, 0x0f, 0xd2, 0x0d, 0x94, 0x0c, 0x56, 0x09, 0x18, 0x08, 0xda, 0x0a, 0x9c, 0x0b, 0x5e,
+0x1c, 0x20, 0x1d, 0xe2, 0x1f, 0xa4, 0x1e, 0x66, 0x1b, 0x28, 0x1a, 0xea, 0x18, 0xac, 0x19, 0x6e,
+0x12, 0x30, 0x13, 0xf2, 0x11, 0xb4, 0x10, 0x76, 0x15, 0x38, 0x14, 0xfa, 0x16, 0xbc, 0x17, 0x7e,
+0x38, 0x40, 0x39, 0x82, 0x3b, 0xc4, 0x3a, 0x06, 0x3f, 0x48, 0x3e, 0x8a, 0x3c, 0xcc, 0x3d, 0x0e,
+0x36, 0x50, 0x37, 0x92, 0x35, 0xd4, 0x34, 0x16, 0x31, 0x58, 0x30, 0x9a, 0x32, 0xdc, 0x33, 0x1e,
+0x24, 0x60, 0x25, 0xa2, 0x27, 0xe4, 0x26, 0x26, 0x23, 0x68, 0x22, 0xaa, 0x20, 0xec, 0x21, 0x2e,
+0x2a, 0x70, 0x2b, 0xb2, 0x29, 0xf4, 0x28, 0x36, 0x2d, 0x78, 0x2c, 0xba, 0x2e, 0xfc, 0x2f, 0x3e,
+0x70, 0x80, 0x71, 0x42, 0x73, 0x04, 0x72, 0xc6, 0x77, 0x88, 0x76, 0x4a, 0x74, 0x0c, 0x75, 0xce,
+0x7e, 0x90, 0x7f, 0x52, 0x7d, 0x14, 0x7c, 0xd6, 0x79, 0x98, 0x78, 0x5a, 0x7a, 0x1c, 0x7b, 0xde,
+0x6c, 0xa0, 0x6d, 0x62, 0x6f, 0x24, 0x6e, 0xe6, 0x6b, 0xa8, 0x6a, 0x6a, 0x68, 0x2c, 0x69, 0xee,
+0x62, 0xb0, 0x63, 0x72, 0x61, 0x34, 0x60, 0xf6, 0x65, 0xb8, 0x64, 0x7a, 0x66, 0x3c, 0x67, 0xfe,
+0x48, 0xc0, 0x49, 0x02, 0x4b, 0x44, 0x4a, 0x86, 0x4f, 0xc8, 0x4e, 0x0a, 0x4c, 0x4c, 0x4d, 0x8e,
+0x46, 0xd0, 0x47, 0x12, 0x45, 0x54, 0x44, 0x96, 0x41, 0xd8, 0x40, 0x1a, 0x42, 0x5c, 0x43, 0x9e,
+0x54, 0xe0, 0x55, 0x22, 0x57, 0x64, 0x56, 0xa6, 0x53, 0xe8, 0x52, 0x2a, 0x50, 0x6c, 0x51, 0xae,
+0x5a, 0xf0, 0x5b, 0x32, 0x59, 0x74, 0x58, 0xb6, 0x5d, 0xf8, 0x5c, 0x3a, 0x5e, 0x7c, 0x5f, 0xbe,
+0xe1, 0x00, 0xe0, 0xc2, 0xe2, 0x84, 0xe3, 0x46, 0xe6, 0x08, 0xe7, 0xca, 0xe5, 0x8c, 0xe4, 0x4e,
+0xef, 0x10, 0xee, 0xd2, 0xec, 0x94, 0xed, 0x56, 0xe8, 0x18, 0xe9, 0xda, 0xeb, 0x9c, 0xea, 0x5e,
+0xfd, 0x20, 0xfc, 0xe2, 0xfe, 0xa4, 0xff, 0x66, 0xfa, 0x28, 0xfb, 0xea, 0xf9, 0xac, 0xf8, 0x6e,
+0xf3, 0x30, 0xf2, 0xf2, 0xf0, 0xb4, 0xf1, 0x76, 0xf4, 0x38, 0xf5, 0xfa, 0xf7, 0xbc, 0xf6, 0x7e,
+0xd9, 0x40, 0xd8, 0x82, 0xda, 0xc4, 0xdb, 0x06, 0xde, 0x48, 0xdf, 0x8a, 0xdd, 0xcc, 0xdc, 0x0e,
+0xd7, 0x50, 0xd6, 0x92, 0xd4, 0xd4, 0xd5, 0x16, 0xd0, 0x58, 0xd1, 0x9a, 0xd3, 0xdc, 0xd2, 0x1e,
+0xc5, 0x60, 0xc4, 0xa2, 0xc6, 0xe4, 0xc7, 0x26, 0xc2, 0x68, 0xc3, 0xaa, 0xc1, 0xec, 0xc0, 0x2e,
+0xcb, 0x70, 0xca, 0xb2, 0xc8, 0xf4, 0xc9, 0x36, 0xcc, 0x78, 0xcd, 0xba, 0xcf, 0xfc, 0xce, 0x3e,
+0x91, 0x80, 0x90, 0x42, 0x92, 0x04, 0x93, 0xc6, 0x96, 0x88, 0x97, 0x4a, 0x95, 0x0c, 0x94, 0xce,
+0x9f, 0x90, 0x9e, 0x52, 0x9c, 0x14, 0x9d, 0xd6, 0x98, 0x98, 0x99, 0x5a, 0x9b, 0x1c, 0x9a, 0xde,
+0x8d, 0xa0, 0x8c, 0x62, 0x8e, 0x24, 0x8f, 0xe6, 0x8a, 0xa8, 0x8b, 0x6a, 0x89, 0x2c, 0x88, 0xee,
+0x83, 0xb0, 0x82, 0x72, 0x80, 0x34, 0x81, 0xf6, 0x84, 0xb8, 0x85, 0x7a, 0x87, 0x3c, 0x86, 0xfe,
+0xa9, 0xc0, 0xa8, 0x02, 0xaa, 0x44, 0xab, 0x86, 0xae, 0xc8, 0xaf, 0x0a, 0xad, 0x4c, 0xac, 0x8e,
+0xa7, 0xd0, 0xa6, 0x12, 0xa4, 0x54, 0xa5, 0x96, 0xa0, 0xd8, 0xa1, 0x1a, 0xa3, 0x5c, 0xa2, 0x9e,
+0xb5, 0xe0, 0xb4, 0x22, 0xb6, 0x64, 0xb7, 0xa6, 0xb2, 0xe8, 0xb3, 0x2a, 0xb1, 0x6c, 0xb0, 0xae,
+0xbb, 0xf0, 0xba, 0x32, 0xb8, 0x74, 0xb9, 0xb6, 0xbc, 0xf8, 0xbd, 0x3a, 0xbf, 0x7c, 0xbe, 0xbe };
+
+#endif
+
+
+#if defined(LTC_GCM_MODE) || defined(LRW_MODE)
+
+#ifndef LTC_FAST
+/* right shift */
+static void gcm_rightshift(unsigned char *a)
+{
+ int x;
+ for (x = 15; x > 0; x--) {
+ a[x] = (a[x]>>1) | ((a[x-1]<<7)&0x80);
+ }
+ a[0] >>= 1;
+}
+
+/* c = b*a */
+static const unsigned char mask[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
+static const unsigned char poly[] = { 0x00, 0xE1 };
+
+
+/**
+ GCM GF multiplier (internal use only) bitserial
+ @param a First value
+ @param b Second value
+ @param c Destination for a * b
+ */
+void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c)
+{
+ unsigned char Z[16], V[16];
+ unsigned char x, y, z;
+
+ zeromem(Z, 16);
+ XMEMCPY(V, a, 16);
+ for (x = 0; x < 128; x++) {
+ if (b[x>>3] & mask[x&7]) {
+ for (y = 0; y < 16; y++) {
+ Z[y] ^= V[y];
+ }
+ }
+ z = V[15] & 0x01;
+ gcm_rightshift(V);
+ V[0] ^= poly[z];
+ }
+ XMEMCPY(c, Z, 16);
+}
+
+#else
+
+/* map normal numbers to "ieee" way ... e.g. bit reversed */
+#define M(x) ( ((x&8)>>3) | ((x&4)>>1) | ((x&2)<<1) | ((x&1)<<3) )
+
+#define BPD (sizeof(LTC_FAST_TYPE) * 8)
+#define WPV (1 + (16 / sizeof(LTC_FAST_TYPE)))
+
+/**
+ GCM GF multiplier (internal use only) word oriented
+ @param a First value
+ @param b Second value
+ @param c Destination for a * b
+ */
+void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c)
+{
+ int i, j, k, u;
+ LTC_FAST_TYPE B[16][WPV], tmp[32 / sizeof(LTC_FAST_TYPE)], pB[16 / sizeof(LTC_FAST_TYPE)], zz, z;
+ unsigned char pTmp[32];
+
+ /* create simple tables */
+ zeromem(B[0], sizeof(B[0]));
+ zeromem(B[M(1)], sizeof(B[M(1)]));
+
+#ifdef ENDIAN_32BITWORD
+ for (i = 0; i < 4; i++) {
+ LOAD32H(B[M(1)][i], a + (i<<2));
+ LOAD32L(pB[i], b + (i<<2));
+ }
+#else
+ for (i = 0; i < 2; i++) {
+ LOAD64H(B[M(1)][i], a + (i<<3));
+ LOAD64L(pB[i], b + (i<<3));
+ }
+#endif
+
+ /* now create 2, 4 and 8 */
+ B[M(2)][0] = B[M(1)][0] >> 1;
+ B[M(4)][0] = B[M(1)][0] >> 2;
+ B[M(8)][0] = B[M(1)][0] >> 3;
+ for (i = 1; i < (int)WPV; i++) {
+ B[M(2)][i] = (B[M(1)][i-1] << (BPD-1)) | (B[M(1)][i] >> 1);
+ B[M(4)][i] = (B[M(1)][i-1] << (BPD-2)) | (B[M(1)][i] >> 2);
+ B[M(8)][i] = (B[M(1)][i-1] << (BPD-3)) | (B[M(1)][i] >> 3);
+ }
+
+ /* now all values with two bits which are 3, 5, 6, 9, 10, 12 */
+ for (i = 0; i < (int)WPV; i++) {
+ B[M(3)][i] = B[M(1)][i] ^ B[M(2)][i];
+ B[M(5)][i] = B[M(1)][i] ^ B[M(4)][i];
+ B[M(6)][i] = B[M(2)][i] ^ B[M(4)][i];
+ B[M(9)][i] = B[M(1)][i] ^ B[M(8)][i];
+ B[M(10)][i] = B[M(2)][i] ^ B[M(8)][i];
+ B[M(12)][i] = B[M(8)][i] ^ B[M(4)][i];
+
+ /* now all 3 bit values and the only 4 bit value: 7, 11, 13, 14, 15 */
+ B[M(7)][i] = B[M(3)][i] ^ B[M(4)][i];
+ B[M(11)][i] = B[M(3)][i] ^ B[M(8)][i];
+ B[M(13)][i] = B[M(1)][i] ^ B[M(12)][i];
+ B[M(14)][i] = B[M(6)][i] ^ B[M(8)][i];
+ B[M(15)][i] = B[M(7)][i] ^ B[M(8)][i];
+ }
+
+ zeromem(tmp, sizeof(tmp));
+
+ /* compute product four bits of each word at a time */
+ /* for each nibble */
+ for (i = (BPD/4)-1; i >= 0; i--) {
+ /* for each word */
+ for (j = 0; j < (int)(WPV-1); j++) {
+ /* grab the 4 bits recall the nibbles are backwards so it's a shift by (i^1)*4 */
+ u = (pB[j] >> ((i^1)<<2)) & 15;
+
+ /* add offset by the word count the table looked up value to the result */
+ for (k = 0; k < (int)WPV; k++) {
+ tmp[k+j] ^= B[u][k];
+ }
+ }
+ /* shift result up by 4 bits */
+ if (i != 0) {
+ for (z = j = 0; j < (int)(32 / sizeof(LTC_FAST_TYPE)); j++) {
+ zz = tmp[j] << (BPD-4);
+ tmp[j] = (tmp[j] >> 4) | z;
+ z = zz;
+ }
+ }
+ }
+
+ /* store product */
+#ifdef ENDIAN_32BITWORD
+ for (i = 0; i < 8; i++) {
+ STORE32H(tmp[i], pTmp + (i<<2));
+ }
+#else
+ for (i = 0; i < 4; i++) {
+ STORE64H(tmp[i], pTmp + (i<<3));
+ }
+#endif
+
+ /* reduce by taking most significant byte and adding the appropriate two byte sequence 16 bytes down */
+ for (i = 31; i >= 16; i--) {
+ pTmp[i-16] ^= gcm_shift_table[((unsigned)pTmp[i]<<1)];
+ pTmp[i-15] ^= gcm_shift_table[((unsigned)pTmp[i]<<1)+1];
+ }
+
+ for (i = 0; i < 16; i++) {
+ c[i] = pTmp[i];
+ }
+
+}
+
+#endif
+
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_gf_mult.c,v $ */
+/* $Revision: 1.25 $ */
+/* $Date: 2007/05/12 14:32:35 $ */
+
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/**
+ @file gcm_add_aad.c
+ GCM implementation, Add AAD data to the stream, by Tom St Denis
+*/
+
+
+#ifdef LTC_GCM_MODE
+
+/**
+ Add AAD to the GCM state
+ @param gcm The GCM state
+ @param adata The additional authentication data to add to the GCM state
+ @param adatalen The length of the AAD data.
+ @return CRYPT_OK on success
+ */
+int gcm_add_aad(gcm_state *gcm,
+ const unsigned char *adata, unsigned long adatalen)
+{
+ unsigned long x;
+ int err;
+#ifdef LTC_FAST
+ unsigned long y;
+#endif
+
+ LTC_ARGCHK(gcm != NULL);
+ if (adatalen > 0) {
+ LTC_ARGCHK(adata != NULL);
+ }
+
+ if (gcm->buflen > 16 || gcm->buflen < 0) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* in IV mode? */
+ if (gcm->mode == LTC_GCM_MODE_IV) {
+ /* let's process the IV */
+ if (gcm->ivmode || gcm->buflen != 12) {
+ for (x = 0; x < (unsigned long)gcm->buflen; x++) {
+ gcm->X[x] ^= gcm->buf[x];
+ }
+ if (gcm->buflen) {
+ gcm->totlen += gcm->buflen * CONST64(8);
+ gcm_mult_h(gcm, gcm->X);
+ }
+
+ /* mix in the length */
+ zeromem(gcm->buf, 8);
+ STORE64H(gcm->totlen, gcm->buf+8);
+ for (x = 0; x < 16; x++) {
+ gcm->X[x] ^= gcm->buf[x];
+ }
+ gcm_mult_h(gcm, gcm->X);
+
+ /* copy counter out */
+ XMEMCPY(gcm->Y, gcm->X, 16);
+ zeromem(gcm->X, 16);
+ } else {
+ XMEMCPY(gcm->Y, gcm->buf, 12);
+ gcm->Y[12] = 0;
+ gcm->Y[13] = 0;
+ gcm->Y[14] = 0;
+ gcm->Y[15] = 1;
+ }
+ XMEMCPY(gcm->Y_0, gcm->Y, 16);
+ zeromem(gcm->buf, 16);
+ gcm->buflen = 0;
+ gcm->totlen = 0;
+ gcm->mode = LTC_GCM_MODE_AAD;
+ }
+
+ if (gcm->mode != LTC_GCM_MODE_AAD || gcm->buflen >= 16) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ x = 0;
+#ifdef LTC_FAST
+ if (gcm->buflen == 0) {
+ for (x = 0; x < (adatalen & ~15); x += 16) {
+ for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) {
+ *((LTC_FAST_TYPE*)(&gcm->X[y])) ^= *((LTC_FAST_TYPE*)(&adata[x + y]));
+ }
+ gcm_mult_h(gcm, gcm->X);
+ gcm->totlen += 128;
+ }
+ adata += x;
+ }
+#endif
+
+
+ /* start adding AAD data to the state */
+ for (; x < adatalen; x++) {
+ gcm->X[gcm->buflen++] ^= *adata++;
+
+ if (gcm->buflen == 16) {
+ /* GF mult it */
+ gcm_mult_h(gcm, gcm->X);
+ gcm->buflen = 0;
+ gcm->totlen += 128;
+ }
+ }
+
+ return CRYPT_OK;
+}
+#endif
+
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/**
+ @file gcm_reset.c
+ GCM implementation, reset a used state so it can accept IV data, by Tom St Denis
+*/
+
+
+#ifdef LTC_GCM_MODE
+
+/**
+ Reset a GCM state to as if you just called gcm_init(). This saves the initialization time.
+ @param gcm The GCM state to reset
+ @return CRYPT_OK on success
+*/
+int gcm_reset(gcm_state *gcm)
+{
+ LTC_ARGCHK(gcm != NULL);
+
+ zeromem(gcm->buf, sizeof(gcm->buf));
+ zeromem(gcm->X, sizeof(gcm->X));
+ gcm->mode = LTC_GCM_MODE_IV;
+ gcm->ivmode = 0;
+ gcm->buflen = 0;
+ gcm->totlen = 0;
+ gcm->pttotlen = 0;
+
+ return CRYPT_OK;
+}
+
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+
+
+/**
+ @file md5.c
+ LTC_MD5 hash function by Tom St Denis
+*/
+
+#ifdef LTC_MD5
+
+const struct ltc_hash_descriptor md5_desc =
+{
+ "md5",
+ 3,
+ 16,
+ 64,
+
+ /* OID */
+ { 1, 2, 840, 113549, 2, 5, },
+ 6,
+
+ &md5_init,
+ &md5_process,
+ &md5_done,
+ &md5_test,
+ NULL
+};
+
+#define F(x,y,z) (z ^ (x & (y ^ z)))
+#define G(x,y,z) (y ^ (z & (y ^ x)))
+#define H(x,y,z) (x^y^z)
+#define I(x,y,z) (y^(x|(~z)))
+
+#ifdef LTC_SMALL_CODE
+
+#define FF(a,b,c,d,M,s,t) \
+ a = (a + F(b,c,d) + M + t); a = ROL(a, s) + b;
+
+#define GG(a,b,c,d,M,s,t) \
+ a = (a + G(b,c,d) + M + t); a = ROL(a, s) + b;
+
+#define HH(a,b,c,d,M,s,t) \
+ a = (a + H(b,c,d) + M + t); a = ROL(a, s) + b;
+
+#define II(a,b,c,d,M,s,t) \
+ a = (a + I(b,c,d) + M + t); a = ROL(a, s) + b;
+
+static const unsigned char Worder[64] = {
+ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
+ 1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12,
+ 5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2,
+ 0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9
+};
+
+static const unsigned char Rorder[64] = {
+ 7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
+ 5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
+ 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
+ 6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
+};
+
+static const ulong32 Korder[64] = {
+0xd76aa478UL, 0xe8c7b756UL, 0x242070dbUL, 0xc1bdceeeUL, 0xf57c0fafUL, 0x4787c62aUL, 0xa8304613UL, 0xfd469501UL,
+0x698098d8UL, 0x8b44f7afUL, 0xffff5bb1UL, 0x895cd7beUL, 0x6b901122UL, 0xfd987193UL, 0xa679438eUL, 0x49b40821UL,
+0xf61e2562UL, 0xc040b340UL, 0x265e5a51UL, 0xe9b6c7aaUL, 0xd62f105dUL, 0x02441453UL, 0xd8a1e681UL, 0xe7d3fbc8UL,
+0x21e1cde6UL, 0xc33707d6UL, 0xf4d50d87UL, 0x455a14edUL, 0xa9e3e905UL, 0xfcefa3f8UL, 0x676f02d9UL, 0x8d2a4c8aUL,
+0xfffa3942UL, 0x8771f681UL, 0x6d9d6122UL, 0xfde5380cUL, 0xa4beea44UL, 0x4bdecfa9UL, 0xf6bb4b60UL, 0xbebfbc70UL,
+0x289b7ec6UL, 0xeaa127faUL, 0xd4ef3085UL, 0x04881d05UL, 0xd9d4d039UL, 0xe6db99e5UL, 0x1fa27cf8UL, 0xc4ac5665UL,
+0xf4292244UL, 0x432aff97UL, 0xab9423a7UL, 0xfc93a039UL, 0x655b59c3UL, 0x8f0ccc92UL, 0xffeff47dUL, 0x85845dd1UL,
+0x6fa87e4fUL, 0xfe2ce6e0UL, 0xa3014314UL, 0x4e0811a1UL, 0xf7537e82UL, 0xbd3af235UL, 0x2ad7d2bbUL, 0xeb86d391UL
+};
+
+#else
+
+#define FF(a,b,c,d,M,s,t) \
+ a = (a + F(b,c,d) + M + t); a = ROLc(a, s) + b;
+
+#define GG(a,b,c,d,M,s,t) \
+ a = (a + G(b,c,d) + M + t); a = ROLc(a, s) + b;
+
+#define HH(a,b,c,d,M,s,t) \
+ a = (a + H(b,c,d) + M + t); a = ROLc(a, s) + b;
+
+#define II(a,b,c,d,M,s,t) \
+ a = (a + I(b,c,d) + M + t); a = ROLc(a, s) + b;
+
+
+#endif
+
+#ifdef LTC_CLEAN_STACK
+static int _md5_compress(hash_state *md, unsigned char *buf)
+#else
+static int md5_compress(hash_state *md, unsigned char *buf)
+#endif
+{
+ ulong32 i, W[16], a, b, c, d;
+#ifdef LTC_SMALL_CODE
+ ulong32 t;
+#endif
+
+ /* copy the state into 512-bits into W[0..15] */
+ for (i = 0; i < 16; i++) {
+ LOAD32L(W[i], buf + (4*i));
+ }
+
+ /* copy state */
+ a = md->md5.state[0];
+ b = md->md5.state[1];
+ c = md->md5.state[2];
+ d = md->md5.state[3];
+
+#ifdef LTC_SMALL_CODE
+ for (i = 0; i < 16; ++i) {
+ FF(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]);
+ t = d; d = c; c = b; b = a; a = t;
+ }
+
+ for (; i < 32; ++i) {
+ GG(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]);
+ t = d; d = c; c = b; b = a; a = t;
+ }
+
+ for (; i < 48; ++i) {
+ HH(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]);
+ t = d; d = c; c = b; b = a; a = t;
+ }
+
+ for (; i < 64; ++i) {
+ II(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]);
+ t = d; d = c; c = b; b = a; a = t;
+ }
+
+#else
+ FF(a,b,c,d,W[0],7,0xd76aa478UL)
+ FF(d,a,b,c,W[1],12,0xe8c7b756UL)
+ FF(c,d,a,b,W[2],17,0x242070dbUL)
+ FF(b,c,d,a,W[3],22,0xc1bdceeeUL)
+ FF(a,b,c,d,W[4],7,0xf57c0fafUL)
+ FF(d,a,b,c,W[5],12,0x4787c62aUL)
+ FF(c,d,a,b,W[6],17,0xa8304613UL)
+ FF(b,c,d,a,W[7],22,0xfd469501UL)
+ FF(a,b,c,d,W[8],7,0x698098d8UL)
+ FF(d,a,b,c,W[9],12,0x8b44f7afUL)
+ FF(c,d,a,b,W[10],17,0xffff5bb1UL)
+ FF(b,c,d,a,W[11],22,0x895cd7beUL)
+ FF(a,b,c,d,W[12],7,0x6b901122UL)
+ FF(d,a,b,c,W[13],12,0xfd987193UL)
+ FF(c,d,a,b,W[14],17,0xa679438eUL)
+ FF(b,c,d,a,W[15],22,0x49b40821UL)
+ GG(a,b,c,d,W[1],5,0xf61e2562UL)
+ GG(d,a,b,c,W[6],9,0xc040b340UL)
+ GG(c,d,a,b,W[11],14,0x265e5a51UL)
+ GG(b,c,d,a,W[0],20,0xe9b6c7aaUL)
+ GG(a,b,c,d,W[5],5,0xd62f105dUL)
+ GG(d,a,b,c,W[10],9,0x02441453UL)
+ GG(c,d,a,b,W[15],14,0xd8a1e681UL)
+ GG(b,c,d,a,W[4],20,0xe7d3fbc8UL)
+ GG(a,b,c,d,W[9],5,0x21e1cde6UL)
+ GG(d,a,b,c,W[14],9,0xc33707d6UL)
+ GG(c,d,a,b,W[3],14,0xf4d50d87UL)
+ GG(b,c,d,a,W[8],20,0x455a14edUL)
+ GG(a,b,c,d,W[13],5,0xa9e3e905UL)
+ GG(d,a,b,c,W[2],9,0xfcefa3f8UL)
+ GG(c,d,a,b,W[7],14,0x676f02d9UL)
+ GG(b,c,d,a,W[12],20,0x8d2a4c8aUL)
+ HH(a,b,c,d,W[5],4,0xfffa3942UL)
+ HH(d,a,b,c,W[8],11,0x8771f681UL)
+ HH(c,d,a,b,W[11],16,0x6d9d6122UL)
+ HH(b,c,d,a,W[14],23,0xfde5380cUL)
+ HH(a,b,c,d,W[1],4,0xa4beea44UL)
+ HH(d,a,b,c,W[4],11,0x4bdecfa9UL)
+ HH(c,d,a,b,W[7],16,0xf6bb4b60UL)
+ HH(b,c,d,a,W[10],23,0xbebfbc70UL)
+ HH(a,b,c,d,W[13],4,0x289b7ec6UL)
+ HH(d,a,b,c,W[0],11,0xeaa127faUL)
+ HH(c,d,a,b,W[3],16,0xd4ef3085UL)
+ HH(b,c,d,a,W[6],23,0x04881d05UL)
+ HH(a,b,c,d,W[9],4,0xd9d4d039UL)
+ HH(d,a,b,c,W[12],11,0xe6db99e5UL)
+ HH(c,d,a,b,W[15],16,0x1fa27cf8UL)
+ HH(b,c,d,a,W[2],23,0xc4ac5665UL)
+ II(a,b,c,d,W[0],6,0xf4292244UL)
+ II(d,a,b,c,W[7],10,0x432aff97UL)
+ II(c,d,a,b,W[14],15,0xab9423a7UL)
+ II(b,c,d,a,W[5],21,0xfc93a039UL)
+ II(a,b,c,d,W[12],6,0x655b59c3UL)
+ II(d,a,b,c,W[3],10,0x8f0ccc92UL)
+ II(c,d,a,b,W[10],15,0xffeff47dUL)
+ II(b,c,d,a,W[1],21,0x85845dd1UL)
+ II(a,b,c,d,W[8],6,0x6fa87e4fUL)
+ II(d,a,b,c,W[15],10,0xfe2ce6e0UL)
+ II(c,d,a,b,W[6],15,0xa3014314UL)
+ II(b,c,d,a,W[13],21,0x4e0811a1UL)
+ II(a,b,c,d,W[4],6,0xf7537e82UL)
+ II(d,a,b,c,W[11],10,0xbd3af235UL)
+ II(c,d,a,b,W[2],15,0x2ad7d2bbUL)
+ II(b,c,d,a,W[9],21,0xeb86d391UL)
+#endif
+
+ md->md5.state[0] = md->md5.state[0] + a;
+ md->md5.state[1] = md->md5.state[1] + b;
+ md->md5.state[2] = md->md5.state[2] + c;
+ md->md5.state[3] = md->md5.state[3] + d;
+
+ return CRYPT_OK;
+}
+
+#ifdef LTC_CLEAN_STACK
+static int md5_compress(hash_state *md, unsigned char *buf)
+{
+ int err;
+ err = _md5_compress(md, buf);
+ burn_stack(sizeof(ulong32) * 21);
+ return err;
+}
+#endif
+
+/**
+ Initialize the hash state
+ @param md The hash state you wish to initialize
+ @return CRYPT_OK if successful
+*/
+int md5_init(hash_state * md)
+{
+ LTC_ARGCHK(md != NULL);
+ md->md5.state[0] = 0x67452301UL;
+ md->md5.state[1] = 0xefcdab89UL;
+ md->md5.state[2] = 0x98badcfeUL;
+ md->md5.state[3] = 0x10325476UL;
+ md->md5.curlen = 0;
+ md->md5.length = 0;
+ return CRYPT_OK;
+}
+
+/**
+ Process a block of memory though the hash
+ @param md The hash state
+ @param in The data to hash
+ @param inlen The length of the data (octets)
+ @return CRYPT_OK if successful
+*/
+HASH_PROCESS(md5_process, md5_compress, md5, 64)
+
+/**
+ Terminate the hash to get the digest
+ @param md The hash state
+ @param out [out] The destination of the hash (16 bytes)
+ @return CRYPT_OK if successful
+*/
+int md5_done(hash_state * md, unsigned char *out)
+{
+ int i;
+
+ LTC_ARGCHK(md != NULL);
+ LTC_ARGCHK(out != NULL);
+
+ if (md->md5.curlen >= sizeof(md->md5.buf)) {
+ return CRYPT_INVALID_ARG;
+ }
+
+
+ /* increase the length of the message */
+ md->md5.length += md->md5.curlen * 8;
+
+ /* append the '1' bit */
+ md->md5.buf[md->md5.curlen++] = (unsigned char)0x80;
+
+ /* if the length is currently above 56 bytes we append zeros
+ * then compress. Then we can fall back to padding zeros and length
+ * encoding like normal.
+ */
+ if (md->md5.curlen > 56) {
+ while (md->md5.curlen < 64) {
+ md->md5.buf[md->md5.curlen++] = (unsigned char)0;
+ }
+ md5_compress(md, md->md5.buf);
+ md->md5.curlen = 0;
+ }
+
+ /* pad upto 56 bytes of zeroes */
+ while (md->md5.curlen < 56) {
+ md->md5.buf[md->md5.curlen++] = (unsigned char)0;
+ }
+
+ /* store length */
+ STORE64L(md->md5.length, md->md5.buf+56);
+ md5_compress(md, md->md5.buf);
+
+ /* copy output */
+ for (i = 0; i < 4; i++) {
+ STORE32L(md->md5.state[i], out+(4*i));
+ }
+#ifdef LTC_CLEAN_STACK
+ zeromem(md, sizeof(hash_state));
+#endif
+ return CRYPT_OK;
+}
+
+/**
+ Self-test the hash
+ @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
+*/
+int md5_test(void)
+{
+ #ifndef LTC_TEST
+ return CRYPT_NOP;
+ #else
+ static const struct {
+ char *msg;
+ unsigned char hash[16];
+ } tests[] = {
+ { "",
+ { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
+ 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e } },
+ { "a",
+ {0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8,
+ 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 } },
+ { "abc",
+ { 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0,
+ 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 } },
+ { "message digest",
+ { 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d,
+ 0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 } },
+ { "abcdefghijklmnopqrstuvwxyz",
+ { 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00,
+ 0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b } },
+ { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ { 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5,
+ 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f } },
+ { "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ { 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55,
+ 0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a } },
+ { NULL, { 0 } }
+ };
+
+ int i;
+ unsigned char tmp[16];
+ hash_state md;
+
+ for (i = 0; tests[i].msg != NULL; i++) {
+ md5_init(&md);
+ md5_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg));
+ md5_done(&md, tmp);
+ if (XMEMCMP(tmp, tests[i].hash, 16) != 0) {
+ return CRYPT_FAIL_TESTVECTOR;
+ }
+ }
+ return CRYPT_OK;
+ #endif
+}
+
+#endif
+
+/* $Source$ */
+/* $Revision$ */
+/* $Date$ */
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/**
+ @file ctr_encrypt.c
+ CTR implementation, encrypt data, Tom St Denis
+*/
+
+
+#ifdef LTC_CTR_MODE
+
+/**
+ CTR encrypt
+ @param pt Plaintext
+ @param ct [out] Ciphertext
+ @param len Length of plaintext (octets)
+ @param ctr CTR state
+ @return CRYPT_OK if successful
+*/
+int ctr_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CTR *ctr)
+{
+ int x, err;
+
+ LTC_ARGCHK(pt != NULL);
+ LTC_ARGCHK(ct != NULL);
+ LTC_ARGCHK(ctr != NULL);
+
+ if ((err = cipher_is_valid(ctr->cipher)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* is blocklen/padlen valid? */
+ if (ctr->blocklen < 1 || ctr->blocklen > (int)sizeof(ctr->ctr) ||
+ ctr->padlen < 0 || ctr->padlen > (int)sizeof(ctr->pad)) {
+ return CRYPT_INVALID_ARG;
+ }
+
+#ifdef LTC_FAST
+ if (ctr->blocklen % sizeof(LTC_FAST_TYPE)) {
+ return CRYPT_INVALID_ARG;
+ }
+#endif
+
+ /* handle acceleration only if pad is empty, accelerator is present and length is >= a block size */
+ if ((ctr->padlen == ctr->blocklen) && cipher_descriptor[ctr->cipher].accel_ctr_encrypt != NULL && (len >= (unsigned long)ctr->blocklen)) {
+ if ((err = cipher_descriptor[ctr->cipher].accel_ctr_encrypt(pt, ct, len/ctr->blocklen, ctr->ctr, ctr->mode, &ctr->key)) != CRYPT_OK) {
+ return err;
+ }
+ len %= ctr->blocklen;
+ }
+
+ while (len) {
+ /* is the pad empty? */
+ if (ctr->padlen == ctr->blocklen) {
+ /* increment counter */
+ if (ctr->mode == CTR_COUNTER_LITTLE_ENDIAN) {
+ /* little-endian */
+ for (x = 0; x < ctr->ctrlen; x++) {
+ ctr->ctr[x] = (ctr->ctr[x] + (unsigned char)1) & (unsigned char)255;
+ if (ctr->ctr[x] != (unsigned char)0) {
+ break;
+ }
+ }
+ } else {
+ /* big-endian */
+ for (x = ctr->blocklen-1; x >= ctr->ctrlen; x--) {
+ ctr->ctr[x] = (ctr->ctr[x] + (unsigned char)1) & (unsigned char)255;
+ if (ctr->ctr[x] != (unsigned char)0) {
+ break;
+ }
+ }
+ }
+
+ /* encrypt it */
+ if ((err = cipher_descriptor[ctr->cipher].ecb_encrypt(ctr->ctr, ctr->pad, &ctr->key)) != CRYPT_OK) {
+ return err;
+ }
+ ctr->padlen = 0;
+ }
+#ifdef LTC_FAST
+ if (ctr->padlen == 0 && len >= (unsigned long)ctr->blocklen) {
+ for (x = 0; x < ctr->blocklen; x += sizeof(LTC_FAST_TYPE)) {
+ *((LTC_FAST_TYPE*)((unsigned char *)ct + x)) = *((LTC_FAST_TYPE*)((unsigned char *)pt + x)) ^
+ *((LTC_FAST_TYPE*)((unsigned char *)ctr->pad + x));
+ }
+ pt += ctr->blocklen;
+ ct += ctr->blocklen;
+ len -= ctr->blocklen;
+ ctr->padlen = ctr->blocklen;
+ continue;
+ }
+#endif
+ *ct++ = *pt++ ^ ctr->pad[ctr->padlen++];
+ --len;
+ }
+ return CRYPT_OK;
+}
+
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_encrypt.c,v $ */
+/* $Revision: 1.22 $ */
+/* $Date: 2007/02/22 20:26:05 $ */
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/**
+ @file ctr_done.c
+ CTR implementation, finish chain, Tom St Denis
+*/
+
+#ifdef LTC_CTR_MODE
+
+/** Terminate the chain
+ @param ctr The CTR chain to terminate
+ @return CRYPT_OK on success
+*/
+int ctr_done(symmetric_CTR *ctr)
+{
+ int err;
+ LTC_ARGCHK(ctr != NULL);
+
+ if ((err = cipher_is_valid(ctr->cipher)) != CRYPT_OK) {
+ return err;
+ }
+ cipher_descriptor[ctr->cipher].done(&ctr->key);
+ return CRYPT_OK;
+}
+
+
+
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_done.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/**
+ @file ctr_decrypt.c
+ CTR implementation, decrypt data, Tom St Denis
+*/
+
+#ifdef LTC_CTR_MODE
+
+/**
+ CTR decrypt
+ @param ct Ciphertext
+ @param pt [out] Plaintext
+ @param len Length of ciphertext (octets)
+ @param ctr CTR state
+ @return CRYPT_OK if successful
+*/
+int ctr_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CTR *ctr)
+{
+ LTC_ARGCHK(pt != NULL);
+ LTC_ARGCHK(ct != NULL);
+ LTC_ARGCHK(ctr != NULL);
+
+ return ctr_encrypt(ct, pt, len, ctr);
+}
+
+#endif
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_decrypt.c,v $ */
+/* $Revision: 1.6 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/**
+ @file ctr_start.c
+ CTR implementation, start chain, Tom St Denis
+*/
+
+
+#ifdef LTC_CTR_MODE
+
+/**
+ Initialize a CTR context
+ @param cipher The index of the cipher desired
+ @param IV The initial vector
+ @param key The secret key
+ @param keylen The length of the secret key (octets)
+ @param num_rounds Number of rounds in the cipher desired (0 for default)
+ @param ctr_mode The counter mode (CTR_COUNTER_LITTLE_ENDIAN or CTR_COUNTER_BIG_ENDIAN)
+ @param ctr The CTR state to initialize
+ @return CRYPT_OK if successful
+*/
+int ctr_start( int cipher,
+ const unsigned char *IV,
+ const unsigned char *key, int keylen,
+ int num_rounds, int ctr_mode,
+ symmetric_CTR *ctr)
+{
+ int x, err;
+
+ LTC_ARGCHK(IV != NULL);
+ LTC_ARGCHK(key != NULL);
+ LTC_ARGCHK(ctr != NULL);
+
+ /* bad param? */
+ if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* ctrlen == counter width */
+ ctr->ctrlen = (ctr_mode & 255) ? (ctr_mode & 255) : cipher_descriptor[cipher].block_length;
+ if (ctr->ctrlen > cipher_descriptor[cipher].block_length) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ if ((ctr_mode & 0x1000) == CTR_COUNTER_BIG_ENDIAN) {
+ ctr->ctrlen = cipher_descriptor[cipher].block_length - ctr->ctrlen;
+ }
+
+ /* setup cipher */
+ if ((err = cipher_descriptor[cipher].setup(key, keylen, num_rounds, &ctr->key)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* copy ctr */
+ ctr->blocklen = cipher_descriptor[cipher].block_length;
+ ctr->cipher = cipher;
+ ctr->padlen = 0;
+ ctr->mode = ctr_mode & 0x1000;
+ for (x = 0; x < ctr->blocklen; x++) {
+ ctr->ctr[x] = IV[x];
+ }
+
+ if (ctr_mode & LTC_CTR_RFC3686) {
+ /* increment the IV as per RFC 3686 */
+ if (ctr->mode == CTR_COUNTER_LITTLE_ENDIAN) {
+ /* little-endian */
+ for (x = 0; x < ctr->ctrlen; x++) {
+ ctr->ctr[x] = (ctr->ctr[x] + (unsigned char)1) & (unsigned char)255;
+ if (ctr->ctr[x] != (unsigned char)0) {
+ break;
+ }
+ }
+ } else {
+ /* big-endian */
+ for (x = ctr->blocklen-1; x >= ctr->ctrlen; x--) {
+ ctr->ctr[x] = (ctr->ctr[x] + (unsigned char)1) & (unsigned char)255;
+ if (ctr->ctr[x] != (unsigned char)0) {
+ break;
+ }
+ }
+ }
+ }
+
+ return cipher_descriptor[ctr->cipher].ecb_encrypt(ctr->ctr, ctr->pad, &ctr->key);
+}
+
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_start.c,v $ */
+/* $Revision: 1.15 $ */
+/* $Date: 2007/02/23 14:18:37 $ */
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/**
+ @file ctr_setiv.c
+ CTR implementation, set IV, Tom St Denis
+*/
+
+#ifdef LTC_CTR_MODE
+
+/**
+ Set an initial vector
+ @param IV The initial vector
+ @param len The length of the vector (in octets)
+ @param ctr The CTR state
+ @return CRYPT_OK if successful
+*/
+int ctr_setiv(const unsigned char *IV, unsigned long len, symmetric_CTR *ctr)
+{
+ int err;
+
+ LTC_ARGCHK(IV != NULL);
+ LTC_ARGCHK(ctr != NULL);
+
+ /* bad param? */
+ if ((err = cipher_is_valid(ctr->cipher)) != CRYPT_OK) {
+ return err;
+ }
+
+ if (len != (unsigned long)ctr->blocklen) {
+ return CRYPT_INVALID_ARG;
+ }
+
+ /* set IV */
+ XMEMCPY(ctr->ctr, IV, len);
+
+ /* force next block */
+ ctr->padlen = 0;
+ return cipher_descriptor[ctr->cipher].ecb_encrypt(IV, ctr->pad, &ctr->key);
+}
+
+#endif
+
+
+/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_setiv.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+ */
+
+/**
+ @file ctr_getiv.c
+ CTR implementation, get IV, Tom St Denis
+*/
+
+#ifdef LTC_CTR_MODE
+
+/**
+ Get the current initial vector
+ @param IV [out] The destination of the initial vector
+ @param len [in/out] The max size and resulting size of the initial vector
+ @param ctr The CTR state
+ @return CRYPT_OK if successful
+*/
+int ctr_getiv(unsigned char *IV, unsigned long *len, symmetric_CTR *ctr)
+{
+ LTC_ARGCHK(IV != NULL);
+ LTC_ARGCHK(len != NULL);
+ LTC_ARGCHK(ctr != NULL);
+ if ((unsigned long)ctr->blocklen > *len) {
+ *len = ctr->blocklen;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+ XMEMCPY(IV, ctr->ctr, ctr->blocklen);
+ *len = ctr->blocklen;
+
+ return CRYPT_OK;
+}
+
+#endif
+
+/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_getiv.c,v $ */
+/* $Revision: 1.7 $ */
+/* $Date: 2006/12/28 01:27:24 $ */
diff --git a/src/tlse/tlse.c b/src/tlse/tlse.c
new file mode 100644
index 0000000..957053d
--- /dev/null
+++ b/src/tlse/tlse.c
@@ -0,0 +1,12374 @@
+/********************************************************************************
+ Copyright (c) 2016-2024, Eduard Suica
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this
+ list of conditions and the following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************/
+#ifndef TLSE_C
+#define TLSE_C
+
+#include
+#include
+#include
+#include
+#include
+#ifdef _WIN32
+#ifdef SSL_COMPATIBLE_INTERFACE
+#include
+#endif
+#include
+#include
+#ifndef strcasecmp
+ #define strcasecmp stricmp
+#endif
+#else
+// hton* and ntoh* functions
+#include
+#include
+#include
+#endif
+
+#ifdef TLS_AMALGAMATION
+#ifdef I
+#pragma push_macro("I")
+#define TLS_I_MACRO
+#undef I
+#endif
+#include "libtomcrypt.c"
+#ifdef TLS_I_MACRO
+#pragma pop_macro("I")
+#undef TLS_I_MACRO
+#endif
+#else
+#include
+#endif
+
+#if (CRYPT <= 0x0117)
+ #define LTC_PKCS_1_EMSA LTC_LTC_PKCS_1_EMSA
+ #define LTC_PKCS_1_V1_5 LTC_LTC_PKCS_1_V1_5
+ #define LTC_PKCS_1_PSS LTC_LTC_PKCS_1_PSS
+#endif
+
+#ifdef WITH_KTLS
+ #include
+ #include
+ #include
+ // should get /usr/include/linux/tls.h (linux headers)
+ // rename it to ktls.h and add it to your project
+ #include "ktls.h"
+ // or just include tls.h instead of ktls.h
+ // #include "linux/tls.h"
+#endif
+
+#include "tlse.h"
+#ifdef TLS_CURVE25519
+ #include "curve25519.c"
+#endif
+// using ChaCha20 implementation by D. J. Bernstein
+
+#ifndef TLS_FORWARD_SECRECY
+#undef TLS_ECDSA_SUPPORTED
+#endif
+
+#ifndef TLS_ECDSA_SUPPORTED
+// disable client ECDSA if not supported
+#undef TLS_CLIENT_ECDSA
+#endif
+
+#define TLS_DH_DEFAULT_P "87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597"
+#define TLS_DH_DEFAULT_G "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659"
+#define TLS_DHE_KEY_SIZE 2048
+
+// you should never use weak DH groups (1024 bits)
+// but if you have old devices (like grandstream ip phones)
+// that can't handle 2048bit DHE, uncomment next lines
+// and define TLS_WEAK_DH_LEGACY_DEVICES
+// #ifdef TLS_WEAK_DH_LEGACY_DEVICES
+// #define TLS_DH_DEFAULT_P "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C69A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C013ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD7098488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708DF1FB2BC2E4A4371"
+// #define TLS_DH_DEFAULT_G "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507FD6406CFF14266D31266FEA1E5C41564B777E690F5504F213160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28AD662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24855E6EEB22B3B2E5"
+// #define TLS_DHE_KEY_SIZE 1024
+// #endif
+
+#ifndef TLS_MALLOC
+ #define TLS_MALLOC(size) malloc(size)
+#endif
+#ifndef TLS_REALLOC
+ #define TLS_REALLOC(ptr, size) realloc(ptr, size)
+#endif
+#ifndef TLS_FREE
+ #define TLS_FREE(ptr) if (ptr) free(ptr)
+#endif
+
+#define TLS_ERROR(err, statement) if (err) statement;
+
+#ifdef DEBUG
+#define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__)
+#define DEBUG_DUMP_HEX(buf, len) {if (buf) { int _i_; for (_i_ = 0; _i_ < len; _i_++) { DEBUG_PRINT("%02X ", (unsigned int)(buf)[_i_]); } } else { fprintf(stderr, "(null)"); } }
+#define DEBUG_INDEX(fields) print_index(fields)
+#define DEBUG_DUMP(buf, length) fwrite(buf, 1, length, stderr);
+#define DEBUG_DUMP_HEX_LABEL(title, buf, len) {fprintf(stderr, "%s (%i): ", title, (int)len); DEBUG_DUMP_HEX(buf, len); fprintf(stderr, "\n");}
+#else
+#define DEBUG_PRINT(...) { }
+#define DEBUG_DUMP_HEX(buf, len) { }
+#define DEBUG_INDEX(fields) { }
+#define DEBUG_DUMP(buf, length) { }
+#define DEBUG_DUMP_HEX_LABEL(title, buf, len) { }
+#endif
+
+#ifndef htonll
+#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
+#endif
+
+#ifndef ntohll
+#define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))
+#endif
+
+#define TLS_CHANGE_CIPHER 0x14
+#define TLS_ALERT 0x15
+#define TLS_HANDSHAKE 0x16
+#define TLS_APPLICATION_DATA 0x17
+
+#define TLS_SERIALIZED_OBJECT 0xFE
+
+#define TLS_CLIENT_HELLO_MINSIZE 41
+#define TLS_CLIENT_RANDOM_SIZE 32
+#define TLS_SERVER_RANDOM_SIZE 32
+#define TLS_MAX_SESSION_ID 32
+#define TLS_SHA256_MAC_SIZE 32
+#define TLS_SHA1_MAC_SIZE 20
+#define TLS_SHA384_MAC_SIZE 48
+#define TLS_MAX_MAC_SIZE TLS_SHA384_MAC_SIZE
+ // 160
+#define TLS_MAX_KEY_EXPANSION_SIZE 192
+// 512bits (sha256) = 64 bytes
+#define TLS_MAX_HASH_LEN 64
+#define TLS_AES_IV_LENGTH 16
+#define TLS_AES_BLOCK_SIZE 16
+#define TLS_AES_GCM_IV_LENGTH 4
+#define TLS_13_AES_GCM_IV_LENGTH 12
+#define TLS_GCM_TAG_LEN 16
+#define TLS_MAX_TAG_LEN 16
+#define TLS_MIN_FINISHED_OPAQUE_LEN 12
+
+#define TLS_BLOB_INCREMENT 0xFFF
+#define TLS_ASN1_MAXLEVEL 0xFF
+
+#define DTLS_COOKIE_SIZE 32
+#define DTLS_MAX_FRAGMENT_SIZE 0x40000
+
+#define TLS_MAX_SHA_SIZE 48
+// 16(md5) + 20(sha1)
+#define TLS_V11_HASH_SIZE 36
+#define TLS_MAX_HASH_SIZE TLS_MAX_SHA_SIZE
+// 16(md5) + 20(sha1)
+#define TLS_MAX_RSA_KEY 2048
+
+#define TLS_MAXTLS_APP_SIZE 0x4000
+// max 1 second sleep
+#define TLS_MAX_ERROR_SLEEP_uS 1000000
+// max 5 seconds context sleep
+#define TLS_MAX_ERROR_IDLE_S 5
+
+#define TLS_V13_MAX_KEY_SIZE 32
+#define TLS_V13_MAX_IV_SIZE 12
+
+#define VERSION_SUPPORTED(version, err) if ((version != TLS_V13) && (version != TLS_V12) && (version != TLS_V11) && (version != TLS_V10) && (version != DTLS_V13) && (version != DTLS_V12) && (version != DTLS_V10)) { if ((version == SSL_V30) && (context->connection_status == 0)) { version = TLS_V12; } else { DEBUG_PRINT("UNSUPPORTED TLS VERSION %x\n", (int)version); return err;} }
+#define CHECK_SIZE(size, buf_size, err) if (((int)(size) > (int)(buf_size)) || ((int)(buf_size) < 0)) { DEBUG_PRINT("[EXPECTED AT LEAST %i IN BUFFER OF SIZE %i]\n", (int)(size), (int)(buf_size)); return err; }
+#define TLS_IMPORT_CHECK_SIZE(buf_pos, size, buf_size) if (((int)size > (int)buf_size - buf_pos) || ((int)buf_pos > (int)buf_size)) { DEBUG_PRINT("IMPORT ELEMENT SIZE ERROR\n"); tls_destroy_context(context); return NULL; }
+#define CHECK_HANDSHAKE_STATE(context, n, limit) { if (context->hs_messages[n] >= limit) { if (context->dtls) { DEBUG_PRINT("* REPEATED MESSAGE, RE-HASHING\n"); _private_dtls_rehash(context, type); context->hs_messages[n]++;} else { DEBUG_PRINT("* UNEXPECTED MESSAGE (%i)\n", (int)n); payload_res = TLS_UNEXPECTED_MESSAGE; break; } } context->hs_messages[n]++; }
+#define TLS_24_BIT(buf, index, val) { unsigned int u_val = (unsigned int)val; buf[index] = u_val / 0x10000; u_val %= 0x10000; buf[index + 1] = u_val / 0x100; u_val %= 0x100; buf[index + 2] = u_val; }
+
+#if CRYPT >= 0x0118
+ #define TLS_TOMCRYPT_PRIVATE_DP(key) (&((key)->dp))
+ #define TLS_TOMCRYPT_PRIVATE_SET_INDEX(key, k_idx)
+#else
+ #define TLS_TOMCRYPT_PRIVATE_DP(key) ((key)->dp)
+ #define TLS_TOMCRYPT_PRIVATE_SET_INDEX(key, k_idx) key->idx = k_idx
+#endif
+
+#ifdef TLS_WITH_CHACHA20_POLY1305
+#define TLS_CHACHA20_IV_LENGTH 12
+
+// ChaCha20 implementation by D. J. Bernstein
+// Public domain.
+
+#define CHACHA_MINKEYLEN 16
+#define CHACHA_NONCELEN 8
+#define CHACHA_NONCELEN_96 12
+#define CHACHA_CTRLEN 8
+#define CHACHA_CTRLEN_96 4
+#define CHACHA_STATELEN (CHACHA_NONCELEN+CHACHA_CTRLEN)
+#define CHACHA_BLOCKLEN 64
+
+#define POLY1305_MAX_AAD 32
+#define POLY1305_KEYLEN 32
+#define POLY1305_TAGLEN 16
+
+#define u_int unsigned int
+#define uint8_t unsigned char
+#define u_char unsigned char
+#ifndef NULL
+#define NULL (void *)0
+#endif
+
+#if (CRYPT >= 0x0117) && (0)
+ // to do: use ltc chacha/poly1305 implementation (working on big-endian machines)
+ #define chacha_ctx chacha20poly1305_state
+ #define poly1305_context poly1305_state
+
+ #define _private_tls_poly1305_init(ctx, key, len) poly1305_init(ctx, key, len)
+ #define _private_tls_poly1305_update(ctx, in, len) poly1305_process(ctx, in, len)
+ #define _private_tls_poly1305_finish(ctx, mac) poly1305_done(ctx, mac, 16)
+#else
+struct chacha_ctx {
+ u_int input[16];
+ uint8_t ks[CHACHA_BLOCKLEN];
+ uint8_t unused;
+};
+
+static inline void chacha_keysetup(struct chacha_ctx *x, const u_char *k, u_int kbits);
+static inline void chacha_ivsetup(struct chacha_ctx *x, const u_char *iv, const u_char *ctr);
+static inline void chacha_ivsetup_96bitnonce(struct chacha_ctx *x, const u_char *iv, const u_char *ctr);
+static inline void chacha_encrypt_bytes(struct chacha_ctx *x, const u_char *m, u_char *c, u_int bytes);
+static inline int poly1305_generate_key(unsigned char *key256, unsigned char *nonce, unsigned int noncelen, unsigned char *poly_key, unsigned int counter);
+
+#define poly1305_block_size 16
+#define poly1305_context poly1305_state_internal_t
+
+//========== ChaCha20 from D. J. Bernstein ========= //
+// Source available at https://cr.yp.to/chacha.html //
+
+typedef unsigned char u8;
+typedef unsigned int u32;
+
+typedef struct chacha_ctx chacha_ctx;
+
+#define U8C(v) (v##U)
+#define U32C(v) (v##U)
+
+#define U8V(v) ((u8)(v) & U8C(0xFF))
+#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF))
+
+#define ROTL32(v, n) \
+ (U32V((v) << (n)) | ((v) >> (32 - (n))))
+
+#define _private_tls_U8TO32_LITTLE(p) \
+ (((u32)((p)[0])) | \
+ ((u32)((p)[1]) << 8) | \
+ ((u32)((p)[2]) << 16) | \
+ ((u32)((p)[3]) << 24))
+
+#define _private_tls_U32TO8_LITTLE(p, v) \
+ do { \
+ (p)[0] = U8V((v)); \
+ (p)[1] = U8V((v) >> 8); \
+ (p)[2] = U8V((v) >> 16); \
+ (p)[3] = U8V((v) >> 24); \
+ } while (0)
+
+#define ROTATE(v,c) (ROTL32(v,c))
+#define XOR(v,w) ((v) ^ (w))
+#define PLUS(v,w) (U32V((v) + (w)))
+#define PLUSONE(v) (PLUS((v),1))
+
+#define QUARTERROUND(a,b,c,d) \
+ a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \
+ c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \
+ a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \
+ c = PLUS(c,d); b = ROTATE(XOR(b,c), 7);
+
+static const char sigma[] = "expand 32-byte k";
+static const char tau[] = "expand 16-byte k";
+
+static inline void chacha_keysetup(chacha_ctx *x, const u8 *k, u32 kbits) {
+ const char *constants;
+
+ x->input[4] = _private_tls_U8TO32_LITTLE(k + 0);
+ x->input[5] = _private_tls_U8TO32_LITTLE(k + 4);
+ x->input[6] = _private_tls_U8TO32_LITTLE(k + 8);
+ x->input[7] = _private_tls_U8TO32_LITTLE(k + 12);
+ if (kbits == 256) { /* recommended */
+ k += 16;
+ constants = sigma;
+ } else { /* kbits == 128 */
+ constants = tau;
+ }
+ x->input[8] = _private_tls_U8TO32_LITTLE(k + 0);
+ x->input[9] = _private_tls_U8TO32_LITTLE(k + 4);
+ x->input[10] = _private_tls_U8TO32_LITTLE(k + 8);
+ x->input[11] = _private_tls_U8TO32_LITTLE(k + 12);
+ x->input[0] = _private_tls_U8TO32_LITTLE(constants + 0);
+ x->input[1] = _private_tls_U8TO32_LITTLE(constants + 4);
+ x->input[2] = _private_tls_U8TO32_LITTLE(constants + 8);
+ x->input[3] = _private_tls_U8TO32_LITTLE(constants + 12);
+}
+
+static inline void chacha_key(chacha_ctx *x, u8 *k) {
+ _private_tls_U32TO8_LITTLE(k, x->input[4]);
+ _private_tls_U32TO8_LITTLE(k + 4, x->input[5]);
+ _private_tls_U32TO8_LITTLE(k + 8, x->input[6]);
+ _private_tls_U32TO8_LITTLE(k + 12, x->input[7]);
+
+ _private_tls_U32TO8_LITTLE(k + 16, x->input[8]);
+ _private_tls_U32TO8_LITTLE(k + 20, x->input[9]);
+ _private_tls_U32TO8_LITTLE(k + 24, x->input[10]);
+ _private_tls_U32TO8_LITTLE(k + 28, x->input[11]);
+}
+
+static inline void chacha_nonce(chacha_ctx *x, u8 *nonce) {
+ _private_tls_U32TO8_LITTLE(nonce + 0, x->input[13]);
+ _private_tls_U32TO8_LITTLE(nonce + 4, x->input[14]);
+ _private_tls_U32TO8_LITTLE(nonce + 8, x->input[15]);
+}
+
+static inline void chacha_ivsetup(chacha_ctx *x, const u8 *iv, const u8 *counter) {
+ x->input[12] = counter == NULL ? 0 : _private_tls_U8TO32_LITTLE(counter + 0);
+ x->input[13] = counter == NULL ? 0 : _private_tls_U8TO32_LITTLE(counter + 4);
+ if (iv) {
+ x->input[14] = _private_tls_U8TO32_LITTLE(iv + 0);
+ x->input[15] = _private_tls_U8TO32_LITTLE(iv + 4);
+ }
+}
+
+static inline void chacha_ivsetup_96bitnonce(chacha_ctx *x, const u8 *iv, const u8 *counter) {
+ x->input[12] = counter == NULL ? 0 : _private_tls_U8TO32_LITTLE(counter + 0);
+ if (iv) {
+ x->input[13] = _private_tls_U8TO32_LITTLE(iv + 0);
+ x->input[14] = _private_tls_U8TO32_LITTLE(iv + 4);
+ x->input[15] = _private_tls_U8TO32_LITTLE(iv + 8);
+ }
+}
+
+static inline void chacha_ivupdate(chacha_ctx *x, const u8 *iv, const u8 *aad, const u8 *counter) {
+ x->input[12] = counter == NULL ? 0 : _private_tls_U8TO32_LITTLE(counter + 0);
+ x->input[13] = _private_tls_U8TO32_LITTLE(iv + 0);
+ x->input[14] = _private_tls_U8TO32_LITTLE(iv + 4) ^ _private_tls_U8TO32_LITTLE(aad);
+ x->input[15] = _private_tls_U8TO32_LITTLE(iv + 8) ^ _private_tls_U8TO32_LITTLE(aad + 4);
+}
+
+static inline void chacha_encrypt_bytes(chacha_ctx *x, const u8 *m, u8 *c, u32 bytes) {
+ u32 x0, x1, x2, x3, x4, x5, x6, x7;
+ u32 x8, x9, x10, x11, x12, x13, x14, x15;
+ u32 j0, j1, j2, j3, j4, j5, j6, j7;
+ u32 j8, j9, j10, j11, j12, j13, j14, j15;
+ u8 *ctarget = NULL;
+ u8 tmp[64];
+ u_int i;
+
+ if (!bytes)
+ return;
+
+ j0 = x->input[0];
+ j1 = x->input[1];
+ j2 = x->input[2];
+ j3 = x->input[3];
+ j4 = x->input[4];
+ j5 = x->input[5];
+ j6 = x->input[6];
+ j7 = x->input[7];
+ j8 = x->input[8];
+ j9 = x->input[9];
+ j10 = x->input[10];
+ j11 = x->input[11];
+ j12 = x->input[12];
+ j13 = x->input[13];
+ j14 = x->input[14];
+ j15 = x->input[15];
+
+ for (;;) {
+ if (bytes < 64) {
+ for (i = 0; i < bytes; ++i)
+ tmp[i] = m[i];
+ m = tmp;
+ ctarget = c;
+ c = tmp;
+ }
+ x0 = j0;
+ x1 = j1;
+ x2 = j2;
+ x3 = j3;
+ x4 = j4;
+ x5 = j5;
+ x6 = j6;
+ x7 = j7;
+ x8 = j8;
+ x9 = j9;
+ x10 = j10;
+ x11 = j11;
+ x12 = j12;
+ x13 = j13;
+ x14 = j14;
+ x15 = j15;
+ for (i = 20; i > 0; i -= 2) {
+ QUARTERROUND(x0, x4, x8, x12)
+ QUARTERROUND(x1, x5, x9, x13)
+ QUARTERROUND(x2, x6, x10, x14)
+ QUARTERROUND(x3, x7, x11, x15)
+ QUARTERROUND(x0, x5, x10, x15)
+ QUARTERROUND(x1, x6, x11, x12)
+ QUARTERROUND(x2, x7, x8, x13)
+ QUARTERROUND(x3, x4, x9, x14)
+ }
+ x0 = PLUS(x0, j0);
+ x1 = PLUS(x1, j1);
+ x2 = PLUS(x2, j2);
+ x3 = PLUS(x3, j3);
+ x4 = PLUS(x4, j4);
+ x5 = PLUS(x5, j5);
+ x6 = PLUS(x6, j6);
+ x7 = PLUS(x7, j7);
+ x8 = PLUS(x8, j8);
+ x9 = PLUS(x9, j9);
+ x10 = PLUS(x10, j10);
+ x11 = PLUS(x11, j11);
+ x12 = PLUS(x12, j12);
+ x13 = PLUS(x13, j13);
+ x14 = PLUS(x14, j14);
+ x15 = PLUS(x15, j15);
+
+ if (bytes < 64) {
+ _private_tls_U32TO8_LITTLE(x->ks + 0, x0);
+ _private_tls_U32TO8_LITTLE(x->ks + 4, x1);
+ _private_tls_U32TO8_LITTLE(x->ks + 8, x2);
+ _private_tls_U32TO8_LITTLE(x->ks + 12, x3);
+ _private_tls_U32TO8_LITTLE(x->ks + 16, x4);
+ _private_tls_U32TO8_LITTLE(x->ks + 20, x5);
+ _private_tls_U32TO8_LITTLE(x->ks + 24, x6);
+ _private_tls_U32TO8_LITTLE(x->ks + 28, x7);
+ _private_tls_U32TO8_LITTLE(x->ks + 32, x8);
+ _private_tls_U32TO8_LITTLE(x->ks + 36, x9);
+ _private_tls_U32TO8_LITTLE(x->ks + 40, x10);
+ _private_tls_U32TO8_LITTLE(x->ks + 44, x11);
+ _private_tls_U32TO8_LITTLE(x->ks + 48, x12);
+ _private_tls_U32TO8_LITTLE(x->ks + 52, x13);
+ _private_tls_U32TO8_LITTLE(x->ks + 56, x14);
+ _private_tls_U32TO8_LITTLE(x->ks + 60, x15);
+ }
+
+ x0 = XOR(x0, _private_tls_U8TO32_LITTLE(m + 0));
+ x1 = XOR(x1, _private_tls_U8TO32_LITTLE(m + 4));
+ x2 = XOR(x2, _private_tls_U8TO32_LITTLE(m + 8));
+ x3 = XOR(x3, _private_tls_U8TO32_LITTLE(m + 12));
+ x4 = XOR(x4, _private_tls_U8TO32_LITTLE(m + 16));
+ x5 = XOR(x5, _private_tls_U8TO32_LITTLE(m + 20));
+ x6 = XOR(x6, _private_tls_U8TO32_LITTLE(m + 24));
+ x7 = XOR(x7, _private_tls_U8TO32_LITTLE(m + 28));
+ x8 = XOR(x8, _private_tls_U8TO32_LITTLE(m + 32));
+ x9 = XOR(x9, _private_tls_U8TO32_LITTLE(m + 36));
+ x10 = XOR(x10, _private_tls_U8TO32_LITTLE(m + 40));
+ x11 = XOR(x11, _private_tls_U8TO32_LITTLE(m + 44));
+ x12 = XOR(x12, _private_tls_U8TO32_LITTLE(m + 48));
+ x13 = XOR(x13, _private_tls_U8TO32_LITTLE(m + 52));
+ x14 = XOR(x14, _private_tls_U8TO32_LITTLE(m + 56));
+ x15 = XOR(x15, _private_tls_U8TO32_LITTLE(m + 60));
+
+ j12 = PLUSONE(j12);
+ if (!j12) {
+ j13 = PLUSONE(j13);
+ /*
+ * Stopping at 2^70 bytes per nonce is the user's
+ * responsibility.
+ */
+ }
+
+ _private_tls_U32TO8_LITTLE(c + 0, x0);
+ _private_tls_U32TO8_LITTLE(c + 4, x1);
+ _private_tls_U32TO8_LITTLE(c + 8, x2);
+ _private_tls_U32TO8_LITTLE(c + 12, x3);
+ _private_tls_U32TO8_LITTLE(c + 16, x4);
+ _private_tls_U32TO8_LITTLE(c + 20, x5);
+ _private_tls_U32TO8_LITTLE(c + 24, x6);
+ _private_tls_U32TO8_LITTLE(c + 28, x7);
+ _private_tls_U32TO8_LITTLE(c + 32, x8);
+ _private_tls_U32TO8_LITTLE(c + 36, x9);
+ _private_tls_U32TO8_LITTLE(c + 40, x10);
+ _private_tls_U32TO8_LITTLE(c + 44, x11);
+ _private_tls_U32TO8_LITTLE(c + 48, x12);
+ _private_tls_U32TO8_LITTLE(c + 52, x13);
+ _private_tls_U32TO8_LITTLE(c + 56, x14);
+ _private_tls_U32TO8_LITTLE(c + 60, x15);
+
+ if (bytes <= 64) {
+ if (bytes < 64) {
+ for (i = 0; i < bytes; ++i)
+ ctarget[i] = c[i];
+ }
+ x->input[12] = j12;
+ x->input[13] = j13;
+ x->unused = 64 - bytes;
+ return;
+ }
+ bytes -= 64;
+ c += 64;
+ m += 64;
+ }
+}
+
+static inline void chacha20_block(chacha_ctx *x, unsigned char *c, u_int len) {
+ u_int i;
+
+ unsigned int state[16];
+ for (i = 0; i < 16; i++)
+ state[i] = x->input[i];
+ for (i = 20; i > 0; i -= 2) {
+ QUARTERROUND(state[0], state[4], state[8], state[12])
+ QUARTERROUND(state[1], state[5], state[9], state[13])
+ QUARTERROUND(state[2], state[6], state[10], state[14])
+ QUARTERROUND(state[3], state[7], state[11], state[15])
+ QUARTERROUND(state[0], state[5], state[10], state[15])
+ QUARTERROUND(state[1], state[6], state[11], state[12])
+ QUARTERROUND(state[2], state[7], state[8], state[13])
+ QUARTERROUND(state[3], state[4], state[9], state[14])
+ }
+
+ for (i = 0; i < 16; i++)
+ x->input[i] = PLUS(x->input[i], state[i]);
+
+ for (i = 0; i < len; i += 4) {
+ _private_tls_U32TO8_LITTLE(c + i, x->input[i/4]);
+ }
+}
+
+static inline int poly1305_generate_key(unsigned char *key256, unsigned char *nonce, unsigned int noncelen, unsigned char *poly_key, unsigned int counter) {
+ struct chacha_ctx ctx;
+ uint64_t ctr;
+ memset(&ctx, 0, sizeof(ctx));
+ chacha_keysetup(&ctx, key256, 256);
+ switch (noncelen) {
+ case 8:
+ ctr = counter;
+ chacha_ivsetup(&ctx, nonce, (unsigned char *)&ctr);
+ break;
+ case 12:
+ chacha_ivsetup_96bitnonce(&ctx, nonce, (unsigned char *)&counter);
+ break;
+ default:
+ return -1;
+ }
+ chacha20_block(&ctx, poly_key, POLY1305_KEYLEN);
+ return 0;
+}
+
+/* 17 + sizeof(size_t) + 14*sizeof(unsigned long) */
+typedef struct poly1305_state_internal_t {
+ unsigned long r[5];
+ unsigned long h[5];
+ unsigned long pad[4];
+ size_t leftover;
+ unsigned char buffer[poly1305_block_size];
+ unsigned char final;
+} poly1305_state_internal_t;
+
+/* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */
+static unsigned long _private_tls_U8TO32(const unsigned char *p) {
+ return
+ (((unsigned long)(p[0] & 0xff) ) |
+ ((unsigned long)(p[1] & 0xff) << 8) |
+ ((unsigned long)(p[2] & 0xff) << 16) |
+ ((unsigned long)(p[3] & 0xff) << 24));
+}
+
+/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */
+static void _private_tls_U32TO8(unsigned char *p, unsigned long v) {
+ p[0] = (v ) & 0xff;
+ p[1] = (v >> 8) & 0xff;
+ p[2] = (v >> 16) & 0xff;
+ p[3] = (v >> 24) & 0xff;
+}
+
+void _private_tls_poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
+ poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
+
+ /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
+ st->r[0] = (_private_tls_U8TO32(&key[ 0]) ) & 0x3ffffff;
+ st->r[1] = (_private_tls_U8TO32(&key[ 3]) >> 2) & 0x3ffff03;
+ st->r[2] = (_private_tls_U8TO32(&key[ 6]) >> 4) & 0x3ffc0ff;
+ st->r[3] = (_private_tls_U8TO32(&key[ 9]) >> 6) & 0x3f03fff;
+ st->r[4] = (_private_tls_U8TO32(&key[12]) >> 8) & 0x00fffff;
+
+ /* h = 0 */
+ st->h[0] = 0;
+ st->h[1] = 0;
+ st->h[2] = 0;
+ st->h[3] = 0;
+ st->h[4] = 0;
+
+ /* save pad for later */
+ st->pad[0] = _private_tls_U8TO32(&key[16]);
+ st->pad[1] = _private_tls_U8TO32(&key[20]);
+ st->pad[2] = _private_tls_U8TO32(&key[24]);
+ st->pad[3] = _private_tls_U8TO32(&key[28]);
+
+ st->leftover = 0;
+ st->final = 0;
+}
+
+static void _private_tls_poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) {
+ const unsigned long hibit = (st->final) ? 0 : (1UL << 24); /* 1 << 128 */
+ unsigned long r0,r1,r2,r3,r4;
+ unsigned long s1,s2,s3,s4;
+ unsigned long h0,h1,h2,h3,h4;
+ unsigned long long d0,d1,d2,d3,d4;
+ unsigned long c;
+
+ r0 = st->r[0];
+ r1 = st->r[1];
+ r2 = st->r[2];
+ r3 = st->r[3];
+ r4 = st->r[4];
+
+ s1 = r1 * 5;
+ s2 = r2 * 5;
+ s3 = r3 * 5;
+ s4 = r4 * 5;
+
+ h0 = st->h[0];
+ h1 = st->h[1];
+ h2 = st->h[2];
+ h3 = st->h[3];
+ h4 = st->h[4];
+
+ while (bytes >= poly1305_block_size) {
+ /* h += m[i] */
+ h0 += (_private_tls_U8TO32(m+ 0) ) & 0x3ffffff;
+ h1 += (_private_tls_U8TO32(m+ 3) >> 2) & 0x3ffffff;
+ h2 += (_private_tls_U8TO32(m+ 6) >> 4) & 0x3ffffff;
+ h3 += (_private_tls_U8TO32(m+ 9) >> 6) & 0x3ffffff;
+ h4 += (_private_tls_U8TO32(m+12) >> 8) | hibit;
+
+ /* h *= r */
+ d0 = ((unsigned long long)h0 * r0) + ((unsigned long long)h1 * s4) + ((unsigned long long)h2 * s3) + ((unsigned long long)h3 * s2) + ((unsigned long long)h4 * s1);
+ d1 = ((unsigned long long)h0 * r1) + ((unsigned long long)h1 * r0) + ((unsigned long long)h2 * s4) + ((unsigned long long)h3 * s3) + ((unsigned long long)h4 * s2);
+ d2 = ((unsigned long long)h0 * r2) + ((unsigned long long)h1 * r1) + ((unsigned long long)h2 * r0) + ((unsigned long long)h3 * s4) + ((unsigned long long)h4 * s3);
+ d3 = ((unsigned long long)h0 * r3) + ((unsigned long long)h1 * r2) + ((unsigned long long)h2 * r1) + ((unsigned long long)h3 * r0) + ((unsigned long long)h4 * s4);
+ d4 = ((unsigned long long)h0 * r4) + ((unsigned long long)h1 * r3) + ((unsigned long long)h2 * r2) + ((unsigned long long)h3 * r1) + ((unsigned long long)h4 * r0);
+
+ /* (partial) h %= p */
+ c = (unsigned long)(d0 >> 26); h0 = (unsigned long)d0 & 0x3ffffff;
+ d1 += c; c = (unsigned long)(d1 >> 26); h1 = (unsigned long)d1 & 0x3ffffff;
+ d2 += c; c = (unsigned long)(d2 >> 26); h2 = (unsigned long)d2 & 0x3ffffff;
+ d3 += c; c = (unsigned long)(d3 >> 26); h3 = (unsigned long)d3 & 0x3ffffff;
+ d4 += c; c = (unsigned long)(d4 >> 26); h4 = (unsigned long)d4 & 0x3ffffff;
+ h0 += c * 5; c = (h0 >> 26); h0 = h0 & 0x3ffffff;
+ h1 += c;
+
+ m += poly1305_block_size;
+ bytes -= poly1305_block_size;
+ }
+
+ st->h[0] = h0;
+ st->h[1] = h1;
+ st->h[2] = h2;
+ st->h[3] = h3;
+ st->h[4] = h4;
+}
+
+void _private_tls_poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) {
+ poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
+ unsigned long h0,h1,h2,h3,h4,c;
+ unsigned long g0,g1,g2,g3,g4;
+ unsigned long long f;
+ unsigned long mask;
+
+ /* process the remaining block */
+ if (st->leftover) {
+ size_t i = st->leftover;
+ st->buffer[i++] = 1;
+ for (; i < poly1305_block_size; i++)
+ st->buffer[i] = 0;
+ st->final = 1;
+ _private_tls_poly1305_blocks(st, st->buffer, poly1305_block_size);
+ }
+
+ /* fully carry h */
+ h0 = st->h[0];
+ h1 = st->h[1];
+ h2 = st->h[2];
+ h3 = st->h[3];
+ h4 = st->h[4];
+
+ c = h1 >> 26; h1 = h1 & 0x3ffffff;
+ h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff;
+ h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff;
+ h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff;
+ h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff;
+ h1 += c;
+
+ /* compute h + -p */
+ g0 = h0 + 5; c = g0 >> 26; g0 &= 0x3ffffff;
+ g1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffff;
+ g2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffff;
+ g3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffff;
+ g4 = h4 + c - (1UL << 26);
+
+ /* select h if h < p, or h + -p if h >= p */
+ mask = (g4 >> ((sizeof(unsigned long) * 8) - 1)) - 1;
+ g0 &= mask;
+ g1 &= mask;
+ g2 &= mask;
+ g3 &= mask;
+ g4 &= mask;
+ mask = ~mask;
+ h0 = (h0 & mask) | g0;
+ h1 = (h1 & mask) | g1;
+ h2 = (h2 & mask) | g2;
+ h3 = (h3 & mask) | g3;
+ h4 = (h4 & mask) | g4;
+
+ /* h = h % (2^128) */
+ h0 = ((h0 ) | (h1 << 26)) & 0xffffffff;
+ h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff;
+ h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff;
+ h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff;
+
+ /* mac = (h + pad) % (2^128) */
+ f = (unsigned long long)h0 + st->pad[0] ; h0 = (unsigned long)f;
+ f = (unsigned long long)h1 + st->pad[1] + (f >> 32); h1 = (unsigned long)f;
+ f = (unsigned long long)h2 + st->pad[2] + (f >> 32); h2 = (unsigned long)f;
+ f = (unsigned long long)h3 + st->pad[3] + (f >> 32); h3 = (unsigned long)f;
+
+ _private_tls_U32TO8(mac + 0, h0);
+ _private_tls_U32TO8(mac + 4, h1);
+ _private_tls_U32TO8(mac + 8, h2);
+ _private_tls_U32TO8(mac + 12, h3);
+
+ /* zero out the state */
+ st->h[0] = 0;
+ st->h[1] = 0;
+ st->h[2] = 0;
+ st->h[3] = 0;
+ st->h[4] = 0;
+ st->r[0] = 0;
+ st->r[1] = 0;
+ st->r[2] = 0;
+ st->r[3] = 0;
+ st->r[4] = 0;
+ st->pad[0] = 0;
+ st->pad[1] = 0;
+ st->pad[2] = 0;
+ st->pad[3] = 0;
+}
+
+void _private_tls_poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) {
+ poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
+ size_t i;
+ /* handle leftover */
+ if (st->leftover) {
+ size_t want = (poly1305_block_size - st->leftover);
+ if (want > bytes)
+ want = bytes;
+ for (i = 0; i < want; i++)
+ st->buffer[st->leftover + i] = m[i];
+ bytes -= want;
+ m += want;
+ st->leftover += want;
+ if (st->leftover < poly1305_block_size)
+ return;
+ _private_tls_poly1305_blocks(st, st->buffer, poly1305_block_size);
+ st->leftover = 0;
+ }
+
+ /* process full blocks */
+ if (bytes >= poly1305_block_size) {
+ size_t want = (bytes & ~(poly1305_block_size - 1));
+ _private_tls_poly1305_blocks(st, m, want);
+ m += want;
+ bytes -= want;
+ }
+
+ /* store leftover */
+ if (bytes) {
+ for (i = 0; i < bytes; i++)
+ st->buffer[st->leftover + i] = m[i];
+ st->leftover += bytes;
+ }
+}
+
+int poly1305_verify(const unsigned char mac1[16], const unsigned char mac2[16]) {
+ size_t i;
+ unsigned int dif = 0;
+ for (i = 0; i < 16; i++)
+ dif |= (mac1[i] ^ mac2[i]);
+ dif = (dif - 1) >> ((sizeof(unsigned int) * 8) - 1);
+ return (dif & 1);
+}
+
+void chacha20_poly1305_key(struct chacha_ctx *ctx, unsigned char *poly1305_key) {
+ unsigned char key[32];
+ unsigned char nonce[12];
+ chacha_key(ctx, key);
+ chacha_nonce(ctx, nonce);
+ poly1305_generate_key(key, nonce, sizeof(nonce), poly1305_key, 0);
+}
+
+int chacha20_poly1305_aead(struct chacha_ctx *ctx, unsigned char *pt, unsigned int len, unsigned char *aad, unsigned int aad_len, unsigned char *poly_key, unsigned char *out) {
+ static unsigned char zeropad[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ if (aad_len > POLY1305_MAX_AAD)
+ return -1;
+
+ unsigned int counter = 1;
+ chacha_ivsetup_96bitnonce(ctx, NULL, (unsigned char *)&counter);
+ chacha_encrypt_bytes(ctx, pt, out, len);
+
+ poly1305_context aead_ctx;
+ _private_tls_poly1305_init(&aead_ctx, poly_key);
+ _private_tls_poly1305_update(&aead_ctx, aad, aad_len);
+ int rem = aad_len % 16;
+ if (rem)
+ _private_tls_poly1305_update(&aead_ctx, zeropad, 16 - rem);
+ _private_tls_poly1305_update(&aead_ctx, out, len);
+ rem = len % 16;
+ if (rem)
+ _private_tls_poly1305_update(&aead_ctx, zeropad, 16 - rem);
+
+ unsigned char trail[16];
+ _private_tls_U32TO8(trail, aad_len);
+ *(int *)(trail + 4) = 0;
+ _private_tls_U32TO8(trail + 8, len);
+ *(int *)(trail + 12) = 0;
+
+ _private_tls_poly1305_update(&aead_ctx, trail, 16);
+ _private_tls_poly1305_finish(&aead_ctx, out + len);
+
+ return len + POLY1305_TAGLEN;
+}
+#endif
+#endif
+
+typedef enum {
+ KEA_dhe_dss,
+ KEA_dhe_rsa,
+ KEA_dh_anon,
+ KEA_rsa,
+ KEA_dh_dss,
+ KEA_dh_rsa,
+ KEA_ec_diffie_hellman
+} KeyExchangeAlgorithm;
+
+typedef enum {
+ rsa_sign = 1,
+ dss_sign = 2,
+ rsa_fixed_dh = 3,
+ dss_fixed_dh = 4,
+ rsa_ephemeral_dh_RESERVED = 5,
+ dss_ephemeral_dh_RESERVED = 6,
+ fortezza_dms_RESERVED = 20,
+ ecdsa_sign = 64,
+ rsa_fixed_ecdh = 65,
+ ecdsa_fixed_ecdh = 66
+} TLSClientCertificateType;
+
+typedef enum {
+ none = 0,
+ md5 = 1,
+ sha1 = 2,
+ sha224 = 3,
+ sha256 = 4,
+ sha384 = 5,
+ sha512 = 6,
+ _md5_sha1 = 255
+} TLSHashAlgorithm;
+
+#define TLS_HASH_ALGO_NUMBER (sha512 - md5 + 1)
+
+typedef enum {
+ anonymous = 0,
+ rsa = 1,
+ dsa = 2,
+ ecdsa = 3
+} TLSSignatureAlgorithm;
+
+#define TLS_SIGN_ALGO_NUMBER (ecdsa - rsa + 1)
+
+struct _private_OID_chain {
+ void *top;
+ unsigned char *oid;
+};
+
+struct TLSCertificate {
+ unsigned short version;
+ unsigned int algorithm;
+ unsigned int key_algorithm;
+ unsigned int ec_algorithm;
+ unsigned char *exponent;
+ unsigned int exponent_len;
+ unsigned char *pk;
+ unsigned int pk_len;
+ unsigned char *priv;
+ unsigned int priv_len;
+ unsigned char *issuer_country;
+ unsigned char *issuer_state;
+ unsigned char *issuer_location;
+ unsigned char *issuer_entity;
+ unsigned char *issuer_subject;
+ unsigned char *not_before;
+ unsigned char *not_after;
+ unsigned char *country;
+ unsigned char *state;
+ unsigned char *location;
+ unsigned char *entity;
+ unsigned char *subject;
+ unsigned char **san;
+ unsigned short san_length;
+ unsigned char *ocsp;
+ unsigned char *serial_number;
+ unsigned int serial_len;
+ unsigned char *sign_key;
+ unsigned int sign_len;
+ unsigned char *fingerprint;
+ unsigned char *der_bytes;
+ unsigned int der_len;
+ unsigned char *bytes;
+ unsigned int len;
+};
+
+typedef struct {
+ union {
+ symmetric_CBC aes_local;
+ gcm_state aes_gcm_local;
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ chacha_ctx chacha_local;
+#endif
+ } ctx_local;
+ union {
+ symmetric_CBC aes_remote;
+ gcm_state aes_gcm_remote;
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ chacha_ctx chacha_remote;
+#endif
+ } ctx_remote;
+ union {
+ unsigned char local_mac[TLS_MAX_MAC_SIZE];
+ unsigned char local_aead_iv[TLS_AES_GCM_IV_LENGTH];
+#ifdef WITH_TLS_13
+ unsigned char local_iv[TLS_13_AES_GCM_IV_LENGTH];
+#endif
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ unsigned char local_nonce[TLS_CHACHA20_IV_LENGTH];
+#endif
+ } ctx_local_mac;
+ union {
+ unsigned char remote_aead_iv[TLS_AES_GCM_IV_LENGTH];
+ unsigned char remote_mac[TLS_MAX_MAC_SIZE];
+#ifdef WITH_TLS_13
+ unsigned char remote_iv[TLS_13_AES_GCM_IV_LENGTH];
+#endif
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ unsigned char remote_nonce[TLS_CHACHA20_IV_LENGTH];
+#endif
+ } ctx_remote_mac;
+ unsigned char created;
+} TLSCipher;
+
+typedef struct {
+ hash_state hash32;
+ hash_state hash48;
+#ifdef TLS_LEGACY_SUPPORT
+ hash_state hash2;
+#endif
+ unsigned char created;
+} TLSHash;
+
+#ifdef TLS_FORWARD_SECRECY
+#define mp_init(a) ltc_mp.init(a)
+#define mp_init_multi ltc_init_multi
+#define mp_clear(a) ltc_mp.deinit(a)
+#define mp_clear_multi ltc_deinit_multi
+#define mp_count_bits(a) ltc_mp.count_bits(a)
+#define mp_read_radix(a, b, c) ltc_mp.read_radix(a, b, c)
+#define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a)
+#define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b)
+#define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c)
+#define mp_exptmod(a, b, c, d) ltc_mp.exptmod(a, b, c, d)
+#define mp_add(a, b, c) ltc_mp.add(a, b, c)
+#define mp_mul(a, b, c) ltc_mp.mul(a, b, c)
+#define mp_cmp(a, b) ltc_mp.compare(a, b)
+#define mp_cmp_d(a, b) ltc_mp.compare_d(a, b)
+#define mp_sqr(a, b) ltc_mp.sqr(a, b)
+#define mp_mod(a, b, c) ltc_mp.mpdiv(a, b, NULL, c)
+#define mp_sub(a, b, c) ltc_mp.sub(a, b, c)
+#define mp_set(a, b) ltc_mp.set_int(a, b)
+#define mp_copy(a, b) ltc_mp.copy(a, b)
+#define mp_submod(a, b, c, d) ltc_mp.submod(a, b, c, d)
+#define mp_mulmod(a, b, c, d) ltc_mp.mulmod(a, b, c, d)
+#define mp_addmod(a, b, c, d) ltc_mp.addmod(a, b, c, d)
+
+typedef struct {
+ int iana;
+ void *x;
+ void *y;
+ void *p;
+ void *g;
+} DHKey;
+
+#ifdef WITH_TLS_13
+static DHKey ffdhe2048 = {
+ 0x0100,
+ NULL,
+ NULL,
+ (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B423861285C97FFFFFFFFFFFFFFFF",
+ (void *)"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"
+};
+
+static DHKey ffdhe3072 = {
+ 0x0101,
+ NULL,
+ NULL,
+ (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF",
+ (void *)"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"
+};
+
+static DHKey ffdhe4096 = {
+ 0x0102,
+ NULL,
+ NULL,
+ (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6AFFFFFFFFFFFFFFFF",
+ (void *)"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"
+};
+
+static DHKey ffdhe6144 = {
+ 0x0103,
+ NULL,
+ NULL,
+ (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CD0E40E65FFFFFFFFFFFFFFFF",
+ (void *)"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"
+};
+
+static DHKey ffdhe8192 = {
+ 0x0104,
+ NULL,
+ NULL,
+ (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CCFF46AAA36AD004CF600C8381E425A31D951AE64FDB23FCEC9509D43687FEB69EDD1CC5E0B8CC3BDF64B10EF86B63142A3AB8829555B2F747C932665CB2C0F1CC01BD70229388839D2AF05E454504AC78B7582822846C0BA35C35F5C59160CC046FD8251541FC68C9C86B022BB7099876A460E7451A8A93109703FEE1C217E6C3826E52C51AA691E0E423CFC99E9E31650C1217B624816CDAD9A95F9D5B8019488D9C0A0A1FE3075A577E23183F81D4A3F2FA4571EFC8CE0BA8A4FE8B6855DFE72B0A66EDED2FBABFBE58A30FAFABE1C5D71A87E2F741EF8C1FE86FEA6BBFDE530677F0D97D11D49F7A8443D0822E506A9F4614E011E2A94838FF88CD68C8BB7C5C6424CFFFFFFFFFFFFFFFF",
+ (void *)"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"
+};
+#endif
+
+#if CRYPT > 0x0117
+ #define ltc_ecc_set_type ltc_ecc_curve
+#endif
+
+struct ECCCurveParameters {
+ int size;
+ int iana;
+ const char *name;
+ const char *P;
+ const char *A;
+ const char *B;
+ const char *Gx;
+ const char *Gy;
+ const char *order;
+ const char *oid;
+ ltc_ecc_set_type dp;
+};
+
+static struct ECCCurveParameters secp192r1 = {
+ 24,
+ 19,
+ "secp192r1",
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", // P
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", // A
+ "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", // B
+ "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", // Gx
+ "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811", // Gy
+ "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831", // order (n)
+ "1.2.840.10045.3.1.1" // oid
+};
+
+
+static struct ECCCurveParameters secp224r1 = {
+ 28,
+ 21,
+ "secp224r1",
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", // P
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE", // A
+ "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", // B
+ "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21", // Gx
+ "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34", // Gy
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D", // order (n)
+ "1.3.132.0.33" // oid
+};
+
+static struct ECCCurveParameters secp224k1 = {
+ 28,
+ 20,
+ "secp224k1",
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D", // P
+ "00000000000000000000000000000000000000000000000000000000", // A
+ "00000000000000000000000000000000000000000000000000000005", // B
+ "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C", // Gx
+ "7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5", // Gy
+ "0000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7", // order (n)
+ "1.3.132.0.32" // oid
+};
+
+static struct ECCCurveParameters secp256r1 = {
+ 32,
+ 23,
+ "secp256r1",
+ "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", // P
+ "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", // A
+ "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", // B
+ "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", // Gx
+ "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", // Gy
+ "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", // order (n)
+ "1.2.840.10045.3.1.7" // oid
+};
+
+static struct ECCCurveParameters secp256k1 = {
+ 32,
+ 22,
+ "secp256k1",
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", // P
+ "0000000000000000000000000000000000000000000000000000000000000000", // A
+ "0000000000000000000000000000000000000000000000000000000000000007", // B
+ "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", // Gx
+ "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", // Gy
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", // order (n)
+ "1.3.132.0.10" // oid
+};
+
+static struct ECCCurveParameters secp384r1 = {
+ 48,
+ 24,
+ "secp384r1",
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", // P
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", // A
+ "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", // B
+ "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", // Gx
+ "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", // Gy
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", // order (n)
+ "1.3.132.0.34" // oid
+};
+
+static struct ECCCurveParameters secp521r1 = {
+ 66,
+ 25,
+ "secp521r1",
+ "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", // P
+ "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", // A
+ "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", // B
+ "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", // Gx
+ "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", // Gy
+ "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", // order (n)
+ "1.3.132.0.35" // oid
+};
+
+#ifdef TLS_CURVE25519
+// dummy
+static struct ECCCurveParameters x25519 = {
+ 32,
+ 29,
+ "x25519",
+ "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED", // P
+ "0000000000000000000000000000000000000000000000000000000000076D06", // A
+ "0000000000000000000000000000000000000000000000000000000000000000", // B
+ "0000000000000000000000000000000000000000000000000000000000000009", // Gx
+ "20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9", // Gy
+ "1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED", // order (n)
+ "1.3.101.110" // oid
+};
+#endif
+
+static struct ECCCurveParameters * const default_curve = &secp256r1;
+
+void init_curve(struct ECCCurveParameters *curve) {
+#if CRYPT < 0x0118
+ curve->dp.size = curve->size;
+ curve->dp.name = (char *)curve->name;
+#else
+ curve->dp.cofactor = 1;
+ curve->dp.A = (char *)curve->A;
+ curve->dp.OID = curve->oid;
+#endif
+ curve->dp.B = (char *)curve->B;
+ curve->dp.prime = (char *)curve->P;
+ curve->dp.Gx = (char *)curve->Gx;
+ curve->dp.Gy = (char *)curve->Gy;
+ curve->dp.order = (char *)curve->order;
+}
+
+void init_curves() {
+ init_curve(&secp192r1);
+ init_curve(&secp224r1);
+ init_curve(&secp224k1);
+ init_curve(&secp256r1);
+ init_curve(&secp256k1);
+ init_curve(&secp384r1);
+ init_curve(&secp521r1);
+}
+#endif
+
+struct DTLSFragment {
+ char *buffer;
+ int len;
+ int written;
+};
+
+struct TLSHandshakeList {
+ unsigned char connection_status;
+ unsigned char direction;
+ unsigned char *msg;
+ unsigned int len;
+ void *next;
+};
+
+struct DTLSData {
+ struct TLSHandshakeList *dtls_handshake_list;
+ struct DTLSFragment *fragment;
+ unsigned char *key_exchange;
+ unsigned int key_exchange_len;
+#ifdef TLS_DTLS_EXTENDED_MASTER_SECRET
+ unsigned char extended_master_secret;
+#endif
+ unsigned char has_random;
+ char *remote_fingerprint;
+};
+
+struct TLSContext {
+ unsigned char remote_random[TLS_CLIENT_RANDOM_SIZE];
+ unsigned char local_random[TLS_SERVER_RANDOM_SIZE];
+ unsigned char session[TLS_MAX_SESSION_ID];
+ unsigned char session_size;
+ unsigned short cipher;
+ unsigned short version;
+ unsigned char is_server;
+ struct TLSCertificate **certificates;
+ struct TLSCertificate *private_key;
+#ifdef TLS_ECDSA_SUPPORTED
+ struct TLSCertificate *ec_private_key;
+#endif
+#ifdef TLS_FORWARD_SECRECY
+ DHKey *dhe;
+ ecc_key *ecc_dhe;
+ char *default_dhe_p;
+ char *default_dhe_g;
+ const struct ECCCurveParameters *curve;
+#endif
+ struct TLSCertificate **client_certificates;
+ unsigned int certificates_count;
+ unsigned int client_certificates_count;
+ unsigned char *master_key;
+ unsigned int master_key_len;
+ unsigned char *premaster_key;
+ unsigned int premaster_key_len;
+ unsigned char cipher_spec_set;
+ TLSCipher crypto;
+ TLSHash *handshake_hash;
+
+ unsigned char *message_buffer;
+ unsigned int message_buffer_len;
+ uint64_t remote_sequence_number;
+ uint64_t local_sequence_number;
+
+ unsigned char connection_status;
+ unsigned char critical_error;
+ unsigned char error_code;
+
+ unsigned char *tls_buffer;
+ unsigned int tls_buffer_len;
+
+ unsigned char *application_buffer;
+ unsigned int application_buffer_len;
+ unsigned char is_child;
+ unsigned char exportable;
+ unsigned char *exportable_keys;
+ unsigned char exportable_size;
+ char *sni;
+ unsigned char request_client_certificate;
+ unsigned char dtls;
+ unsigned short dtls_epoch_local;
+ unsigned short dtls_epoch_remote;
+ unsigned char *dtls_cookie;
+ unsigned char dtls_cookie_len;
+ unsigned char dtls_seq;
+ unsigned char *cached_handshake;
+ unsigned int cached_handshake_len;
+ unsigned char client_verified;
+ // handshake messages flags
+ unsigned char hs_messages[11];
+
+ void *user_data;
+ struct TLSCertificate **root_certificates;
+ unsigned int root_count;
+#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION
+ unsigned char *verify_data;
+ unsigned char verify_len;
+#endif
+#ifdef WITH_TLS_13
+ unsigned char *finished_key;
+ unsigned char *remote_finished_key;
+ unsigned char *server_finished_hash;
+#endif
+#ifdef TLS_CURVE25519
+ unsigned char *client_secret;
+#endif
+ char **alpn;
+ unsigned char alpn_count;
+ char *negotiated_alpn;
+ unsigned int sleep_until;
+ unsigned short tls13_version;
+#ifdef TLS_12_FALSE_START
+ unsigned char false_start;
+#endif
+
+ struct DTLSData *dtls_data;
+};
+
+struct TLSPacket {
+ unsigned char *buf;
+ unsigned int len;
+ unsigned int size;
+ unsigned char broken;
+ struct TLSContext *context;
+};
+
+struct TLSRTCPeerBuffer {
+ unsigned char *buf;
+ unsigned int len;
+
+ void *next;
+};
+
+#define SRTP_MASTER_KEY_KEY_LEN 16
+#define SRTP_MASTER_KEY_SALT_LEN 14
+
+struct TLSRTCPeerConnection {
+ struct TLSContext *context;
+ unsigned char stun_transcation_id[12];
+
+ char local_user[5];
+ char local_pwd[25];
+
+ unsigned char *remote_user;
+ int remote_user_len;
+ unsigned char *remote_pwd;
+ int remote_pwd_len;
+
+ tls_validation_function certificate_verify;
+
+ void *userdata;
+
+ unsigned char local_state;
+ unsigned char remote_state;
+
+ unsigned char active;
+
+#ifdef TLS_SRTP
+ struct SRTPContext *srtp_local;
+ struct SRTPContext *srtp_remote;
+#endif
+
+ struct TLSRTCPeerBuffer *write_buffer;
+ struct TLSRTCPeerBuffer *read_buffer;
+};
+
+#ifdef SSL_COMPATIBLE_INTERFACE
+
+typedef int (*SOCKET_RECV_CALLBACK)(int socket, void *buffer, size_t length, int flags);
+typedef int (*SOCKET_SEND_CALLBACK)(int socket, const void *buffer, size_t length, int flags);
+
+#ifndef _WIN32
+#include
+#endif
+#endif
+
+static const unsigned int version_id[] = {1, 1, 1, 0};
+static const unsigned int pk_id[] = {1, 1, 7, 0};
+static const unsigned int serial_id[] = {1, 1, 2, 1, 0};
+static const unsigned int issurer_id[] = {1, 1, 4, 0};
+static const unsigned int owner_id[] = {1, 1, 6, 0};
+static const unsigned int validity_id[] = {1, 1, 5, 0};
+static const unsigned int algorithm_id[] = {1, 1, 3, 0};
+static const unsigned int sign_id[] = {1, 3, 2, 1, 0};
+static const unsigned int priv_id[] = {1, 4, 0};
+static const unsigned int priv_der_id[] = {1, 3, 1, 0};
+static const unsigned int ecc_priv_id[] = {1, 2, 0};
+
+static const unsigned char country_oid[] = {0x55, 0x04, 0x06, 0x00};
+static const unsigned char state_oid[] = {0x55, 0x04, 0x08, 0x00};
+static const unsigned char location_oid[] = {0x55, 0x04, 0x07, 0x00};
+static const unsigned char entity_oid[] = {0x55, 0x04, 0x0A, 0x00};
+static const unsigned char subject_oid[] = {0x55, 0x04, 0x03, 0x00};
+static const unsigned char san_oid[] = {0x55, 0x1D, 0x11, 0x00};
+static const unsigned char ocsp_oid[] = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x00};
+
+static const unsigned char TLS_RSA_SIGN_RSA_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x00};
+static const unsigned char TLS_RSA_SIGN_MD5_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04, 0x00};
+static const unsigned char TLS_RSA_SIGN_SHA1_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x00};
+static const unsigned char TLS_RSA_SIGN_SHA256_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x00};
+static const unsigned char TLS_RSA_SIGN_SHA384_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C, 0x00};
+static const unsigned char TLS_RSA_SIGN_SHA512_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D, 0x00};
+
+// static const unsigned char TLS_ECDSA_SIGN_SHA1_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x01, 0x05, 0x00, 0x00};
+// static const unsigned char TLS_ECDSA_SIGN_SHA224_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01, 0x05, 0x00, 0x00};
+static const unsigned char TLS_ECDSA_SIGN_SHA256_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x05, 0x00, 0x00};
+// static const unsigned char TLS_ECDSA_SIGN_SHA384_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03, 0x05, 0x00, 0x00};
+// static const unsigned char TLS_ECDSA_SIGN_SHA512_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04, 0x05, 0x00, 0x00};
+
+static const unsigned char TLS_EC_PUBLIC_KEY_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x00};
+
+static const unsigned char TLS_EC_prime192v1_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01, 0x00};
+static const unsigned char TLS_EC_prime192v2_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x02, 0x00};
+static const unsigned char TLS_EC_prime192v3_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x03, 0x00};
+static const unsigned char TLS_EC_prime239v1_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x04, 0x00};
+static const unsigned char TLS_EC_prime239v2_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x05, 0x00};
+static const unsigned char TLS_EC_prime239v3_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x06, 0x00};
+static const unsigned char TLS_EC_prime256v1_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x00};
+
+#define TLS_EC_secp256r1_OID TLS_EC_prime256v1_OID
+static const unsigned char TLS_EC_secp224r1_OID[] = {0x2B, 0x81, 0x04, 0x00, 0x21, 0x00};
+static const unsigned char TLS_EC_secp384r1_OID[] = {0x2B, 0x81, 0x04, 0x00, 0x22, 0x00};
+static const unsigned char TLS_EC_secp521r1_OID[] = {0x2B, 0x81, 0x04, 0x00, 0x23, 0x00};
+
+struct TLSCertificate *asn1_parse(struct TLSContext *context, const unsigned char *buffer, unsigned int size, int client_cert);
+int _private_tls_update_hash(struct TLSContext *context, const unsigned char *in, unsigned int len, unsigned char direction, unsigned char connection_status);
+struct TLSPacket *tls_build_finished(struct TLSContext *context);
+unsigned int _private_tls_hmac_message(unsigned char local, struct TLSContext *context, const unsigned char *buf, int buf_len, const unsigned char *buf2, int buf_len2, unsigned char *out, unsigned int outlen, uint64_t remote_sequence_number);
+int tls_random(unsigned char *key, int len);
+void tls_destroy_packet(struct TLSPacket *packet);
+struct TLSPacket *tls_build_hello(struct TLSContext *context, int tls13_downgrade);
+struct TLSPacket *tls_build_certificate(struct TLSContext *context);
+struct TLSPacket *tls_build_done(struct TLSContext *context);
+struct TLSPacket *tls_build_alert(struct TLSContext *context, char critical, unsigned char code);
+struct TLSPacket *tls_build_change_cipher_spec(struct TLSContext *context);
+struct TLSPacket *tls_build_verify_request(struct TLSContext *context);
+int _private_tls_crypto_create(struct TLSContext *context, int key_length, unsigned char *localkey, unsigned char *localiv, unsigned char *remotekey, unsigned char *remoteiv);
+int _private_tls_get_hash(struct TLSContext *context, unsigned char *hout);
+int _private_tls_done_hash(struct TLSContext *context, unsigned char *hout);
+int _private_tls_get_hash_idx(struct TLSContext *context);
+int _private_tls_build_random(struct TLSPacket *packet);
+unsigned int _private_tls_mac_length(struct TLSContext *context);
+void _private_dtls_handshake_data(struct TLSContext *context, struct TLSPacket *packet, unsigned int dataframe);
+#ifdef TLS_FORWARD_SECRECY
+void _private_tls_dhe_free(struct TLSContext *context);
+void _private_tls_ecc_dhe_free(struct TLSContext *context);
+void _private_tls_dh_clear_key(DHKey *key);
+#endif
+
+#ifdef WITH_TLS_13
+struct TLSPacket *tls_build_encrypted_extensions(struct TLSContext *context);
+struct TLSPacket *tls_build_certificate_verify(struct TLSContext *context);
+#endif
+
+// dtls base secret
+static unsigned char dtls_secret[32];
+
+static unsigned char dependecies_loaded = 0;
+// not supported
+// static unsigned char TLS_DSA_SIGN_SHA1_OID[] = {0x2A, 0x86, 0x52, 0xCE, 0x38, 0x04, 0x03, 0x00};
+
+// base64 stuff
+static const char cd64[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
+
+void _private_b64_decodeblock(unsigned char in[4], unsigned char out[3]) {
+ out[0] = (unsigned char )(in[0] << 2 | in[1] >> 4);
+ out[1] = (unsigned char )(in[1] << 4 | in[2] >> 2);
+ out[2] = (unsigned char )(((in[2] << 6) & 0xc0) | in[3]);
+}
+
+int _private_b64_decode(const char *in_buffer, int in_buffer_size, unsigned char *out_buffer) {
+ unsigned char in[4], out[3], v;
+ int i, len;
+
+ const char *ptr = in_buffer;
+ char *out_ptr = (char *)out_buffer;
+
+ while (ptr <= in_buffer + in_buffer_size) {
+ for (len = 0, i = 0; i < 4 && (ptr <= in_buffer + in_buffer_size); i++) {
+ v = 0;
+ while ((ptr <= in_buffer + in_buffer_size) && v == 0) {
+ v = (unsigned char)ptr[0];
+ ptr++;
+ v = (unsigned char)((v < 43 || v > 122) ? 0 : cd64[v - 43]);
+ if (v)
+ v = (unsigned char)((v == '$') ? 0 : v - 61);
+ }
+ if (ptr <= in_buffer + in_buffer_size) {
+ len++;
+ if (v)
+ in[i] = (unsigned char)(v - 1);
+ } else {
+ in[i] = 0;
+ }
+ }
+ if (len) {
+ _private_b64_decodeblock(in, out);
+ for (i = 0; i < len - 1; i++) {
+ out_ptr[0] = out[i];
+ out_ptr++;
+ }
+ }
+ }
+ return (int)((intptr_t)out_ptr - (intptr_t)out_buffer);
+}
+
+void dtls_reset_cookie_secret() {
+ tls_random(dtls_secret, sizeof(dtls_secret));
+}
+
+void tls_init() {
+ if (dependecies_loaded)
+ return;
+ DEBUG_PRINT("Initializing dependencies\n");
+ dependecies_loaded = 1;
+#ifdef LTM_DESC
+ ltc_mp = ltm_desc;
+#else
+#ifdef TFM_DESC
+ ltc_mp = tfm_desc;
+#else
+#ifdef GMP_DESC
+ ltc_mp = gmp_desc;
+#endif
+#endif
+#endif
+ register_prng(&sprng_desc);
+ register_hash(&sha256_desc);
+ register_hash(&sha1_desc);
+ register_hash(&sha384_desc);
+ register_hash(&sha512_desc);
+ register_hash(&md5_desc);
+ register_cipher(&aes_desc);
+#ifdef TLS_FORWARD_SECRECY
+ init_curves();
+#endif
+ dtls_reset_cookie_secret();
+}
+
+#ifdef TLS_FORWARD_SECRECY
+int _private_tls_dh_shared_secret(DHKey *private_key, DHKey *public_key, unsigned char *out, unsigned long *outlen) {
+ void *tmp;
+ unsigned long x;
+ int err;
+
+ if ((!private_key) || (!public_key) || (!out) || (!outlen))
+ return TLS_GENERIC_ERROR;
+
+ /* compute y^x mod p */
+ if ((err = mp_init(&tmp)) != CRYPT_OK)
+ return err;
+
+ if ((err = mp_exptmod(public_key->y, private_key->x, private_key->p, tmp)) != CRYPT_OK) {
+ mp_clear(tmp);
+ return err;
+ }
+
+ x = (unsigned long)mp_unsigned_bin_size(tmp);
+ if (*outlen < x) {
+ err = CRYPT_BUFFER_OVERFLOW;
+ mp_clear(tmp);
+ return err;
+ }
+
+ if ((err = mp_to_unsigned_bin(tmp, out)) != CRYPT_OK) {
+ mp_clear(tmp);
+ return err;
+ }
+ *outlen = x;
+ mp_clear(tmp);
+ return 0;
+}
+
+unsigned char *_private_tls_decrypt_dhe(struct TLSContext *context, const unsigned char *buffer, unsigned int len, unsigned int *size, int clear_key) {
+ *size = 0;
+ if ((!len) || (!context) || (!context->dhe)) {
+ DEBUG_PRINT("No private DHE key set\n");
+ return NULL;
+ }
+
+ unsigned long out_size = len;
+ void *Yc = NULL;
+
+ if (mp_init(&Yc)) {
+ DEBUG_PRINT("ERROR CREATING Yc\n");
+ return NULL;
+ }
+ if (mp_read_unsigned_bin(Yc, (unsigned char *)buffer, len)) {
+ DEBUG_PRINT("ERROR LOADING DHE Yc\n");
+ mp_clear(Yc);
+ return NULL;
+ }
+
+ unsigned char *out = (unsigned char *)TLS_MALLOC(len);
+ DHKey client_key;
+ memset(&client_key, 0, sizeof(DHKey));
+
+ client_key.p = context->dhe->p;
+ client_key.g = context->dhe->g;
+ client_key.y = Yc;
+ int err = _private_tls_dh_shared_secret(context->dhe, &client_key, out, &out_size);
+ // don't delete p and g
+ client_key.p = NULL;
+ client_key.g = NULL;
+ _private_tls_dh_clear_key(&client_key);
+ // not needing the dhe key anymore
+ if (clear_key)
+ _private_tls_dhe_free(context);
+ if (err) {
+ DEBUG_PRINT("DHE DECRYPT ERROR %i\n", err);
+ TLS_FREE(out);
+ return NULL;
+ }
+ DEBUG_PRINT("OUT_SIZE: %lu\n", out_size);
+ DEBUG_DUMP_HEX_LABEL("DHE", out, out_size);
+ *size = (unsigned int)out_size;
+ return out;
+}
+
+unsigned char *_private_tls_decrypt_ecc_dhe(struct TLSContext *context, const unsigned char *buffer, unsigned int len, unsigned int *size, int clear_key) {
+ *size = 0;
+ if ((!len) || (!context) || (!context->ecc_dhe)) {
+ DEBUG_PRINT("No private ECC DHE key set\n");
+ return NULL;
+ }
+
+ const struct ECCCurveParameters *curve;
+ if (context->curve)
+ curve = context->curve;
+ else
+ curve = default_curve;
+
+ ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&curve->dp;
+
+ ecc_key client_key;
+ memset(&client_key, 0, sizeof(client_key));
+ if (ecc_ansi_x963_import_ex(buffer, len, &client_key, dp)) {
+ DEBUG_PRINT("Error importing ECC DHE key\n");
+ return NULL;
+ }
+ unsigned char *out = (unsigned char *)TLS_MALLOC(len);
+ unsigned long out_size = len;
+
+ int err = ecc_shared_secret(context->ecc_dhe, &client_key, out, &out_size);
+ ecc_free(&client_key);
+ if (clear_key)
+ _private_tls_ecc_dhe_free(context);
+ if (err) {
+ DEBUG_PRINT("ECC DHE DECRYPT ERROR %i\n", err);
+ TLS_FREE(out);
+ return NULL;
+ }
+ DEBUG_PRINT("OUT_SIZE: %lu\n", out_size);
+ DEBUG_DUMP_HEX_LABEL("ECC DHE", out, out_size);
+ *size = (unsigned int)out_size;
+ return out;
+}
+#endif
+
+unsigned char *_private_tls_decrypt_rsa(struct TLSContext *context, const unsigned char *buffer, unsigned int len, unsigned int *size) {
+ *size = 0;
+ if ((!len) || (!context) || (!context->private_key) || (!context->private_key->der_bytes) || (!context->private_key->der_len)) {
+ DEBUG_PRINT("No private key set\n");
+ return NULL;
+ }
+ tls_init();
+ rsa_key key;
+ int err;
+ err = rsa_import(context->private_key->der_bytes, context->private_key->der_len, &key);
+
+ if (err) {
+ DEBUG_PRINT("Error importing RSA key (code: %i)\n", err);
+ return NULL;
+ }
+ unsigned char *out = (unsigned char *)TLS_MALLOC(len);
+ unsigned long out_size = len;
+ int res = 0;
+
+#if (CRYPT >= 0x0118)
+ err = rsa_decrypt_key_ex(buffer, len, out, &out_size, NULL, 0, -1, -1, LTC_PKCS_1_V1_5, &res, &key);
+#else
+ err = rsa_decrypt_key_ex(buffer, len, out, &out_size, NULL, 0, -1, LTC_PKCS_1_V1_5, &res, &key);
+#endif
+ rsa_free(&key);
+
+ if ((err) || (out_size != 48) || (ntohs(*(unsigned short *)out) != context->version)) {
+ // generate a random secret and continue (ROBOT fix)
+ // silently ignore and generate a random secret
+ out_size = 48;
+ tls_random(out, out_size);
+ *(unsigned short *)out = htons(context->version);
+ }
+ *size = (unsigned int)out_size;
+ return out;
+}
+
+unsigned char *_private_tls_encrypt_rsa(struct TLSContext *context, const unsigned char *buffer, unsigned int len, unsigned int *size) {
+ *size = 0;
+ if ((!len) || (!context) || (!context->certificates) || (!context->certificates_count) || (!context->certificates[0]) ||
+ (!context->certificates[0]->der_bytes) || (!context->certificates[0]->der_len)) {
+ DEBUG_PRINT("No certificate set\n");
+ return NULL;
+ }
+ tls_init();
+ rsa_key key;
+ int err;
+ err = rsa_import(context->certificates[0]->der_bytes, context->certificates[0]->der_len, &key);
+
+ if (err) {
+ DEBUG_PRINT("Error importing RSA certificate (code: %i)\n", err);
+ return NULL;
+ }
+ unsigned long out_size = TLS_MAX_RSA_KEY;
+ unsigned char *out = (unsigned char *)TLS_MALLOC(out_size);
+ int hash_idx = find_hash("sha256");
+ int prng_idx = find_prng("sprng");
+#if (CRYPT >= 0x0118)
+ err = rsa_encrypt_key_ex(buffer, len, out, &out_size, (unsigned char *)"Concept", 7, NULL, prng_idx, hash_idx, -1, LTC_PKCS_1_V1_5, &key);
+#else
+ err = rsa_encrypt_key_ex(buffer, len, out, &out_size, (unsigned char *)"Concept", 7, NULL, prng_idx, hash_idx, LTC_PKCS_1_V1_5, &key);
+#endif
+ rsa_free(&key);
+ if ((err) || (!out_size)) {
+ TLS_FREE(out);
+ return NULL;
+ }
+ *size = (unsigned int)out_size;
+ return out;
+}
+
+#ifdef TLS_LEGACY_SUPPORT
+int _private_rsa_verify_hash_md5sha1(const unsigned char *sig, unsigned long siglen, unsigned char *hash, unsigned long hashlen, int *stat, rsa_key *key) {
+ unsigned long modulus_bitlen, modulus_bytelen, x;
+ int err;
+ unsigned char *tmpbuf = NULL;
+
+ if ((hash == NULL) || (sig == NULL) || (stat == NULL) || (key == NULL) || (!siglen) || (!hashlen))
+ return TLS_GENERIC_ERROR;
+
+ *stat = 0;
+
+ modulus_bitlen = mp_count_bits((key->N));
+
+ modulus_bytelen = mp_unsigned_bin_size((key->N));
+ if (modulus_bytelen != siglen)
+ return TLS_GENERIC_ERROR;
+
+ tmpbuf = (unsigned char *)TLS_MALLOC(siglen);
+ if (!tmpbuf)
+ return TLS_GENERIC_ERROR;
+
+ x = siglen;
+ if ((err = ltc_mp.rsa_me(sig, siglen, tmpbuf, &x, PK_PUBLIC, key)) != CRYPT_OK) {
+ TLS_FREE(tmpbuf);
+ return err;
+ }
+
+ if (x != siglen) {
+ TLS_FREE(tmpbuf);
+ return CRYPT_INVALID_PACKET;
+ }
+ unsigned long out_len = siglen;
+ unsigned char *out = (unsigned char *)TLS_MALLOC(siglen);
+ if (!out) {
+ TLS_FREE(tmpbuf);
+ return TLS_GENERIC_ERROR;
+ }
+
+ int decoded = 0;
+ err = pkcs_1_v1_5_decode(tmpbuf, x, LTC_PKCS_1_EMSA, modulus_bitlen, out, &out_len, &decoded);
+ if (decoded) {
+ if (out_len == hashlen) {
+ if (!memcmp(out, hash, hashlen))
+ *stat = 1;
+ }
+ }
+
+ TLS_FREE(tmpbuf);
+ TLS_FREE(out);
+ return err;
+}
+#endif
+
+int _private_tls_verify_rsa(struct TLSContext *context, unsigned int hash_type, const unsigned char *buffer, unsigned int len, const unsigned char *message, unsigned int message_len) {
+ tls_init();
+ rsa_key key;
+ int err;
+
+ if (context->is_server) {
+ if ((!len) || (!context->client_certificates) || (!context->client_certificates_count) || (!context->client_certificates[0]) ||
+ (!context->client_certificates[0]->der_bytes) || (!context->client_certificates[0]->der_len)) {
+ DEBUG_PRINT("No client certificate set\n");
+ return TLS_GENERIC_ERROR;
+ }
+ err = rsa_import(context->client_certificates[0]->der_bytes, context->client_certificates[0]->der_len, &key);
+ } else {
+ if ((!len) || (!context->certificates) || (!context->certificates_count) || (!context->certificates[0]) ||
+ (!context->certificates[0]->der_bytes) || (!context->certificates[0]->der_len)) {
+ DEBUG_PRINT("No server certificate set\n");
+ return TLS_GENERIC_ERROR;
+ }
+ err = rsa_import(context->certificates[0]->der_bytes, context->certificates[0]->der_len, &key);
+ }
+ if (err) {
+ DEBUG_PRINT("Error importing RSA certificate (code: %i)\n", err);
+ return TLS_GENERIC_ERROR;
+ }
+ int hash_idx = -1;
+ unsigned char hash[TLS_MAX_HASH_LEN];
+ unsigned int hash_len = 0;
+ hash_state state;
+ switch (hash_type) {
+ case md5:
+ hash_idx = find_hash("md5");
+ err = md5_init(&state);
+ TLS_ERROR(err, break);
+ err = md5_process(&state, message, message_len);
+ TLS_ERROR(err, break);
+ err = md5_done(&state, hash);
+ TLS_ERROR(err, break);
+ hash_len = 16;
+ break;
+ case sha1:
+ hash_idx = find_hash("sha1");
+ err = sha1_init(&state);
+ TLS_ERROR(err, break)
+ err = sha1_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha1_done(&state, hash);
+ TLS_ERROR(err, break)
+ hash_len = 20;
+ break;
+ case sha256:
+ hash_idx = find_hash("sha256");
+ err = sha256_init(&state);
+ TLS_ERROR(err, break)
+ err = sha256_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha256_done(&state, hash);
+ TLS_ERROR(err, break)
+ hash_len = 32;
+ break;
+ case sha384:
+ hash_idx = find_hash("sha384");
+ err = sha384_init(&state);
+ TLS_ERROR(err, break)
+ err = sha384_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha384_done(&state, hash);
+ TLS_ERROR(err, break)
+ hash_len = 48;
+ break;
+ case sha512:
+ hash_idx = find_hash("sha512");
+ err = sha512_init(&state);
+ TLS_ERROR(err, break)
+ err = sha512_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha512_done(&state, hash);
+ TLS_ERROR(err, break)
+ hash_len = 64;
+ break;
+#ifdef TLS_LEGACY_SUPPORT
+ case _md5_sha1:
+ hash_idx = find_hash("md5");
+ err = md5_init(&state);
+ TLS_ERROR(err, break)
+ err = md5_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = md5_done(&state, hash);
+ TLS_ERROR(err, break)
+ hash_idx = find_hash("sha1");
+ err = sha1_init(&state);
+ TLS_ERROR(err, break)
+ err = sha1_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha1_done(&state, hash + 16);
+ TLS_ERROR(err, break)
+ hash_len = 36;
+ err = sha1_init(&state);
+ TLS_ERROR(err, break)
+ err = sha1_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha1_done(&state, hash + 16);
+ TLS_ERROR(err, break)
+ hash_len = 36;
+ break;
+#endif
+ }
+ if ((hash_idx < 0) || (err)) {
+ DEBUG_PRINT("Unsupported hash type: %i\n", hash_type);
+ return TLS_GENERIC_ERROR;
+ }
+ int rsa_stat = 0;
+#ifdef TLS_LEGACY_SUPPORT
+ if (hash_type == _md5_sha1)
+ err = _private_rsa_verify_hash_md5sha1(buffer, len, hash, hash_len, &rsa_stat, &key);
+ else
+#endif
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13))
+ err = rsa_verify_hash_ex(buffer, len, hash, hash_len, LTC_PKCS_1_PSS, hash_idx, hash_len, &rsa_stat, &key);
+ else
+#endif
+ err = rsa_verify_hash_ex(buffer, len, hash, hash_len, LTC_PKCS_1_V1_5, hash_idx, 0, &rsa_stat, &key);
+ rsa_free(&key);
+ if (err)
+ return 0;
+ return rsa_stat;
+}
+
+#ifdef TLS_LEGACY_SUPPORT
+int _private_rsa_sign_hash_md5sha1(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, rsa_key *key) {
+ unsigned long modulus_bitlen, modulus_bytelen, x;
+ int err;
+
+ if ((in == NULL) || (out == NULL) || (outlen == NULL) || (key == NULL))
+ return TLS_GENERIC_ERROR;
+
+ modulus_bitlen = mp_count_bits((key->N));
+
+ modulus_bytelen = mp_unsigned_bin_size((key->N));
+ if (modulus_bytelen > *outlen) {
+ *outlen = modulus_bytelen;
+ return CRYPT_BUFFER_OVERFLOW;
+ }
+ x = modulus_bytelen;
+ err = pkcs_1_v1_5_encode(in, inlen, LTC_PKCS_1_EMSA, modulus_bitlen, NULL, 0, out, &x);
+ if (err != CRYPT_OK)
+ return err;
+
+ return ltc_mp.rsa_me(out, x, out, outlen, PK_PRIVATE, key);
+}
+#endif
+
+int _private_tls_sign_rsa(struct TLSContext *context, unsigned int hash_type, const unsigned char *message, unsigned int message_len, unsigned char *out, unsigned long *outlen) {
+ if ((!outlen) || (!context) || (!out) || (!outlen) || (!context->private_key) || (!context->private_key->der_bytes) || (!context->private_key->der_len)) {
+ DEBUG_PRINT("No private key set\n");
+ return TLS_GENERIC_ERROR;
+ }
+ tls_init();
+ rsa_key key;
+ int err;
+ err = rsa_import(context->private_key->der_bytes, context->private_key->der_len, &key);
+
+ if (err) {
+ DEBUG_PRINT("Error importing RSA certificate (code: %i)\n", err);
+ return TLS_GENERIC_ERROR;
+ }
+ int hash_idx = -1;
+ unsigned char hash[TLS_MAX_HASH_LEN];
+ unsigned int hash_len = 0;
+ hash_state state;
+ switch (hash_type) {
+ case md5:
+ hash_idx = find_hash("md5");
+ err = md5_init(&state);
+ TLS_ERROR(err, break)
+ err = md5_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = md5_done(&state, hash);
+ TLS_ERROR(err, break)
+ hash_len = 16;
+ break;
+ case sha1:
+ hash_idx = find_hash("sha1");
+ err = sha1_init(&state);
+ TLS_ERROR(err, break)
+ err = sha1_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha1_done(&state, hash);
+ TLS_ERROR(err, break)
+ hash_len = 20;
+ break;
+ case sha256:
+ hash_idx = find_hash("sha256");
+ err = sha256_init(&state);
+ TLS_ERROR(err, break)
+ err = sha256_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha256_done(&state, hash);
+ TLS_ERROR(err, break)
+ hash_len = 32;
+ break;
+ case sha384:
+ hash_idx = find_hash("sha384");
+ err = sha384_init(&state);
+ TLS_ERROR(err, break)
+ err = sha384_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha384_done(&state, hash);
+ TLS_ERROR(err, break)
+ hash_len = 48;
+ break;
+ case sha512:
+ hash_idx = find_hash("sha512");
+ err = sha512_init(&state);
+ TLS_ERROR(err, break)
+ err = sha512_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha512_done(&state, hash);
+ TLS_ERROR(err, break)
+ hash_len = 64;
+ break;
+ case _md5_sha1:
+ hash_idx = find_hash("md5");
+ err = md5_init(&state);
+ TLS_ERROR(err, break)
+ err = md5_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = md5_done(&state, hash);
+ TLS_ERROR(err, break)
+ hash_idx = find_hash("sha1");
+ err = sha1_init(&state);
+ TLS_ERROR(err, break)
+ err = sha1_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha1_done(&state, hash + 16);
+ TLS_ERROR(err, break)
+ hash_len = 36;
+ err = sha1_init(&state);
+ TLS_ERROR(err, break)
+ err = sha1_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha1_done(&state, hash + 16);
+ TLS_ERROR(err, break)
+ hash_len = 36;
+ break;
+ }
+#ifdef TLS_LEGACY_SUPPORT
+ if (hash_type == _md5_sha1) {
+ if (err) {
+ DEBUG_PRINT("Unsupported hash type: %i\n", hash_type);
+ return TLS_GENERIC_ERROR;
+ }
+ err = _private_rsa_sign_hash_md5sha1(hash, hash_len, out, outlen, &key);
+ } else
+#endif
+ {
+ if ((hash_idx < 0) || (err)) {
+ DEBUG_PRINT("Unsupported hash type: %i\n", hash_type);
+ return TLS_GENERIC_ERROR;
+ }
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13))
+ err = rsa_sign_hash_ex(hash, hash_len, out, outlen, LTC_PKCS_1_PSS, NULL, find_prng("sprng"), hash_idx, hash_type == sha256 ? 32 : 48, &key);
+ else
+#endif
+ err = rsa_sign_hash_ex(hash, hash_len, out, outlen, LTC_PKCS_1_V1_5, NULL, find_prng("sprng"), hash_idx, 0, &key);
+ }
+ rsa_free(&key);
+ if (err)
+ return 0;
+
+ return 1;
+}
+
+#ifdef TLS_ECDSA_SUPPORTED
+
+#if CRYPT >= 0x0118
+
+int _private_tls_is_point(ecc_key *key) {
+ void *prime, *a, *b, *t1, *t2;
+ int err;
+
+ void *x = key->pubkey.x;
+ void *y = key->pubkey.y;
+
+ prime = key->dp.prime;
+ b = key->dp.B;
+ a = key->dp.A;
+
+ if ((err = mp_init_multi(&t1, &t2, NULL)) != CRYPT_OK)
+ return err;
+
+ /* compute y^2 */
+ if ((err = mp_sqr(y, t1)) != CRYPT_OK)
+ goto cleanup;
+
+ /* compute x^3 */
+ if ((err = mp_sqr(x, t2)) != CRYPT_OK)
+ goto cleanup;
+ if ((err = mp_mod(t2, prime, t2)) != CRYPT_OK)
+ goto cleanup;
+ if ((err = mp_mul(x, t2, t2)) != CRYPT_OK)
+ goto cleanup;
+
+ /* compute y^2 - x^3 */
+ if ((err = mp_sub(t1, t2, t1)) != CRYPT_OK)
+ goto cleanup;
+
+ /* compute y^2 - x^3 - a*x */
+ if ((err = mp_submod(prime, a, prime, t2)) != CRYPT_OK)
+ goto cleanup;
+ if ((err = mp_mulmod(t2, x, prime, t2)) != CRYPT_OK)
+ goto cleanup;
+ if ((err = mp_addmod(t1, t2, prime, t1)) != CRYPT_OK)
+ goto cleanup;
+
+ /* adjust range (0, prime) */
+ while (mp_cmp_d(t1, 0) == LTC_MP_LT) {
+ if ((err = mp_add(t1, prime, t1)) != CRYPT_OK)
+ goto cleanup;
+ }
+ while (mp_cmp(t1, prime) != LTC_MP_LT) {
+ if ((err = mp_sub(t1, prime, t1)) != CRYPT_OK)
+ goto cleanup;
+ }
+
+ /* compare to b */
+ if (mp_cmp(t1, b) != LTC_MP_EQ) {
+ err = CRYPT_INVALID_PACKET;
+ } else {
+ err = CRYPT_OK;
+ }
+
+cleanup:
+ mp_clear_multi(t1, t2, NULL);
+ return err;
+}
+
+#else
+
+static int _private_tls_is_point(ecc_key *key) {
+ void *prime, *b, *t1, *t2;
+ int err;
+
+ if ((err = mp_init_multi(&prime, &b, &t1, &t2, NULL)) != CRYPT_OK) {
+ return err;
+ }
+
+ /* load prime and b */
+ if ((err = mp_read_radix(prime, (const char *)TLS_TOMCRYPT_PRIVATE_DP(key)->prime, 16)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_read_radix(b, (const char *)TLS_TOMCRYPT_PRIVATE_DP(key)->B, 16)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* compute y^2 */
+ if ((err = mp_sqr(key->pubkey.y, t1)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* compute x^3 */
+ if ((err = mp_sqr(key->pubkey.x, t2)) != CRYPT_OK) {
+ goto error;
+ }
+
+ if ((err = mp_mod(t2, prime, t2)) != CRYPT_OK) {
+ goto error;
+ }
+
+ if ((err = mp_mul(key->pubkey.x, t2, t2)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* compute y^2 - x^3 */
+ if ((err = mp_sub(t1, t2, t1)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* compute y^2 - x^3 + 3x */
+ if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) {
+ goto error;
+ }
+ if ((err = mp_mod(t1, prime, t1)) != CRYPT_OK) {
+ goto error;
+ }
+ while (mp_cmp_d(t1, 0) == LTC_MP_LT) {
+ if ((err = mp_add(t1, prime, t1)) != CRYPT_OK) {
+ goto error;
+ }
+ }
+ while (mp_cmp(t1, prime) != LTC_MP_LT) {
+ if ((err = mp_sub(t1, prime, t1)) != CRYPT_OK) {
+ goto error;
+ }
+ }
+
+ /* compare to b */
+ if (mp_cmp(t1, b) != LTC_MP_EQ) {
+ err = CRYPT_INVALID_PACKET;
+ } else {
+ err = CRYPT_OK;
+ }
+
+error:
+ mp_clear_multi(prime, b, t1, t2, NULL);
+ return err;
+}
+
+#endif
+
+int _private_tls_ecc_import_key(const unsigned char *private_key, int private_len, const unsigned char *public_key, int public_len, ecc_key *key, const ltc_ecc_set_type *dp) {
+ int err;
+
+ if ((!key) || (!ltc_mp.name))
+ return CRYPT_MEM;
+
+ key->type = PK_PRIVATE;
+
+#if CRYPT >= 0x0118
+ if ((err = ecc_set_curve(dp, key)) != CRYPT_OK)
+ return err;
+#else
+ if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK)
+ return CRYPT_MEM;
+#endif
+
+ if ((public_len) && (!public_key[0])) {
+ public_key++;
+ public_len--;
+ }
+ if ((err = mp_read_unsigned_bin(key->pubkey.x, (unsigned char *)public_key + 1, (public_len - 1) >> 1)) != CRYPT_OK) {
+ mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
+ return err;
+ }
+
+ if ((err = mp_read_unsigned_bin(key->pubkey.y, (unsigned char *)public_key + 1 + ((public_len - 1) >> 1), (public_len - 1) >> 1)) != CRYPT_OK) {
+ mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
+ return err;
+ }
+
+ if ((err = mp_read_unsigned_bin(key->k, (unsigned char *)private_key, private_len)) != CRYPT_OK) {
+ mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
+ return err;
+ }
+
+#if CRYPT < 0x0118
+ TLS_TOMCRYPT_PRIVATE_SET_INDEX(key, -1);
+ TLS_TOMCRYPT_PRIVATE_DP(key) = dp;
+#endif
+
+ /* set z */
+ if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) {
+ mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
+ return err;
+ }
+
+ /* is it a point on the curve? */
+ if ((err = _private_tls_is_point(key)) != CRYPT_OK) {
+ DEBUG_PRINT("KEY IS NOT ON CURVE\n");
+ mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
+ return err;
+ }
+
+ /* we're good */
+ return CRYPT_OK;
+}
+
+int _private_tls_sign_ecdsa(struct TLSContext *context, unsigned int hash_type, const unsigned char *message, unsigned int message_len, unsigned char *out, unsigned long *outlen) {
+ if ((!outlen) || (!context) || (!out) || (!outlen) || (!context->ec_private_key) ||
+ (!context->ec_private_key->priv) || (!context->ec_private_key->priv_len) || (!context->ec_private_key->pk) || (!context->ec_private_key->pk_len)) {
+ DEBUG_PRINT("No private ECDSA key set\n");
+ return TLS_GENERIC_ERROR;
+ }
+
+ const struct ECCCurveParameters *curve = NULL;
+
+ switch (context->ec_private_key->ec_algorithm) {
+ case 19:
+ curve = &secp192r1;
+ break;
+ case 20:
+ curve = &secp224k1;
+ break;
+ case 21:
+ curve = &secp224r1;
+ break;
+ case 22:
+ curve = &secp256k1;
+ break;
+ case 23:
+ curve = &secp256r1;
+ break;
+ case 24:
+ curve = &secp384r1;
+ break;
+ case 25:
+ curve = &secp521r1;
+ break;
+ default:
+ DEBUG_PRINT("UNSUPPORTED CURVE\n");
+ }
+
+ if (!curve)
+ return TLS_GENERIC_ERROR;
+
+ tls_init();
+ ecc_key key;
+ int err;
+ memset(&key, 0, sizeof(key));
+ ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&curve->dp;
+
+ // broken ... fix this
+ err = _private_tls_ecc_import_key(context->ec_private_key->priv, context->ec_private_key->priv_len, context->ec_private_key->pk, context->ec_private_key->pk_len, &key, dp);
+ if (err) {
+ DEBUG_PRINT("Error importing ECC certificate (code: %i)\n", (int)err);
+ return TLS_GENERIC_ERROR;
+ }
+ unsigned char hash[TLS_MAX_HASH_LEN];
+ unsigned int hash_len = 0;
+ hash_state state;
+ switch (hash_type) {
+ case md5:
+ err = md5_init(&state);
+ TLS_ERROR(err, break)
+ err = md5_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = md5_done(&state, hash);
+ TLS_ERROR(err, break)
+ hash_len = 16;
+ break;
+ case sha1:
+ err = sha1_init(&state);
+ TLS_ERROR(err, break)
+ err = sha1_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha1_done(&state, hash);
+ TLS_ERROR(err, break)
+ hash_len = 20;
+ break;
+ case sha256:
+ err = sha256_init(&state);
+ TLS_ERROR(err, break)
+ err = sha256_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha256_done(&state, hash);
+ TLS_ERROR(err, break)
+ hash_len = 32;
+ break;
+ case sha384:
+ err = sha384_init(&state);
+ TLS_ERROR(err, break)
+ err = sha384_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha384_done(&state, hash);
+ TLS_ERROR(err, break)
+ hash_len = 48;
+ break;
+ case sha512:
+ err = sha512_init(&state);
+ TLS_ERROR(err, break)
+ err = sha512_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha512_done(&state, hash);
+ TLS_ERROR(err, break)
+ hash_len = 64;
+ break;
+ case _md5_sha1:
+ err = md5_init(&state);
+ TLS_ERROR(err, break)
+ err = md5_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = md5_done(&state, hash);
+ TLS_ERROR(err, break)
+ err = sha1_init(&state);
+ TLS_ERROR(err, break)
+ err = sha1_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha1_done(&state, hash + 16);
+ TLS_ERROR(err, break)
+ hash_len = 36;
+ err = sha1_init(&state);
+ TLS_ERROR(err, break)
+ err = sha1_process(&state, message, message_len);
+ TLS_ERROR(err, break)
+ err = sha1_done(&state, hash + 16);
+ TLS_ERROR(err, break)
+ hash_len = 36;
+ break;
+ }
+
+ if (err) {
+ DEBUG_PRINT("Unsupported hash type: %i\n", hash_type);
+ return TLS_GENERIC_ERROR;
+ }
+
+ // "Let z be the Ln leftmost bits of e, where Ln is the bit length of the group order n."
+ if (hash_len > (unsigned int)curve->size)
+ hash_len = (unsigned int)curve->size;
+
+ err = ecc_sign_hash(hash, hash_len, out, outlen, NULL, find_prng("sprng"), &key);
+ DEBUG_DUMP_HEX_LABEL("ECC SIGNATURE", out, *outlen);
+ ecc_free(&key);
+ if (err)
+ return 0;
+
+ return 1;
+}
+
+#if defined(TLS_CLIENT_ECDSA) || defined(WITH_TLS_13)
+int _private_tls_ecc_import_pk(const unsigned char *public_key, int public_len, ecc_key *key, const ltc_ecc_set_type *dp) {
+ int err;
+
+ if ((!key) || (!ltc_mp.name))
+ return CRYPT_MEM;
+
+ key->type = PK_PUBLIC;
+
+#if CRYPT >= 0x0118
+ if ((err = ecc_set_curve(dp, key)) != CRYPT_OK)
+ return err;
+#else
+ if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK)
+ return CRYPT_MEM;
+#endif
+
+ if ((public_len) && (!public_key[0])) {
+ public_key++;
+ public_len--;
+ }
+ if ((err = mp_read_unsigned_bin(key->pubkey.x, (unsigned char *)public_key + 1, (public_len - 1) >> 1)) != CRYPT_OK) {
+ mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
+ return err;
+ }
+
+ if ((err = mp_read_unsigned_bin(key->pubkey.y, (unsigned char *)public_key + 1 + ((public_len - 1) >> 1), (public_len - 1) >> 1)) != CRYPT_OK) {
+ mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
+ return err;
+ }
+
+#if CRYPT < 0x0118
+ TLS_TOMCRYPT_PRIVATE_SET_INDEX(key, -1);
+ TLS_TOMCRYPT_PRIVATE_DP(key) = dp;
+#endif
+
+ /* set z */
+ if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) {
+ mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
+ return err;
+ }
+
+ /* is it a point on the curve? */
+ if ((err = _private_tls_is_point(key)) != CRYPT_OK) {
+ DEBUG_PRINT("KEY IS NOT ON CURVE\n");
+ mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL);
+ return err;
+ }
+
+ /* we're good */
+ return CRYPT_OK;
+}
+
+int _private_tls_verify_ecdsa(struct TLSContext *context, unsigned int hash_type, const unsigned char *buffer, unsigned int len, const unsigned char *message, unsigned int message_len, const struct ECCCurveParameters *curve_hint) {
+ tls_init();
+ ecc_key key;
+ int err;
+
+ if (!curve_hint)
+ curve_hint = context->curve;
+
+ if (context->is_server) {
+ if ((!len) || (!context->client_certificates) || (!context->client_certificates_count) || (!context->client_certificates[0]) ||
+ (!context->client_certificates[0]->pk) || (!context->client_certificates[0]->pk_len) || (!curve_hint)) {
+ DEBUG_PRINT("No client certificate set\n");
+ return TLS_GENERIC_ERROR;
+ }
+ err = _private_tls_ecc_import_pk(context->client_certificates[0]->pk, context->client_certificates[0]->pk_len, &key, (ltc_ecc_set_type *)&curve_hint->dp);
+ } else {
+ if ((!len) || (!context->certificates) || (!context->certificates_count) || (!context->certificates[0]) ||
+ (!context->certificates[0]->pk) || (!context->certificates[0]->pk_len) || (!curve_hint)) {
+ DEBUG_PRINT("No server certificate set\n");
+ return TLS_GENERIC_ERROR;
+ }
+ err = _private_tls_ecc_import_pk(context->certificates[0]->pk, context->certificates[0]->pk_len, &key, (ltc_ecc_set_type *)&curve_hint->dp);
+ }
+ if (err) {
+ DEBUG_PRINT("Error importing ECC certificate (code: %i)", err);
+ return TLS_GENERIC_ERROR;
+ }
+ int hash_idx = -1;
+ unsigned char hash[TLS_MAX_HASH_LEN];
+ unsigned int hash_len = 0;
+ hash_state state;
+ switch (hash_type) {
+ case md5:
+ hash_idx = find_hash("md5");
+ err = md5_init(&state);
+ if (!err) {
+ err = md5_process(&state, message, message_len);
+ if (!err)
+ err = md5_done(&state, hash);
+ }
+ hash_len = 16;
+ break;
+ case sha1:
+ hash_idx = find_hash("sha1");
+ err = sha1_init(&state);
+ if (!err) {
+ err = sha1_process(&state, message, message_len);
+ if (!err)
+ err = sha1_done(&state, hash);
+ }
+ hash_len = 20;
+ break;
+ case sha256:
+ hash_idx = find_hash("sha256");
+ err = sha256_init(&state);
+ if (!err) {
+ err = sha256_process(&state, message, message_len);
+ if (!err)
+ err = sha256_done(&state, hash);
+ }
+ hash_len = 32;
+ break;
+ case sha384:
+ hash_idx = find_hash("sha384");
+ err = sha384_init(&state);
+ if (!err) {
+ err = sha384_process(&state, message, message_len);
+ if (!err)
+ err = sha384_done(&state, hash);
+ }
+ hash_len = 48;
+ break;
+ case sha512:
+ hash_idx = find_hash("sha512");
+ err = sha512_init(&state);
+ if (!err) {
+ err = sha512_process(&state, message, message_len);
+ if (!err)
+ err = sha512_done(&state, hash);
+ }
+ hash_len = 64;
+ break;
+#ifdef TLS_LEGACY_SUPPORT
+ case _md5_sha1:
+ hash_idx = find_hash("md5");
+ err = md5_init(&state);
+ if (!err) {
+ err = md5_process(&state, message, message_len);
+ if (!err)
+ err = md5_done(&state, hash);
+ }
+ hash_idx = find_hash("sha1");
+ err = sha1_init(&state);
+ if (!err) {
+ err = sha1_process(&state, message, message_len);
+ if (!err)
+ err = sha1_done(&state, hash + 16);
+ }
+ hash_len = 36;
+ err = sha1_init(&state);
+ if (!err) {
+ err = sha1_process(&state, message, message_len);
+ if (!err)
+ err = sha1_done(&state, hash + 16);
+ }
+ hash_len = 36;
+ break;
+#endif
+ }
+ if ((hash_idx < 0) || (err)) {
+ DEBUG_PRINT("Unsupported hash type: %i\n", hash_type);
+ return TLS_GENERIC_ERROR;
+ }
+ int ecc_stat = 0;
+ err = ecc_verify_hash(buffer, len, hash, hash_len, &ecc_stat, &key);
+ ecc_free(&key);
+ if (err)
+ return 0;
+ return ecc_stat;
+}
+#endif
+
+#endif
+
+unsigned int _private_tls_random_int(int limit) {
+ unsigned int res = 0;
+ tls_random((unsigned char *)&res, sizeof(int));
+ if (limit)
+ res %= limit;
+ return res;
+}
+
+void _private_tls_sleep(unsigned int microseconds) {
+#ifdef _WIN32
+ Sleep(microseconds/1000);
+#else
+ struct timespec ts;
+
+ ts.tv_sec = (unsigned int) (microseconds / 1000000);
+ ts.tv_nsec = (unsigned int) (microseconds % 1000000) * 1000ul;
+
+ nanosleep(&ts, NULL);
+#endif
+}
+
+void _private_random_sleep(struct TLSContext *context, int max_microseconds) {
+ if (context)
+ context->sleep_until = (unsigned int)time(NULL) + _private_tls_random_int(max_microseconds/1000000 * TLS_MAX_ERROR_IDLE_S);
+ else
+ _private_tls_sleep(_private_tls_random_int(max_microseconds));
+}
+
+void _private_tls_prf_helper(int hash_idx, unsigned long dlen, unsigned char *output, unsigned int outlen, const unsigned char *secret, const unsigned int secret_len,
+ const unsigned char *label, unsigned int label_len, unsigned char *seed, unsigned int seed_len,
+ unsigned char *seed_b, unsigned int seed_b_len) {
+ unsigned char digest_out0[TLS_MAX_HASH_LEN];
+ unsigned char digest_out1[TLS_MAX_HASH_LEN];
+ unsigned int i;
+ hmac_state hmac;
+
+ hmac_init(&hmac, hash_idx, secret, secret_len);
+ hmac_process(&hmac, label, label_len);
+
+ hmac_process(&hmac, seed, seed_len);
+ if ((seed_b) && (seed_b_len))
+ hmac_process(&hmac, seed_b, seed_b_len);
+ hmac_done(&hmac, digest_out0, &dlen);
+ int idx = 0;
+ while (outlen) {
+ hmac_init(&hmac, hash_idx, secret, secret_len);
+ hmac_process(&hmac, digest_out0, dlen);
+ hmac_process(&hmac, label, label_len);
+ hmac_process(&hmac, seed, seed_len);
+ if ((seed_b) && (seed_b_len))
+ hmac_process(&hmac, seed_b, seed_b_len);
+ hmac_done(&hmac, digest_out1, &dlen);
+
+ unsigned int copylen = outlen;
+ if (copylen > dlen)
+ copylen = dlen;
+
+ for (i = 0; i < copylen; i++) {
+ output[idx++] ^= digest_out1[i];
+ outlen--;
+ }
+
+ if (!outlen)
+ break;
+
+ hmac_init(&hmac, hash_idx, secret, secret_len);
+ hmac_process(&hmac, digest_out0, dlen);
+ hmac_done(&hmac, digest_out0, &dlen);
+ }
+}
+
+#ifdef WITH_TLS_13
+int _private_tls_hkdf_label(const char *label, unsigned char label_len, const unsigned char *data, unsigned char data_len, unsigned char *hkdflabel, unsigned short length, const char *prefix) {
+ *(unsigned short *)hkdflabel = htons(length);
+ int prefix_len;
+ if (prefix) {
+ prefix_len = (int)strlen(prefix);
+ memcpy(&hkdflabel[3], prefix, prefix_len);
+ } else {
+ memcpy(&hkdflabel[3], "tls13 ", 6);
+ prefix_len = 6;
+ }
+ hkdflabel[2] = (unsigned char)prefix_len + label_len;
+ memcpy(&hkdflabel[3 + prefix_len], label, label_len);
+ hkdflabel[3 + prefix_len + label_len] = (unsigned char)data_len;
+ if (data_len)
+ memcpy(&hkdflabel[4 + prefix_len + label_len], data, data_len);
+ return 4 + prefix_len + label_len + data_len;
+}
+
+int _private_tls_hkdf_extract(unsigned int mac_length, unsigned char *output, unsigned int outlen, const unsigned char *salt, unsigned int salt_len, const unsigned char *ikm, unsigned char ikm_len) {
+ unsigned long dlen = outlen;
+ static unsigned char dummy_label[1] = { 0 };
+ if ((!salt) || (salt_len == 0)) {
+ salt_len = 1;
+ salt = dummy_label;
+ }
+ int hash_idx;
+ if (mac_length == TLS_SHA384_MAC_SIZE) {
+ hash_idx = find_hash("sha384");
+ dlen = mac_length;
+ } else
+ hash_idx = find_hash("sha256");
+
+ hmac_state hmac;
+ hmac_init(&hmac, hash_idx, salt, salt_len);
+ hmac_process(&hmac, ikm, ikm_len);
+ hmac_done(&hmac, output, &dlen);
+ DEBUG_DUMP_HEX_LABEL("EXTRACT", output, dlen);
+ return dlen;
+}
+
+void _private_tls_hkdf_expand(unsigned int mac_length, unsigned char *output, unsigned int outlen, const unsigned char *secret, unsigned int secret_len, const unsigned char *info, unsigned char info_len) {
+ unsigned char digest_out[TLS_MAX_HASH_LEN];
+ unsigned long dlen = 32;
+ int hash_idx;
+ if (mac_length == TLS_SHA384_MAC_SIZE) {
+ hash_idx = find_hash("sha384");
+ dlen = mac_length;
+ } else
+ hash_idx = find_hash("sha256");
+ unsigned int i;
+ unsigned int idx = 0;
+ hmac_state hmac;
+ unsigned char i2 = 0;
+ while (outlen) {
+ hmac_init(&hmac, hash_idx, secret, secret_len);
+ if (i2)
+ hmac_process(&hmac, digest_out, dlen);
+ if ((info) && (info_len))
+ hmac_process(&hmac, info, info_len);
+ i2++;
+ hmac_process(&hmac, &i2, 1);
+ hmac_done(&hmac, digest_out, &dlen);
+
+ unsigned int copylen = outlen;
+ if (copylen > dlen)
+ copylen = (unsigned int)dlen;
+
+ for (i = 0; i < copylen; i++) {
+ output[idx++] = digest_out[i];
+ outlen--;
+ }
+
+ if (!outlen)
+ break;
+ }
+}
+
+void _private_tls_hkdf_expand_label(unsigned int mac_length, unsigned char *output, unsigned int outlen, const unsigned char *secret, unsigned int secret_len, const char *label, unsigned char label_len, const unsigned char *data, unsigned char data_len) {
+ unsigned char hkdf_label[512];
+ int len = _private_tls_hkdf_label(label, label_len, data, data_len, hkdf_label, outlen, NULL);
+ DEBUG_DUMP_HEX_LABEL("INFO", hkdf_label, len);
+ _private_tls_hkdf_expand(mac_length, output, outlen, secret, secret_len, hkdf_label, len);
+}
+#endif
+
+void _private_tls_prf(struct TLSContext *context,
+ unsigned char *output, unsigned int outlen, const unsigned char *secret, const unsigned int secret_len,
+ const unsigned char *label, unsigned int label_len, unsigned char *seed, unsigned int seed_len,
+ unsigned char *seed_b, unsigned int seed_b_len) {
+ if ((!secret) || (!secret_len)) {
+ DEBUG_PRINT("NULL SECRET\n");
+ return;
+ }
+ if ((context->version != TLS_V12) && (context->version != DTLS_V12)) {
+ int md5_hash_idx = find_hash("md5");
+ int sha1_hash_idx = find_hash("sha1");
+ int half_secret = (secret_len + 1) / 2;
+
+ memset(output, 0, outlen);
+ _private_tls_prf_helper(md5_hash_idx, 16, output, outlen, secret, half_secret, label, label_len, seed, seed_len, seed_b, seed_b_len);
+ _private_tls_prf_helper(sha1_hash_idx, 20, output, outlen, secret + (secret_len - half_secret), secret_len - half_secret, label, label_len, seed, seed_len, seed_b, seed_b_len);
+ } else {
+ // sha256_hmac
+ unsigned char digest_out0[TLS_MAX_HASH_LEN];
+ unsigned char digest_out1[TLS_MAX_HASH_LEN];
+ unsigned long dlen = 32;
+ int hash_idx;
+ unsigned int mac_length = _private_tls_mac_length(context);
+ if (mac_length == TLS_SHA384_MAC_SIZE) {
+ hash_idx = find_hash("sha384");
+ dlen = mac_length;
+ } else
+ hash_idx = find_hash("sha256");
+ unsigned int i;
+ hmac_state hmac;
+
+ hmac_init(&hmac, hash_idx, secret, secret_len);
+ hmac_process(&hmac, label, label_len);
+
+ hmac_process(&hmac, seed, seed_len);
+ if ((seed_b) && (seed_b_len))
+ hmac_process(&hmac, seed_b, seed_b_len);
+ hmac_done(&hmac, digest_out0, &dlen);
+ int idx = 0;
+ while (outlen) {
+ hmac_init(&hmac, hash_idx, secret, secret_len);
+ hmac_process(&hmac, digest_out0, dlen);
+ hmac_process(&hmac, label, label_len);
+ hmac_process(&hmac, seed, seed_len);
+ if ((seed_b) && (seed_b_len))
+ hmac_process(&hmac, seed_b, seed_b_len);
+ hmac_done(&hmac, digest_out1, &dlen);
+
+ unsigned int copylen = outlen;
+ if (copylen > dlen)
+ copylen = (unsigned int)dlen;
+
+ for (i = 0; i < copylen; i++) {
+ output[idx++] = digest_out1[i];
+ outlen--;
+ }
+
+ if (!outlen)
+ break;
+
+ hmac_init(&hmac, hash_idx, secret, secret_len);
+ hmac_process(&hmac, digest_out0, dlen);
+ hmac_done(&hmac, digest_out0, &dlen);
+ }
+ }
+}
+
+int _private_tls_key_length(struct TLSContext *context) {
+ switch (context->cipher) {
+ case TLS_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case TLS_AES_128_GCM_SHA256:
+ return 16;
+ case TLS_RSA_WITH_AES_256_CBC_SHA:
+ case TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case TLS_RSA_WITH_AES_256_GCM_SHA384:
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case TLS_AES_256_GCM_SHA384:
+ case TLS_CHACHA20_POLY1305_SHA256:
+ return 32;
+ }
+ return 0;
+}
+
+int _private_tls_is_aead(struct TLSContext *context) {
+ switch (context->cipher) {
+ case TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_RSA_WITH_AES_256_GCM_SHA384:
+ case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case TLS_AES_128_GCM_SHA256:
+ case TLS_AES_256_GCM_SHA384:
+ return 1;
+ case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case TLS_CHACHA20_POLY1305_SHA256:
+ return 2;
+ }
+ return 0;
+}
+
+
+
+unsigned int _private_tls_mac_length(struct TLSContext *context) {
+ switch (context->cipher) {
+ case TLS_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_RSA_WITH_AES_256_CBC_SHA:
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ return TLS_SHA1_MAC_SIZE;
+ case TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+#ifdef WITH_TLS_13
+ case TLS_AES_128_GCM_SHA256:
+ case TLS_CHACHA20_POLY1305_SHA256:
+ case TLS_AES_128_CCM_SHA256:
+ case TLS_AES_128_CCM_8_SHA256:
+#endif
+ return TLS_SHA256_MAC_SIZE;
+ case TLS_RSA_WITH_AES_256_GCM_SHA384:
+ case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+#ifdef WITH_TLS_13
+ case TLS_AES_256_GCM_SHA384:
+#endif
+ return TLS_SHA384_MAC_SIZE;
+ }
+ return 0;
+}
+
+#ifdef WITH_TLS_13
+int _private_tls13_key(struct TLSContext *context, int handshake) {
+ tls_init();
+
+ int key_length = _private_tls_key_length(context);
+ unsigned int mac_length = _private_tls_mac_length(context);
+
+ if ((!context->premaster_key) || (!context->premaster_key_len))
+ return 0;
+
+ if ((!key_length) || (!mac_length)) {
+ DEBUG_PRINT("KEY EXPANSION FAILED, KEY LENGTH: %i, MAC LENGTH: %i\n", key_length, mac_length);
+ return 0;
+ }
+
+ unsigned char *clientkey = NULL;
+ unsigned char *serverkey = NULL;
+ unsigned char *clientiv = NULL;
+ unsigned char *serveriv = NULL;
+ int is_aead = _private_tls_is_aead(context);
+
+ unsigned char local_keybuffer[TLS_V13_MAX_KEY_SIZE];
+ unsigned char local_ivbuffer[TLS_V13_MAX_IV_SIZE];
+ unsigned char remote_keybuffer[TLS_V13_MAX_KEY_SIZE];
+ unsigned char remote_ivbuffer[TLS_V13_MAX_IV_SIZE];
+
+ unsigned char prk[TLS_MAX_HASH_SIZE];
+ unsigned char hash[TLS_MAX_HASH_SIZE];
+ static unsigned char earlysecret[TLS_MAX_HASH_SIZE];
+
+ const char *server_key = "s ap traffic";
+ const char *client_key = "c ap traffic";
+ if (handshake) {
+ server_key = "s hs traffic";
+ client_key = "c hs traffic";
+ }
+
+ unsigned char salt[TLS_MAX_HASH_SIZE];
+
+ hash_state md;
+ if (mac_length == TLS_SHA384_MAC_SIZE) {
+ sha384_init(&md);
+ sha384_done(&md, hash);
+ } else {
+ sha256_init(&md);
+ sha256_done(&md, hash);
+ }
+ // extract secret "early"
+ if ((context->master_key) && (context->master_key_len) && (!handshake)) {
+ DEBUG_DUMP_HEX_LABEL("USING PREVIOUS SECRET", context->master_key, context->master_key_len);
+ _private_tls_hkdf_expand_label(mac_length, salt, mac_length, context->master_key, context->master_key_len, "derived", 7, hash, mac_length);
+ DEBUG_DUMP_HEX_LABEL("salt", salt, mac_length);
+ _private_tls_hkdf_extract(mac_length, prk, mac_length, salt, mac_length, earlysecret, mac_length);
+ } else {
+ _private_tls_hkdf_extract(mac_length, prk, mac_length, NULL, 0, earlysecret, mac_length);
+ // derive secret for handshake "tls13 derived":
+ DEBUG_DUMP_HEX_LABEL("null hash", hash, mac_length);
+ _private_tls_hkdf_expand_label(mac_length, salt, mac_length, prk, mac_length, "derived", 7, hash, mac_length);
+ // extract secret "handshake":
+ DEBUG_DUMP_HEX_LABEL("salt", salt, mac_length);
+ _private_tls_hkdf_extract(mac_length, prk, mac_length, salt, mac_length, context->premaster_key, context->premaster_key_len);
+ }
+
+ if (!is_aead) {
+ DEBUG_PRINT("KEY EXPANSION FAILED, NON AEAD CIPHER\n");
+ return 0;
+ }
+
+ unsigned char secret[TLS_MAX_MAC_SIZE];
+ unsigned char hs_secret[TLS_MAX_HASH_SIZE];
+
+ int hash_size;
+ if (handshake)
+ hash_size = _private_tls_get_hash(context, hash);
+ else
+ hash_size = _private_tls_done_hash(context, hash);
+ DEBUG_DUMP_HEX_LABEL("messages hash", hash, hash_size);
+
+ if (context->is_server) {
+ _private_tls_hkdf_expand_label(mac_length, hs_secret, mac_length, prk, mac_length, server_key, 12, context->server_finished_hash ? context->server_finished_hash : hash, hash_size);
+ DEBUG_DUMP_HEX_LABEL(server_key, hs_secret, mac_length);
+ serverkey = local_keybuffer;
+ serveriv = local_ivbuffer;
+ clientkey = remote_keybuffer;
+ clientiv = remote_ivbuffer;
+ } else {
+ _private_tls_hkdf_expand_label(mac_length, hs_secret, mac_length, prk, mac_length, client_key, 12, context->server_finished_hash ? context->server_finished_hash : hash, hash_size);
+ DEBUG_DUMP_HEX_LABEL(client_key, hs_secret, mac_length);
+ serverkey = remote_keybuffer;
+ serveriv = remote_ivbuffer;
+ clientkey = local_keybuffer;
+ clientiv = local_ivbuffer;
+ }
+
+ int iv_length = TLS_13_AES_GCM_IV_LENGTH;
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ if (is_aead == 2)
+ iv_length = TLS_CHACHA20_IV_LENGTH;
+#endif
+
+ _private_tls_hkdf_expand_label(mac_length, local_keybuffer, key_length, hs_secret, mac_length, "key", 3, NULL, 0);
+ _private_tls_hkdf_expand_label(mac_length, local_ivbuffer, iv_length, hs_secret, mac_length, "iv", 2, NULL, 0);
+ if (context->is_server)
+ _private_tls_hkdf_expand_label(mac_length, secret, mac_length, prk, mac_length, client_key, 12, context->server_finished_hash ? context->server_finished_hash : hash, hash_size);
+ else
+ _private_tls_hkdf_expand_label(mac_length, secret, mac_length, prk, mac_length, server_key, 12, context->server_finished_hash ? context->server_finished_hash : hash, hash_size);
+
+ _private_tls_hkdf_expand_label(mac_length, remote_keybuffer, key_length, secret, mac_length, "key", 3, NULL, 0);
+ _private_tls_hkdf_expand_label(mac_length, remote_ivbuffer, iv_length, secret, mac_length, "iv", 2, NULL, 0);
+
+ DEBUG_DUMP_HEX_LABEL("CLIENT KEY", clientkey, key_length)
+ DEBUG_DUMP_HEX_LABEL("CLIENT IV", clientiv, iv_length)
+ DEBUG_DUMP_HEX_LABEL("SERVER KEY", serverkey, key_length)
+ DEBUG_DUMP_HEX_LABEL("SERVER IV", serveriv, iv_length)
+
+ TLS_FREE(context->finished_key);
+ TLS_FREE(context->remote_finished_key);
+ if (handshake) {
+ context->finished_key = (unsigned char *)TLS_MALLOC(mac_length);
+ context->remote_finished_key = (unsigned char *)TLS_MALLOC(mac_length);
+
+ if (context->finished_key) {
+ _private_tls_hkdf_expand_label(mac_length, context->finished_key, mac_length, hs_secret, mac_length, "finished", 8, NULL, 0);
+ DEBUG_DUMP_HEX_LABEL("FINISHED", context->finished_key, mac_length)
+ }
+
+ if (context->remote_finished_key) {
+ _private_tls_hkdf_expand_label(mac_length, context->remote_finished_key, mac_length, secret, mac_length, "finished", 8, NULL, 0);
+ DEBUG_DUMP_HEX_LABEL("REMOTE FINISHED", context->remote_finished_key, mac_length)
+ }
+ } else {
+ context->finished_key = NULL;
+ context->remote_finished_key = NULL;
+ TLS_FREE(context->server_finished_hash);
+ context->server_finished_hash = NULL;
+ }
+
+ if (context->is_server) {
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ if (is_aead == 2) {
+ memcpy(context->crypto.ctx_remote_mac.remote_nonce, clientiv, iv_length);
+ memcpy(context->crypto.ctx_local_mac.local_nonce, serveriv, iv_length);
+ } else
+#endif
+ if (is_aead) {
+ memcpy(context->crypto.ctx_remote_mac.remote_iv, clientiv, iv_length);
+ memcpy(context->crypto.ctx_local_mac.local_iv, serveriv, iv_length);
+ }
+ if (_private_tls_crypto_create(context, key_length, serverkey, serveriv, clientkey, clientiv))
+ return 0;
+ } else {
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ if (is_aead == 2) {
+ memcpy(context->crypto.ctx_local_mac.local_nonce, clientiv, iv_length);
+ memcpy(context->crypto.ctx_remote_mac.remote_nonce, serveriv, iv_length);
+ } else
+#endif
+ if (is_aead) {
+ memcpy(context->crypto.ctx_local_mac.local_iv, clientiv, iv_length);
+ memcpy(context->crypto.ctx_remote_mac.remote_iv, serveriv, iv_length);
+ }
+ if (_private_tls_crypto_create(context, key_length, clientkey, clientiv, serverkey, serveriv))
+ return 0;
+ }
+ context->crypto.created = 1 + is_aead;
+ if (context->exportable) {
+ TLS_FREE(context->exportable_keys);
+ context->exportable_keys = (unsigned char *)TLS_MALLOC(key_length * 2);
+ if (context->exportable_keys) {
+ if (context->is_server) {
+ memcpy(context->exportable_keys, serverkey, key_length);
+ memcpy(context->exportable_keys + key_length, clientkey, key_length);
+ } else {
+ memcpy(context->exportable_keys, clientkey, key_length);
+ memcpy(context->exportable_keys + key_length, serverkey, key_length);
+ }
+ context->exportable_size = key_length * 2;
+ }
+ }
+ TLS_FREE(context->master_key);
+ context->master_key = (unsigned char *)TLS_MALLOC(mac_length);
+ if (context->master_key) {
+ memcpy(context->master_key, prk, mac_length);
+ context->master_key_len = mac_length;
+ }
+ context->local_sequence_number = 0;
+ context->remote_sequence_number = 0;
+
+ // extract client_mac_key(mac_key_length)
+ // extract server_mac_key(mac_key_length)
+ // extract client_key(enc_key_length)
+ // extract server_key(enc_key_length)
+ // extract client_iv(fixed_iv_lengh)
+ // extract server_iv(fixed_iv_length)
+ return 1;
+}
+#endif
+
+int _private_tls_expand_key(struct TLSContext *context) {
+ unsigned char key[TLS_MAX_KEY_EXPANSION_SIZE];
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13))
+ return 0;
+#endif
+
+ if ((!context->master_key) || (!context->master_key_len))
+ return 0;
+
+ int key_length = _private_tls_key_length(context);
+ int mac_length = _private_tls_mac_length(context);
+
+ if ((!key_length) || (!mac_length)) {
+ DEBUG_PRINT("KEY EXPANSION FAILED, KEY LENGTH: %i, MAC LENGTH: %i\n", key_length, mac_length);
+ return 0;
+ }
+ unsigned char *clientkey = NULL;
+ unsigned char *serverkey = NULL;
+ unsigned char *clientiv = NULL;
+ unsigned char *serveriv = NULL;
+ int iv_length = TLS_AES_IV_LENGTH;
+ int is_aead = _private_tls_is_aead(context);
+ if (context->is_server)
+ _private_tls_prf(context, key, sizeof(key), context->master_key, context->master_key_len, (unsigned char *)"key expansion", 13, context->local_random, TLS_SERVER_RANDOM_SIZE, context->remote_random, TLS_CLIENT_RANDOM_SIZE);
+ else
+ _private_tls_prf(context, key, sizeof(key), context->master_key, context->master_key_len, (unsigned char *)"key expansion", 13, context->remote_random, TLS_SERVER_RANDOM_SIZE, context->local_random, TLS_CLIENT_RANDOM_SIZE);
+
+ DEBUG_DUMP_HEX_LABEL("LOCAL RANDOM ", context->local_random, TLS_SERVER_RANDOM_SIZE);
+ DEBUG_DUMP_HEX_LABEL("REMOTE RANDOM", context->remote_random, TLS_CLIENT_RANDOM_SIZE);
+ DEBUG_PRINT("\n=========== EXPANSION ===========\n");
+ DEBUG_DUMP_HEX(key, TLS_MAX_KEY_EXPANSION_SIZE);
+ DEBUG_PRINT("\n");
+
+ int pos = 0;
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ if (is_aead == 2) {
+ iv_length = TLS_CHACHA20_IV_LENGTH;
+ } else
+#endif
+ if (is_aead) {
+ iv_length = TLS_AES_GCM_IV_LENGTH;
+ } else {
+ if (context->is_server) {
+ memcpy(context->crypto.ctx_remote_mac.remote_mac, &key[pos], mac_length);
+ pos += mac_length;
+ memcpy(context->crypto.ctx_local_mac.local_mac, &key[pos], mac_length);
+ pos += mac_length;
+ } else {
+ memcpy(context->crypto.ctx_local_mac.local_mac, &key[pos], mac_length);
+ pos += mac_length;
+ memcpy(context->crypto.ctx_remote_mac.remote_mac, &key[pos], mac_length);
+ pos += mac_length;
+ }
+ }
+
+ clientkey = &key[pos];
+ pos += key_length;
+ serverkey = &key[pos];
+ pos += key_length;
+ clientiv = &key[pos];
+ pos += iv_length;
+ serveriv = &key[pos];
+ pos += iv_length;
+ DEBUG_PRINT("EXPANSION %i/%i\n", (int)pos, (int)TLS_MAX_KEY_EXPANSION_SIZE);
+ DEBUG_DUMP_HEX_LABEL("CLIENT KEY", clientkey, key_length)
+ DEBUG_DUMP_HEX_LABEL("CLIENT IV", clientiv, iv_length)
+ DEBUG_DUMP_HEX_LABEL("CLIENT MAC KEY", context->is_server ? context->crypto.ctx_remote_mac.remote_mac : context->crypto.ctx_local_mac.local_mac, mac_length)
+ DEBUG_DUMP_HEX_LABEL("SERVER KEY", serverkey, key_length)
+ DEBUG_DUMP_HEX_LABEL("SERVER IV", serveriv, iv_length)
+ DEBUG_DUMP_HEX_LABEL("SERVER MAC KEY", context->is_server ? context->crypto.ctx_local_mac.local_mac : context->crypto.ctx_remote_mac.remote_mac, mac_length)
+
+ if (context->is_server) {
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ if (is_aead == 2) {
+ memcpy(context->crypto.ctx_remote_mac.remote_nonce, clientiv, iv_length);
+ memcpy(context->crypto.ctx_local_mac.local_nonce, serveriv, iv_length);
+ } else
+#endif
+ if (is_aead) {
+ memcpy(context->crypto.ctx_remote_mac.remote_aead_iv, clientiv, iv_length);
+ memcpy(context->crypto.ctx_local_mac.local_aead_iv, serveriv, iv_length);
+ }
+ if (_private_tls_crypto_create(context, key_length, serverkey, serveriv, clientkey, clientiv))
+ return 0;
+ } else {
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ if (is_aead == 2) {
+ memcpy(context->crypto.ctx_local_mac.local_nonce, clientiv, iv_length);
+ memcpy(context->crypto.ctx_remote_mac.remote_nonce, serveriv, iv_length);
+ } else
+#endif
+ if (is_aead) {
+ memcpy(context->crypto.ctx_local_mac.local_aead_iv, clientiv, iv_length);
+ memcpy(context->crypto.ctx_remote_mac.remote_aead_iv, serveriv, iv_length);
+ }
+ if (_private_tls_crypto_create(context, key_length, clientkey, clientiv, serverkey, serveriv))
+ return 0;
+ }
+
+ if (context->exportable) {
+ TLS_FREE(context->exportable_keys);
+ context->exportable_keys = (unsigned char *)TLS_MALLOC(key_length * 2);
+ if (context->exportable_keys) {
+ if (context->is_server) {
+ memcpy(context->exportable_keys, serverkey, key_length);
+ memcpy(context->exportable_keys + key_length, clientkey, key_length);
+ } else {
+ memcpy(context->exportable_keys, clientkey, key_length);
+ memcpy(context->exportable_keys + key_length, serverkey, key_length);
+ }
+ context->exportable_size = key_length * 2;
+ }
+ }
+
+ // extract client_mac_key(mac_key_length)
+ // extract server_mac_key(mac_key_length)
+ // extract client_key(enc_key_length)
+ // extract server_key(enc_key_length)
+ // extract client_iv(fixed_iv_lengh)
+ // extract server_iv(fixed_iv_length)
+ return 1;
+}
+
+int _private_tls_compute_key(struct TLSContext *context, unsigned int key_len) {
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13))
+ return 0;
+#endif
+ if ((!context->premaster_key) || (!context->premaster_key_len) || (key_len < 48)) {
+ DEBUG_PRINT("CANNOT COMPUTE MASTER SECRET\n");
+ return 0;
+ }
+ unsigned char master_secret_label[] = "master secret";
+#ifdef TLS_CHECK_PREMASTER_KEY
+ if (!tls_cipher_is_ephemeral(context)) {
+ unsigned short version = ntohs(*(unsigned short *)context->premaster_key);
+ // this check is not true for DHE/ECDHE ciphers
+ if (context->version > version) {
+ DEBUG_PRINT("Mismatch protocol version 0x(%x)\n", version);
+ return 0;
+ }
+ }
+#endif
+ TLS_FREE(context->master_key);
+ context->master_key_len = 0;
+ context->master_key = NULL;
+ if ((context->version == TLS_V13) || (context->version == TLS_V12) || (context->version == TLS_V11) || (context->version == TLS_V10) || (context->version == DTLS_V13) || (context->version == DTLS_V12) || (context->version == DTLS_V10)) {
+ context->master_key = (unsigned char *)TLS_MALLOC(key_len);
+ if (!context->master_key)
+ return 0;
+ context->master_key_len = key_len;
+ if (context->is_server) {
+#ifdef TLS_DTLS_EXTENDED_MASTER_SECRET
+ if ((context->dtls) && (context->dtls_data) && (context->dtls_data->extended_master_secret)) {
+ // master_secret = PRF(pre_master_secret, "extended master secret", session_hash);
+ unsigned char handshake_hash[TLS_MAX_SHA_SIZE];
+ int hash_size = _private_tls_get_hash(context, handshake_hash);
+ DEBUG_DUMP_HEX_LABEL(">> HANDSHAKE HASH", handshake_hash, hash_size);
+ _private_tls_prf(context,
+ context->master_key, context->master_key_len,
+ context->premaster_key, context->premaster_key_len,
+ "extended master secret", 22,
+ handshake_hash, hash_size,
+ NULL, 0
+ );
+ } else
+#endif
+ _private_tls_prf(context,
+ context->master_key, context->master_key_len,
+ context->premaster_key, context->premaster_key_len,
+ master_secret_label, 13,
+ context->remote_random, TLS_CLIENT_RANDOM_SIZE,
+ context->local_random, TLS_SERVER_RANDOM_SIZE
+ );
+ } else {
+ _private_tls_prf(context,
+ context->master_key, context->master_key_len,
+ context->premaster_key, context->premaster_key_len,
+ master_secret_label, 13,
+ context->local_random, TLS_CLIENT_RANDOM_SIZE,
+ context->remote_random, TLS_SERVER_RANDOM_SIZE
+ );
+ }
+ TLS_FREE(context->premaster_key);
+ context->premaster_key = NULL;
+ context->premaster_key_len = 0;
+ DEBUG_PRINT("\n=========== Master key ===========\n");
+ DEBUG_DUMP_HEX(context->master_key, context->master_key_len);
+ DEBUG_PRINT("\n");
+ _private_tls_expand_key(context);
+ return 1;
+ }
+ return 0;
+}
+
+unsigned char *tls_pem_decode(const unsigned char *data_in, unsigned int input_length, int cert_index, unsigned int *output_len) {
+ unsigned int i;
+ *output_len = 0;
+ int alloc_len = input_length / 4 * 3;
+ unsigned char *output = (unsigned char *)TLS_MALLOC(alloc_len);
+ if (!output)
+ return NULL;
+ unsigned int start_at = 0;
+ unsigned int idx = 0;
+ for (i = 0; i < input_length; i++) {
+ if ((data_in[i] == '\n') || (data_in[i] == '\r'))
+ continue;
+
+ if (data_in[i] != '-') {
+ // read entire line
+ while ((i < input_length) && (data_in[i] != '\n'))
+ i++;
+ continue;
+ }
+
+ if (data_in[i] == '-') {
+ unsigned int end_idx = i;
+ //read until end of line
+ while ((i < input_length) && (data_in[i] != '\n'))
+ i++;
+ if (start_at) {
+ if (cert_index > 0) {
+ cert_index--;
+ start_at = 0;
+ } else {
+ idx = _private_b64_decode((const char *)&data_in[start_at], end_idx - start_at, output);
+ break;
+ }
+ } else
+ start_at = i + 1;
+ }
+ }
+ *output_len = idx;
+ if (!idx) {
+ TLS_FREE(output);
+ return NULL;
+ }
+ return output;
+}
+
+int _is_oid(const unsigned char *oid, const unsigned char *compare_to, int compare_to_len) {
+ int i = 0;
+ while ((oid[i]) && (i < compare_to_len)) {
+ if (oid[i] != compare_to[i])
+ return 0;
+
+ i++;
+ }
+ return 1;
+}
+
+int _is_oid2(const unsigned char *oid, const unsigned char *compare_to, int compare_to_len, int oid_len) {
+ int i = 0;
+ if (oid_len < compare_to_len)
+ compare_to_len = oid_len;
+ while (i < compare_to_len) {
+ if (oid[i] != compare_to[i])
+ return 0;
+
+ i++;
+ }
+ return 1;
+}
+
+struct TLSCertificate *tls_create_certificate() {
+ struct TLSCertificate *cert = (struct TLSCertificate *)TLS_MALLOC(sizeof(struct TLSCertificate));
+ if (cert)
+ memset(cert, 0, sizeof(struct TLSCertificate));
+ return cert;
+}
+
+int tls_certificate_valid_subject_name(const unsigned char *cert_subject, const char *subject) {
+ // no subjects ...
+ if (((!cert_subject) || (!cert_subject[0])) && ((!subject) || (!subject[0])))
+ return 0;
+
+ if ((!subject) || (!subject[0]))
+ return bad_certificate;
+
+ if ((!cert_subject) || (!cert_subject[0]))
+ return bad_certificate;
+
+ // exact match
+ if (!strcmp((const char *)cert_subject, subject))
+ return 0;
+
+ const char *wildcard = strchr((const char *)cert_subject, '*');
+ if (wildcard) {
+ // 6.4.3 (1) The client SHOULD NOT attempt to match a presented identifier in
+ // which the wildcard character comprises a label other than the left-most label
+ if (!wildcard[1]) {
+ // subject is [*]
+ // or
+ // subject is [something*] .. invalid
+ return bad_certificate;
+ }
+ wildcard++;
+ const char *match = strstr(subject, wildcard);
+ if ((!match) && (wildcard[0] == '.')) {
+ // check *.domain.com agains domain.com
+ wildcard++;
+ if (!strcasecmp(subject, wildcard))
+ return 0;
+ }
+ if (match) {
+ uintptr_t offset = (uintptr_t)match - (uintptr_t)subject;
+ if (offset) {
+ // check for foo.*.domain.com against *.domain.com (invalid)
+ if (memchr(subject, '.', offset))
+ return bad_certificate;
+ }
+ // check if exact match
+ if (!strcasecmp(match, wildcard))
+ return 0;
+ }
+ }
+
+ return bad_certificate;
+}
+
+int tls_certificate_valid_subject(struct TLSCertificate *cert, const char *subject) {
+ int i;
+ if (!cert)
+ return certificate_unknown;
+ int err = tls_certificate_valid_subject_name(cert->subject, subject);
+ if ((err) && (cert->san)) {
+ for (i = 0; i < cert->san_length; i++) {
+ err = tls_certificate_valid_subject_name(cert->san[i], subject);
+ if (!err)
+ return err;
+ }
+ }
+ return err;
+}
+
+int tls_certificate_is_valid(struct TLSCertificate *cert) {
+ if (!cert)
+ return certificate_unknown;
+ if (!cert->not_before)
+ return certificate_unknown;
+ if (!cert->not_after)
+ return certificate_unknown;
+ //20160224182300Z//
+ char current_time[16];
+ time_t t = time(NULL);
+ struct tm *utct = gmtime(&t);
+ if (utct) {
+ current_time[0] = 0;
+ snprintf(current_time, sizeof(current_time), "%04hu%02hhu%02hhu%02hhu%02hhu%02hhuZ", 1900 + utct->tm_year, utct->tm_mon + 1, utct->tm_mday, utct->tm_hour, utct->tm_min, utct->tm_sec);
+ if (strcasecmp((char *)cert->not_before, current_time) > 0) {
+ DEBUG_PRINT("Certificate is not yer valid, now: %s (validity: %s - %s)\n", current_time, cert->not_before, cert->not_after);
+ return certificate_expired;
+ }
+ if (strcasecmp((char *)cert->not_after, current_time) < 0) {
+ DEBUG_PRINT("Expired certificate, now: %s (validity: %s - %s)\n", current_time, cert->not_before, cert->not_after);
+ return certificate_expired;
+ }
+ DEBUG_PRINT("Valid certificate, now: %s (validity: %s - %s)\n", current_time, cert->not_before, cert->not_after);
+ }
+ return 0;
+}
+
+void tls_certificate_set_copy(unsigned char **member, const unsigned char *val, int len) {
+ if (!member)
+ return;
+ TLS_FREE(*member);
+ if (len) {
+ *member = (unsigned char *)TLS_MALLOC(len + 1);
+ if (*member) {
+ memcpy(*member, val, len);
+ (*member)[len] = 0;
+ }
+ } else
+ *member = NULL;
+}
+
+void tls_certificate_set_copy_date(unsigned char **member, const unsigned char *val, int len) {
+ if (!member)
+ return;
+ TLS_FREE(*member);
+ if (len > 4) {
+ *member = (unsigned char *)TLS_MALLOC(len + 3);
+ if (*member) {
+ if (val[0] == '9') {
+ (*member)[0]='1';
+ (*member)[1]='9';
+ } else {
+ (*member)[0]='2';
+ (*member)[1]='0';
+ }
+ memcpy(*member + 2, val, len);
+ (*member)[len] = 0;
+ }
+ } else
+ *member = NULL;
+}
+
+void tls_certificate_set_key(struct TLSCertificate *cert, const unsigned char *val, int len) {
+ if ((!val[0]) && (len % 2)) {
+ val++;
+ len--;
+ }
+ tls_certificate_set_copy(&cert->pk, val, len);
+ if (cert->pk)
+ cert->pk_len = len;
+}
+
+void tls_certificate_set_priv(struct TLSCertificate *cert, const unsigned char *val, int len) {
+ tls_certificate_set_copy(&cert->priv, val, len);
+ if (cert->priv)
+ cert->priv_len = len;
+}
+
+void tls_certificate_set_sign_key(struct TLSCertificate *cert, const unsigned char *val, int len) {
+ if ((!val[0]) && (len % 2)) {
+ val++;
+ len--;
+ }
+ tls_certificate_set_copy(&cert->sign_key, val, len);
+ if (cert->sign_key)
+ cert->sign_len = len;
+}
+
+char *tls_certificate_to_string(struct TLSCertificate *cert, char *buffer, int len) {
+ unsigned int i;
+ if (!buffer)
+ return NULL;
+ buffer[0] = 0;
+ if (cert->version) {
+ int res = snprintf(buffer, len, "X.509v%i certificate\n Issued by: [%s]%s (%s)\n Issued to: [%s]%s (%s, %s)\n Subject: %s\n Validity: %s - %s\n OCSP: %s\n Serial number: ",
+ (int)cert->version,
+ cert->issuer_country, cert->issuer_entity, cert->issuer_subject,
+ cert->country, cert->entity, cert->state, cert->location,
+ cert->subject,
+ cert->not_before, cert->not_after,
+ cert->ocsp
+ );
+ if (res > 0) {
+ for (i = 0; i < cert->serial_len; i++)
+ res += snprintf(buffer + res, len - res, "%02x", (int)cert->serial_number[i]);
+ }
+ if ((cert->san) && (cert->san_length)) {
+ res += snprintf(buffer + res, len - res, "\n Alternative subjects: ");
+ for (i = 0; i < cert->san_length; i++) {
+ if (i)
+ res += snprintf(buffer + res, len - res, ", %s", cert->san[i]);
+ else
+ res += snprintf(buffer + res, len - res, "%s", cert->san[i]);
+ }
+ }
+ res += snprintf(buffer + res, len - res, "\n Key (%i bits, ", cert->pk_len * 8);
+ if (res > 0) {
+ switch (cert->key_algorithm) {
+ case TLS_RSA_SIGN_RSA:
+ res += snprintf(buffer + res, len - res, "RSA_SIGN_RSA");
+ break;
+ case TLS_RSA_SIGN_MD5:
+ res += snprintf(buffer + res, len - res, "RSA_SIGN_MD5");
+ break;
+ case TLS_RSA_SIGN_SHA1:
+ res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA1");
+ break;
+ case TLS_RSA_SIGN_SHA256:
+ res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA256");
+ break;
+ case TLS_RSA_SIGN_SHA384:
+ res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA384");
+ break;
+ case TLS_RSA_SIGN_SHA512:
+ res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA512");
+ break;
+ case TLS_ECDSA_SIGN_SHA256:
+ res += snprintf(buffer + res, len - res, "ECDSA_SIGN_SHA512");
+ break;
+ case TLS_EC_PUBLIC_KEY:
+ res += snprintf(buffer + res, len - res, "EC_PUBLIC_KEY");
+ break;
+ default:
+ res += snprintf(buffer + res, len - res, "not supported (%i)", (int)cert->key_algorithm);
+ }
+ }
+ if ((res > 0) && (cert->ec_algorithm)) {
+ switch (cert->ec_algorithm) {
+ case TLS_EC_prime192v1:
+ res += snprintf(buffer + res, len - res, " prime192v1");
+ break;
+ case TLS_EC_prime192v2:
+ res += snprintf(buffer + res, len - res, " prime192v2");
+ break;
+ case TLS_EC_prime192v3:
+ res += snprintf(buffer + res, len - res, " prime192v3");
+ break;
+ case TLS_EC_prime239v2:
+ res += snprintf(buffer + res, len - res, " prime239v2");
+ break;
+ case TLS_EC_secp256r1:
+ res += snprintf(buffer + res, len - res, " EC_secp256r1");
+ break;
+ case TLS_EC_secp224r1:
+ res += snprintf(buffer + res, len - res, " EC_secp224r1");
+ break;
+ case TLS_EC_secp384r1:
+ res += snprintf(buffer + res, len - res, " EC_secp384r1");
+ break;
+ case TLS_EC_secp521r1:
+ res += snprintf(buffer + res, len - res, " EC_secp521r1");
+ break;
+ default:
+ res += snprintf(buffer + res, len - res, " unknown(%i)", (int)cert->ec_algorithm);
+ }
+ }
+ res += snprintf(buffer + res, len - res, "):\n");
+ if (res > 0) {
+ for (i = 0; i < cert->pk_len; i++)
+ res += snprintf(buffer + res, len - res, "%02x", (int)cert->pk[i]);
+ res += snprintf(buffer + res, len - res, "\n Signature (%i bits, ", cert->sign_len * 8);
+ switch (cert->algorithm) {
+ case TLS_RSA_SIGN_RSA:
+ res += snprintf(buffer + res, len - res, "RSA_SIGN_RSA):\n");
+ break;
+ case TLS_RSA_SIGN_MD5:
+ res += snprintf(buffer + res, len - res, "RSA_SIGN_MD5):\n");
+ break;
+ case TLS_RSA_SIGN_SHA1:
+ res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA1):\n");
+ break;
+ case TLS_RSA_SIGN_SHA256:
+ res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA256):\n");
+ break;
+ case TLS_RSA_SIGN_SHA384:
+ res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA384):\n");
+ break;
+ case TLS_RSA_SIGN_SHA512:
+ res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA512):\n");
+ break;
+ case TLS_EC_PUBLIC_KEY:
+ res += snprintf(buffer + res, len - res, "EC_PUBLIC_KEY):\n");
+ break;
+ default:
+ res += snprintf(buffer + res, len - res, "not supported):\n");
+ }
+
+ for (i = 0; i < cert->sign_len; i++)
+ res += snprintf(buffer + res, len - res, "%02x", (int)cert->sign_key[i]);
+ }
+ } else
+ if ((cert->priv) && (cert->priv_len)) {
+ int res = snprintf(buffer, len, "X.509 private key\n");
+ res += snprintf(buffer + res, len - res, " Private Key: ");
+ if (res > 0) {
+ for (i = 0; i < cert->priv_len; i++)
+ res += snprintf(buffer + res, len - res, "%02x", (int)cert->priv[i]);
+ }
+ } else
+ snprintf(buffer, len, "Empty ASN1 file");
+ return buffer;
+}
+
+void tls_certificate_set_exponent(struct TLSCertificate *cert, const unsigned char *val, int len) {
+ tls_certificate_set_copy(&cert->exponent, val, len);
+ if (cert->exponent)
+ cert->exponent_len = len;
+}
+
+void tls_certificate_set_serial(struct TLSCertificate *cert, const unsigned char *val, int len) {
+ tls_certificate_set_copy(&cert->serial_number, val, len);
+ if (cert->serial_number)
+ cert->serial_len = len;
+}
+
+void tls_certificate_set_algorithm(struct TLSContext *context, unsigned int *algorithm, const unsigned char *val, int len) {
+ if ((len == 7) && (_is_oid(val, TLS_EC_PUBLIC_KEY_OID, 7))) {
+ *algorithm = TLS_EC_PUBLIC_KEY;
+ return;
+ }
+ if (len == 8) {
+ if (_is_oid(val, TLS_EC_prime192v1_OID, len)) {
+ *algorithm = TLS_EC_prime192v1;
+ return;
+ }
+ if (_is_oid(val, TLS_EC_prime192v2_OID, len)) {
+ *algorithm = TLS_EC_prime192v2;
+ return;
+ }
+ if (_is_oid(val, TLS_EC_prime192v3_OID, len)) {
+ *algorithm = TLS_EC_prime192v3;
+ return;
+ }
+ if (_is_oid(val, TLS_EC_prime239v1_OID, len)) {
+ *algorithm = TLS_EC_prime239v1;
+ return;
+ }
+ if (_is_oid(val, TLS_EC_prime239v2_OID, len)) {
+ *algorithm = TLS_EC_prime239v2;
+ return;
+ }
+ if (_is_oid(val, TLS_EC_prime239v3_OID, len)) {
+ *algorithm = TLS_EC_prime239v3;
+ return;
+ }
+ if (_is_oid(val, TLS_EC_prime256v1_OID, len)) {
+ *algorithm = TLS_EC_prime256v1;
+ return;
+ }
+ }
+ if (len == 5) {
+ if (_is_oid2(val, TLS_EC_secp224r1_OID, len, sizeof(TLS_EC_secp224r1_OID) - 1)) {
+ *algorithm = TLS_EC_secp224r1;
+ return;
+ }
+ if (_is_oid2(val, TLS_EC_secp384r1_OID, len, sizeof(TLS_EC_secp384r1_OID) - 1)) {
+ *algorithm = TLS_EC_secp384r1;
+ return;
+ }
+ if (_is_oid2(val, TLS_EC_secp521r1_OID, len, sizeof(TLS_EC_secp521r1_OID) - 1)) {
+ *algorithm = TLS_EC_secp521r1;
+ return;
+ }
+ }
+ if (len != 9)
+ return;
+
+ if (_is_oid(val, TLS_RSA_SIGN_SHA256_OID, 9)) {
+ *algorithm = TLS_RSA_SIGN_SHA256;
+ return;
+ }
+
+ if (_is_oid(val, TLS_RSA_SIGN_RSA_OID, 9)) {
+ *algorithm = TLS_RSA_SIGN_RSA;
+ return;
+ }
+
+ if (_is_oid(val, TLS_RSA_SIGN_SHA1_OID, 9)) {
+ *algorithm = TLS_RSA_SIGN_SHA1;
+ return;
+ }
+
+ if (_is_oid(val, TLS_RSA_SIGN_SHA512_OID, 9)) {
+ *algorithm = TLS_RSA_SIGN_SHA512;
+ return;
+ }
+
+ if (_is_oid(val, TLS_RSA_SIGN_SHA384_OID, 9)) {
+ *algorithm = TLS_RSA_SIGN_SHA384;
+ return;
+ }
+
+ if (_is_oid(val, TLS_RSA_SIGN_MD5_OID, 9)) {
+ *algorithm = TLS_RSA_SIGN_MD5;
+ return;
+ }
+
+ if (_is_oid(val, TLS_ECDSA_SIGN_SHA256_OID, 9)) {
+ *algorithm = TLS_ECDSA_SIGN_SHA256;
+ return;
+ }
+ // client should fail on unsupported signature
+ if (!context->is_server) {
+ DEBUG_PRINT("UNSUPPORTED SIGNATURE ALGORITHM\n");
+ context->critical_error = 1;
+ }
+}
+
+void tls_destroy_certificate(struct TLSCertificate *cert) {
+ if (cert) {
+ int i;
+ TLS_FREE(cert->exponent);
+ TLS_FREE(cert->pk);
+ TLS_FREE(cert->issuer_country);
+ TLS_FREE(cert->issuer_state);
+ TLS_FREE(cert->issuer_location);
+ TLS_FREE(cert->issuer_entity);
+ TLS_FREE(cert->issuer_subject);
+ TLS_FREE(cert->country);
+ TLS_FREE(cert->state);
+ TLS_FREE(cert->location);
+ TLS_FREE(cert->subject);
+ for (i = 0; i < cert->san_length; i++) {
+ TLS_FREE(cert->san[i]);
+ }
+ TLS_FREE(cert->san);
+ TLS_FREE(cert->ocsp);
+ TLS_FREE(cert->serial_number);
+ TLS_FREE(cert->entity);
+ TLS_FREE(cert->not_before);
+ TLS_FREE(cert->not_after);
+ TLS_FREE(cert->sign_key);
+ TLS_FREE(cert->priv);
+ TLS_FREE(cert->der_bytes);
+ TLS_FREE(cert->bytes);
+ TLS_FREE(cert->fingerprint);
+ TLS_FREE(cert);
+ }
+}
+
+struct TLSPacket *tls_create_packet(struct TLSContext *context, unsigned char type, unsigned short version, int payload_size_hint) {
+ struct TLSPacket *packet = (struct TLSPacket *)TLS_MALLOC(sizeof(struct TLSPacket));
+ if (!packet)
+ return NULL;
+ packet->broken = 0;
+ if (payload_size_hint > 0)
+ packet->size = payload_size_hint + 10;
+ else
+ packet->size = TLS_BLOB_INCREMENT;
+ packet->buf = (unsigned char *)TLS_MALLOC(packet->size);
+ packet->context = context;
+ if (!packet->buf) {
+ TLS_FREE(packet);
+ return NULL;
+ }
+ if ((context) && (context->dtls))
+ packet->len = 13;
+ else
+ packet->len = 5;
+ packet->buf[0] = type;
+#ifdef WITH_TLS_13
+ switch (version) {
+ case TLS_V13:
+ // check if context is not null. If null, is a tls_export_context call
+ if (context)
+ *(unsigned short *)(packet->buf + 1) = 0x0303; // no need to reorder (same bytes)
+ else
+ *(unsigned short *)(packet->buf + 1) = htons(version);
+ break;
+ case DTLS_V13:
+ *(unsigned short *)(packet->buf + 1) = htons(DTLS_V13);
+ break;
+ default:
+ *(unsigned short *)(packet->buf + 1) = htons(version);
+ }
+#else
+ *(unsigned short *)(packet->buf + 1) = htons(version);
+#endif
+ return packet;
+}
+
+void tls_destroy_packet(struct TLSPacket *packet) {
+ if (packet) {
+ if (packet->buf)
+ TLS_FREE(packet->buf);
+ TLS_FREE(packet);
+ }
+}
+
+int _private_tls_crypto_create(struct TLSContext *context, int key_length, unsigned char *localkey, unsigned char *localiv, unsigned char *remotekey, unsigned char *remoteiv) {
+ if (context->crypto.created) {
+ if (context->crypto.created == 1) {
+ cbc_done(&context->crypto.ctx_remote.aes_remote);
+ cbc_done(&context->crypto.ctx_local.aes_local);
+ } else {
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ if (context->crypto.created == 2) {
+#endif
+ unsigned char dummy_buffer[32];
+ unsigned long tag_len = 0;
+ gcm_done(&context->crypto.ctx_remote.aes_gcm_remote, dummy_buffer, &tag_len);
+ gcm_done(&context->crypto.ctx_local.aes_gcm_local, dummy_buffer, &tag_len);
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ }
+#endif
+ }
+ context->crypto.created = 0;
+ }
+ tls_init();
+ int is_aead = _private_tls_is_aead(context);
+ int cipherID = find_cipher("aes");
+ DEBUG_PRINT("Using cipher ID: %x\n", (int)context->cipher);
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ if (is_aead == 2) {
+ unsigned int counter = 1;
+
+ chacha_keysetup(&context->crypto.ctx_local.chacha_local, localkey, key_length * 8);
+ chacha_ivsetup_96bitnonce(&context->crypto.ctx_local.chacha_local, localiv, (unsigned char *)&counter);
+
+ chacha_keysetup(&context->crypto.ctx_remote.chacha_remote, remotekey, key_length * 8);
+ chacha_ivsetup_96bitnonce(&context->crypto.ctx_remote.chacha_remote, remoteiv, (unsigned char *)&counter);
+
+ context->crypto.created = 3;
+ } else
+#endif
+ if (is_aead) {
+ int res1 = gcm_init(&context->crypto.ctx_local.aes_gcm_local, cipherID, localkey, key_length);
+ int res2 = gcm_init(&context->crypto.ctx_remote.aes_gcm_remote, cipherID, remotekey, key_length);
+
+ if ((res1) || (res2))
+ return TLS_GENERIC_ERROR;
+ context->crypto.created = 2;
+ } else {
+ int res1 = cbc_start(cipherID, localiv, localkey, key_length, 0, &context->crypto.ctx_local.aes_local);
+ int res2 = cbc_start(cipherID, remoteiv, remotekey, key_length, 0, &context->crypto.ctx_remote.aes_remote);
+
+ if ((res1) || (res2))
+ return TLS_GENERIC_ERROR;
+ context->crypto.created = 1;
+ }
+ return 0;
+}
+
+int _private_tls_crypto_encrypt(struct TLSContext *context, unsigned char *buf, unsigned char *ct, unsigned int len) {
+ if (context->crypto.created == 1)
+ return cbc_encrypt(buf, ct, len, &context->crypto.ctx_local.aes_local);
+
+ memset(ct, 0, len);
+ return TLS_GENERIC_ERROR;
+}
+
+int _private_tls_crypto_decrypt(struct TLSContext *context, unsigned char *buf, unsigned char *pt, unsigned int len) {
+ if (context->crypto.created == 1)
+ return cbc_decrypt(buf, pt, len, &context->crypto.ctx_remote.aes_remote);
+
+ memset(pt, 0, len);
+ return TLS_GENERIC_ERROR;
+}
+
+void _private_tls_crypto_done(struct TLSContext *context) {
+ unsigned char dummy_buffer[32];
+ unsigned long tag_len = 0;
+ switch (context->crypto.created) {
+ case 1:
+ cbc_done(&context->crypto.ctx_remote.aes_remote);
+ cbc_done(&context->crypto.ctx_local.aes_local);
+ break;
+ case 2:
+ gcm_done(&context->crypto.ctx_remote.aes_gcm_remote, dummy_buffer, &tag_len);
+ gcm_done(&context->crypto.ctx_local.aes_gcm_local, dummy_buffer, &tag_len);
+ break;
+ }
+ context->crypto.created = 0;
+}
+
+void tls_packet_update(struct TLSPacket *packet) {
+ if ((packet) && (!packet->broken)) {
+ int footer_size = 0;
+#ifdef WITH_TLS_13
+ if ((packet->context) && ((packet->context->version == TLS_V13) || (packet->context->version == DTLS_V13)) && (packet->context->cipher_spec_set) && (packet->context->crypto.created)) {
+ // type
+ tls_packet_uint8(packet, packet->buf[0]);
+ // no padding
+ // tls_packet_uint8(packet, 0);
+ footer_size = 1;
+ }
+#endif
+ unsigned int header_size = 5;
+ if ((packet->context) && (packet->context->dtls)) {
+ header_size = 13;
+ *(unsigned short *)(packet->buf + 3) = htons(packet->context->dtls_epoch_local);
+ uint64_t sequence_number = packet->context->local_sequence_number;
+ packet->buf[5] = (unsigned char)(sequence_number / 0x10000000000LL);
+ sequence_number %= 0x10000000000LL;
+ packet->buf[6] = (unsigned char)(sequence_number / 0x100000000LL);
+ sequence_number %= 0x100000000LL;
+ packet->buf[7] = (unsigned char)(sequence_number / 0x1000000);
+ sequence_number %= 0x1000000;
+ packet->buf[8] = (unsigned char)(sequence_number / 0x10000);
+ sequence_number %= 0x10000;
+ packet->buf[9] = (unsigned char)(sequence_number / 0x100);
+ sequence_number %= 0x100;
+ packet->buf[10] = (unsigned char)sequence_number;
+
+ *(unsigned short *)(packet->buf + 11) = htons(packet->len - header_size);
+ } else
+ *(unsigned short *)(packet->buf + 3) = htons(packet->len - header_size);
+ if (packet->context) {
+ if (packet->buf[0] != TLS_CHANGE_CIPHER) {
+ if ((packet->buf[0] == TLS_HANDSHAKE) && (packet->len > header_size)) {
+ unsigned char handshake_type = packet->buf[header_size];
+ if ((handshake_type != 0x00) && (handshake_type != 0x03))
+ _private_tls_update_hash(packet->context, packet->buf + header_size, packet->len - header_size - footer_size, 1, 0);
+ }
+#ifdef TLS_12_FALSE_START
+ if (((packet->context->cipher_spec_set) || (packet->context->false_start)) && (packet->context->crypto.created)) {
+#else
+ if ((packet->context->cipher_spec_set) && (packet->context->crypto.created)) {
+#endif
+ int block_size = TLS_AES_BLOCK_SIZE;
+ int mac_size = 0;
+ unsigned int length = 0;
+ unsigned char padding = 0;
+ unsigned int pt_length = packet->len - header_size;
+
+ if (packet->context->crypto.created == 1) {
+ mac_size = _private_tls_mac_length(packet->context);
+#ifdef TLS_LEGACY_SUPPORT
+ if (packet->context->version == TLS_V10)
+ length = packet->len - header_size + mac_size;
+ else
+#endif
+ length = packet->len - header_size + TLS_AES_IV_LENGTH + mac_size;
+ padding = block_size - length % block_size;
+ length += padding;
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ } else
+ if (packet->context->crypto.created == 3) {
+ mac_size = POLY1305_TAGLEN;
+ length = packet->len - header_size + mac_size;
+#endif
+ } else {
+ mac_size = TLS_GCM_TAG_LEN;
+ length = packet->len - header_size + 8 + mac_size;
+ }
+ if (packet->context->crypto.created == 1) {
+ unsigned char *buf = (unsigned char *)TLS_MALLOC(length);
+ if (buf) {
+ unsigned char *ct = (unsigned char *)TLS_MALLOC(length + header_size);
+ if (ct) {
+ unsigned int buf_pos = 0;
+ memcpy(ct, packet->buf, header_size - 2);
+ *(unsigned short *)&ct[header_size - 2] = htons(length);
+#ifdef TLS_LEGACY_SUPPORT
+ if (packet->context->version != TLS_V10)
+#endif
+ {
+ tls_random(buf, TLS_AES_IV_LENGTH);
+ buf_pos += TLS_AES_IV_LENGTH;
+ }
+ // copy payload
+ memcpy(buf + buf_pos, packet->buf + header_size, packet->len - header_size);
+ buf_pos += packet->len - header_size;
+ if (packet->context->dtls) {
+ unsigned char temp_buf[5];
+ memcpy(temp_buf, packet->buf, 3);
+ *(unsigned short *)(temp_buf + 3) = *(unsigned short *)&packet->buf[header_size - 2];
+ uint64_t dtls_sequence_number = ntohll(*(uint64_t *)&packet->buf[3]);
+ _private_tls_hmac_message(1, packet->context, temp_buf, 5, packet->buf + header_size, packet->len - header_size, buf + buf_pos, mac_size, dtls_sequence_number);
+ } else
+ _private_tls_hmac_message(1, packet->context, packet->buf, packet->len, NULL, 0, buf + buf_pos, mac_size, 0);
+ buf_pos += mac_size;
+
+ memset(buf + buf_pos, padding - 1, padding);
+ buf_pos += padding;
+
+ //DEBUG_DUMP_HEX_LABEL("PT BUFFER", buf, length);
+ _private_tls_crypto_encrypt(packet->context, buf, ct + header_size, length);
+ TLS_FREE(packet->buf);
+ packet->buf = ct;
+ packet->len = length + header_size;
+ packet->size = packet->len;
+ } else {
+ // invalidate packet
+ memset(packet->buf, 0, packet->len);
+ }
+ TLS_FREE(buf);
+ } else {
+ // invalidate packet
+ memset(packet->buf, 0, packet->len);
+ }
+ } else
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ if (packet->context->crypto.created >= 2) {
+#else
+ if (packet->context->crypto.created == 2) {
+#endif
+ // + 1 = type
+ int ct_size = length + header_size + 12 + TLS_MAX_TAG_LEN + 1;
+ unsigned char *ct = (unsigned char *)TLS_MALLOC(ct_size);
+ if (ct) {
+ memset(ct, 0, ct_size);
+ // AEAD
+ // sequence number (8 bytes)
+ // content type (1 byte)
+ // version (2 bytes)
+ // length (2 bytes)
+ unsigned char aad[13];
+ int aad_size = sizeof(aad);
+ unsigned char *sequence = aad;
+#ifdef WITH_TLS_13
+ if ((packet->context->version == TLS_V13) || (packet->context->version == DTLS_V13)) {
+ aad[0] = TLS_APPLICATION_DATA;
+ aad[1] = packet->buf[1];
+ aad[2] = packet->buf[2];
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ if (packet->context->crypto.created == 3)
+ *((unsigned short *)(aad + 3)) = htons(packet->len + POLY1305_TAGLEN - header_size);
+ else
+#endif
+ *((unsigned short *)(aad + 3)) = htons(packet->len + TLS_GCM_TAG_LEN - header_size);
+ aad_size = 5;
+ sequence = aad + 5;
+ if (packet->context->dtls)
+ *((uint64_t *)sequence) = *(uint64_t *)&packet->buf[3];
+ else
+ *((uint64_t *)sequence) = htonll(packet->context->local_sequence_number);
+ } else {
+#endif
+ if (packet->context->dtls)
+ *((uint64_t *)aad) = *(uint64_t *)&packet->buf[3];
+ else
+ *((uint64_t *)aad) = htonll(packet->context->local_sequence_number);
+ aad[8] = packet->buf[0];
+ aad[9] = packet->buf[1];
+ aad[10] = packet->buf[2];
+ *((unsigned short *)(aad + 11)) = htons(packet->len - header_size);
+#ifdef WITH_TLS_13
+ }
+#endif
+ int ct_pos = header_size;
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ if (packet->context->crypto.created == 3) {
+ unsigned int counter = 1;
+ unsigned char poly1305_key[POLY1305_KEYLEN];
+ chacha_ivupdate(&packet->context->crypto.ctx_local.chacha_local, packet->context->crypto.ctx_local_mac.local_aead_iv, sequence, (u8 *)&counter);
+ chacha20_poly1305_key(&packet->context->crypto.ctx_local.chacha_local, poly1305_key);
+ ct_pos += chacha20_poly1305_aead(&packet->context->crypto.ctx_local.chacha_local, packet->buf + header_size, pt_length, aad, aad_size, poly1305_key, ct + ct_pos);
+ } else {
+#endif
+ unsigned char iv[TLS_13_AES_GCM_IV_LENGTH];
+#ifdef WITH_TLS_13
+ if ((packet->context->version == TLS_V13) || (packet->context->version == DTLS_V13)) {
+ memcpy(iv, packet->context->crypto.ctx_local_mac.local_iv, TLS_13_AES_GCM_IV_LENGTH);
+ int i;
+ int offset = TLS_13_AES_GCM_IV_LENGTH - 8;
+ for (i = 0; i < 8; i++)
+ iv[offset + i] = packet->context->crypto.ctx_local_mac.local_iv[offset + i] ^ sequence[i];
+ } else {
+#endif
+ memcpy(iv, packet->context->crypto.ctx_local_mac.local_aead_iv, TLS_AES_GCM_IV_LENGTH);
+ tls_random(iv + TLS_AES_GCM_IV_LENGTH, 8);
+ memcpy(ct + ct_pos, iv + TLS_AES_GCM_IV_LENGTH, 8);
+ ct_pos += 8;
+#ifdef WITH_TLS_13
+ }
+#endif
+
+ gcm_reset(&packet->context->crypto.ctx_local.aes_gcm_local);
+ gcm_add_iv(&packet->context->crypto.ctx_local.aes_gcm_local, iv, 12);
+ gcm_add_aad(&packet->context->crypto.ctx_local.aes_gcm_local, aad, aad_size);
+ gcm_process(&packet->context->crypto.ctx_local.aes_gcm_local, packet->buf + header_size, pt_length, ct + ct_pos, GCM_ENCRYPT);
+ ct_pos += pt_length;
+
+ unsigned long taglen = TLS_GCM_TAG_LEN;
+ gcm_done(&packet->context->crypto.ctx_local.aes_gcm_local, ct + ct_pos, &taglen);
+ ct_pos += taglen;
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ }
+#endif
+#ifdef WITH_TLS_13
+ if ((packet->context->version == TLS_V13) || (packet->context->version == DTLS_V13)) {
+ ct[0] = TLS_APPLICATION_DATA;
+ *(unsigned short *)&ct[1] = htons(packet->context->version == TLS_V13 ? TLS_V12 : DTLS_V12);
+ // is dtls ?
+ if (header_size != 5)
+ memcpy(ct, packet->buf + 3, header_size - 2);
+ } else
+#endif
+ memcpy(ct, packet->buf, header_size - 2);
+ *(unsigned short *)&ct[header_size - 2] = htons(ct_pos - header_size);
+ TLS_FREE(packet->buf);
+ packet->buf = ct;
+ packet->len = ct_pos;
+ packet->size = ct_pos;
+ } else {
+ // invalidate packet
+ memset(packet->buf, 0, packet->len);
+ }
+ } else {
+ // invalidate packet (never reached)
+ memset(packet->buf, 0, packet->len);
+ }
+ }
+ } else
+ packet->context->dtls_epoch_local++;
+ packet->context->local_sequence_number++;
+ }
+ }
+}
+
+int tls_packet_append(struct TLSPacket *packet, const unsigned char *buf, unsigned int len) {
+ if ((!packet) || (packet->broken))
+ return -1;
+
+ if (!len)
+ return 0;
+
+ unsigned int new_len = packet->len + len;
+
+ if (new_len > packet->size) {
+ packet->size = (new_len / TLS_BLOB_INCREMENT + 1) * TLS_BLOB_INCREMENT;
+ packet->buf = (unsigned char *)TLS_REALLOC(packet->buf, packet->size);
+ if (!packet->buf) {
+ packet->size = 0;
+ packet->len = 0;
+ packet->broken = 1;
+ return -1;
+ }
+ }
+ memcpy(packet->buf + packet->len, buf, len);
+ packet->len = new_len;
+ return new_len;
+}
+
+int tls_packet_uint8(struct TLSPacket *packet, unsigned char i) {
+ return tls_packet_append(packet, &i, 1);
+}
+
+int tls_packet_uint16(struct TLSPacket *packet, unsigned short i) {
+ unsigned short ni = htons(i);
+ return tls_packet_append(packet, (unsigned char *)&ni, 2);
+}
+
+int tls_packet_uint32(struct TLSPacket *packet, unsigned int i) {
+ unsigned int ni = htonl(i);
+ return tls_packet_append(packet, (unsigned char *)&ni, 4);
+}
+
+int tls_packet_uint24(struct TLSPacket *packet, unsigned int i) {
+ unsigned char buf[3];
+ buf[0] = i / 0x10000;
+ i %= 0x10000;
+ buf[1] = i / 0x100;
+ i %= 0x100;
+ buf[2] = i;
+
+ return tls_packet_append(packet, buf, 3);
+}
+
+int tls_random(unsigned char *key, int len) {
+ for (int i = 0; i < len; i++)
+ key[i] = rand() & 0xff;
+ return 1;
+}
+
+TLSHash *_private_tls_ensure_hash(struct TLSContext *context) {
+ TLSHash *hash = context->handshake_hash;
+ if (!hash) {
+ hash = (TLSHash *)TLS_MALLOC(sizeof(TLSHash));
+ if (hash)
+ memset(hash, 0, sizeof(TLSHash));
+ context->handshake_hash = hash;
+ }
+ return hash;
+}
+
+void _private_tls_destroy_hash(struct TLSContext *context) {
+ if (context) {
+ TLS_FREE(context->handshake_hash);
+ context->handshake_hash = NULL;
+ }
+}
+
+void _private_tls_create_hash(struct TLSContext *context) {
+ if (!context)
+ return;
+ TLSHash *hash = _private_tls_ensure_hash(context);
+ if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ int hash_size = _private_tls_mac_length(context);
+ if (hash->created) {
+ unsigned char temp[TLS_MAX_SHA_SIZE];
+ sha384_done(&hash->hash32, temp);
+ sha256_done(&hash->hash48, temp);
+ }
+ sha384_init(&hash->hash48);
+ sha256_init(&hash->hash32);
+ hash->created = 1;
+ } else {
+#ifdef TLS_LEGACY_SUPPORT
+ // TLS_V11
+ if (hash->created) {
+ unsigned char temp[TLS_V11_HASH_SIZE];
+ md5_done(&hash->hash32, temp);
+ sha1_done(&hash->hash2, temp);
+ }
+ md5_init(&hash->hash32);
+ sha1_init(&hash->hash2);
+ hash->created = 1;
+#endif
+ }
+}
+
+void _private_tls_update_handshake_list(struct TLSContext *context, const unsigned char *in, unsigned int len, unsigned char direction, unsigned char connection_status) {
+ if ((!context) || (!context->dtls) || (!in) || (!len))
+ return;
+
+ struct TLSHandshakeList *msg = (struct TLSHandshakeList *)TLS_MALLOC(sizeof(struct TLSHandshakeList));
+ if (!msg)
+ return;
+
+ msg->msg = (unsigned char *)TLS_MALLOC(len);
+ if (!msg) {
+ TLS_FREE(msg);
+ return;
+ }
+
+ memcpy(msg->msg, in, len);
+ msg->len = len;
+ msg->direction = direction;
+ msg->connection_status = connection_status ? connection_status : context->connection_status;
+ msg->next = NULL;
+
+ if (!context->dtls_data->dtls_handshake_list) {
+ context->dtls_data->dtls_handshake_list = msg;
+ return;
+ }
+
+ struct TLSHandshakeList *last = context->dtls_data->dtls_handshake_list;
+ while (last->next)
+ last = (struct TLSHandshakeList *)last->next;
+
+ last->next = msg;
+}
+
+int _private_tls_update_hash(struct TLSContext *context, const unsigned char *in, unsigned int len, unsigned char direction, unsigned char connection_status) {
+ if (!context)
+ return 0;
+
+ if (context->dtls)
+ _private_tls_update_handshake_list(context, in, len, direction, connection_status);
+
+ TLSHash *hash = _private_tls_ensure_hash(context);
+ if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ if (!hash->created) {
+ _private_tls_create_hash(context);
+#ifdef TLS_LEGACY_SUPPORT
+ // cache first hello in case of protocol downgrade
+ if ((!context->is_server) && (!context->cached_handshake) && (!context->request_client_certificate) && (len)) {
+ context->cached_handshake = (unsigned char *)TLS_MALLOC(len);
+ if (context->cached_handshake) {
+ memcpy(context->cached_handshake, in, len);
+ context->cached_handshake_len = len;
+ }
+ }
+#endif
+ }
+ int hash_size = _private_tls_mac_length(context);
+ sha256_process(&hash->hash32, in, len);
+ sha384_process(&hash->hash48, in, len);
+ if (!hash_size)
+ hash_size = TLS_SHA256_MAC_SIZE;
+ } else {
+#ifdef TLS_LEGACY_SUPPORT
+ if (!hash->created)
+ _private_tls_create_hash(context);
+ md5_process(&hash->hash32, in, len);
+ sha1_process(&hash->hash2, in, len);
+#endif
+ }
+ if ((context->request_client_certificate) && (len)) {
+ // cache all messages for verification
+ int new_len = context->cached_handshake_len + len;
+ context->cached_handshake = (unsigned char *)TLS_REALLOC(context->cached_handshake, new_len);
+ if (context->cached_handshake) {
+ memcpy(context->cached_handshake + context->cached_handshake_len, in, len);
+ context->cached_handshake_len = new_len;
+ } else
+ context->cached_handshake_len = 0;
+ }
+ return 0;
+}
+
+#ifdef TLS_LEGACY_SUPPORT
+int _private_tls_change_hash_type(struct TLSContext *context) {
+ if (!context)
+ return 0;
+ TLSHash *hash = _private_tls_ensure_hash(context);
+ if ((hash) && (hash->created) && (context->cached_handshake) && (context->cached_handshake_len)) {
+ _private_tls_destroy_hash(context);
+ int res = _private_tls_update_hash(context, context->cached_handshake, context->cached_handshake_len, 0, 0);
+ TLS_FREE(context->cached_handshake);
+ context->cached_handshake = NULL;
+ context->cached_handshake_len = 0;
+ return res;
+ }
+ return 0;
+}
+#endif
+
+int _private_tls_done_hash(struct TLSContext *context, unsigned char *hout) {
+ if (!context)
+ return 0;
+
+ TLSHash *hash = _private_tls_ensure_hash(context);
+ if (!hash->created)
+ return 0;
+
+ int hash_size = 0;
+ if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ unsigned char temp[TLS_MAX_SHA_SIZE];
+ if (!hout)
+ hout = temp;
+ //TLS_HASH_DONE(&hash->hash, hout);
+ hash_size = _private_tls_mac_length(context);
+ if (hash_size == TLS_SHA384_MAC_SIZE) {
+ sha256_done(&hash->hash32, temp);
+ sha384_done(&hash->hash48, hout);
+ } else {
+ sha256_done(&hash->hash32, hout);
+ sha384_done(&hash->hash48, temp);
+ hash_size = TLS_SHA256_MAC_SIZE;
+ }
+ } else {
+#ifdef TLS_LEGACY_SUPPORT
+ // TLS_V11
+ unsigned char temp[TLS_V11_HASH_SIZE];
+ if (!hout)
+ hout = temp;
+ md5_done(&hash->hash32, hout);
+ sha1_done(&hash->hash2, hout + 16);
+ hash_size = TLS_V11_HASH_SIZE;
+#endif
+ }
+ hash->created = 0;
+ if (context->cached_handshake) {
+ // not needed anymore
+ TLS_FREE(context->cached_handshake);
+ context->cached_handshake = NULL;
+ context->cached_handshake_len = 0;
+ }
+ return hash_size;
+}
+
+int _private_tls_get_hash_idx(struct TLSContext *context) {
+ if (!context)
+ return -1;
+ switch (_private_tls_mac_length(context)) {
+ case TLS_SHA256_MAC_SIZE:
+ return find_hash("sha256");
+ case TLS_SHA384_MAC_SIZE:
+ return find_hash("sha384");
+ case TLS_SHA1_MAC_SIZE:
+ return find_hash("sha1");
+ }
+ return -1;
+}
+
+int _private_tls_get_hash(struct TLSContext *context, unsigned char *hout) {
+ if (!context)
+ return 0;
+
+ TLSHash *hash = _private_tls_ensure_hash(context);
+ if (!hash->created)
+ return 0;
+
+ int hash_size = 0;
+ if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ hash_size = _private_tls_mac_length(context);
+ hash_state prec;
+ if (hash_size == TLS_SHA384_MAC_SIZE) {
+ memcpy(&prec, &hash->hash48, sizeof(hash_state));
+ sha384_done(&hash->hash48, hout);
+ memcpy(&hash->hash48, &prec, sizeof(hash_state));
+ } else {
+ memcpy(&prec, &hash->hash32, sizeof(hash_state));
+ hash_size = TLS_SHA256_MAC_SIZE;
+ sha256_done(&hash->hash32, hout);
+ memcpy(&hash->hash32, &prec, sizeof(hash_state));
+ }
+ } else {
+#ifdef TLS_LEGACY_SUPPORT
+ // TLS_V11
+ hash_state prec;
+
+ memcpy(&prec, &hash->hash32, sizeof(hash_state));
+ md5_done(&hash->hash32, hout);
+ memcpy(&hash->hash32, &prec, sizeof(hash_state));
+
+ memcpy(&prec, &hash->hash2, sizeof(hash_state));
+ sha1_done(&hash->hash2, hout + 16);
+ memcpy(&hash->hash2, &prec, sizeof(hash_state));
+
+ hash_size = TLS_V11_HASH_SIZE;
+#endif
+ }
+ return hash_size;
+}
+
+int _private_tls_write_packet(struct TLSPacket *packet) {
+ if (!packet)
+ return -1;
+ struct TLSContext *context = packet->context;
+ if (!context)
+ return -1;
+
+ if (context->tls_buffer) {
+ int len = context->tls_buffer_len + packet->len;
+ context->tls_buffer = (unsigned char *)TLS_REALLOC(context->tls_buffer, len);
+ if (!context->tls_buffer) {
+ context->tls_buffer_len = 0;
+ return -1;
+ }
+ memcpy(context->tls_buffer + context->tls_buffer_len, packet->buf, packet->len);
+ context->tls_buffer_len = len;
+ int written = packet->len;
+ tls_destroy_packet(packet);
+ return written;
+ }
+ context->tls_buffer_len = packet->len;
+ context->tls_buffer = packet->buf;
+ packet->buf = NULL;
+ packet->len = 0;
+ packet->size = 0;
+ tls_destroy_packet(packet);
+ return context->tls_buffer_len;
+}
+
+int _private_tls_write_app_data(struct TLSContext *context, const unsigned char *buf, unsigned int buf_len) {
+ if (!context)
+ return -1;
+ if ((!buf) || (!buf_len))
+ return 0;
+
+ int len = context->application_buffer_len + buf_len;
+ context->application_buffer = (unsigned char *)TLS_REALLOC(context->application_buffer, len);
+ if (!context->application_buffer) {
+ context->application_buffer_len = 0;
+ return -1;
+ }
+ memcpy(context->application_buffer + context->application_buffer_len, buf, buf_len);
+ context->application_buffer_len = len;
+ return buf_len;
+}
+
+const unsigned char *tls_get_write_buffer(struct TLSContext *context, unsigned int *outlen) {
+ if (!outlen)
+ return NULL;
+ if (!context) {
+ *outlen = 0;
+ return NULL;
+ }
+ // check if any error
+ if (context->sleep_until) {
+ if (context->sleep_until < time(NULL)) {
+ *outlen = 0;
+ return NULL;
+ }
+ context->sleep_until = 0;
+ }
+ *outlen = context->tls_buffer_len;
+ return context->tls_buffer;
+}
+
+const unsigned char *tls_get_message(struct TLSContext *context, unsigned int *outlen, unsigned int offset) {
+ if (!outlen)
+ return NULL;
+ if ((!context) || (!context->tls_buffer)) {
+ *outlen = 0;
+ return NULL;
+ }
+
+ if (offset >= context->tls_buffer_len) {
+ *outlen = 0;
+ return NULL;
+ }
+ // check if any error
+ if (context->sleep_until) {
+ if (context->sleep_until < time(NULL)) {
+ *outlen = 0;
+ return NULL;
+ }
+ context->sleep_until = 0;
+ }
+ unsigned char *tls_buffer = &context->tls_buffer[offset];
+ unsigned int tls_buffer_len = context->tls_buffer_len - offset;
+ unsigned int len = 0;
+ if (context->dtls) {
+ if (tls_buffer_len < 13) {
+ *outlen = 0;
+ return NULL;
+ }
+
+ len = ntohs(*(unsigned short *)&tls_buffer[11]) + 13;
+ } else {
+ if (tls_buffer_len < 5) {
+ *outlen = 0;
+ return NULL;
+ }
+ len = ntohs(*(unsigned short *)&tls_buffer[3]) + 5;
+ }
+ if (len > tls_buffer_len) {
+ *outlen = 0;
+ return NULL;
+ }
+
+ *outlen = len;
+ return tls_buffer;
+}
+
+void tls_buffer_clear(struct TLSContext *context) {
+ if ((context) && (context->tls_buffer)) {
+ TLS_FREE(context->tls_buffer);
+ context->tls_buffer = NULL;
+ context->tls_buffer_len = 0;
+ }
+}
+
+int tls_established(struct TLSContext *context) {
+ if (context) {
+ if (context->critical_error)
+ return -1;
+
+ if (context->connection_status == 0xFF)
+ return 1;
+
+#ifdef TLS_12_FALSE_START
+ // allow false start
+ if ((!context->is_server) && (context->version == TLS_V12) && (context->false_start))
+ return 1;
+#endif
+ }
+ return 0;
+}
+
+void tls_read_clear(struct TLSContext *context) {
+ if ((context) && (context->application_buffer)) {
+ TLS_FREE(context->application_buffer);
+ context->application_buffer = NULL;
+ context->application_buffer_len = 0;
+ }
+}
+
+int tls_read(struct TLSContext *context, unsigned char *buf, unsigned int size) {
+ if (!context)
+ return -1;
+ if ((context->application_buffer) && (context->application_buffer_len)) {
+ if (context->application_buffer_len < size)
+ size = context->application_buffer_len;
+
+ memcpy(buf, context->application_buffer, size);
+ if (context->application_buffer_len == size) {
+ TLS_FREE(context->application_buffer);
+ context->application_buffer = NULL;
+ context->application_buffer_len = 0;
+ return size;
+ }
+ context->application_buffer_len -= size;
+ memmove(context->application_buffer, context->application_buffer + size, context->application_buffer_len);
+ return size;
+ }
+ return 0;
+}
+
+struct TLSContext *tls_create_context(unsigned char is_server, unsigned short version) {
+ struct TLSContext *context = (struct TLSContext *)TLS_MALLOC(sizeof(struct TLSContext));
+ if (context) {
+ memset(context, 0, sizeof(struct TLSContext));
+ context->is_server = is_server;
+ if ((version == DTLS_V13) || (version == DTLS_V12) || (version == DTLS_V10)) {
+ context->dtls = 1;
+ context->dtls_data = (struct DTLSData *)TLS_MALLOC(sizeof(struct DTLSData));
+ if (!context->dtls_data) {
+ TLS_FREE(context);
+ return NULL;
+ }
+ memset(context->dtls_data, 0, sizeof(struct DTLSData));
+ }
+ context->version = version;
+ }
+ return context;
+}
+
+#ifdef TLS_FORWARD_SECRECY
+const struct ECCCurveParameters *tls_set_curve(struct TLSContext *context, const struct ECCCurveParameters *curve) {
+ if (!context->is_server)
+ return NULL;
+ const struct ECCCurveParameters *old_curve = context->curve;
+ context->curve = curve;
+ return old_curve;
+}
+#endif
+
+struct TLSContext *tls_accept(struct TLSContext *context) {
+ if ((!context) || (!context->is_server))
+ return NULL;
+
+ struct TLSContext *child = (struct TLSContext *)TLS_MALLOC(sizeof(struct TLSContext));
+ if (child) {
+ memset(child, 0, sizeof(struct TLSContext));
+ child->is_server = 1;
+ child->is_child = 1;
+ child->dtls = context->dtls;
+ if (context->dtls) {
+ child->dtls_data = (struct DTLSData *)TLS_MALLOC(sizeof(struct DTLSData));
+ if (!child->dtls_data) {
+ TLS_FREE(child);
+ return NULL;
+ }
+ memset(child->dtls_data, 0, sizeof(struct DTLSData));
+ }
+ child->version = context->version;
+ child->certificates = context->certificates;
+ child->certificates_count = context->certificates_count;
+ child->private_key = context->private_key;
+#ifdef TLS_ECDSA_SUPPORTED
+ child->ec_private_key = context->ec_private_key;
+#endif
+ child->exportable = context->exportable;
+ child->root_certificates = context->root_certificates;
+ child->root_count = context->root_count;
+#ifdef TLS_FORWARD_SECRECY
+ child->default_dhe_p = context->default_dhe_p;
+ child->default_dhe_g = context->default_dhe_g;
+ child->curve = context->curve;
+#endif
+ child->alpn = context->alpn;
+ child->alpn_count = context->alpn_count;
+ child->request_client_certificate = context->request_client_certificate;
+ }
+ return child;
+}
+
+#ifdef TLS_FORWARD_SECRECY
+void _private_tls_dhe_free(struct TLSContext *context) {
+ if (context->dhe) {
+ _private_tls_dh_clear_key(context->dhe);
+ TLS_FREE(context->dhe);
+ context->dhe = NULL;
+ }
+}
+
+void _private_tls_dhe_create(struct TLSContext *context) {
+ _private_tls_dhe_free(context);
+ context->dhe = (DHKey *)TLS_MALLOC(sizeof(DHKey));
+ if (context->dhe)
+ memset(context->dhe, 0, sizeof(DHKey));
+}
+
+void _private_tls_ecc_dhe_free(struct TLSContext *context) {
+ if (context->ecc_dhe) {
+ ecc_free(context->ecc_dhe);
+ TLS_FREE(context->ecc_dhe);
+ context->ecc_dhe = NULL;
+ }
+}
+
+void _private_tls_ecc_dhe_create(struct TLSContext *context) {
+ _private_tls_ecc_dhe_free(context);
+ context->ecc_dhe = (ecc_key *)TLS_MALLOC(sizeof(ecc_key));
+ memset(context->ecc_dhe, 0, sizeof(ecc_key));
+}
+
+int tls_set_default_dhe_pg(struct TLSContext *context, const char *p_hex_str, const char *g_hex_str) {
+ if ((!context) || (context->is_child) || (!context->is_server) || (!p_hex_str) || (!g_hex_str))
+ return 0;
+
+ TLS_FREE(context->default_dhe_p);
+ TLS_FREE(context->default_dhe_g);
+
+ context->default_dhe_p = NULL;
+ context->default_dhe_g = NULL;
+
+ size_t p_len = strlen(p_hex_str);
+ size_t g_len = strlen(g_hex_str);
+ if ((p_len <= 0) || (g_len <= 0))
+ return 0;
+ context->default_dhe_p = (char *)TLS_MALLOC(p_len + 1);
+ if (!context->default_dhe_p)
+ return 0;
+ context->default_dhe_g = (char *)TLS_MALLOC(g_len + 1);
+ if (!context->default_dhe_g)
+ return 0;
+
+ memcpy(context->default_dhe_p, p_hex_str, p_len);
+ context->default_dhe_p[p_len] = 0;
+
+ memcpy(context->default_dhe_g, g_hex_str, g_len);
+ context->default_dhe_g[g_len] = 0;
+ return 1;
+}
+#endif
+
+const char *tls_alpn(struct TLSContext *context) {
+ if (!context)
+ return NULL;
+ return context->negotiated_alpn;
+}
+
+int tls_add_alpn(struct TLSContext *context, const char *alpn) {
+ if ((!context) || (!alpn) || (!alpn[0]) || ((context->is_server) && (context->is_child)))
+ return TLS_GENERIC_ERROR;
+ int len = strlen(alpn);
+ if (tls_alpn_contains(context, alpn, len))
+ return 0;
+ context->alpn = (char **)TLS_REALLOC(context->alpn, (context->alpn_count + 1) * sizeof(char *));
+ if (!context->alpn) {
+ context->alpn_count = 0;
+ return TLS_NO_MEMORY;
+ }
+ char *alpn_ref = (char *)TLS_MALLOC(len+1);
+ context->alpn[context->alpn_count] = alpn_ref;
+ if (alpn_ref) {
+ memcpy(alpn_ref, alpn, len);
+ alpn_ref[len] = 0;
+ context->alpn_count++;
+ } else
+ return TLS_NO_MEMORY;
+ return 0;
+}
+
+int tls_alpn_contains(struct TLSContext *context, const char *alpn, unsigned char alpn_size) {
+ if ((!context) || (!alpn) || (!alpn_size))
+ return 0;
+
+ if (context->alpn) {
+ int i;
+ for (i = 0; i < context->alpn_count; i++) {
+ const char *alpn_local = context->alpn[i];
+ if (alpn_local) {
+ int len = strlen(alpn_local);
+ if (alpn_size == len) {
+ if (!memcmp(alpn_local, alpn, alpn_size))
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+void tls_destroy_context(struct TLSContext *context) {
+ unsigned int i;
+ if (!context)
+ return;
+ if (!context->is_child) {
+ if (context->certificates) {
+ for (i = 0; i < context->certificates_count; i++)
+ tls_destroy_certificate(context->certificates[i]);
+ }
+ if (context->root_certificates) {
+ for (i = 0; i < context->root_count; i++)
+ tls_destroy_certificate(context->root_certificates[i]);
+ TLS_FREE(context->root_certificates);
+ context->root_certificates = NULL;
+ }
+ if (context->private_key)
+ tls_destroy_certificate(context->private_key);
+#ifdef TLS_ECDSA_SUPPORTED
+ if (context->ec_private_key)
+ tls_destroy_certificate(context->ec_private_key);
+#endif
+ TLS_FREE(context->certificates);
+#ifdef TLS_FORWARD_SECRECY
+ TLS_FREE(context->default_dhe_p);
+ TLS_FREE(context->default_dhe_g);
+#endif
+ if (context->alpn) {
+ for (i = 0; i < context->alpn_count; i++)
+ TLS_FREE(context->alpn[i]);
+ TLS_FREE(context->alpn);
+ }
+ }
+ if (context->client_certificates) {
+ for (i = 0; i < context->client_certificates_count; i++)
+ tls_destroy_certificate(context->client_certificates[i]);
+ TLS_FREE(context->client_certificates);
+ }
+ context->client_certificates = NULL;
+ TLS_FREE(context->master_key);
+ TLS_FREE(context->premaster_key);
+ if (context->crypto.created)
+ _private_tls_crypto_done(context);
+ TLS_FREE(context->message_buffer);
+ _private_tls_done_hash(context, NULL);
+ _private_tls_destroy_hash(context);
+ TLS_FREE(context->tls_buffer);
+ TLS_FREE(context->application_buffer);
+ // zero out the keys before free
+ if ((context->exportable_keys) && (context->exportable_size))
+ memset(context->exportable_keys, 0, context->exportable_size);
+ TLS_FREE(context->exportable_keys);
+ TLS_FREE(context->sni);
+ TLS_FREE(context->dtls_cookie);
+ TLS_FREE(context->cached_handshake);
+#ifdef TLS_FORWARD_SECRECY
+ _private_tls_dhe_free(context);
+ _private_tls_ecc_dhe_free(context);
+#endif
+#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION
+ TLS_FREE(context->verify_data);
+#endif
+ TLS_FREE(context->negotiated_alpn);
+#ifdef WITH_TLS_13
+ TLS_FREE(context->finished_key);
+ TLS_FREE(context->remote_finished_key);
+ TLS_FREE(context->server_finished_hash);
+#endif
+#ifdef TLS_CURVE25519
+ TLS_FREE(context->client_secret);
+#endif
+ // DTLS-related buffer
+ if (context->dtls_data) {
+ if (context->dtls_data->fragment) {
+ TLS_FREE(context->dtls_data->fragment->buffer);
+ TLS_FREE(context->dtls_data->fragment);
+ }
+ while (context->dtls_data->dtls_handshake_list) {
+ struct TLSHandshakeList *next = (struct TLSHandshakeList *)context->dtls_data->dtls_handshake_list->next;
+ if (context->dtls_data->dtls_handshake_list->msg) {
+ TLS_FREE(context->dtls_data->dtls_handshake_list->msg);
+ }
+ TLS_FREE(context->dtls_data->dtls_handshake_list);
+ context->dtls_data->dtls_handshake_list = next;
+ }
+ if (context->dtls_data->key_exchange) {
+ TLS_FREE(context->dtls_data->key_exchange);
+ }
+ if (context->dtls_data->remote_fingerprint) {
+ TLS_FREE(context->dtls_data->remote_fingerprint);
+ }
+ TLS_FREE(context->dtls_data);
+ }
+ TLS_FREE(context);
+}
+
+#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION
+void _private_tls_reset_context(struct TLSContext *context) {
+ unsigned int i;
+ if (!context)
+ return;
+ if (!context->is_child) {
+ if (context->certificates) {
+ for (i = 0; i < context->certificates_count; i++)
+ tls_destroy_certificate(context->certificates[i]);
+ }
+ context->certificates = NULL;
+ if (context->private_key) {
+ tls_destroy_certificate(context->private_key);
+ context->private_key = NULL;
+ }
+#ifdef TLS_ECDSA_SUPPORTED
+ if (context->ec_private_key) {
+ tls_destroy_certificate(context->ec_private_key);
+ context->ec_private_key = NULL;
+ }
+#endif
+ TLS_FREE(context->certificates);
+ context->certificates = NULL;
+#ifdef TLS_FORWARD_SECRECY
+ TLS_FREE(context->default_dhe_p);
+ TLS_FREE(context->default_dhe_g);
+ context->default_dhe_p = NULL;
+ context->default_dhe_g = NULL;
+#endif
+ }
+ if (context->client_certificates) {
+ for (i = 0; i < context->client_certificates_count; i++)
+ tls_destroy_certificate(context->client_certificates[i]);
+ TLS_FREE(context->client_certificates);
+ }
+ context->client_certificates = NULL;
+ TLS_FREE(context->master_key);
+ context->master_key = NULL;
+ TLS_FREE(context->premaster_key);
+ context->premaster_key = NULL;
+ if (context->crypto.created)
+ _private_tls_crypto_done(context);
+ _private_tls_done_hash(context, NULL);
+ _private_tls_destroy_hash(context);
+ TLS_FREE(context->application_buffer);
+ context->application_buffer = NULL;
+ // zero out the keys before free
+ if ((context->exportable_keys) && (context->exportable_size))
+ memset(context->exportable_keys, 0, context->exportable_size);
+ TLS_FREE(context->exportable_keys);
+ context->exportable_keys = NULL;
+ TLS_FREE(context->sni);
+ context->sni = NULL;
+ TLS_FREE(context->dtls_cookie);
+ context->dtls_cookie = NULL;
+ TLS_FREE(context->cached_handshake);
+ context->cached_handshake = NULL;
+ context->connection_status = 0;
+#ifdef TLS_FORWARD_SECRECY
+ _private_tls_dhe_free(context);
+ _private_tls_ecc_dhe_free(context);
+#endif
+}
+#endif
+
+int tls_cipher_supported(struct TLSContext *context, unsigned short cipher) {
+ if (!context)
+ return 0;
+
+ switch (cipher) {
+#ifdef WITH_TLS_13
+ case TLS_AES_128_GCM_SHA256:
+ case TLS_AES_256_GCM_SHA384:
+ case TLS_CHACHA20_POLY1305_SHA256:
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13))
+ return 1;
+ return 0;
+#endif
+#ifdef TLS_FORWARD_SECRECY
+#ifdef TLS_ECDSA_SUPPORTED
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+#ifdef TLS_CLIENT_ECDSA
+ if ((context) && (((context->certificates) && (context->certificates_count) && (context->ec_private_key)) || (!context->is_server)))
+#else
+ if ((context) && (context->certificates) && (context->certificates_count) && (context->ec_private_key))
+#endif
+ return 1;
+ return 0;
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+#endif
+ if ((context->version == TLS_V12) || (context->version == DTLS_V12)) {
+#ifdef TLS_CLIENT_ECDSA
+ if ((context) && (((context->certificates) && (context->certificates_count) && (context->ec_private_key)) || (!context->is_server)))
+#else
+ if ((context) && (context->certificates) && (context->certificates_count) && (context->ec_private_key))
+#endif
+ return 1;
+ }
+ return 0;
+#endif
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+#endif
+ case TLS_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_RSA_WITH_AES_256_CBC_SHA:
+ return 1;
+#ifdef TLS_FORWARD_SECRECY
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+#endif
+#endif
+ case TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case TLS_RSA_WITH_AES_256_GCM_SHA384:
+ if ((context->version == TLS_V12) || (context->version == DTLS_V12))
+ return 1;
+ return 0;
+ }
+ return 0;
+}
+
+int tls_cipher_is_fs(struct TLSContext *context, unsigned short cipher) {
+ if (!context)
+ return 0;
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ switch (cipher) {
+ case TLS_AES_128_GCM_SHA256:
+ case TLS_AES_256_GCM_SHA384:
+ case TLS_CHACHA20_POLY1305_SHA256:
+ return 1;
+ }
+ return 0;
+ }
+#endif
+ switch (cipher) {
+#ifdef TLS_ECDSA_SUPPORTED
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+#endif
+ if ((context) && (context->certificates) && (context->certificates_count) && (context->ec_private_key))
+ return 1;
+ return 0;
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ if ((context->version == TLS_V12) || (context->version == DTLS_V12)) {
+ if ((context) && (context->certificates) && (context->certificates_count) && (context->ec_private_key))
+ return 1;
+ }
+ return 0;
+#endif
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ return 1;
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+#endif
+ if ((context->version == TLS_V12) || (context->version == DTLS_V12))
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+#ifdef WITH_KTLS
+int _private_tls_prefer_ktls(struct TLSContext *context, unsigned short cipher) {
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13) || ((context->version != TLS_V12) && (context->version != DTLS_V12)))
+ return 0;
+
+ switch (cipher) {
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13) || (context->version == TLS_V12) || (context->version == DTLS_V12)) {
+ if ((context->certificates) && (context->certificates_count) && (context->ec_private_key))
+ return 1;
+ }
+ break;
+ case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+int tls_choose_cipher(struct TLSContext *context, const unsigned char *buf, int buf_len, int *scsv_set) {
+ int i;
+ if (scsv_set)
+ *scsv_set = 0;
+ if (!context)
+ return 0;
+ int selected_cipher = TLS_NO_COMMON_CIPHER;
+#ifdef TLS_FORWARD_SECRECY
+#ifdef WITH_KTLS
+ for (i = 0; i < buf_len; i+=2) {
+ unsigned short cipher = ntohs(*(unsigned short *)&buf[i]);
+ if (_private_tls_prefer_ktls(context, cipher)) {
+ selected_cipher = cipher;
+ break;
+ }
+ }
+#endif
+ if (selected_cipher == TLS_NO_COMMON_CIPHER) {
+ for (i = 0; i < buf_len; i+=2) {
+ unsigned short cipher = ntohs(*(unsigned short *)&buf[i]);
+ if (tls_cipher_is_fs(context, cipher)) {
+ selected_cipher = cipher;
+ break;
+ }
+ }
+ }
+#endif
+ for (i = 0; i < buf_len; i+=2) {
+ unsigned short cipher = ntohs(*(unsigned short *)&buf[i]);
+ if (cipher == TLS_FALLBACK_SCSV) {
+ if (scsv_set)
+ *scsv_set = 1;
+ if (selected_cipher != TLS_NO_COMMON_CIPHER)
+ break;
+ }
+#ifndef TLS_ROBOT_MITIGATION
+ else
+ if ((selected_cipher == TLS_NO_COMMON_CIPHER) && (tls_cipher_supported(context, cipher)))
+ selected_cipher = cipher;
+#endif
+ }
+ return selected_cipher;
+}
+
+int tls_cipher_is_ephemeral(struct TLSContext *context) {
+ if (context) {
+ switch (context->cipher) {
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ return 1;
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ return 2;
+#ifdef WITH_TLS_13
+ case TLS_AES_128_GCM_SHA256:
+ case TLS_CHACHA20_POLY1305_SHA256:
+ case TLS_AES_128_CCM_SHA256:
+ case TLS_AES_128_CCM_8_SHA256:
+ case TLS_AES_256_GCM_SHA384:
+ if (context->dhe)
+ return 1;
+ return 2;
+#endif
+ }
+ }
+ return 0;
+}
+
+const char *tls_cipher_name(struct TLSContext *context) {
+ if (context) {
+ switch (context->cipher) {
+ case TLS_RSA_WITH_AES_128_CBC_SHA:
+ return "RSA-AES128CBC-SHA";
+ case TLS_RSA_WITH_AES_256_CBC_SHA:
+ return "RSA-AES256CBC-SHA";
+ case TLS_RSA_WITH_AES_128_CBC_SHA256:
+ return "RSA-AES128CBC-SHA256";
+ case TLS_RSA_WITH_AES_256_CBC_SHA256:
+ return "RSA-AES256CBC-SHA256";
+ case TLS_RSA_WITH_AES_128_GCM_SHA256:
+ return "RSA-AES128GCM-SHA256";
+ case TLS_RSA_WITH_AES_256_GCM_SHA384:
+ return "RSA-AES256GCM-SHA384";
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ return "DHE-RSA-AES128CBC-SHA";
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ return "DHE-RSA-AES256CBC-SHA";
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ return "DHE-RSA-AES128CBC-SHA256";
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ return "DHE-RSA-AES256CBC-SHA256";
+ case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ return "DHE-RSA-AES128GCM-SHA256";
+ case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ return "DHE-RSA-AES256GCM-SHA384";
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ return "ECDHE-RSA-AES128CBC-SHA";
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ return "ECDHE-RSA-AES256CBC-SHA";
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ return "ECDHE-RSA-AES128CBC-SHA256";
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ return "ECDHE-RSA-AES128GCM-SHA256";
+ case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ return "ECDHE-RSA-AES256GCM-SHA384";
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ return "ECDHE-ECDSA-AES128CBC-SHA";
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ return "ECDHE-ECDSA-AES256CBC-SHA";
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ return "ECDHE-ECDSA-AES128CBC-SHA256";
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ return "ECDHE-ECDSA-AES256CBC-SHA384";
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ return "ECDHE-ECDSA-AES128GCM-SHA256";
+ case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ return "ECDHE-ECDSA-AES256GCM-SHA384";
+ case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ return "ECDHE-RSA-CHACHA20-POLY1305-SHA256";
+ case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ return "ECDHE-ECDSA-CHACHA20-POLY1305-SHA256";
+ case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ return "ECDHE-DHE-CHACHA20-POLY1305-SHA256";
+ case TLS_AES_128_GCM_SHA256:
+ return "TLS-AES-128-GCM-SHA256";
+ case TLS_AES_256_GCM_SHA384:
+ return "TLS-AES-256-GCM-SHA384";
+ case TLS_CHACHA20_POLY1305_SHA256:
+ return "TLS-CHACHA20-POLY1305-SHA256";
+ case TLS_AES_128_CCM_SHA256:
+ return "TLS-AES-128-CCM-SHA256";
+ case TLS_AES_128_CCM_8_SHA256:
+ return "TLS-AES-128-CCM-8-SHA256";
+ }
+ }
+ return "UNKNOWN";
+}
+
+#ifdef TLS_FORWARD_SECRECY
+int _private_tls_dh_export_Y(unsigned char *Ybuf, unsigned long *Ylen, DHKey *key) {
+ unsigned long len;
+
+ if ((Ybuf == NULL) || (Ylen == NULL) || (key == NULL))
+ return TLS_GENERIC_ERROR;
+
+ len = mp_unsigned_bin_size(key->y);
+ if (len > *Ylen)
+ return TLS_GENERIC_ERROR;
+
+ *Ylen = len;
+ return 0;
+ }
+
+int _private_tls_dh_export_pqY(unsigned char *pbuf, unsigned long *plen, unsigned char *gbuf, unsigned long *glen, unsigned char *Ybuf, unsigned long *Ylen, DHKey *key) {
+ unsigned long len;
+ int err;
+
+ if ((pbuf == NULL) || (plen == NULL) || (gbuf == NULL) || (glen == NULL) || (Ybuf == NULL) || (Ylen == NULL) || (key == NULL))
+ return TLS_GENERIC_ERROR;
+
+ len = mp_unsigned_bin_size(key->y);
+ if (len > *Ylen)
+ return TLS_GENERIC_ERROR;
+
+ if ((err = mp_to_unsigned_bin(key->y, Ybuf)) != CRYPT_OK)
+ return err;
+
+ *Ylen = len;
+
+ len = mp_unsigned_bin_size(key->p);
+ if (len > *plen)
+ return TLS_GENERIC_ERROR;
+
+ if ((err = mp_to_unsigned_bin(key->p, pbuf)) != CRYPT_OK)
+ return err;
+
+ *plen = len;
+
+ len = mp_unsigned_bin_size(key->g);
+ if (len > *glen)
+ return TLS_GENERIC_ERROR;
+
+ if ((err = mp_to_unsigned_bin(key->g, gbuf)) != CRYPT_OK)
+ return err;
+
+ *glen = len;
+
+ return 0;
+}
+
+void _private_tls_dh_clear_key(DHKey *key) {
+ mp_clear_multi(key->g, key->p, key->x, key->y, NULL);
+ key->g = NULL;
+ key->p = NULL;
+ key->x = NULL;
+ key->y = NULL;
+}
+
+int _private_tls_dh_make_key(int keysize, DHKey *key, const char *pbuf, const char *gbuf, int pbuf_len, int gbuf_len) {
+ unsigned char *buf;
+ int err;
+ if (!key)
+ return TLS_GENERIC_ERROR;
+
+ static prng_state prng;
+ int wprng = find_prng("sprng");
+ if ((err = prng_is_valid(wprng)) != CRYPT_OK)
+ return err;
+
+ buf = (unsigned char *)TLS_MALLOC(keysize);
+ if (!buf)
+ return TLS_NO_MEMORY;
+
+ if (rng_make_prng(keysize, wprng, &prng, NULL) != CRYPT_OK) {
+ TLS_FREE(buf);
+ return TLS_GENERIC_ERROR;
+ }
+
+ if (prng_descriptor[wprng].read(buf, keysize, &prng) != (unsigned long)keysize) {
+ TLS_FREE(buf);
+ return TLS_GENERIC_ERROR;
+ }
+
+ if ((err = mp_init_multi(&key->g, &key->p, &key->x, &key->y, NULL)) != CRYPT_OK) {
+ TLS_FREE(buf);
+
+ return TLS_GENERIC_ERROR;
+ }
+
+ if (gbuf_len <= 0) {
+ if ((err = mp_read_radix(key->g, gbuf, 16)) != CRYPT_OK) {
+ TLS_FREE(buf);
+ _private_tls_dh_clear_key(key);
+ return TLS_GENERIC_ERROR;
+ }
+ } else {
+ if ((err = mp_read_unsigned_bin(key->g, (unsigned char *)gbuf, gbuf_len)) != CRYPT_OK) {
+ TLS_FREE(buf);
+ _private_tls_dh_clear_key(key);
+ return TLS_GENERIC_ERROR;
+ }
+ }
+
+ if (pbuf_len <= 0) {
+ if ((err = mp_read_radix(key->p, pbuf, 16)) != CRYPT_OK) {
+ TLS_FREE(buf);
+ _private_tls_dh_clear_key(key);
+ return TLS_GENERIC_ERROR;
+ }
+ } else {
+ if ((err = mp_read_unsigned_bin(key->p, (unsigned char *)pbuf, pbuf_len)) != CRYPT_OK) {
+ TLS_FREE(buf);
+ _private_tls_dh_clear_key(key);
+ return TLS_GENERIC_ERROR;
+ }
+ }
+
+ if ((err = mp_read_unsigned_bin(key->x, buf, keysize)) != CRYPT_OK) {
+ TLS_FREE(buf);
+ _private_tls_dh_clear_key(key);
+ return TLS_GENERIC_ERROR;
+ }
+
+ if ((err = mp_exptmod(key->g, key->x, key->p, key->y)) != CRYPT_OK) {
+ TLS_FREE(buf);
+ _private_tls_dh_clear_key(key);
+ return TLS_GENERIC_ERROR;
+ }
+
+ TLS_FREE(buf);
+ return 0;
+}
+#endif
+
+int tls_is_ecdsa(struct TLSContext *context) {
+ if (!context)
+ return 0;
+ switch (context->cipher) {
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+#endif
+ return 1;
+ }
+#ifdef WITH_TLS_13
+ if (context->ec_private_key)
+ return 1;
+#endif
+ return 0;
+}
+
+struct TLSPacket *tls_build_client_key_exchange(struct TLSContext *context) {
+ if (context->is_server) {
+ DEBUG_PRINT("CANNOT BUILD CLIENT KEY EXCHANGE MESSAGE FOR SERVERS\n");
+ return NULL;
+ }
+
+ struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0);
+ tls_packet_uint8(packet, 0x10);
+#ifdef TLS_FORWARD_SECRECY
+ int ephemeral = tls_cipher_is_ephemeral(context);
+ if ((ephemeral) && (context->premaster_key) && (context->premaster_key_len)) {
+ if (ephemeral == 1) {
+ unsigned char dh_Ys[0xFFF];
+ unsigned char dh_p[0xFFF];
+ unsigned char dh_g[0xFFF];
+ unsigned long dh_p_len = sizeof(dh_p);
+ unsigned long dh_g_len = sizeof(dh_g);
+ unsigned long dh_Ys_len = sizeof(dh_Ys);
+
+ if (_private_tls_dh_export_pqY(dh_p, &dh_p_len, dh_g, &dh_g_len, dh_Ys, &dh_Ys_len, context->dhe)) {
+ DEBUG_PRINT("ERROR EXPORTING DHE KEY %p\n", context->dhe);
+ TLS_FREE(packet);
+ _private_tls_dhe_free(context);
+ return NULL;
+ }
+ _private_tls_dhe_free(context);
+ DEBUG_DUMP_HEX_LABEL("Yc", dh_Ys, dh_Ys_len);
+ tls_packet_uint24(packet, dh_Ys_len + 2);
+ if (context->dtls)
+ _private_dtls_handshake_data(context, packet, dh_Ys_len + 2);
+ tls_packet_uint16(packet, dh_Ys_len);
+ tls_packet_append(packet, dh_Ys, dh_Ys_len);
+ } else
+ if (context->ecc_dhe) {
+ unsigned char out[TLS_MAX_RSA_KEY];
+ unsigned long out_len = TLS_MAX_RSA_KEY;
+
+ if (ecc_ansi_x963_export(context->ecc_dhe, out, &out_len)) {
+ DEBUG_PRINT("Error exporting ECC key\n");
+ TLS_FREE(packet);
+ return NULL;
+ }
+ _private_tls_ecc_dhe_free(context);
+ tls_packet_uint24(packet, out_len + 1);
+ if (context->dtls) {
+ _private_dtls_handshake_data(context, packet, out_len + 1);
+ context->dtls_seq ++;
+ }
+ tls_packet_uint8(packet, out_len);
+ tls_packet_append(packet, out, out_len);
+ }
+#ifdef TLS_CURVE25519
+ else
+ if ((context->curve == &x25519) && (context->client_secret)) {
+ static const unsigned char basepoint[32] = {9};
+ unsigned char shared_key[32];
+ curve25519(shared_key, context->client_secret, basepoint);
+ tls_packet_uint24(packet, 32 + 1);
+ tls_packet_uint8(packet, 32);
+ tls_packet_append(packet, shared_key, 32);
+ TLS_FREE(context->client_secret);
+ context->client_secret = NULL;
+ }
+#endif
+ _private_tls_compute_key(context, 48);
+ } else
+#endif
+ _private_tls_build_random(packet);
+ context->connection_status = 2;
+ tls_packet_update(packet);
+ return packet;
+}
+
+void _private_dtls_handshake_data(struct TLSContext *context, struct TLSPacket *packet, unsigned int framelength) {
+ // message seq
+ tls_packet_uint16(packet, context->dtls_seq);
+ // fragment offset
+ tls_packet_uint24(packet, 0);
+ // fragment length
+ tls_packet_uint24(packet, framelength);
+}
+
+void _private_dtls_handshake_copyframesize(struct TLSPacket *packet) {
+ packet->buf[22] = packet->buf[14];
+ packet->buf[23] = packet->buf[15];
+ packet->buf[24] = packet->buf[16];
+}
+
+struct TLSPacket *tls_build_server_key_exchange(struct TLSContext *context, int method) {
+ if (!context->is_server) {
+ DEBUG_PRINT("CANNOT BUILD SERVER KEY EXCHANGE MESSAGE FOR CLIENTS\n");
+ return NULL;
+ }
+
+ struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0);
+ if ((context->dtls) && (context->dtls_data->key_exchange) && (context->dtls_data->key_exchange_len > packet->len)) {
+ tls_packet_append(packet, context->dtls_data->key_exchange, context->dtls_data->key_exchange_len);
+ tls_packet_update(packet);
+ context->dtls_seq ++;
+ return packet;
+ }
+ int packet_offset = packet->len;
+ tls_packet_uint8(packet, 0x0C);
+ unsigned char dummy[3];
+ tls_packet_append(packet, dummy, 3);
+ if (context->dtls)
+ _private_dtls_handshake_data(context, packet, 0);
+ int start_len = packet->len;
+#ifdef TLS_FORWARD_SECRECY
+ if (method == KEA_dhe_rsa) {
+
+ if (!context->dhe) {
+ tls_init();
+ _private_tls_dhe_create(context);
+
+ const char *default_dhe_p = context->default_dhe_p;
+ const char *default_dhe_g = context->default_dhe_g;
+ int key_size;
+ if ((!default_dhe_p) || (!default_dhe_g)) {
+ default_dhe_p = TLS_DH_DEFAULT_P;
+ default_dhe_g = TLS_DH_DEFAULT_G;
+ key_size = TLS_DHE_KEY_SIZE / 8;
+ } else {
+ key_size = strlen(default_dhe_p);
+ }
+
+ if (_private_tls_dh_make_key(key_size, context->dhe, default_dhe_p, default_dhe_g, 0, 0)) {
+ DEBUG_PRINT("ERROR CREATING DHE KEY\n");
+ TLS_FREE(packet);
+ TLS_FREE(context->dhe);
+ context->dhe = NULL;
+ return NULL;
+ }
+ }
+
+ unsigned char dh_Ys[0xFFF];
+ unsigned char dh_p[0xFFF];
+ unsigned char dh_g[0xFFF];
+ unsigned long dh_p_len = sizeof(dh_p);
+ unsigned long dh_g_len = sizeof(dh_g);
+ unsigned long dh_Ys_len = sizeof(dh_Ys);
+
+ if (_private_tls_dh_export_pqY(dh_p, &dh_p_len, dh_g, &dh_g_len, dh_Ys, &dh_Ys_len, context->dhe)) {
+ DEBUG_PRINT("ERROR EXPORTING DHE KEY\n");
+ TLS_FREE(packet);
+ return NULL;
+ }
+
+ DEBUG_PRINT("LEN: %lu (%lu, %lu)\n", dh_Ys_len, dh_p_len, dh_g_len);
+ DEBUG_DUMP_HEX_LABEL("DHE PK", dh_Ys, dh_Ys_len);
+ DEBUG_DUMP_HEX_LABEL("DHE P", dh_p, dh_p_len);
+ DEBUG_DUMP_HEX_LABEL("DHE G", dh_g, dh_g_len);
+
+ tls_packet_uint16(packet, dh_p_len);
+ tls_packet_append(packet, dh_p, dh_p_len);
+
+ tls_packet_uint16(packet, dh_g_len);
+ tls_packet_append(packet, dh_g, dh_g_len);
+
+ tls_packet_uint16(packet, dh_Ys_len);
+ tls_packet_append(packet, dh_Ys, dh_Ys_len);
+ //dh_p
+ //dh_g
+ //dh_Ys
+ } else
+ if (method == KEA_ec_diffie_hellman) {
+ // 3 = named curve
+ if (!context->curve)
+ context->curve = default_curve;
+ tls_packet_uint8(packet, 3);
+ tls_packet_uint16(packet, context->curve->iana);
+ if (!context->ecc_dhe) {
+ tls_init();
+ _private_tls_ecc_dhe_create(context);
+
+ ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&context->curve->dp;
+
+ if (ecc_make_key_ex(NULL, find_prng("sprng"), context->ecc_dhe, dp)) {
+ TLS_FREE(context->ecc_dhe);
+ context->ecc_dhe = NULL;
+ DEBUG_PRINT("Error generating ECC key\n");
+ TLS_FREE(packet);
+ return NULL;
+ }
+ }
+ unsigned char out[TLS_MAX_RSA_KEY];
+ unsigned long out_len = TLS_MAX_RSA_KEY;
+ if (ecc_ansi_x963_export(context->ecc_dhe, out, &out_len)) {
+ DEBUG_PRINT("Error exporting ECC key\n");
+ TLS_FREE(packet);
+ return NULL;
+ }
+ tls_packet_uint8(packet, out_len);
+ tls_packet_append(packet, out, out_len);
+ } else
+#endif
+ {
+ TLS_FREE(packet);
+ DEBUG_PRINT("Unsupported ephemeral method: %i\n", method);
+ return NULL;
+ }
+
+ // signature
+ unsigned int params_len = packet->len - start_len;
+ unsigned int message_len = params_len + TLS_CLIENT_RANDOM_SIZE + TLS_SERVER_RANDOM_SIZE;
+ unsigned char *message = (unsigned char *)TLS_MALLOC(message_len);
+ if (message) {
+ unsigned char out[TLS_MAX_RSA_KEY];
+ unsigned long out_len = TLS_MAX_RSA_KEY;
+
+ int hash_algorithm;
+ if ((context->version != TLS_V13) && (context->version != DTLS_V13) && (context->version != TLS_V12) && (context->version != DTLS_V12)) {
+ hash_algorithm = _md5_sha1;
+ } else {
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13) || (context->version == TLS_V12) || (context->version == DTLS_V12))
+ hash_algorithm = sha256;
+ else
+ hash_algorithm = sha1;
+
+#ifdef TLS_ECDSA_SUPPORTED
+ if (tls_is_ecdsa(context)) {
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13) || (context->version == TLS_V12))
+ hash_algorithm = sha512;
+ tls_packet_uint8(packet, hash_algorithm);
+ tls_packet_uint8(packet, ecdsa);
+ } else
+#endif
+ {
+ tls_packet_uint8(packet, hash_algorithm);
+ tls_packet_uint8(packet, rsa_sign);
+ }
+ }
+
+ memcpy(message, context->remote_random, TLS_CLIENT_RANDOM_SIZE);
+ memcpy(message + TLS_CLIENT_RANDOM_SIZE, context->local_random, TLS_SERVER_RANDOM_SIZE);
+ memcpy(message + TLS_CLIENT_RANDOM_SIZE + TLS_SERVER_RANDOM_SIZE, packet->buf + start_len, params_len);
+#ifdef TLS_ECDSA_SUPPORTED
+ if (tls_is_ecdsa(context)) {
+ if (_private_tls_sign_ecdsa(context, hash_algorithm, message, message_len, out, &out_len) == 1) {
+ DEBUG_PRINT("Signing OK! (ECDSA, length %lu)\n", out_len);
+ tls_packet_uint16(packet, out_len);
+ tls_packet_append(packet, out, out_len);
+ }
+ } else
+#endif
+ if (_private_tls_sign_rsa(context, hash_algorithm, message, message_len, out, &out_len) == 1) {
+ DEBUG_PRINT("Signing OK! (length %lu)\n", out_len);
+ tls_packet_uint16(packet, out_len);
+ tls_packet_append(packet, out, out_len);
+ }
+ TLS_FREE(message);
+ }
+ if ((!packet->broken) && (packet->buf)) {
+ int remaining = packet->len - start_len;
+ int payload_pos = 6;
+ if (context->dtls)
+ payload_pos = 14;
+ packet->buf[payload_pos] = remaining / 0x10000;
+ remaining %= 0x10000;
+ packet->buf[payload_pos + 1] = remaining / 0x100;
+ remaining %= 0x100;
+ packet->buf[payload_pos + 2] = remaining;
+ if (context->dtls) {
+ _private_dtls_handshake_copyframesize(packet);
+ context->dtls_seq ++;
+ }
+ }
+ tls_packet_update(packet);
+ if (context->dtls_data) {
+ if (context->dtls_data->key_exchange) {
+ TLS_FREE(context->dtls_data->key_exchange);
+ }
+ context->dtls_data->key_exchange = (unsigned char *)TLS_MALLOC(packet->len - packet_offset);
+ if (context->dtls_data->key_exchange) {
+ context->dtls_data->key_exchange_len = packet->len - packet_offset;
+ memcpy(context->dtls_data->key_exchange, packet->buf + packet_offset, context->dtls_data->key_exchange_len);
+ }
+ }
+ return packet;
+}
+
+void _private_tls_set_session_id(struct TLSContext *context) {
+ if (((context->version == TLS_V13) || (context->version == DTLS_V13)) && (context->session_size == TLS_MAX_SESSION_ID))
+ return;
+ if (tls_random(context->session, TLS_MAX_SESSION_ID))
+ context->session_size = TLS_MAX_SESSION_ID;
+ else
+ context->session_size = 0;
+}
+
+struct TLSPacket *tls_build_hello(struct TLSContext *context, int tls13_downgrade) {
+ tls_init();
+#ifdef WITH_TLS_13
+ if (context->connection_status == 4) {
+ static unsigned char sha256_helloretryrequest[] = {0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C};
+ memcpy(context->local_random, sha256_helloretryrequest, 32);
+ unsigned char header[4] = {0xFE, 0, 0, 0};
+ unsigned char hash[TLS_MAX_SHA_SIZE ];
+ int hash_len = _private_tls_done_hash(context, hash);
+ header[3] = (unsigned char)hash_len;
+ _private_tls_update_hash(context, header, sizeof(header), 1, 0);
+ _private_tls_update_hash(context, hash, hash_len, 1, 0);
+ } else
+ if ((!context->is_server) || ((context->version != TLS_V13) && (context->version != DTLS_V13)))
+#endif
+ if ((!context->dtls) || (!context->dtls_data->has_random)) {
+ if (!tls_random(context->local_random, context->is_server ? TLS_SERVER_RANDOM_SIZE : TLS_CLIENT_RANDOM_SIZE))
+ return NULL;
+ // if (!context->is_server)
+ *(unsigned int *)context->local_random = htonl((unsigned int)time(NULL));
+
+ if (context->dtls)
+ context->dtls_data->has_random = 1;
+ }
+
+ if ((context->is_server) && (tls13_downgrade)) {
+ if ((tls13_downgrade == TLS_V12) || (tls13_downgrade == DTLS_V12))
+ memcpy(context->local_random + TLS_SERVER_RANDOM_SIZE - 8, "DOWNGRD\x01", 8);
+ else
+ memcpy(context->local_random + TLS_SERVER_RANDOM_SIZE - 8, "DOWNGRD\x00", 8);
+ }
+ unsigned short packet_version = context->version;
+ unsigned short version = context->version;
+#ifdef WITH_TLS_13
+ if (context->version == TLS_V13)
+ version = TLS_V12;
+ else
+ if (context->version == DTLS_V13)
+ version = DTLS_V12;
+#endif
+ struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, version, 0);
+ if (packet) {
+ // hello
+ if (context->is_server)
+ tls_packet_uint8(packet, 0x02);
+ else
+ tls_packet_uint8(packet, 0x01);
+ unsigned char dummy[3];
+ tls_packet_append(packet, dummy, 3);
+
+ if (context->dtls)
+ _private_dtls_handshake_data(context, packet, 0);
+
+ int start_len = packet->len;
+ tls_packet_uint16(packet, version);
+ if (context->is_server)
+ tls_packet_append(packet, context->local_random, TLS_SERVER_RANDOM_SIZE);
+ else
+ tls_packet_append(packet, context->local_random, TLS_CLIENT_RANDOM_SIZE);
+
+#ifdef IGNORE_SESSION_ID
+ // session size
+ tls_packet_uint8(packet, 0);
+#else
+ if ((!context->dtls) || (!context->session_size))
+ _private_tls_set_session_id(context);
+ // session size
+ tls_packet_uint8(packet, context->session_size);
+ if (context->session_size)
+ tls_packet_append(packet, context->session, context->session_size);
+#endif
+
+ int extension_len = 0;
+ int alpn_len = 0;
+ int alpn_negotiated_len = 0;
+ int i;
+#ifdef WITH_TLS_13
+ unsigned char shared_key[TLS_MAX_RSA_KEY];
+ unsigned long shared_key_len = TLS_MAX_RSA_KEY;
+ unsigned short shared_key_short = 0;
+ int selected_group = 0;
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ if (context->connection_status == 4) {
+ // connection_status == 4 => hello retry request
+ extension_len += 6;
+ } else
+ if (context->is_server) {
+#ifdef TLS_CURVE25519
+ if (context->curve == &x25519) {
+ extension_len += 8 + 32;
+ shared_key_short = (unsigned short)32;
+ if (context->finished_key) {
+ memcpy(shared_key, context->finished_key, 32);
+ TLS_FREE(context->finished_key);
+ context->finished_key = NULL;
+ }
+ selected_group = context->curve->iana;
+ // make context->curve NULL (x25519 is a different implementation)
+ context->curve = NULL;
+ } else
+#endif
+ if (context->ecc_dhe) {
+ if (ecc_ansi_x963_export(context->ecc_dhe, shared_key, &shared_key_len)) {
+ DEBUG_PRINT("Error exporting ECC DHE key\n");
+ tls_destroy_packet(packet);
+ return tls_build_alert(context, 1, internal_error);
+ }
+ _private_tls_ecc_dhe_free(context);
+ extension_len += 8 + shared_key_len;
+ shared_key_short = (unsigned short)shared_key_len;
+ if (context->curve)
+ selected_group = context->curve->iana;
+ } else
+ if (context->dhe) {
+ selected_group = context->dhe->iana;
+ _private_tls_dh_export_Y(shared_key, &shared_key_len, context->dhe);
+ _private_tls_dhe_free(context);
+ extension_len += 8 + shared_key_len;
+ shared_key_short = (unsigned short)shared_key_len;
+ }
+ }
+ // supported versions
+ if (context->is_server)
+ extension_len += 6;
+ else
+ extension_len += 9;
+ }
+ if ((context->is_server) && (context->negotiated_alpn) && (context->version != TLS_V13) && (context->version != DTLS_V13)) {
+#else
+ if ((context->is_server) && (context->negotiated_alpn)) {
+#endif
+ alpn_negotiated_len = strlen(context->negotiated_alpn);
+ alpn_len = alpn_negotiated_len + 1;
+ extension_len += alpn_len + 6;
+ } else
+ if ((!context->is_server) && (context->alpn_count)) {
+ for (i = 0; i < context->alpn_count;i++) {
+ if (context->alpn[i]) {
+ int len = strlen(context->alpn[i]);
+ if (len)
+ alpn_len += len + 1;
+ }
+ }
+ if (alpn_len)
+ extension_len += alpn_len + 6;
+ }
+
+ // ciphers
+ if (context->is_server) {
+ // fallback ... this should never happen
+ if (!context->cipher)
+ context->cipher = TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
+
+ tls_packet_uint16(packet, context->cipher);
+ // no compression
+ tls_packet_uint8(packet, 0);
+#ifndef STRICT_TLS
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13) || (context->version == TLS_V12) || (context->version == DTLS_V12)) {
+ // extensions size
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ tls_packet_uint16(packet, extension_len);
+ } else
+#endif
+ {
+ if (context->dtls == 4) {
+ // use_srtp
+ extension_len += 9;
+ // record_size_limit
+ // extension_len += 6;
+#ifdef TLS_DTLS_EXTENDED_MASTER_SECRET
+ if ((context->dtls) && (context->dtls_data) && (context->dtls_data->extended_master_secret))
+ extension_len += 4;
+#endif
+ }
+
+ tls_packet_uint16(packet, 5 + extension_len);
+ // secure renegotation
+ // advertise it, but refuse renegotiation
+ tls_packet_uint16(packet, 0xff01);
+#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION
+ // a little defensive
+ if ((context->verify_len) && (!context->verify_data))
+ context->verify_len = 0;
+ tls_packet_uint16(packet, context->verify_len + 1);
+ tls_packet_uint8(packet, context->verify_len);
+ if (context->verify_len)
+ tls_packet_append(packet, (unsigned char *)context->verify_data, context->verify_len);
+#else
+ tls_packet_uint16(packet, 1);
+ tls_packet_uint8(packet, 0);
+#endif
+ }
+ if (alpn_len) {
+ tls_packet_uint16(packet, 0x10);
+ tls_packet_uint16(packet, alpn_len + 2);
+ tls_packet_uint16(packet, alpn_len);
+
+ tls_packet_uint8(packet, alpn_negotiated_len);
+ tls_packet_append(packet, (unsigned char *)context->negotiated_alpn, alpn_negotiated_len);
+ }
+ if (context->dtls == 4) {
+#ifdef TLS_DTLS_EXTENDED_MASTER_SECRET
+ if ((context->dtls) && (context->dtls_data) && (context->dtls_data->extended_master_secret)) {
+ tls_packet_uint16(packet, 0x17);
+ tls_packet_uint16(packet, 0);
+ }
+#endif
+ // record_size_limit
+ // tls_packet_uint16(packet, 0x1C);
+ // tls_packet_uint16(packet, 2);
+ // tls_packet_uint16(packet, 0x4000);
+
+ tls_packet_uint16(packet, 0x0E);
+ tls_packet_uint16(packet, 5);
+ tls_packet_uint16(packet, 2);
+ tls_packet_uint16(packet, SRTP_AES128_CM_HMAC_SHA1_80);
+ tls_packet_uint8(packet, 0);
+ }
+ }
+#endif
+ } else {
+ if (context->dtls) {
+ tls_packet_uint8(packet, context->dtls_cookie_len);
+ if (context->dtls_cookie_len)
+ tls_packet_append(packet, context->dtls_cookie, context->dtls_cookie_len);
+ }
+
+#ifndef STRICT_TLS
+#ifdef WITH_TLS_13
+#ifdef TLS_FORWARD_SECRECY
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ #ifdef TLS_WITH_CHACHA20_POLY1305
+ tls_packet_uint16(packet, TLS_CIPHERS_SIZE(9, 0));
+ tls_packet_uint16(packet, TLS_AES_128_GCM_SHA256);
+ tls_packet_uint16(packet, TLS_AES_256_GCM_SHA384);
+ tls_packet_uint16(packet, TLS_CHACHA20_POLY1305_SHA256);
+ #else
+ tls_packet_uint16(packet, TLS_CIPHERS_SIZE(8, 0));
+ tls_packet_uint16(packet, TLS_AES_128_GCM_SHA256);
+ tls_packet_uint16(packet, TLS_AES_256_GCM_SHA384);
+ #endif
+ #ifdef TLS_PREFER_CHACHA20
+ tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256);
+ tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
+ tls_packet_uint16(packet, TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
+ tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256);
+ tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256);
+ tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256);
+ #else
+ tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256);
+ tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256);
+ tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256);
+ tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
+ tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256);
+ tls_packet_uint16(packet, TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
+ #endif
+ } else
+#endif
+#endif
+ if ((context->version == TLS_V12) || (context->version == DTLS_V12)) {
+#endif
+#ifdef TLS_FORWARD_SECRECY
+#ifdef TLS_CLIENT_ECDHE
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ #ifdef TLS_CLIENT_ECDSA
+ tls_packet_uint16(packet, TLS_CIPHERS_SIZE(16, 5));
+ #ifdef TLS_PREFER_CHACHA20
+ tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256);
+ #endif
+ tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256);
+ #ifndef TLS_PREFER_CHACHA20
+ tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256);
+ #endif
+ tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256);
+ tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA);
+ tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
+ #else
+ // sizeof ciphers (16 ciphers * 2 bytes)
+ tls_packet_uint16(packet, TLS_CIPHERS_SIZE(11, 5));
+ #endif
+#else
+ #ifdef TLS_CLIENT_ECDSA
+ tls_packet_uint16(packet, TLS_CIPHERS_SIZE(13, 5));
+ tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256);
+ tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256);
+ tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA);
+ tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
+ #else
+ // sizeof ciphers (14 ciphers * 2 bytes)
+ tls_packet_uint16(packet, TLS_CIPHERS_SIZE(9, 5));
+ #endif
+#endif
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ #ifdef TLS_PREFER_CHACHA20
+ tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
+ #endif
+#endif
+ tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256);
+ tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA);
+ tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA);
+ tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256);
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ #ifndef TLS_PREFER_CHACHA20
+ tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
+ #endif
+#endif
+#else
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ // sizeof ciphers (11 ciphers * 2 bytes)
+ tls_packet_uint16(packet, TLS_CIPHERS_SIZE(6, 5));
+#else
+ // sizeof ciphers (10 ciphers * 2 bytes)
+ tls_packet_uint16(packet, TLS_CIPHERS_SIZE(5, 5));
+#endif
+#endif
+ // not yet supported, because the first message sent (this one)
+ // is already hashed by the client with sha256 (sha384 not yet supported client-side)
+ // but is fully suported server-side
+ // tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384);
+ tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256);
+ tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256);
+ tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256);
+ tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_CBC_SHA);
+ tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ tls_packet_uint16(packet, TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
+#endif
+#else
+ tls_packet_uint16(packet, TLS_CIPHERS_SIZE(0, 5));
+#endif
+ // tls_packet_uint16(packet, TLS_RSA_WITH_AES_256_GCM_SHA384);
+#ifndef TLS_ROBOT_MITIGATION
+ tls_packet_uint16(packet, TLS_RSA_WITH_AES_128_GCM_SHA256);
+ tls_packet_uint16(packet, TLS_RSA_WITH_AES_256_CBC_SHA256);
+ tls_packet_uint16(packet, TLS_RSA_WITH_AES_128_CBC_SHA256);
+ tls_packet_uint16(packet, TLS_RSA_WITH_AES_256_CBC_SHA);
+ tls_packet_uint16(packet, TLS_RSA_WITH_AES_128_CBC_SHA);
+#endif
+#ifndef STRICT_TLS
+ } else {
+#ifdef TLS_FORWARD_SECRECY
+#ifdef TLS_CLIENT_ECDHE
+ tls_packet_uint16(packet, TLS_CIPHERS_SIZE(5, 2));
+ tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA);
+ tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA);
+#else
+ tls_packet_uint16(packet, TLS_CIPHERS_SIZE(3, 2));
+#endif
+ tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_CBC_SHA);
+ tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_CBC_SHA);
+ tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
+#else
+ tls_packet_uint16(packet, TLS_CIPHERS_SIZE(0, 2));
+#endif
+#ifndef TLS_ROBOT_MITIGATION
+ tls_packet_uint16(packet, TLS_RSA_WITH_AES_256_CBC_SHA);
+ tls_packet_uint16(packet, TLS_RSA_WITH_AES_128_CBC_SHA);
+#endif
+ }
+#endif
+ // compression
+ tls_packet_uint8(packet, 1);
+ // no compression
+ tls_packet_uint8(packet, 0);
+
+ if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ if (context->dtls == 4) {
+ // use_srtp
+ extension_len += 9;// 15;
+ }
+
+ int sni_len = 0;
+ if (context->sni)
+ sni_len = strlen(context->sni);
+
+#ifdef TLS_CLIENT_ECDHE
+ extension_len += 12;
+#endif
+ if (sni_len)
+ extension_len += sni_len + 9;
+#ifdef WITH_TLS_13
+ if ((!context->is_server) && ((context->version == TLS_V13) || (context->version == DTLS_V13))) {
+#ifdef TLS_CURVE25519
+ extension_len += 70;
+#else
+ // secp256r1 produces 65 bytes export
+ extension_len += 103;
+#endif
+ }
+#endif
+ if ((context->version == TLS_V12) || (context->version == DTLS_V12)) {
+ // signature algorithms
+ extension_len += 6 + 2 * TLS_HASH_ALGO_NUMBER * TLS_SIGN_ALGO_NUMBER;
+ }
+
+ tls_packet_uint16(packet, extension_len);
+
+ if (sni_len) {
+ // sni extension
+ tls_packet_uint16(packet, 0x00);
+ // sni extension len
+ tls_packet_uint16(packet, sni_len + 5);
+ // sni len
+ tls_packet_uint16(packet, sni_len + 3);
+ // sni type
+ tls_packet_uint8(packet, 0);
+ // sni host len
+ tls_packet_uint16(packet, sni_len);
+ tls_packet_append(packet, (unsigned char *)context->sni, sni_len);
+ }
+#ifdef TLS_FORWARD_SECRECY
+#ifdef TLS_CLIENT_ECDHE
+ // supported groups
+ tls_packet_uint16(packet, 0x0A);
+ tls_packet_uint16(packet, 8);
+ // 3 curves x 2 bytes
+ tls_packet_uint16(packet, 6);
+ tls_packet_uint16(packet, secp256r1.iana);
+ tls_packet_uint16(packet, secp384r1.iana);
+#ifdef TLS_CURVE25519
+ tls_packet_uint16(packet, x25519.iana);
+#else
+ tls_packet_uint16(packet, secp224r1.iana);
+#endif
+#endif
+#endif
+ if (alpn_len) {
+ tls_packet_uint16(packet, 0x10);
+ tls_packet_uint16(packet, alpn_len + 2);
+ tls_packet_uint16(packet, alpn_len);
+
+ for (i = 0; i < context->alpn_count;i++) {
+ if (context->alpn[i]) {
+ int len = strlen(context->alpn[i]);
+ if (len) {
+ tls_packet_uint8(packet, len);
+ tls_packet_append(packet, (unsigned char *)context->alpn[i], len);
+ }
+ }
+ }
+ }
+
+ if (context->dtls == 4) {
+ tls_packet_uint16(packet, 0x0E);
+ tls_packet_uint16(packet, 5);
+ tls_packet_uint16(packet, 2);
+ tls_packet_uint16(packet, SRTP_AES128_CM_HMAC_SHA1_80);
+ tls_packet_uint8(packet, 0);
+
+ /* tls_packet_uint16(packet, 0x0E);
+ tls_packet_uint16(packet, 11);
+ tls_packet_uint16(packet, 8);
+ tls_packet_uint16(packet, SRTP_AEAD_AES_128_GCM);
+ tls_packet_uint16(packet, SRTP_AEAD_AES_256_GCM);
+ tls_packet_uint16(packet, SRTP_AES128_CM_HMAC_SHA1_80);
+ tls_packet_uint16(packet, SRTP_AES128_CM_HMAC_SHA1_32);
+ tls_packet_uint8(packet, 0); */
+ }
+
+ }
+ }
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ // supported versions
+ tls_packet_uint16(packet, 0x2B);
+ if (context->is_server) {
+ tls_packet_uint16(packet, 2);
+ if (context->version == TLS_V13)
+ tls_packet_uint16(packet, context->tls13_version ? context->tls13_version : TLS_V13);
+ else
+ tls_packet_uint16(packet, context->version);
+ } else {
+ tls_packet_uint16(packet, 5);
+ tls_packet_uint8(packet, 4);
+ tls_packet_uint16(packet, TLS_V13);
+ tls_packet_uint16(packet, 0x7F1C);
+ }
+ if (context->connection_status == 4) {
+ // fallback to the mandatory secp256r1
+ tls_packet_uint16(packet, 0x33);
+ tls_packet_uint16(packet, 2);
+ tls_packet_uint16(packet, (unsigned short)secp256r1.iana);
+ }
+ if (((shared_key_short) && (selected_group)) || (!context->is_server)) {
+ // key share
+ tls_packet_uint16(packet, 0x33);
+ if (context->is_server) {
+ tls_packet_uint16(packet, shared_key_short + 4);
+ tls_packet_uint16(packet, (unsigned short)selected_group);
+ tls_packet_uint16(packet, shared_key_short);
+ tls_packet_append(packet, (unsigned char *)shared_key, shared_key_short);
+ } else {
+#ifdef TLS_CURVE25519
+ // make key
+ shared_key_short = 32;
+ tls_packet_uint16(packet, shared_key_short + 6);
+ tls_packet_uint16(packet, shared_key_short + 4);
+
+ TLS_FREE(context->client_secret);
+ context->client_secret = (unsigned char *)TLS_MALLOC(32);
+ if (!context->client_secret) {
+ DEBUG_PRINT("ERROR IN TLS_MALLOC");
+ TLS_FREE(packet);
+ return NULL;
+
+ }
+
+ static const unsigned char basepoint[32] = {9};
+
+ tls_random(context->client_secret, 32);
+
+ context->client_secret[0] &= 248;
+ context->client_secret[31] &= 127;
+ context->client_secret[31] |= 64;
+
+ curve25519(shared_key, context->client_secret, basepoint);
+
+ tls_packet_uint16(packet, (unsigned short)x25519.iana);
+ tls_packet_uint16(packet, shared_key_short);
+ tls_packet_append(packet, (unsigned char *)shared_key, shared_key_short);
+#else
+ // make key
+ shared_key_short = 65;
+ tls_packet_uint16(packet, shared_key_short + 6);
+ tls_packet_uint16(packet, shared_key_short + 4);
+
+ _private_tls_ecc_dhe_create(context);
+ ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&secp256r1.dp;
+
+ if (ecc_make_key_ex(NULL, find_prng("sprng"), context->ecc_dhe, dp)) {
+ TLS_FREE(context->ecc_dhe);
+ context->ecc_dhe = NULL;
+ DEBUG_PRINT("Error generating ECC key\n");
+ TLS_FREE(packet);
+ return NULL;
+ }
+ unsigned char out[TLS_MAX_RSA_KEY];
+ unsigned long out_len = shared_key_short;
+ if (ecc_ansi_x963_export(context->ecc_dhe, out, &out_len)) {
+ DEBUG_PRINT("Error exporting ECC key\n");
+ TLS_FREE(packet);
+ return NULL;
+ }
+
+ tls_packet_uint16(packet, (unsigned short)secp256r1.iana);
+ tls_packet_uint16(packet, out_len);
+ tls_packet_append(packet, (unsigned char *)out, shared_key_short);
+#endif
+ }
+ }
+ }
+#endif
+ if ((context->version == TLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ if (!context->is_server) {
+ // signature algorithms
+ tls_packet_uint16(packet, 0x0D); // type
+ tls_packet_uint16(packet, 2 + 2 * TLS_HASH_ALGO_NUMBER * TLS_SIGN_ALGO_NUMBER); // length
+ tls_packet_uint16(packet, 2 * TLS_HASH_ALGO_NUMBER * TLS_SIGN_ALGO_NUMBER); // actual length of the list and items themselves further
+ for (TLSHashAlgorithm hash = md5; !(hash > sha512); ++hash) {
+ for (TLSSignatureAlgorithm sign = rsa; !(sign > ecdsa); ++sign) {
+ tls_packet_uint16(packet, ((uint16_t)(hash) << 8) | (sign & 0xFF));
+ }
+ }
+ }
+ }
+
+ if ((!packet->broken) && (packet->buf)) {
+ int remaining = packet->len - start_len;
+ int payload_pos = 6;
+ if (context->dtls)
+ payload_pos = 14;
+ packet->buf[payload_pos] = remaining / 0x10000;
+ remaining %= 0x10000;
+ packet->buf[payload_pos + 1] = remaining / 0x100;
+ remaining %= 0x100;
+ packet->buf[payload_pos + 2] = remaining;
+ if (context->dtls) {
+ _private_dtls_handshake_copyframesize(packet);
+ context->dtls_seq ++;
+ }
+ }
+ tls_packet_update(packet);
+ }
+ return packet;
+}
+
+struct TLSPacket *tls_certificate_request(struct TLSContext *context) {
+ if ((!context) || (!context->is_server))
+ return NULL;
+
+ unsigned short packet_version = context->version;
+ struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, packet_version, 0);
+ if (packet) {
+ // certificate request
+ tls_packet_uint8(packet, 0x0D);
+ unsigned char dummy[3];
+ tls_packet_append(packet, dummy, 3);
+ if (context->dtls)
+ _private_dtls_handshake_data(context, packet, 0);
+ int start_len = packet->len;
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ // certificate request context
+ tls_packet_uint8(packet, 0);
+ // extensions
+ tls_packet_uint16(packet, 18);
+ // signature algorithms
+ tls_packet_uint16(packet, 0x0D);
+ tls_packet_uint16(packet, 14);
+ tls_packet_uint16(packet, 12);
+ // rsa_pkcs1_sha256
+ // tls_packet_uint16(packet, 0x0401);
+ // rsa_pkcs1_sha384
+ // tls_packet_uint16(packet, 0x0501);
+ // rsa_pkcs1_sha512
+ // tls_packet_uint16(packet, 0x0601);
+
+ // ecdsa_secp256r1_sha256
+ tls_packet_uint16(packet, 0x0403);
+ // ecdsa_secp384r1_sha384
+ tls_packet_uint16(packet, 0x0503);
+ // ecdsa_secp521r1_sha512
+ tls_packet_uint16(packet, 0x0604);
+ // rsa_pss_rsae_sha256
+ tls_packet_uint16(packet, 0x0804);
+ // rsa_pss_rsae_sha384
+ tls_packet_uint16(packet, 0x0805);
+ // rsa_pss_rsae_sha512
+ tls_packet_uint16(packet, 0x0806);
+ } else
+#endif
+ {
+#ifdef TLS_ECDSA_SUPPORTED
+ tls_packet_uint8(packet, 2);
+ tls_packet_uint8(packet, rsa_sign);
+ tls_packet_uint8(packet, ecdsa_sign);
+#else
+ tls_packet_uint8(packet, 1);
+ tls_packet_uint8(packet, rsa_sign);
+#endif
+ if ((context->version == TLS_V12) || (context->version == DTLS_V12)) {
+ // 10 pairs of 2 bytes
+#ifdef TLS_ECDSA_SUPPORTED
+ tls_packet_uint16(packet, 14);
+ // ecdsa_secp256r1_sha256
+ tls_packet_uint16(packet, 0x0403);
+ // // ecdsa_secp384r1_sha384
+ tls_packet_uint16(packet, 0x0503);
+#else
+ tls_packet_uint16(packet, 10);
+#endif
+ tls_packet_uint8(packet, sha256);
+ tls_packet_uint8(packet, rsa);
+ tls_packet_uint8(packet, sha1);
+ tls_packet_uint8(packet, rsa);
+ tls_packet_uint8(packet, sha384);
+ tls_packet_uint8(packet, rsa);
+ tls_packet_uint8(packet, sha512);
+ tls_packet_uint8(packet, rsa);
+ tls_packet_uint8(packet, md5);
+ tls_packet_uint8(packet, rsa);
+ }
+ // no DistinguishedName yet
+ tls_packet_uint16(packet, 0);
+ }
+ if (!packet->broken) {
+ int remaining = packet->len - start_len;
+ int payload_pos = 6;
+ if (context->dtls)
+ payload_pos = 14;
+ packet->buf[payload_pos] = remaining / 0x10000;
+ remaining %= 0x10000;
+ packet->buf[payload_pos + 1] = remaining / 0x100;
+ remaining %= 0x100;
+ packet->buf[payload_pos + 2] = remaining;
+
+ if (context->dtls) {
+ _private_dtls_handshake_copyframesize(packet);
+ context->dtls_seq++;
+ }
+ }
+ tls_packet_update(packet);
+ }
+ return packet;
+}
+
+int _private_dtls_build_cookie(struct TLSContext *context) {
+ if ((!context->dtls_cookie) || (!context->dtls_cookie_len)) {
+ context->dtls_cookie = (unsigned char *)TLS_MALLOC(DTLS_COOKIE_SIZE);
+ if (!context->dtls_cookie)
+ return 0;
+
+#ifdef WITH_RANDOM_DLTS_COOKIE
+ if (!tls_random(context->dtls_cookie, DTLS_COOKIE_SIZE)) {
+ TLS_FREE(context->dtls_cookie);
+ context->dtls_cookie = NULL;
+ return 0;
+ }
+ context->dtls_cookie_len = DTLS_COOKIE_SIZE;
+#else
+ hmac_state hmac;
+ hmac_init(&hmac, find_hash("sha256"), dtls_secret, sizeof(dtls_secret));
+ hmac_process(&hmac, context->remote_random, TLS_CLIENT_RANDOM_SIZE);
+
+ unsigned long out_size = DTLS_COOKIE_SIZE;
+ hmac_done(&hmac, context->dtls_cookie, &out_size);
+
+ context->dtls_cookie_len = out_size;
+#endif
+ }
+ return 1;
+}
+
+struct TLSPacket *tls_build_verify_request(struct TLSContext *context) {
+ if ((!context->is_server) || (!context->dtls))
+ return NULL;
+
+ if ((!context->dtls_cookie) || (!context->dtls_cookie_len)) {
+ if (!_private_dtls_build_cookie(context))
+ return NULL;
+ }
+
+ unsigned short packet_version = context->version;
+ struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, packet_version, 0);
+ if (packet) {
+ // verify request
+ tls_packet_uint8(packet, 0x03);
+ // 24-bit length
+ tls_packet_uint24(packet, context->dtls_cookie_len + 3);
+ // 16-bit message_sequence
+ tls_packet_uint16(packet, 0);
+ // 24-bit fragment_offset
+ tls_packet_uint24(packet, 0);
+ // 24-bit fragment_length
+ tls_packet_uint24(packet, context->dtls_cookie_len + 3);
+ // server_version
+ tls_packet_uint16(packet, context->version);
+ tls_packet_uint8(packet, context->dtls_cookie_len);
+ tls_packet_append(packet, context->dtls_cookie, context->dtls_cookie_len);
+ tls_packet_update(packet);
+ }
+ return packet;
+}
+
+int _private_dtls_check_packet(struct TLSContext *context, const unsigned char *buf, int buf_len) {
+ CHECK_SIZE(11, buf_len, TLS_NEED_MORE_DATA)
+
+ unsigned int bytes_to_follow = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2];
+ // not used: unsigned short message_seq = ntohs(*(unsigned short *)&buf[3]);
+ unsigned int fragment_offset = buf[5] * 0x10000 + buf[6] * 0x100 + buf[7];
+ unsigned int fragment_length = buf[8] * 0x10000 + buf[9] * 0x100 + buf[10];
+
+ if ((fragment_offset) || (fragment_length != bytes_to_follow)) {
+ if ((context->dtls_data->fragment) && (context->dtls_data->fragment->written == bytes_to_follow))
+ return bytes_to_follow;
+
+ return TLS_FEATURE_NOT_SUPPORTED;
+ }
+ return bytes_to_follow;
+}
+
+void _private_dtls_reset(struct TLSContext *context) {
+ context->dtls_epoch_local = 0;
+ context->dtls_epoch_remote = 0;
+ // context->local_sequence_number = 1;
+ context->dtls_seq = 0;
+ _private_tls_destroy_hash(context);
+ context->connection_status = 0;
+}
+
+int tls_parse_verify_request(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets) {
+ *write_packets = 0;
+ if ((context->connection_status != 0) || (!context->dtls)) {
+ DEBUG_PRINT("UNEXPECTED VERIFY REQUEST MESSAGE\n");
+ return TLS_UNEXPECTED_MESSAGE;
+ }
+ int res = 11;
+ int bytes_to_follow = _private_dtls_check_packet(context, buf, buf_len);
+ if (bytes_to_follow < 0)
+ return bytes_to_follow;
+
+ CHECK_SIZE(bytes_to_follow, buf_len - res, TLS_NEED_MORE_DATA)
+ // not used: unsigned short version = ntohs(*(unsigned short *)&buf[res]);
+ res += 2;
+ unsigned char len = buf[res];
+ res++;
+ TLS_FREE(context->dtls_cookie);
+ context->dtls_cookie_len = 0;
+ if (len) {
+ CHECK_SIZE(len, buf_len - res, TLS_NEED_MORE_DATA)
+ context->dtls_cookie = (unsigned char *)TLS_MALLOC(len);
+ if (!context->dtls_cookie)
+ return TLS_NO_MEMORY;
+ context->dtls_cookie_len = len;
+ memcpy(context->dtls_cookie, &buf[res], len);
+ res += len;
+ *write_packets = 4;
+ }
+
+ // reset context
+ _private_dtls_reset(context);
+ return res;
+}
+
+void _private_dtls_reset_cookie(struct TLSContext *context) {
+ TLS_FREE(context->dtls_cookie);
+ context->dtls_cookie = NULL;
+ context->dtls_cookie_len = 0;
+}
+
+#ifdef WITH_TLS_13
+int _private_tls_parse_key_share(struct TLSContext *context, const unsigned char *buf, int buf_len) {
+ int i = 0;
+ struct ECCCurveParameters *curve = 0;
+ DHKey *dhkey = 0;
+ int dhe_key_size = 0;
+ const unsigned char *buffer = NULL;
+ unsigned char *out2;
+ unsigned long out_size;
+ unsigned short key_size = 0;
+ while (buf_len >= 4) {
+ unsigned short named_group = ntohs(*(unsigned short *)&buf[i]);
+ i += 2;
+ buf_len -= 2;
+
+ key_size = ntohs(*(unsigned short *)&buf[i]);
+ i += 2;
+ buf_len -= 2;
+
+ if (key_size > buf_len)
+ return TLS_BROKEN_PACKET;
+
+ switch (named_group) {
+ case 0x0017:
+ curve = &secp256r1;
+ buffer = &buf[i];
+ DEBUG_PRINT("KEY SHARE => secp256r1\n");
+ buf_len = 0;
+ continue;
+ case 0x0018:
+ // secp384r1
+ curve = &secp384r1;
+ buffer = &buf[i];
+ DEBUG_PRINT("KEY SHARE => secp384r1\n");
+ buf_len = 0;
+ continue;
+ case 0x0019:
+ // secp521r1
+ break;
+ case 0x001D:
+ // x25519
+#ifdef TLS_CURVE25519
+ if (key_size != 32) {
+ DEBUG_PRINT("INVALID x25519 KEY SIZE (%i)\n", key_size);
+ continue;
+ }
+ curve = &x25519;
+ buffer = &buf[i];
+ DEBUG_PRINT("KEY SHARE => x25519\n");
+ buf_len = 0;
+ continue;
+#endif
+ break;
+
+ case 0x001E:
+ // x448
+ break;
+ case 0x0100:
+ dhkey = &ffdhe2048;
+ dhe_key_size = 2048;
+ break;
+ case 0x0101:
+ dhkey = &ffdhe3072;
+ dhe_key_size = 3072;
+ break;
+ case 0x0102:
+ dhkey = &ffdhe4096;
+ dhe_key_size = 4096;
+ break;
+ case 0x0103:
+ dhkey = &ffdhe6144;
+ dhe_key_size = 6144;
+ break;
+ case 0x0104:
+ dhkey = &ffdhe8192;
+ dhe_key_size = 8192;
+ break;
+ }
+ i += key_size;
+ buf_len -= key_size;
+
+ if (!context->is_server)
+ break;
+ }
+ tls_init();
+ if (curve) {
+ context->curve = curve;
+#ifdef TLS_CURVE25519
+ if (curve == &x25519) {
+ if ((context->is_server) && (!tls_random(context->local_random, TLS_SERVER_RANDOM_SIZE)))
+ return TLS_GENERIC_ERROR;
+ unsigned char secret[32];
+ static const unsigned char basepoint[32] = {9};
+
+ if ((context->is_server) || (!context->client_secret)) {
+ tls_random(secret, 32);
+
+ secret[0] &= 248;
+ secret[31] &= 127;
+ secret[31] |= 64;
+
+ // use finished key to store public key
+ TLS_FREE(context->finished_key);
+ context->finished_key = (unsigned char *)TLS_MALLOC(32);
+ if (!context->finished_key)
+ return TLS_GENERIC_ERROR;
+
+ curve25519(context->finished_key, secret, basepoint);
+
+ TLS_FREE(context->premaster_key);
+ context->premaster_key = (unsigned char *)TLS_MALLOC(32);
+ if (!context->premaster_key)
+ return TLS_GENERIC_ERROR;
+
+ curve25519(context->premaster_key, secret, buffer);
+ context->premaster_key_len = 32;
+ } else {
+ TLS_FREE(context->premaster_key);
+ context->premaster_key = (unsigned char *)TLS_MALLOC(32);
+ if (!context->premaster_key)
+ return TLS_GENERIC_ERROR;
+
+ curve25519(context->premaster_key, context->client_secret, buffer);
+ context->premaster_key_len = 32;
+
+ TLS_FREE(context->client_secret);
+ context->client_secret = NULL;
+ }
+ DEBUG_DUMP_HEX_LABEL("x25519 KEY", context->premaster_key, context->premaster_key_len);
+
+ return 0;
+ }
+#endif
+ if (context->is_server) {
+ _private_tls_ecc_dhe_create(context);
+ if (ecc_make_key_ex(NULL, find_prng("sprng"), context->ecc_dhe, (ltc_ecc_set_type *)&context->curve->dp)) {
+ TLS_FREE(context->ecc_dhe);
+ context->ecc_dhe = NULL;
+ DEBUG_PRINT("Error generating ECC DHE key\n");
+ return TLS_GENERIC_ERROR;
+ }
+ }
+
+ ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&context->curve->dp;
+
+ if ((context->is_server) && (!tls_random(context->local_random, TLS_SERVER_RANDOM_SIZE)))
+ return TLS_GENERIC_ERROR;
+
+ ecc_key client_key;
+ memset(&client_key, 0, sizeof(client_key));
+ if (ecc_ansi_x963_import_ex(buffer, key_size, &client_key, dp)) {
+ DEBUG_PRINT("Error importing ECC DHE key\n");
+ return TLS_GENERIC_ERROR;
+ }
+ out2 = (unsigned char *)TLS_MALLOC(key_size);
+ out_size = key_size;
+
+ int err = ecc_shared_secret(context->ecc_dhe, &client_key, out2, &out_size);
+ ecc_free(&client_key);
+
+ if (err) {
+ DEBUG_PRINT("ECC DHE DECRYPT ERROR %i\n", err);
+ TLS_FREE(out2);
+ return TLS_GENERIC_ERROR;
+ }
+ DEBUG_PRINT("OUT_SIZE: %lu\n", out_size);
+ DEBUG_DUMP_HEX_LABEL("ECC DHE", out2, out_size);
+
+ TLS_FREE(context->premaster_key);
+ context->premaster_key = out2;
+ context->premaster_key_len = out_size;
+ return 0;
+ } else
+ if ((dhkey) && (buffer)) {
+ _private_tls_dhe_create(context);
+ if (!tls_random(context->local_random, TLS_SERVER_RANDOM_SIZE))
+ return TLS_GENERIC_ERROR;
+ if (_private_tls_dh_make_key(dhe_key_size / 8, context->dhe, (const char *)dhkey->p, (const char *)dhkey->g, 0, 0)) {
+ TLS_FREE(context->dhe);
+ context->dhe = NULL;
+ DEBUG_PRINT("Error generating DHE key\n");
+ return TLS_GENERIC_ERROR;
+ }
+
+ unsigned int dhe_out_size;
+ out2 = _private_tls_decrypt_dhe(context, buffer, key_size, &dhe_out_size, 0);
+ if (!out2) {
+ DEBUG_PRINT("Error generating DHE shared key\n");
+ return TLS_GENERIC_ERROR;
+ }
+
+ TLS_FREE(context->premaster_key);
+ context->premaster_key = out2;
+ context->premaster_key_len = dhe_out_size;
+ if (context->dhe)
+ context->dhe->iana = dhkey->iana;
+ return 0;
+ }
+ DEBUG_PRINT("NO COMMON KEY SHARE SUPPORTED\n");
+ return TLS_NO_COMMON_CIPHER;
+}
+#endif
+
+int tls_parse_hello(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets, unsigned int *dtls_verified) {
+ *write_packets = 0;
+ *dtls_verified = 0;
+ if ((context->connection_status != 0) && (context->connection_status != 4)) {
+ // ignore multiple hello on dtls
+ if (context->dtls) {
+ DEBUG_PRINT("RETRANSMITTED HELLO MESSAGE RECEIVED\n");
+ return 1;
+ }
+ DEBUG_PRINT("UNEXPECTED HELLO MESSAGE\n");
+ return TLS_UNEXPECTED_MESSAGE;
+ }
+
+ int res = 0;
+ int downgraded = 0;
+ int hello_min_size = context->dtls ? TLS_CLIENT_HELLO_MINSIZE + 8 : TLS_CLIENT_HELLO_MINSIZE;
+ CHECK_SIZE(hello_min_size, buf_len, TLS_NEED_MORE_DATA)
+ // big endian
+ unsigned int bytes_to_follow = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2];
+ res += 3;
+ if (context->dtls) {
+ int dtls_check = _private_dtls_check_packet(context, buf, buf_len);
+ if (dtls_check < 0)
+ return dtls_check;
+ // 16 bit message seq + 24 bit fragment offset + 24 bit fragment length
+ res += 8;
+ }
+ CHECK_SIZE(bytes_to_follow, buf_len - res, TLS_NEED_MORE_DATA)
+
+ CHECK_SIZE(2, buf_len - res, TLS_NEED_MORE_DATA)
+ unsigned short version = ntohs(*(unsigned short *)&buf[res]);
+ unsigned short cipher = 0;
+
+ res += 2;
+ VERSION_SUPPORTED(version, TLS_NOT_SAFE)
+ DEBUG_PRINT("VERSION REQUIRED BY REMOTE %x, VERSION NOW %x\n", (int)version, (int)context->version);
+#ifdef TLS_LEGACY_SUPPORT
+ // when no legacy support, don't downgrade
+#ifndef TLS_FORCE_LOCAL_VERSION
+ // downgrade ?
+ if (context->dtls) {
+ // for dlts, newer version has lower id (1.0 = FEFF, 1.2 = FEFD)
+ if (context->version < version)
+ downgraded = 1;
+ } else {
+ if (context->version > version)
+ downgraded = 1;
+ }
+ if (downgraded) {
+ context->version = version;
+ if (!context->is_server)
+ _private_tls_change_hash_type(context);
+ }
+#endif
+#endif
+ memcpy(context->remote_random, &buf[res], TLS_CLIENT_RANDOM_SIZE);
+ res += TLS_CLIENT_RANDOM_SIZE;
+
+ unsigned char session_len = buf[res++];
+ CHECK_SIZE(session_len, buf_len - res, TLS_NEED_MORE_DATA)
+ if ((session_len) && (session_len <= TLS_MAX_SESSION_ID)) {
+ memcpy(context->session, &buf[res], session_len);
+ context->session_size = session_len;
+ DEBUG_DUMP_HEX_LABEL("REMOTE SESSION ID: ", context->session, context->session_size);
+ } else
+ if (!context->dtls)
+ context->session_size = 0;
+ res += session_len;
+
+ const unsigned char *cipher_buffer = NULL;
+ unsigned short cipher_len = 0;
+ int scsv_set = 0;
+ if (context->is_server) {
+ if (context->dtls) {
+ CHECK_SIZE(1, buf_len - res, TLS_NEED_MORE_DATA)
+ unsigned char tls_cookie_len = buf[res++];
+ if (tls_cookie_len) {
+ CHECK_SIZE(tls_cookie_len, buf_len - res, TLS_NEED_MORE_DATA)
+ if ((!context->dtls_cookie_len) || (!context->dtls_cookie))
+ _private_dtls_build_cookie(context);
+
+ if ((context->dtls_cookie_len != tls_cookie_len) || (!context->dtls_cookie)) {
+ *dtls_verified = 2;
+ // _private_dtls_reset_cookie(context);
+ DEBUG_PRINT("INVALID DTLS COOKIE\n");
+ return TLS_BROKEN_PACKET;
+ }
+ if (memcmp(context->dtls_cookie, &buf[res], tls_cookie_len)) {
+ *dtls_verified = 3;
+ // _private_dtls_reset_cookie(context);
+ DEBUG_PRINT("MISMATCH DTLS COOKIE\n");
+ return TLS_BROKEN_PACKET;
+ }
+ // _private_dtls_reset_cookie(context);
+ context->dtls_seq ++;
+ *dtls_verified = 1;
+ res += tls_cookie_len;
+ } else {
+ *write_packets = 2;
+ return buf_len;
+ }
+ }
+ CHECK_SIZE(2, buf_len - res, TLS_NEED_MORE_DATA)
+ cipher_len = ntohs(*(unsigned short *)&buf[res]);
+ res += 2;
+ CHECK_SIZE(cipher_len, buf_len - res, TLS_NEED_MORE_DATA)
+ // faster than cipher_len % 2
+ if (cipher_len & 1)
+ return TLS_BROKEN_PACKET;
+
+ cipher_buffer = &buf[res];
+ res += cipher_len;
+
+ CHECK_SIZE(1, buf_len - res, TLS_NEED_MORE_DATA)
+ unsigned char compression_list_size = buf[res++];
+ CHECK_SIZE(compression_list_size, buf_len - res, TLS_NEED_MORE_DATA)
+
+ // no compression support
+ res += compression_list_size;
+ } else {
+ CHECK_SIZE(2, buf_len - res, TLS_NEED_MORE_DATA)
+ cipher = ntohs(*(unsigned short *)&buf[res]);
+ res += 2;
+ context->cipher = cipher;
+#ifndef WITH_TLS_13
+ if (!tls_cipher_supported(context, cipher)) {
+ context->cipher = 0;
+ DEBUG_PRINT("NO CIPHER SUPPORTED\n");
+ return TLS_NO_COMMON_CIPHER;
+ }
+ DEBUG_PRINT("CIPHER: %s\n", tls_cipher_name(context));
+#endif
+ CHECK_SIZE(1, buf_len - res, TLS_NEED_MORE_DATA)
+ unsigned char compression = buf[res++];
+ if (compression != 0) {
+ DEBUG_PRINT("COMPRESSION NOT SUPPORTED\n");
+ return TLS_COMPRESSION_NOT_SUPPORTED;
+ }
+ }
+
+ if (res > 0) {
+ if (context->is_server)
+ *write_packets = 2;
+ if (context->connection_status != 4)
+ context->connection_status = 1;
+ }
+
+
+ if (res > 2)
+ res += 2;
+#ifdef WITH_TLS_13
+ const unsigned char *key_share = NULL;
+ unsigned short key_size = 0;
+#endif
+ while (buf_len - res >= 4) {
+ // have extensions
+ unsigned short extension_type = ntohs(*(unsigned short *)&buf[res]);
+ res += 2;
+ unsigned short extension_len = ntohs(*(unsigned short *)&buf[res]);
+ res += 2;
+ DEBUG_PRINT("Extension: 0x0%x (%i), len: %i\n", (int)extension_type, (int)extension_type, (int)extension_len);
+ // SNI extension
+ CHECK_SIZE(extension_len, buf_len - res, TLS_NEED_MORE_DATA)
+ if (extension_type == 0x00) {
+ // unsigned short sni_len = ntohs(*(unsigned short *)&buf[res]);
+ // unsigned char sni_type = buf[res + 2];
+ unsigned short sni_host_len = ntohs(*(unsigned short *)&buf[res + 3]);
+ CHECK_SIZE(sni_host_len, buf_len - res - 5, TLS_NEED_MORE_DATA)
+ if (sni_host_len) {
+ TLS_FREE(context->sni);
+ context->sni = (char *)TLS_MALLOC(sni_host_len + 1);
+ if (context->sni) {
+ memcpy(context->sni, &buf[res + 5], sni_host_len);
+ context->sni[sni_host_len] = 0;
+ DEBUG_PRINT("SNI HOST INDICATOR: [%s]\n", context->sni);
+ }
+ }
+ } else
+#ifdef TLS_FORWARD_SECRECY
+ if (extension_type == 0x0A) {
+ // supported groups
+ if (buf_len - res > 2) {
+ unsigned short group_len = ntohs(*(unsigned short *)&buf[res]);
+ if (buf_len - res >= group_len + 2) {
+ DEBUG_DUMP_HEX_LABEL("SUPPORTED GROUPS", &buf[res + 2], group_len);
+ int i;
+ int selected = 0;
+ for (i = 0; i < group_len; i += 2) {
+ unsigned short iana_n = ntohs(*(unsigned short *)&buf[res + 2 + i]);
+ switch (iana_n) {
+ case 23:
+ context->curve = &secp256r1;
+ selected = 1;
+ break;
+ case 24:
+ context->curve = &secp384r1;
+ selected = 1;
+ break;
+#ifdef WITH_TLS_13
+ // needs different implementation
+ // case 29:
+ // context->curve = &x25519;
+ // selected = 1;
+ // break;
+#endif
+ // do not use it anymore
+ // case 25:
+ // context->curve = &secp521r1;
+ // selected = 1;
+ // break;
+ }
+ if (selected) {
+ DEBUG_PRINT("SELECTED CURVE %s\n", context->curve->name);
+ break;
+ }
+ }
+ }
+ }
+ } else
+#endif
+ if ((extension_type == 0x10) && (context->alpn) && (context->alpn_count)) {
+ if (buf_len - res > 2) {
+ unsigned short alpn_len = ntohs(*(unsigned short *)&buf[res]);
+ if ((alpn_len) && (alpn_len <= extension_len - 2)) {
+ unsigned char *alpn = (unsigned char *)&buf[res + 2];
+ int alpn_pos = 0;
+ while (alpn_pos < alpn_len) {
+ unsigned char alpn_size = alpn[alpn_pos++];
+ if (alpn_size + alpn_pos >= extension_len)
+ break;
+ if ((alpn_size) && (tls_alpn_contains(context, (char *)&alpn[alpn_pos], alpn_size))) {
+ TLS_FREE(context->negotiated_alpn);
+ context->negotiated_alpn = (char *)TLS_MALLOC(alpn_size + 1);
+ if (context->negotiated_alpn) {
+ memcpy(context->negotiated_alpn, &alpn[alpn_pos], alpn_size);
+ context->negotiated_alpn[alpn_size] = 0;
+ DEBUG_PRINT("NEGOTIATED ALPN: %s\n", context->negotiated_alpn);
+ }
+ break;
+ }
+ alpn_pos += alpn_size;
+ // ServerHello contains just one alpn
+ if (!context->is_server)
+ break;
+ }
+ }
+ }
+ } else
+ if (extension_type == 0x0D) {
+ // supported signatures
+ DEBUG_DUMP_HEX_LABEL("SUPPORTED SIGNATURES", &buf[res], extension_len);
+ } else
+ if (extension_type == 0x0B) {
+ // supported point formats
+ DEBUG_DUMP_HEX_LABEL("SUPPORTED POINT FORMATS", &buf[res], extension_len);
+ } else
+ if ((extension_type == 0x0E) && (context->dtls)) {
+ // use_srtp
+ DEBUG_DUMP_HEX_LABEL("USE SRTP", &buf[res], extension_len);
+ context->dtls = 4;
+ } else
+ if ((extension_type == 0x17) && (context->dtls)) {
+ // extended_master_secret
+ DEBUG_PRINT("EXTENDED MASTER SECRET");
+#ifdef TLS_DTLS_EXTENDED_MASTER_SECRET
+ if (context->dtls_data)
+ context->dtls_data->extended_master_secret = 1;
+#endif
+ }
+#ifdef WITH_TLS_13
+ else
+ if (extension_type == 0x2B) {
+ // supported versions
+ if ((context->is_server) && (buf[res] == extension_len - 1)) {
+ if (extension_len > 2) {
+ DEBUG_DUMP_HEX_LABEL("SUPPORTED VERSIONS", &buf[res], extension_len);
+ int i;
+ int limit = (int)buf[res];
+ if (limit == extension_len - 1) {
+ for (i = 1; i < limit; i += 2) {
+ if ((ntohs(*(unsigned short *)&buf[res + i]) == TLS_V13) || (ntohs(*(unsigned short *)&buf[res + i]) == 0x7F1C)) {
+ context->version = TLS_V13;
+ context->tls13_version = ntohs(*(unsigned short *)&buf[res + i]);
+ DEBUG_PRINT("TLS 1.3 SUPPORTED\n");
+ break;
+ }
+ }
+ }
+ }
+ } else
+ if ((!context->is_server) && (extension_len == 2)) {
+ if ((ntohs(*(unsigned short *)&buf[res]) == TLS_V13) || (ntohs(*(unsigned short *)&buf[res]) == 0x7F1C)) {
+ context->version = TLS_V13;
+ context->tls13_version = ntohs(*(unsigned short *)&buf[res]);
+ DEBUG_PRINT("TLS 1.3 SUPPORTED\n");
+ }
+ }
+ } else
+ if (extension_type == 0x2A) {
+ // early data
+ DEBUG_DUMP_HEX_LABEL("EXTENSION, EARLY DATA", &buf[res], extension_len);
+ } else
+ if (extension_type == 0x29) {
+ // pre shared key
+ DEBUG_DUMP_HEX_LABEL("EXTENSION, PRE SHARED KEY", &buf[res], extension_len);
+ } else
+ if (extension_type == 0x33) {
+ // key share
+ if (context->is_server) {
+ key_size = ntohs(*(unsigned short *)&buf[res]);
+ if ((context->is_server) && (key_size > extension_len - 2)) {
+ DEBUG_PRINT("BROKEN KEY SHARE\n");
+ return TLS_BROKEN_PACKET;
+ }
+ } else {
+ key_size = extension_len;
+ }
+ DEBUG_DUMP_HEX_LABEL("EXTENSION, KEY SHARE", &buf[res], extension_len);
+ if (context->is_server)
+ key_share = &buf[res + 2];
+ else
+ key_share = &buf[res];
+ } else
+ if (extension_type == 0x0D) {
+ // signature algorithms
+ DEBUG_DUMP_HEX_LABEL("EXTENSION, SIGNATURE ALGORITHMS", &buf[res], extension_len);
+ } else
+ if (extension_type == 0x2D) {
+ // psk key exchange modes
+ DEBUG_DUMP_HEX_LABEL("EXTENSION, PSK KEY EXCHANGE MODES", &buf[res], extension_len);
+ }
+#endif
+ res += extension_len;
+ }
+ if (buf_len != res)
+ return TLS_NEED_MORE_DATA;
+ if ((context->is_server) && (cipher_buffer) && (cipher_len)) {
+ int cipher = tls_choose_cipher(context, cipher_buffer, cipher_len, &scsv_set);
+ if (cipher < 0) {
+ DEBUG_PRINT("NO COMMON CIPHERS\n");
+ return cipher;
+ }
+ if ((downgraded) && (scsv_set)) {
+ DEBUG_PRINT("NO DOWNGRADE (SCSV SET)\n");
+ _private_tls_write_packet(tls_build_alert(context, 1, inappropriate_fallback));
+ context->critical_error = 1;
+ return TLS_NOT_SAFE;
+ }
+ context->cipher = cipher;
+ }
+#ifdef WITH_TLS_13
+ if (!context->is_server) {
+ if (!tls_cipher_supported(context, cipher)) {
+ context->cipher = 0;
+ DEBUG_PRINT("NO CIPHER SUPPORTED\n");
+ return TLS_NO_COMMON_CIPHER;
+ }
+ DEBUG_PRINT("CIPHER: %s\n", tls_cipher_name(context));
+ }
+
+ if ((key_share) && (key_size) && ((context->version == TLS_V13) || (context->version == DTLS_V13))) {
+ int key_share_err = _private_tls_parse_key_share(context, key_share, key_size);
+ if (key_share_err) {
+ // request hello retry
+ if (context->connection_status != 4) {
+ *write_packets = 5;
+ context->hs_messages[1] = 0;
+ context->connection_status = 4;
+ return res;
+ } else
+ return key_share_err;
+ }
+ // we have key share
+ if (context->is_server)
+ context->connection_status = 3;
+ else
+ context->connection_status = 2;
+ }
+#endif
+ return res;
+}
+
+int tls_parse_certificate(struct TLSContext *context, const unsigned char *buf, int buf_len, int is_client) {
+ int res = 0;
+ CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA)
+ unsigned int size_of_all_certificates = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2];
+
+ if (size_of_all_certificates <= 4)
+ return 3 + size_of_all_certificates;
+ res += 3;
+ if (context->dtls) {
+ int dtls_check = _private_dtls_check_packet(context, buf, buf_len);
+ if (dtls_check < 0)
+ return dtls_check;
+ res += 8;
+ }
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ int context_size = buf[res];
+ res++;
+ // must be 0
+ if (context_size)
+ res += context_size;
+ size_of_all_certificates --;
+ }
+#endif
+
+ CHECK_SIZE(size_of_all_certificates, buf_len - res, TLS_NEED_MORE_DATA);
+ int size = size_of_all_certificates;
+
+ int idx = 0;
+ int valid_certificate = 0;
+ while (size > 0) {
+ idx++;
+ CHECK_SIZE(3, buf_len - res, TLS_NEED_MORE_DATA);
+ unsigned int certificate_size = buf[res] * 0x10000 + buf[res + 1] * 0x100 + buf[res + 2];
+ res += 3;
+ CHECK_SIZE(certificate_size, buf_len - res, TLS_NEED_MORE_DATA)
+ // load chain
+ int certificates_in_chain = 0;
+ int res2 = res;
+ unsigned int remaining = certificate_size;
+ do {
+ if (remaining <= 3)
+ break;
+ certificates_in_chain++;
+ unsigned int certificate_size2 = buf[res2] * 0x10000 + buf[res2 + 1] * 0x100 + buf[res2 + 2];
+ res2 += 3;
+ remaining -= 3;
+ if (certificate_size2 > remaining) {
+ DEBUG_PRINT("Invalid certificate size (%i from %i bytes remaining)\n", certificate_size2, remaining);
+ break;
+ }
+ remaining -= certificate_size2;
+
+ struct TLSCertificate *cert = asn1_parse(context, &buf[res2], certificate_size2, is_client);
+ if (cert) {
+ if (certificate_size2) {
+ cert->bytes = (unsigned char *)TLS_MALLOC(certificate_size2);
+ if (cert->bytes) {
+ cert->len = certificate_size2;
+ memcpy(cert->bytes, &buf[res2], certificate_size2);
+ }
+ }
+ if ((context->dtls_data) && (context->dtls_data->remote_fingerprint)) {
+ unsigned char hash[32];
+
+ hash_state state;
+
+ sha256_init(&state);
+ sha256_process(&state, cert->bytes, cert->len);
+ sha256_done(&state, hash);
+
+ int i;
+ char buffer_data[100];
+ char *buffer = buffer_data;
+ int buf_len = sizeof(buffer_data);
+ buffer[0] = 0;
+ for (i = 0; i < 32; i++) {
+ if (buf_len <= 1)
+ break;
+ if (i) {
+ snprintf(buffer, buf_len, ":");
+ buffer ++;
+ buf_len --;
+ }
+ if (buf_len <= 2)
+ break;
+ snprintf(buffer, buf_len, "%02X", (unsigned int)hash[i]);
+ buffer += 2;
+ buf_len -= 2;
+ }
+ if (strcmp(buffer_data, context->dtls_data->remote_fingerprint)) {
+ DEBUG_PRINT("PEER CERTIFICATE FINGERPRINT VALIDATION FAILED, computed %s, expected %s\n", buffer_data, context->dtls_data->remote_fingerprint);
+ return TLS_UNSUPPORTED_CERTIFICATE;
+ }
+ }
+ // valid certificate
+ if (is_client) {
+ valid_certificate = 1;
+ context->client_certificates = (struct TLSCertificate **)TLS_REALLOC(context->client_certificates, (context->client_certificates_count + 1) * sizeof(struct TLSCertificate *));
+ context->client_certificates[context->client_certificates_count] = cert;
+ context->client_certificates_count++;
+ } else {
+ context->certificates = (struct TLSCertificate **)TLS_REALLOC(context->certificates, (context->certificates_count + 1) * sizeof(struct TLSCertificate *));
+ context->certificates[context->certificates_count] = cert;
+ context->certificates_count++;
+ if ((cert->pk) || (cert->priv))
+ valid_certificate = 1;
+ else
+ if (!context->is_server)
+ valid_certificate = 1;
+ }
+ }
+ res2 += certificate_size2;
+#ifdef WITH_TLS_13
+ // extension
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ if (remaining >= 2) {
+ // ignore extensions
+ remaining -= 2;
+ unsigned short size = ntohs(*(unsigned short *)&buf[res2]);
+ if ((size) && (size >= remaining)) {
+ res2 += size;
+ remaining -= size;
+ }
+ res2 += 2;
+ }
+ }
+#endif
+ } while (remaining > 0);
+ if (remaining) {
+ DEBUG_PRINT("Extra %i bytes after certificate\n", remaining);
+ }
+ size -= certificate_size + 3;
+ res += certificate_size;
+ }
+ if (!valid_certificate)
+ return TLS_UNSUPPORTED_CERTIFICATE;
+ if (res != buf_len) {
+ DEBUG_PRINT("Warning: %i bytes read from %i byte buffer\n", (int)res, (int)buf_len);
+ }
+ return res;
+}
+
+int _private_tls_parse_dh(const unsigned char *buf, int buf_len, const unsigned char **out, int *out_size) {
+ int res = 0;
+ *out = NULL;
+ *out_size = 0;
+ CHECK_SIZE(2, buf_len, TLS_NEED_MORE_DATA)
+ unsigned short size = ntohs(*(unsigned short *)buf);
+ res += 2;
+ CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA)
+ DEBUG_DUMP_HEX(&buf[res], size);
+ *out = &buf[res];
+ *out_size = size;
+ res += size;
+ return res;
+}
+
+int _private_tls_parse_random(struct TLSContext *context, const unsigned char *buf, int buf_len) {
+ int res = 0;
+ int ephemeral = tls_cipher_is_ephemeral(context);
+ unsigned short size;
+ if (ephemeral == 2) {
+ CHECK_SIZE(1, buf_len, TLS_NEED_MORE_DATA)
+ size = buf[0];
+ res += 1;
+ } else {
+ CHECK_SIZE(2, buf_len, TLS_NEED_MORE_DATA)
+ size = ntohs(*(unsigned short *)buf);
+ res += 2;
+ }
+
+ CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA)
+ unsigned int out_len = 0;
+ unsigned char *random = NULL;
+ switch (ephemeral) {
+#ifdef TLS_FORWARD_SECRECY
+ case 1:
+ random = _private_tls_decrypt_dhe(context, &buf[res], size, &out_len, !context->dtls);
+ break;
+ case 2:
+ random = _private_tls_decrypt_ecc_dhe(context, &buf[res], size, &out_len, !context->dtls);
+ break;
+#endif
+ default:
+ random = _private_tls_decrypt_rsa(context, &buf[res], size, &out_len);
+ }
+
+ if ((random) && (out_len > 2)) {
+ DEBUG_DUMP_HEX_LABEL("PRE MASTER KEY", random, out_len);
+ TLS_FREE(context->premaster_key);
+ context->premaster_key = random;
+ context->premaster_key_len = out_len;
+ _private_tls_compute_key(context, 48);
+ } else {
+ TLS_FREE(random);
+ return 0;
+ }
+ res += size;
+ return res;
+}
+
+int _private_tls_build_random(struct TLSPacket *packet) {
+ int res = 0;
+ unsigned char rand_bytes[48];
+ int bytes = 48;
+ if (!tls_random(rand_bytes, bytes))
+ return TLS_GENERIC_ERROR;
+
+ // max supported version
+ if (packet->context->is_server)
+ *(unsigned short *)rand_bytes = htons(packet->context->version);
+ else
+ if (packet->context->dtls)
+ *(unsigned short *)rand_bytes = htons(DTLS_V12);
+ else
+ *(unsigned short *)rand_bytes = htons(TLS_V12);
+ //DEBUG_DUMP_HEX_LABEL("PREMASTER KEY", rand_bytes, bytes);
+
+ TLS_FREE(packet->context->premaster_key);
+ packet->context->premaster_key = (unsigned char *)TLS_MALLOC(bytes);
+ if (!packet->context->premaster_key)
+ return TLS_NO_MEMORY;
+
+ packet->context->premaster_key_len = bytes;
+ memcpy(packet->context->premaster_key, rand_bytes, packet->context->premaster_key_len);
+
+ unsigned int out_len;
+ unsigned char *random = _private_tls_encrypt_rsa(packet->context, packet->context->premaster_key, packet->context->premaster_key_len, &out_len);
+
+ _private_tls_compute_key(packet->context, bytes);
+ if ((random) && (out_len > 2)) {
+ tls_packet_uint24(packet, out_len + 2);
+ if (packet->context->dtls)
+ _private_dtls_handshake_data(packet->context, packet, out_len + 2);
+ tls_packet_uint16(packet, out_len);
+ tls_packet_append(packet, random, out_len);
+ } else
+ res = TLS_GENERIC_ERROR;
+ TLS_FREE(random);
+ if (res)
+ return res;
+
+ return out_len + 2;
+}
+
+const unsigned char *_private_tls_parse_signature(struct TLSContext *context, const unsigned char *buf, int buf_len, int *hash_algorithm, int *sign_algorithm, int *sig_size, int *offset) {
+ int res = 0;
+ CHECK_SIZE(2, buf_len, NULL)
+ *hash_algorithm = _md5_sha1;
+ *sign_algorithm = rsa_sign;
+ *sig_size = 0;
+ if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ *hash_algorithm = buf[res];
+ res++;
+ *sign_algorithm = buf[res];
+ res++;
+ }
+ unsigned short size = ntohs(*(unsigned short *)&buf[res]);
+ res += 2;
+ CHECK_SIZE(size, buf_len - res, NULL)
+ DEBUG_DUMP_HEX(&buf[res], size);
+ *sig_size = size;
+ *offset = res + size;
+ return &buf[res];
+}
+
+int tls_parse_server_key_exchange(struct TLSContext *context, const unsigned char *buf, int buf_len) {
+ int res = 0;
+ int dh_res = 0;
+ CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA)
+ unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2];
+ res += 3;
+ if (context->dtls) {
+ int dtls_check = _private_dtls_check_packet(context, buf, buf_len);
+ if (dtls_check < 0)
+ return dtls_check;
+ res += 8;
+ }
+ const unsigned char *packet_ref = buf + res;
+ CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA);
+
+ if (!size)
+ return res;
+
+ unsigned char has_ds_params = 0;
+ unsigned int key_size = 0;
+#ifdef TLS_FORWARD_SECRECY
+ const struct ECCCurveParameters *curve = NULL;
+ const unsigned char *pk_key = NULL;
+ int ephemeral = tls_cipher_is_ephemeral(context);
+ if (ephemeral) {
+ if (ephemeral == 1) {
+ has_ds_params = 1;
+ } else {
+ if (buf[res++] != 3) {
+ // named curve
+ // any other method is not supported
+ return 0;
+ }
+ CHECK_SIZE(3, buf_len - res, TLS_NEED_MORE_DATA);
+ int iana_n = ntohs(*(unsigned short *)&buf[res]);
+ res += 2;
+ key_size = buf[res];
+ res++;
+ CHECK_SIZE(key_size, buf_len - res, TLS_NEED_MORE_DATA);
+ DEBUG_PRINT("IANA CURVE NUMBER: %i\n", iana_n);
+ switch (iana_n) {
+ case 19:
+ curve = &secp192r1;
+ break;
+ case 20:
+ curve = &secp224k1;
+ break;
+ case 21:
+ curve = &secp224r1;
+ break;
+ case 22:
+ curve = &secp256k1;
+ break;
+ case 23:
+ curve = &secp256r1;
+ break;
+ case 24:
+ curve = &secp384r1;
+ break;
+ case 25:
+ curve = &secp521r1;
+ break;
+#ifdef TLS_CURVE25519
+ case 29:
+ curve = &x25519;
+ break;
+#endif
+ default:
+ DEBUG_PRINT("UNSUPPORTED CURVE\n");
+ return TLS_GENERIC_ERROR;
+ }
+ pk_key = &buf[res];
+ res += key_size;
+ context->curve = curve;
+ }
+ }
+#endif
+ const unsigned char *dh_p = NULL;
+ int dh_p_len = 0;
+ const unsigned char *dh_g = NULL;
+ int dh_g_len = 0;
+ const unsigned char *dh_Ys = NULL;
+ int dh_Ys_len = 0;
+ if (has_ds_params) {
+ DEBUG_PRINT(" dh_p: ");
+ dh_res = _private_tls_parse_dh(&buf[res], buf_len - res, &dh_p, &dh_p_len);
+ if (dh_res <= 0)
+ return TLS_BROKEN_PACKET;
+ res += dh_res;
+ DEBUG_PRINT("\n");
+
+ DEBUG_PRINT(" dh_q: ");
+ dh_res = _private_tls_parse_dh(&buf[res], buf_len - res, &dh_g, &dh_g_len);
+ if (dh_res <= 0)
+ return TLS_BROKEN_PACKET;
+ res += dh_res;
+ DEBUG_PRINT("\n");
+
+ DEBUG_PRINT(" dh_Ys: ");
+ dh_res = _private_tls_parse_dh(&buf[res], buf_len - res, &dh_Ys, &dh_Ys_len);
+ if (dh_res <= 0)
+ return TLS_BROKEN_PACKET;
+ res += dh_res;
+ DEBUG_PRINT("\n");
+ }
+ int sign_size;
+ int hash_algorithm;
+ int sign_algorithm;
+ int packet_size = res - 3;
+ if (context->dtls)
+ packet_size -= 8;
+ int offset = 0;
+ DEBUG_PRINT(" SIGNATURE (%i/%i/%i): ", packet_size, dh_res, key_size);
+ const unsigned char *signature = _private_tls_parse_signature(context, &buf[res], buf_len - res, &hash_algorithm, &sign_algorithm, &sign_size, &offset);
+ DEBUG_PRINT("\n");
+ if ((sign_size <= 0) || (!signature))
+ return TLS_BROKEN_PACKET;
+ res += offset;
+ // check signature
+ unsigned int message_len = packet_size + TLS_CLIENT_RANDOM_SIZE + TLS_SERVER_RANDOM_SIZE;
+ unsigned char *message = (unsigned char *)TLS_MALLOC(message_len);
+ if (message) {
+ memcpy(message, context->local_random, TLS_CLIENT_RANDOM_SIZE);
+ memcpy(message + TLS_CLIENT_RANDOM_SIZE, context->remote_random, TLS_SERVER_RANDOM_SIZE);
+ memcpy(message + TLS_CLIENT_RANDOM_SIZE + TLS_SERVER_RANDOM_SIZE, packet_ref, packet_size);
+#ifdef TLS_CLIENT_ECDSA
+ if (tls_is_ecdsa(context)) {
+ if (_private_tls_verify_ecdsa(context, hash_algorithm, signature, sign_size, message, message_len, NULL) != 1) {
+ // DEBUG_PRINT("ECC Server signature FAILED!\n");
+ // TLS_FREE(message);
+ // return TLS_BROKEN_PACKET;
+ }
+ } else
+#endif
+ {
+ if (_private_tls_verify_rsa(context, hash_algorithm, signature, sign_size, message, message_len) != 1) {
+ // DEBUG_PRINT("Server signature FAILED!\n");
+ // TLS_FREE(message);
+ // return TLS_BROKEN_PACKET;
+ }
+ }
+ TLS_FREE(message);
+ }
+
+ if (buf_len - res) {
+ DEBUG_PRINT("EXTRA %i BYTES AT THE END OF MESSAGE\n", buf_len - res);
+ DEBUG_DUMP_HEX(&buf[res], buf_len - res);
+ DEBUG_PRINT("\n");
+ }
+#ifdef TLS_FORWARD_SECRECY
+ if (ephemeral == 1) {
+ _private_tls_dhe_create(context);
+ DEBUG_DUMP_HEX_LABEL("DHP", dh_p, dh_p_len);
+ DEBUG_DUMP_HEX_LABEL("DHG", dh_g, dh_g_len);
+ int dhe_key_size = dh_p_len;
+ if (dh_g_len > dh_p_len)
+ dhe_key_size = dh_g_len;
+ if (_private_tls_dh_make_key(dhe_key_size, context->dhe, (const char *)dh_p, (const char *)dh_g, dh_p_len, dh_g_len)) {
+ DEBUG_PRINT("ERROR CREATING DHE KEY\n");
+ TLS_FREE(context->dhe);
+ context->dhe = NULL;
+ return TLS_GENERIC_ERROR;
+ }
+
+ unsigned int dh_key_size = 0;
+ unsigned char *key = _private_tls_decrypt_dhe(context, dh_Ys, dh_Ys_len, &dh_key_size, 0);
+ DEBUG_DUMP_HEX_LABEL("DH COMMON SECRET", key, dh_key_size);
+ if ((key) && (dh_key_size)) {
+ TLS_FREE(context->premaster_key);
+ context->premaster_key = key;
+ context->premaster_key_len = dh_key_size;
+ }
+ } else
+ if ((ephemeral == 2) && (curve) && (pk_key) && (key_size)) {
+#ifdef TLS_CURVE25519
+ if (curve == &x25519) {
+ if (key_size != 32) {
+ DEBUG_PRINT("INVALID X25519 PUBLIC SIZE");
+ return TLS_GENERIC_ERROR;
+ }
+
+ TLS_FREE(context->client_secret);
+ context->client_secret = (unsigned char *)TLS_MALLOC(32);
+ if (!context->client_secret) {
+ DEBUG_PRINT("ERROR IN TLS_MALLOC");
+ return TLS_GENERIC_ERROR;
+ }
+
+ tls_random(context->client_secret, 32);
+
+ context->client_secret[0] &= 248;
+ context->client_secret[31] &= 127;
+ context->client_secret[31] |= 64;
+
+ TLS_FREE(context->premaster_key);
+ context->premaster_key = (unsigned char *)TLS_MALLOC(32);
+ if (!context->premaster_key)
+ return TLS_GENERIC_ERROR;
+
+ curve25519(context->premaster_key, context->client_secret, pk_key);
+ context->premaster_key_len = 32;
+ } else
+#endif
+ {
+ tls_init();
+ _private_tls_ecc_dhe_create(context);
+
+ ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&curve->dp;
+ if (ecc_make_key_ex(NULL, find_prng("sprng"), context->ecc_dhe, dp)) {
+ TLS_FREE(context->ecc_dhe);
+ context->ecc_dhe = NULL;
+ DEBUG_PRINT("Error generating ECC key\n");
+ return TLS_GENERIC_ERROR;
+ }
+
+ TLS_FREE(context->premaster_key);
+ context->premaster_key_len = 0;
+
+ unsigned int out_len = 0;
+ context->premaster_key = _private_tls_decrypt_ecc_dhe(context, pk_key, key_size, &out_len, 0);
+ if (context->premaster_key)
+ context->premaster_key_len = out_len;
+ }
+ }
+#endif
+ return res;
+}
+
+int tls_parse_client_key_exchange(struct TLSContext *context, const unsigned char *buf, int buf_len) {
+ if ((context->connection_status != 1) && (!context->dtls)) {
+ DEBUG_PRINT("UNEXPECTED CLIENT KEY EXCHANGE MESSAGE (connections status: %i)\n", (int)context->connection_status);
+ return TLS_UNEXPECTED_MESSAGE;
+ }
+
+ int res = 0;
+ int dh_res = 0;
+ CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA)
+
+ unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2];
+ res += 3;
+ if (context->dtls) {
+ int dtls_check = _private_dtls_check_packet(context, buf, buf_len);
+ if (dtls_check < 0)
+ return dtls_check;
+ res += 8;
+ }
+
+ CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA);
+
+ if (!size)
+ return res;
+
+ dh_res = _private_tls_parse_random(context, &buf[res], size);
+ if (dh_res <= 0) {
+ DEBUG_PRINT("broken key\n");
+ return TLS_BROKEN_PACKET;
+ }
+ DEBUG_PRINT("\n");
+
+ res += size;
+ context->connection_status = 2;
+ return res;
+}
+
+int tls_parse_server_hello_done(struct TLSContext *context, const unsigned char *buf, int buf_len) {
+ int res = 0;
+ CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA)
+
+ unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2];
+ res += 3;
+ if (context->dtls) {
+ int dtls_check = _private_dtls_check_packet(context, buf, buf_len);
+ if (dtls_check < 0)
+ return dtls_check;
+ res += 8;
+ }
+
+ CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA);
+
+ res += size;
+ return res;
+}
+
+int tls_parse_finished(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets) {
+ if ((context->connection_status < 2) || (context->connection_status == 0xFF)) {
+ DEBUG_PRINT("UNEXPECTED FINISHED MESSAGE\n");
+ return TLS_UNEXPECTED_MESSAGE;
+ }
+
+ int res = 0;
+ *write_packets = 0;
+ CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA)
+
+ unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2];
+ res += 3;
+ if (context->dtls) {
+ int dtls_check = _private_dtls_check_packet(context, buf, buf_len);
+ if (dtls_check < 0)
+ return dtls_check;
+ res += 8;
+ }
+
+ if (size < TLS_MIN_FINISHED_OPAQUE_LEN) {
+ DEBUG_PRINT("Invalid finished pachet size: %i\n", size);
+ return TLS_BROKEN_PACKET;
+ }
+
+ CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA);
+
+ unsigned char hash[TLS_MAX_SHA_SIZE];
+ unsigned int hash_len = _private_tls_get_hash(context, hash);
+
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ unsigned char hash_out[TLS_MAX_SHA_SIZE];
+ unsigned long out_size = TLS_MAX_SHA_SIZE;
+ if ((!context->remote_finished_key) || (!hash_len)) {
+ DEBUG_PRINT("NO FINISHED KEY COMPUTED OR NO HANDSHAKE HASH\n");
+ return TLS_NOT_VERIFIED;
+ }
+
+ DEBUG_DUMP_HEX_LABEL("HS HASH", hash, hash_len);
+ DEBUG_DUMP_HEX_LABEL("HS FINISH", context->finished_key, hash_len);
+ DEBUG_DUMP_HEX_LABEL("HS REMOTE FINISH", context->remote_finished_key, hash_len);
+
+ out_size = hash_len;
+ hmac_state hmac;
+ hmac_init(&hmac, _private_tls_get_hash_idx(context), context->remote_finished_key, hash_len);
+ hmac_process(&hmac, hash, hash_len);
+ hmac_done(&hmac, hash_out, &out_size);
+
+ if ((size != out_size) || (memcmp(hash_out, &buf[res], size))) {
+ DEBUG_PRINT("Finished validation error (sequence number, local: %i, remote: %i)\n", (int)context->local_sequence_number, (int)context->remote_sequence_number);
+ DEBUG_DUMP_HEX_LABEL("FINISHED OPAQUE", &buf[res], size);
+ DEBUG_DUMP_HEX_LABEL("VERIFY", hash_out, out_size);
+ return TLS_NOT_VERIFIED;
+ }
+ if (context->is_server) {
+ context->connection_status = 0xFF;
+ res += size;
+ _private_tls13_key(context, 0);
+ context->local_sequence_number = 0;
+ context->remote_sequence_number = 0;
+ return res;
+ }
+ } else
+#endif
+ {
+ // verify
+ unsigned char *out = (unsigned char *)TLS_MALLOC(size);
+ if (!out) {
+ DEBUG_PRINT("Error in TLS_MALLOC (%i bytes)\n", (int)size);
+ return TLS_NO_MEMORY;
+ }
+
+ // server verifies client's message
+ if (context->is_server)
+ _private_tls_prf(context, out, size, context->master_key, context->master_key_len, (unsigned char *)"client finished", 15, hash, hash_len, NULL, 0);
+ else
+ _private_tls_prf(context, out, size, context->master_key, context->master_key_len, (unsigned char *)"server finished", 15, hash, hash_len, NULL, 0);
+
+ if (memcmp(out, &buf[res], size)) {
+ TLS_FREE(out);
+ DEBUG_PRINT("Finished validation error (sequence number, local: %i, remote: %i)\n", (int)context->local_sequence_number, (int)context->remote_sequence_number);
+ DEBUG_DUMP_HEX_LABEL("FINISHED OPAQUE", &buf[res], size);
+ DEBUG_DUMP_HEX_LABEL("VERIFY", out, size);
+ return TLS_NOT_VERIFIED;
+ }
+#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION
+ if (size) {
+ if (context->is_server) {
+ TLS_FREE(context->verify_data);
+ context->verify_data = (unsigned char *)TLS_MALLOC(size);
+ if (context->verify_data) {
+ memcpy(context->verify_data, out, size);
+ context->verify_len = size;
+ }
+ } else {
+ // concatenate client verify and server verify
+ context->verify_data = (unsigned char *)TLS_REALLOC(context->verify_data, size);
+ if (context->verify_data) {
+ memcpy(context->verify_data + context->verify_len, out, size);
+ context->verify_len += size;
+ } else
+ context->verify_len = 0;
+ }
+ }
+#endif
+ TLS_FREE(out);
+ }
+ if (context->is_server)
+ *write_packets = 3;
+ else
+ context->connection_status = 0xFF;
+ res += size;
+ return res;
+}
+
+#ifdef WITH_TLS_13
+int tls_parse_verify_tls13(struct TLSContext *context, const unsigned char *buf, int buf_len) {
+ CHECK_SIZE(7, buf_len, TLS_NEED_MORE_DATA)
+ unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2];
+
+ if (size < 2)
+ return buf_len;
+
+ unsigned char signing_data[TLS_MAX_HASH_SIZE + 98];
+ int signing_data_len;
+
+ // first 64 bytes to 0x20 (32)
+ memset(signing_data, 0x20, 64);
+ // context string 33 bytes
+ if (context->is_server)
+ memcpy(signing_data + 64, "TLS 1.3, client CertificateVerify", 33);
+ else
+ memcpy(signing_data + 64, "TLS 1.3, server CertificateVerify", 33);
+ // a single 0 byte separator
+ signing_data[97] = 0;
+ signing_data_len = 98;
+
+ signing_data_len += _private_tls_get_hash(context, signing_data + 98);
+ DEBUG_DUMP_HEX_LABEL("signature data", signing_data, signing_data_len);
+ unsigned short signature = ntohs(*(unsigned short *)&buf[3]);
+ unsigned short signature_size = ntohs(*(unsigned short *)&buf[5]);
+ int valid = 0;
+ CHECK_SIZE(7 + signature_size, buf_len, TLS_NEED_MORE_DATA)
+ switch (signature) {
+#ifdef TLS_ECDSA_SUPPORTED
+ case 0x0403:
+ // secp256r1 + sha256
+ valid = _private_tls_verify_ecdsa(context, sha256, buf + 7, signature_size, signing_data, signing_data_len, &secp256r1);
+ break;
+ case 0x0503:
+ // secp384r1 + sha384
+ valid = _private_tls_verify_ecdsa(context, sha384, buf + 7, signature_size, signing_data, signing_data_len, &secp384r1);
+ break;
+ case 0x0603:
+ // secp521r1 + sha512
+ valid = _private_tls_verify_ecdsa(context, sha512, buf + 7, signature_size, signing_data, signing_data_len, &secp521r1);
+ break;
+#endif
+ case 0x0804:
+ valid = _private_tls_verify_rsa(context, sha256, buf + 7, signature_size, signing_data, signing_data_len);
+ break;
+ default:
+ DEBUG_PRINT("Unsupported signature: %x\n", (int)signature);
+ return TLS_UNSUPPORTED_CERTIFICATE;
+ }
+ if (valid != 1) {
+ DEBUG_PRINT("Signature FAILED!\n");
+ return TLS_DECRYPTION_FAILED;
+ }
+ return buf_len;
+}
+#endif
+
+int tls_parse_verify(struct TLSContext *context, const unsigned char *buf, int buf_len) {
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13))
+ return tls_parse_verify_tls13(context, buf, buf_len);
+#endif
+ CHECK_SIZE(7, buf_len, TLS_BAD_CERTIFICATE)
+ unsigned int bytes_to_follow = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2];
+ CHECK_SIZE(bytes_to_follow, buf_len - 3, TLS_BAD_CERTIFICATE)
+ int res = -1;
+
+ if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ unsigned int hash = buf[3];
+ unsigned int algorithm = buf[4];
+#ifdef TLS_ECDSA_SUPPORTED
+ if ((algorithm != rsa) && (algorithm != ecdsa)) {
+ if (context->dtls == 4) {
+ DEBUG_PRINT("DTLS-SRTP mode, skipping signature check for unsupported signature (%x/%x)\n", algorithm, hash);
+ context->client_verified = 1;
+ return 1;
+ }
+ return TLS_UNSUPPORTED_CERTIFICATE;
+ }
+#else
+ if (algorithm != rsa)
+ return TLS_UNSUPPORTED_CERTIFICATE;
+#endif
+ unsigned short size = ntohs(*(unsigned short *)&buf[5]);
+ CHECK_SIZE(size, bytes_to_follow - 4, TLS_BAD_CERTIFICATE)
+ DEBUG_PRINT("ALGORITHM %i/%i (%i)\n", hash, algorithm, (int)size);
+ DEBUG_DUMP_HEX_LABEL("VERIFY", &buf[7], bytes_to_follow - 7);
+
+ if (algorithm == rsa)
+ res = _private_tls_verify_rsa(context, hash, &buf[7], size, context->cached_handshake, context->cached_handshake_len);
+ else
+ res = _private_tls_verify_ecdsa(context, hash, &buf[7], size, context->cached_handshake, context->cached_handshake_len, NULL);
+ } else {
+#ifdef TLS_LEGACY_SUPPORT
+ unsigned short size = ntohs(*(unsigned short *)&buf[3]);
+ CHECK_SIZE(size, bytes_to_follow - 2, TLS_BAD_CERTIFICATE)
+ res = _private_tls_verify_rsa(context, md5, &buf[5], size, context->cached_handshake, context->cached_handshake_len);
+#endif
+ }
+ if (context->cached_handshake) {
+ // not needed anymore
+ TLS_FREE(context->cached_handshake);
+ context->cached_handshake = NULL;
+ context->cached_handshake_len = 0;
+ }
+ if (res == 1) {
+ DEBUG_PRINT("Signature OK\n");
+ context->client_verified = 1;
+ } else {
+ DEBUG_PRINT("Signature FAILED\n");
+ context->client_verified = 0;
+ }
+ return 1;
+}
+
+void _private_dtls_reset_handshake(struct TLSContext *context) {
+ if ((context) && (context->dtls)) {
+ // reset state
+ memset(context->hs_messages, 0, sizeof(context->hs_messages));
+ context->connection_status = 0;
+ context->cipher_spec_set = 0;
+ context->dtls_seq = 0;
+#ifdef TLS_LEGACY_SUPPORT
+ if (context->cached_handshake) {
+ TLS_FREE(context->cached_handshake);
+ context->cached_handshake = NULL;
+ context->cached_handshake_len = 0;
+ }
+#endif
+ _private_tls_done_hash(context, NULL);
+
+ if (context->is_server) {
+ if (context->client_certificates) {
+ int i;
+ for (i = 0; i < context->client_certificates_count; i++)
+ tls_destroy_certificate(context->client_certificates[i]);
+ TLS_FREE(context->client_certificates);
+
+ context->client_certificates_count = 0;
+ context->client_certificates = NULL;
+ }
+ }
+
+ while (context->dtls_data->dtls_handshake_list) {
+ struct TLSHandshakeList *next = (struct TLSHandshakeList *)context->dtls_data->dtls_handshake_list->next;
+ if (context->dtls_data->dtls_handshake_list->msg) {
+ TLS_FREE(context->dtls_data->dtls_handshake_list->msg);
+ }
+ TLS_FREE(context->dtls_data->dtls_handshake_list);
+ context->dtls_data->dtls_handshake_list = next;
+ }
+ context->dtls_data->dtls_handshake_list = NULL;
+ }
+}
+
+void _private_dtls_rehash(struct TLSContext *context, unsigned char msg_type) {
+ if ((context) && (context->dtls) && (msg_type)) {
+ // create a new list, delete old one
+ struct TLSHandshakeList *handshake_list = context->dtls_data->dtls_handshake_list;
+
+ int found = 0;
+ while (handshake_list) {
+ struct TLSHandshakeList *next = (struct TLSHandshakeList *)handshake_list->next;
+
+ if ((handshake_list->direction == 0) && (handshake_list->msg[0] == msg_type)) {
+ found = 1;
+ context->connection_status = handshake_list->connection_status;
+ break;
+ }
+
+ handshake_list = next;
+ }
+
+ if (!found) {
+ DEBUG_PRINT("message already cleared\n");
+ return;
+ }
+
+ handshake_list = context->dtls_data->dtls_handshake_list;
+
+ struct TLSHandshakeList *to_delete = handshake_list;
+ context->dtls_data->dtls_handshake_list = NULL;
+
+ _private_tls_done_hash(context, NULL);
+
+ while (handshake_list) {
+ struct TLSHandshakeList *next = (struct TLSHandshakeList *)handshake_list->next;
+
+
+ if (handshake_list->direction)
+ context->connection_status = handshake_list->connection_status;
+
+ if ((handshake_list->direction == 0) && (handshake_list->msg[0] == msg_type))
+ break;
+
+ _private_tls_update_hash(context, handshake_list->msg, handshake_list->len, handshake_list->direction, handshake_list->connection_status);
+ handshake_list = next;
+ }
+
+ handshake_list = to_delete;
+ while (handshake_list) {
+ struct TLSHandshakeList *next = (struct TLSHandshakeList *)handshake_list->next;
+ if (handshake_list->msg) {
+ TLS_FREE(handshake_list->msg);
+ }
+ TLS_FREE(handshake_list);
+ handshake_list = next;
+ }
+ }
+}
+
+int tls_parse_payload(struct TLSContext *context, const unsigned char *buf, int buf_len, tls_validation_function certificate_verify) {
+ int orig_len = buf_len;
+ if (context->connection_status == 0xFF) {
+#ifndef TLS_ACCEPT_SECURE_RENEGOTIATION
+ // renegotiation disabled (emit warning alert)
+ if (context->dtls)
+ return orig_len;
+ _private_tls_write_packet(tls_build_alert(context, 0, no_renegotiation));
+ return 1;
+#endif
+ }
+
+ unsigned char local_buffer[DTLS_MAX_FRAGMENT_SIZE + 12];
+ while ((buf_len >= 4) && (!context->critical_error)) {
+ int payload_res = 0;
+ unsigned char update_hash = 1;
+ CHECK_SIZE(1, buf_len, TLS_NEED_MORE_DATA)
+ unsigned char type = buf[0];
+ unsigned int write_packets = 0;
+ unsigned int dtls_cookie_verified = 0;
+ int certificate_verify_alert = no_error;
+ unsigned int payload_size = buf[1] * 0x10000 + buf[2] * 0x100 + buf[3] + 3;
+ if (context->dtls) {
+ payload_size += 8;
+ if (context->dtls_data->fragment) {
+ CHECK_SIZE(payload_size - 11, context->dtls_data->fragment->written, TLS_NEED_MORE_DATA)
+
+ local_buffer[0] = type;
+
+ TLS_24_BIT(local_buffer, 1, context->dtls_data->fragment->written);
+
+ local_buffer[4] = buf[4];
+ local_buffer[5] = buf[5];
+
+ local_buffer[6] = 0;
+ local_buffer[7] = 0;
+ local_buffer[8] = 0;
+
+ TLS_24_BIT(local_buffer, 9, context->dtls_data->fragment->written);
+
+ memcpy(local_buffer + 12, context->dtls_data->fragment->buffer, context->dtls_data->fragment->written);
+
+ buf = local_buffer;
+ buf_len = context->dtls_data->fragment->written + 12;
+
+ TLS_FREE(context->dtls_data->fragment->buffer);
+ TLS_FREE(context->dtls_data->fragment);
+ context->dtls_data->fragment = 0;
+ } else {
+ CHECK_SIZE(payload_size + 1, buf_len, TLS_NEED_MORE_DATA)
+ }
+ } else {
+ CHECK_SIZE(payload_size + 1, buf_len, TLS_NEED_MORE_DATA)
+ }
+ switch (type) {
+ // hello request
+ case 0x00:
+ DEBUG_PRINT(" => HELLO REQUEST (RENEGOTIATION?)\n");
+ CHECK_HANDSHAKE_STATE(context, 0, 1);
+ if (context->dtls)
+ context->dtls_seq = 0;
+ if (context->is_server)
+ payload_res = TLS_UNEXPECTED_MESSAGE;
+ else {
+ if (context->connection_status == 0xFF) {
+ // renegotiation
+#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION
+ if (context->critical_error)
+ payload_res = TLS_UNEXPECTED_MESSAGE;
+ else {
+ _private_tls_reset_context(context);
+ _private_tls_write_packet(tls_build_hello(context, 0));
+ return 1;
+ }
+#else
+ payload_res = TLS_NO_RENEGOTIATION;
+#endif
+ } else
+ payload_res = TLS_UNEXPECTED_MESSAGE;
+ }
+ // no payload
+ break;
+ // client hello
+ case 0x01:
+ DEBUG_PRINT(" => CLIENT HELLO\n");
+ if (context->dtls) {
+ _private_dtls_reset_handshake(context);
+ } else {
+ CHECK_HANDSHAKE_STATE(context, 1, (context->dtls ? 2 : 1));
+ }
+ if ((context->dtls == 4) && (!context->is_server) && (context->connection_status == 0)) {
+ DEBUG_PRINT("SRTP HANDSHAKE: SWITCHING FROM CLIENT TO SERVER\n");
+ context->is_server = 1;
+ context->certificates = context->client_certificates;
+ context->certificates_count = context->client_certificates_count;
+ context->request_client_certificate = 1;
+
+ context->client_certificates = NULL;
+ context->client_certificates_count = 0;
+ }
+
+ if (context->is_server) {
+ payload_res = tls_parse_hello(context, buf + 1, payload_size, &write_packets, &dtls_cookie_verified);
+ DEBUG_PRINT(" => DTLS COOKIE VERIFIED: %i (%i)\n", dtls_cookie_verified, payload_res);
+ if ((context->dtls) && (payload_res > 0) && (!dtls_cookie_verified)) {
+ // wait client hello
+ context->connection_status = 3;
+ update_hash = 0;
+ }
+ } else
+ payload_res = TLS_UNEXPECTED_MESSAGE;
+ break;
+ // server hello
+ case 0x02:
+ DEBUG_PRINT(" => SERVER HELLO\n");
+ CHECK_HANDSHAKE_STATE(context, 2, 1);
+ if (context->is_server)
+ payload_res = TLS_UNEXPECTED_MESSAGE;
+ else
+ payload_res = tls_parse_hello(context, buf + 1, payload_size, &write_packets, &dtls_cookie_verified);
+ break;
+ // hello verify request
+ case 0x03:
+ DEBUG_PRINT(" => VERIFY REQUEST\n");
+ CHECK_HANDSHAKE_STATE(context, 3, 1);
+ if ((context->dtls) && (!context->is_server)) {
+ payload_res = tls_parse_verify_request(context, buf + 1, payload_size, &write_packets);
+ update_hash = 0;
+ } else
+ payload_res = TLS_UNEXPECTED_MESSAGE;
+ break;
+ // certificate
+ case 0x0B:
+ DEBUG_PRINT(" => CERTIFICATE\n");
+ CHECK_HANDSHAKE_STATE(context, 4, 1);
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ if (context->connection_status == 2) {
+ payload_res = tls_parse_certificate(context, buf + 1, payload_size, context->is_server);
+ if (context->is_server) {
+ if ((certificate_verify) && (context->client_certificates_count))
+ certificate_verify_alert = certificate_verify(context, context->client_certificates, context->client_certificates_count);
+ // empty certificates are permitted for client
+ if (payload_res == 0)
+ payload_res = 1;
+ } else {
+ if ((certificate_verify) && (context->certificates_count))
+ certificate_verify_alert = certificate_verify(context, context->certificates, context->certificates_count);
+ }
+ } else
+ payload_res = TLS_UNEXPECTED_MESSAGE;
+ } else
+#endif
+ if (context->connection_status == 1) {
+ if (context->is_server) {
+ // client certificate
+ payload_res = tls_parse_certificate(context, buf + 1, payload_size, 1);
+ if ((certificate_verify) && (context->client_certificates_count))
+ certificate_verify_alert = certificate_verify(context, context->client_certificates, context->client_certificates_count);
+ // empty certificates are permitted for client
+ if (payload_res == 0)
+ payload_res = 1;
+ } else {
+ payload_res = tls_parse_certificate(context, buf + 1, payload_size, 0);
+ if ((certificate_verify) && (context->certificates_count))
+ certificate_verify_alert = certificate_verify(context, context->certificates, context->certificates_count);
+ }
+ } else {
+ DEBUG_PRINT("* UNEXPECTED CERTIFICATE (%i)\n", context->connection_status);
+ payload_res = TLS_UNEXPECTED_MESSAGE;
+ }
+ break;
+ // server key exchange
+ case 0x0C:
+ DEBUG_PRINT(" => SERVER KEY EXCHANGE\n");
+ CHECK_HANDSHAKE_STATE(context, 5, 1);
+ if (context->is_server)
+ payload_res = TLS_UNEXPECTED_MESSAGE;
+ else
+ payload_res = tls_parse_server_key_exchange(context, buf + 1, payload_size);
+ break;
+ // certificate request
+ case 0x0D:
+ DEBUG_PRINT(" => CERTIFICATE REQUEST\n");
+ CHECK_HANDSHAKE_STATE(context, 6, 1);
+ // server to client
+ if (context->is_server)
+ payload_res = TLS_UNEXPECTED_MESSAGE;
+ else
+ context->client_verified = 2;
+ break;
+ // server hello done
+ case 0x0E:
+ DEBUG_PRINT(" => SERVER HELLO DONE\n");
+ CHECK_HANDSHAKE_STATE(context, 7, 1);
+ if (context->is_server) {
+ payload_res = TLS_UNEXPECTED_MESSAGE;
+ } else {
+ payload_res = tls_parse_server_hello_done(context, buf + 1, payload_size);
+ if (payload_res > 0)
+ write_packets = 1;
+ }
+ break;
+ // certificate verify
+ case 0x0F:
+ DEBUG_PRINT(" => CERTIFICATE VERIFY (%i)\n", payload_size);
+ CHECK_HANDSHAKE_STATE(context, 8, 1);
+ if (context->connection_status == 2)
+ payload_res = tls_parse_verify(context, buf + 1, payload_size);
+ else
+ payload_res = TLS_UNEXPECTED_MESSAGE;
+ break;
+ // client key exchange
+ case 0x10:
+ DEBUG_PRINT(" => CLIENT KEY EXCHANGE\n");
+ CHECK_HANDSHAKE_STATE(context, 9, 1);
+ if (context->is_server) {
+ _private_tls_update_hash(context, buf, payload_size + 1, 0, 0);
+ payload_res = tls_parse_client_key_exchange(context, buf + 1, payload_size);
+ update_hash = 0;
+ } else
+ payload_res = TLS_UNEXPECTED_MESSAGE;
+ break;
+ // finished
+ case 0x14:
+ DEBUG_PRINT(" => FINISHED\n");
+ if (context->cached_handshake) {
+ TLS_FREE(context->cached_handshake);
+ context->cached_handshake = NULL;
+ context->cached_handshake_len = 0;
+ }
+ CHECK_HANDSHAKE_STATE(context, 10, 1);
+ payload_res = tls_parse_finished(context, buf + 1, payload_size, &write_packets);
+ if (payload_res > 0)
+ memset(context->hs_messages, 0, sizeof(context->hs_messages));
+ #ifdef WITH_TLS_13
+ if ((!context->is_server) && ((context->version == TLS_V13) || (context->version == DTLS_V13))) {
+ update_hash = 0;
+ DEBUG_PRINT("<= SENDING FINISHED\n");
+ _private_tls_update_hash(context, buf, payload_size + 1, 0, 0xFF);
+ _private_tls_write_packet(tls_build_finished(context));
+ _private_tls13_key(context, 0);
+ context->connection_status = 0xFF;
+ context->local_sequence_number = 0;
+ context->remote_sequence_number = 0;
+ }
+#endif
+ break;
+#ifdef WITH_TLS_13
+ case 0x08:
+ // encrypted extensions ... ignore it for now
+ break;
+#endif
+ default:
+ DEBUG_PRINT(" => NOT UNDERSTOOD PAYLOAD TYPE: %x\n", (int)type);
+ return TLS_NOT_UNDERSTOOD;
+ }
+ if ((type != 0x00) && (update_hash) && (payload_res != TLS_UNEXPECTED_MESSAGE))
+ _private_tls_update_hash(context, buf, payload_size + 1, 0, 0);
+
+ if (certificate_verify_alert != no_error) {
+ _private_tls_write_packet(tls_build_alert(context, 1, certificate_verify_alert));
+ context->critical_error = 1;
+ }
+
+ if (payload_res < 0) {
+ switch (payload_res) {
+ case TLS_UNEXPECTED_MESSAGE:
+ if (context->dtls)
+ return orig_len;
+ else
+ _private_tls_write_packet(tls_build_alert(context, 1, unexpected_message));
+ break;
+ case TLS_COMPRESSION_NOT_SUPPORTED:
+ _private_tls_write_packet(tls_build_alert(context, 1, decompression_failure));
+ break;
+ case TLS_BROKEN_PACKET:
+ _private_tls_write_packet(tls_build_alert(context, 1, decode_error));
+ break;
+ case TLS_NO_MEMORY:
+ _private_tls_write_packet(tls_build_alert(context, 1, internal_error));
+ break;
+ case TLS_NOT_VERIFIED:
+ _private_tls_write_packet(tls_build_alert(context, 1, bad_record_mac));
+ break;
+ case TLS_BAD_CERTIFICATE:
+ if (context->is_server) {
+ // bad client certificate, continue
+ _private_tls_write_packet(tls_build_alert(context, 0, bad_certificate));
+ payload_res = 0;
+ } else
+ _private_tls_write_packet(tls_build_alert(context, 1, bad_certificate));
+ break;
+ case TLS_UNSUPPORTED_CERTIFICATE:
+ _private_tls_write_packet(tls_build_alert(context, 1, unsupported_certificate));
+ break;
+ case TLS_NO_COMMON_CIPHER:
+ _private_tls_write_packet(tls_build_alert(context, 1, insufficient_security));
+ break;
+ case TLS_NOT_UNDERSTOOD:
+ _private_tls_write_packet(tls_build_alert(context, 1, internal_error));
+ break;
+ case TLS_NO_RENEGOTIATION:
+ _private_tls_write_packet(tls_build_alert(context, 0, no_renegotiation));
+ payload_res = 0;
+ break;
+ case TLS_DECRYPTION_FAILED:
+ _private_tls_write_packet(tls_build_alert(context, 1, decryption_failed_RESERVED));
+ break;
+ }
+ if (payload_res < 0)
+ return payload_res;
+ }
+ if (certificate_verify_alert != no_error)
+ payload_res = TLS_BAD_CERTIFICATE;
+
+ // except renegotiation
+ switch (write_packets) {
+ case 1:
+ if (context->client_verified == 2) {
+ DEBUG_PRINT("<= Building CERTIFICATE \n");
+ _private_tls_write_packet(tls_build_certificate(context));
+ context->client_verified = 0;
+ }
+ // client handshake
+ DEBUG_PRINT("<= Building KEY EXCHANGE\n");
+ _private_tls_write_packet(tls_build_client_key_exchange(context));
+ DEBUG_PRINT("<= Building CHANGE CIPHER SPEC\n");
+ _private_tls_write_packet(tls_build_change_cipher_spec(context));
+ context->cipher_spec_set = 1;
+ context->local_sequence_number = 0;
+ DEBUG_PRINT("<= Building CLIENT FINISHED\n");
+ _private_tls_write_packet(tls_build_finished(context));
+ context->cipher_spec_set = 0;
+#ifdef TLS_12_FALSE_START
+ if ((!context->is_server) && (context->version == TLS_V12)) {
+ // https://tools.ietf.org/html/rfc7918
+ // 5.1. Symmetric Cipher
+ // Clients MUST NOT use the False Start protocol modification in a
+ // handshake unless the cipher suite uses a symmetric cipher that is
+ // considered cryptographically strong.
+ switch (context->cipher) {
+ case TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_RSA_WITH_AES_256_GCM_SHA384:
+ case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ context->false_start = 1;
+ break;
+ }
+ }
+#endif
+ break;
+ case 2:
+ // server handshake
+ if ((context->dtls) && (dtls_cookie_verified == 0)) {
+ _private_tls_write_packet(tls_build_verify_request(context));
+ _private_dtls_reset(context);
+ } else {
+ DEBUG_PRINT("<= SENDING SERVER HELLO\n");
+#ifdef WITH_TLS_13
+ if (context->connection_status == 3) {
+ context->connection_status = 2;
+ _private_tls_write_packet(tls_build_hello(context, 0));
+ _private_tls_write_packet(tls_build_change_cipher_spec(context));
+ _private_tls13_key(context, 1);
+ context->cipher_spec_set = 1;
+ DEBUG_PRINT("<= SENDING ENCRYPTED EXTENSIONS\n");
+ _private_tls_write_packet(tls_build_encrypted_extensions(context));
+ if (context->request_client_certificate) {
+ DEBUG_PRINT("<= SENDING CERTIFICATE REQUEST\n");
+ _private_tls_write_packet(tls_certificate_request(context));
+ }
+ DEBUG_PRINT("<= SENDING CERTIFICATE\n");
+ _private_tls_write_packet(tls_build_certificate(context));
+ DEBUG_PRINT("<= SENDING CERTIFICATE VERIFY\n");
+ _private_tls_write_packet(tls_build_certificate_verify(context));
+ DEBUG_PRINT("<= SENDING FINISHED\n");
+ _private_tls_write_packet(tls_build_finished(context));
+ // new key
+ TLS_FREE(context->server_finished_hash);
+ context->server_finished_hash = (unsigned char *)TLS_MALLOC(_private_tls_mac_length(context));
+ if (context->server_finished_hash)
+ _private_tls_get_hash(context, context->server_finished_hash);
+ break;
+ }
+#endif
+ _private_tls_write_packet(tls_build_hello(context, 0));
+ DEBUG_PRINT("<= SENDING CERTIFICATE\n");
+ _private_tls_write_packet(tls_build_certificate(context));
+ int ephemeral_cipher = tls_cipher_is_ephemeral(context);
+ if (ephemeral_cipher) {
+ DEBUG_PRINT("<= SENDING EPHEMERAL DH KEY\n");
+ _private_tls_write_packet(tls_build_server_key_exchange(context, ephemeral_cipher == 1 ? KEA_dhe_rsa : KEA_ec_diffie_hellman));
+ }
+ if (context->request_client_certificate) {
+ DEBUG_PRINT("<= SENDING CERTIFICATE REQUEST\n");
+ _private_tls_write_packet(tls_certificate_request(context));
+ }
+ DEBUG_PRINT("<= SENDING DONE\n");
+ _private_tls_write_packet(tls_build_done(context));
+ }
+ break;
+ case 3:
+ // finished
+ _private_tls_write_packet(tls_build_change_cipher_spec(context));
+ _private_tls_write_packet(tls_build_finished(context));
+ context->connection_status = 0xFF;
+ break;
+ case 4:
+ // dtls only
+ context->dtls_seq = 1;
+ _private_tls_write_packet(tls_build_hello(context, 0));
+ break;
+#ifdef WITH_TLS_13
+ case 5:
+ // hello retry request
+ DEBUG_PRINT("<= SENDING HELLO RETRY REQUEST\n");
+ _private_tls_write_packet(tls_build_hello(context, 0));
+ break;
+#endif
+ }
+ payload_size ++;
+ buf += payload_size;
+ buf_len -= payload_size;
+ }
+ return orig_len;
+}
+
+unsigned int _private_tls_hmac_message(unsigned char local, struct TLSContext *context, const unsigned char *buf, int buf_len, const unsigned char *buf2, int buf_len2, unsigned char *out, unsigned int outlen, uint64_t remote_sequence_number) {
+ hmac_state hash;
+ int mac_size = outlen;
+ int hash_idx;
+ if (mac_size == TLS_SHA1_MAC_SIZE)
+ hash_idx = find_hash("sha1");
+ else
+ if (mac_size == TLS_SHA384_MAC_SIZE)
+ hash_idx = find_hash("sha384");
+ else
+ hash_idx = find_hash("sha256");
+
+ if (hmac_init(&hash, hash_idx, local ? context->crypto.ctx_local_mac.local_mac : context->crypto.ctx_remote_mac.remote_mac, mac_size))
+ return 0;
+
+ uint64_t squence_number;
+ if (context->dtls)
+ squence_number = htonll(remote_sequence_number);
+ else
+ if (local)
+ squence_number = htonll(context->local_sequence_number);
+ else
+ squence_number = htonll(context->remote_sequence_number);
+
+ if (hmac_process(&hash, (unsigned char *)&squence_number, sizeof(uint64_t)))
+ return 0;
+
+ if (hmac_process(&hash, buf, buf_len))
+ return 0;
+ if ((buf2) && (buf_len2)) {
+ if (hmac_process(&hash, buf2, buf_len2))
+ return 0;
+ }
+ unsigned long ref_outlen = outlen;
+ if (hmac_done(&hash, out, &ref_outlen))
+ return 0;
+
+ return (unsigned int)ref_outlen;
+}
+
+int tls_parse_message(struct TLSContext *context, unsigned char *buf, int buf_len, tls_validation_function certificate_verify) {
+ int res = 5;
+ if (context->dtls)
+ res = 13;
+ int header_size = res;
+ int payload_res = 0;
+
+ CHECK_SIZE(res, buf_len, TLS_NEED_MORE_DATA)
+
+ unsigned char type = *buf;
+
+ int buf_pos = 1;
+
+ unsigned short version = ntohs(*(unsigned short *)&buf[buf_pos]);
+ buf_pos += 2;
+
+ uint64_t dtls_sequence_number = 0;
+ unsigned short dtls_epoch = 0;
+ if (context->dtls) {
+ CHECK_SIZE(buf_pos + 8, buf_len, TLS_NEED_MORE_DATA)
+ dtls_epoch = ntohs(*(unsigned short *)&buf[buf_pos]);
+ dtls_sequence_number = ntohll(*(uint64_t *)&buf[buf_pos]);
+
+ buf_pos += 8;
+ }
+
+ VERSION_SUPPORTED(version, TLS_NOT_SAFE)
+ unsigned short length;
+ length = ntohs(*(unsigned short *)&buf[buf_pos]);
+ buf_pos += 2;
+
+ unsigned char *pt = NULL;
+ const unsigned char *ptr = buf + buf_pos;
+
+ CHECK_SIZE(buf_pos + length, buf_len, TLS_NEED_MORE_DATA)
+ DEBUG_PRINT("Message type: %0x, length: %i\n", (int)type, (int)length);
+ if ((context->dtls) && (type == TLS_HANDSHAKE)) {
+ if (!dtls_epoch)
+ context->cipher_spec_set = 0;
+
+ DEBUG_PRINT("HANDSHAKE RETRANSMISSION DETECTED\n");
+ }
+ if ((context->cipher_spec_set) && (type != TLS_CHANGE_CIPHER)) {
+ DEBUG_DUMP_HEX_LABEL("encrypted", &buf[header_size], length);
+ if (!context->crypto.created) {
+ DEBUG_PRINT("Encryption context not created\n");
+ _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS);
+ return TLS_BROKEN_PACKET;
+ }
+ pt = (unsigned char *)TLS_MALLOC(length);
+ if (!pt) {
+ DEBUG_PRINT("Error in TLS_MALLOC (%i bytes)\n", (int)length);
+ _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS);
+ return TLS_NO_MEMORY;
+ }
+
+ unsigned char aad[16];
+ int aad_size = sizeof(aad);
+ unsigned char *sequence = aad;
+
+ if (context->crypto.created == 2) {
+ int delta = 8;
+ int pt_length;
+ unsigned char iv[TLS_13_AES_GCM_IV_LENGTH];
+ gcm_reset(&context->crypto.ctx_remote.aes_gcm_remote);
+
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ aad[0] = TLS_APPLICATION_DATA;
+ aad[1] = 0x03;
+ aad[2] = 0x03;
+ *((unsigned short *)(aad + 3)) = htons(buf_len - header_size);
+ aad_size = 5;
+ sequence = aad + 5;
+ if (context->dtls)
+ *((uint64_t *)sequence) = *(uint64_t *)(buf + 3);
+ else
+ *((uint64_t *)sequence) = htonll(context->remote_sequence_number);
+ memcpy(iv, context->crypto.ctx_remote_mac.remote_iv, TLS_13_AES_GCM_IV_LENGTH);
+ int i;
+ int offset = TLS_13_AES_GCM_IV_LENGTH - 8;
+ for (i = 0; i < 8; i++)
+ iv[offset + i] = context->crypto.ctx_remote_mac.remote_iv[offset + i] ^ sequence[i];
+ pt_length = buf_len - header_size - TLS_GCM_TAG_LEN;
+ delta = 0;
+ } else {
+#endif
+ aad_size = 13;
+ pt_length = length - 8 - TLS_GCM_TAG_LEN;
+ // build aad and iv
+ if (context->dtls)
+ *((uint64_t *)aad) = htonll(dtls_sequence_number);
+ else
+ *((uint64_t *)aad) = htonll(context->remote_sequence_number);
+ aad[8] = buf[0];
+ aad[9] = buf[1];
+ aad[10] = buf[2];
+
+ memcpy(iv, context->crypto.ctx_remote_mac.remote_aead_iv, 4);
+ memcpy(iv + 4, buf + header_size, 8);
+ *((unsigned short *)(aad + 11)) = htons((unsigned short)pt_length);
+#ifdef WITH_TLS_13
+ }
+#endif
+ if (pt_length < 0) {
+ DEBUG_PRINT("Invalid packet length");
+ TLS_FREE(pt);
+ _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS);
+ return TLS_BROKEN_PACKET;
+ }
+ DEBUG_DUMP_HEX_LABEL("aad", aad, aad_size);
+ DEBUG_DUMP_HEX_LABEL("aad iv", iv, 12);
+
+ int res0 = gcm_add_iv(&context->crypto.ctx_remote.aes_gcm_remote, iv, 12);
+ int res1 = gcm_add_aad(&context->crypto.ctx_remote.aes_gcm_remote, aad, aad_size);
+ memset(pt, 0, length);
+ DEBUG_PRINT("PT SIZE: %i\n", pt_length);
+ int res2 = gcm_process(&context->crypto.ctx_remote.aes_gcm_remote, pt, pt_length, buf + header_size + delta, GCM_DECRYPT);
+ unsigned char tag[32];
+ unsigned long taglen = 32;
+ int res3 = gcm_done(&context->crypto.ctx_remote.aes_gcm_remote, tag, &taglen);
+ if ((res0) || (res1) || (res2) || (res3) || (taglen != TLS_GCM_TAG_LEN)) {
+ DEBUG_PRINT("ERROR: gcm_add_iv: %i, gcm_add_aad: %i, gcm_process: %i, gcm_done: %i\n", res0, res1, res2, res3);
+ _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS);
+ return TLS_BROKEN_PACKET;
+ }
+ DEBUG_DUMP_HEX_LABEL("decrypted", pt, pt_length);
+ DEBUG_DUMP_HEX_LABEL("tag", tag, taglen);
+ // check tag
+ if (memcmp(buf + header_size + delta + pt_length, tag, taglen)) {
+ DEBUG_PRINT("INTEGRITY CHECK FAILED (msg length %i)\n", pt_length);
+ DEBUG_DUMP_HEX_LABEL("TAG RECEIVED", buf + header_size + delta + pt_length, taglen);
+ DEBUG_DUMP_HEX_LABEL("TAG COMPUTED", tag, taglen);
+ TLS_FREE(pt);
+
+ // silently ignore packet for DTLS
+ if (context->dtls)
+ return header_size + length;
+
+ _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS);
+ _private_tls_write_packet(tls_build_alert(context, 1, bad_record_mac));
+ return TLS_INTEGRITY_FAILED;
+ }
+ ptr = pt;
+ length = (unsigned short)pt_length;
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ } else
+ if (context->crypto.created == 3) {
+ int pt_length = length - POLY1305_TAGLEN;
+ unsigned int counter = 1;
+ unsigned char poly1305_key[POLY1305_KEYLEN];
+ unsigned char trail[16];
+ unsigned char mac_tag[POLY1305_TAGLEN];
+ aad_size = 16;
+ if (pt_length < 0) {
+ DEBUG_PRINT("Invalid packet length");
+ TLS_FREE(pt);
+ _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS);
+ return TLS_BROKEN_PACKET;
+ }
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ aad[0] = TLS_APPLICATION_DATA;
+ aad[1] = 0x03;
+ aad[2] = 0x03;
+ *((unsigned short *)(aad + 3)) = htons(buf_len - header_size);
+ aad_size = 5;
+ sequence = aad + 5;
+ if (context->dtls)
+ *((uint64_t *)sequence) = *(uint64_t *)(buf + 3);
+ else
+ *((uint64_t *)sequence) = htonll(context->remote_sequence_number);
+ } else {
+#endif
+ if (context->dtls)
+ *((uint64_t *)aad) = htonll(dtls_sequence_number);
+ else
+ *((uint64_t *)aad) = htonll(context->remote_sequence_number);
+ aad[8] = buf[0];
+ aad[9] = buf[1];
+ aad[10] = buf[2];
+ *((unsigned short *)(aad + 11)) = htons((unsigned short)pt_length);
+ aad[13] = 0;
+ aad[14] = 0;
+ aad[15] = 0;
+#ifdef WITH_TLS_13
+ }
+#endif
+
+ chacha_ivupdate(&context->crypto.ctx_remote.chacha_remote, context->crypto.ctx_remote_mac.remote_aead_iv, sequence, (unsigned char *)&counter);
+
+ chacha_encrypt_bytes(&context->crypto.ctx_remote.chacha_remote, buf + header_size, pt, pt_length);
+ DEBUG_DUMP_HEX_LABEL("decrypted", pt, pt_length);
+ ptr = pt;
+ length = (unsigned short)pt_length;
+
+ chacha20_poly1305_key(&context->crypto.ctx_remote.chacha_remote, poly1305_key);
+ poly1305_context ctx;
+ _private_tls_poly1305_init(&ctx, poly1305_key);
+ _private_tls_poly1305_update(&ctx, aad, aad_size);
+ static unsigned char zeropad[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int rem = aad_size % 16;
+ if (rem)
+ _private_tls_poly1305_update(&ctx, zeropad, 16 - rem);
+ _private_tls_poly1305_update(&ctx, buf + header_size, pt_length);
+ rem = pt_length % 16;
+ if (rem)
+ _private_tls_poly1305_update(&ctx, zeropad, 16 - rem);
+
+ _private_tls_U32TO8(&trail[0], aad_size == 5 ? 5 : 13);
+ *(int *)&trail[4] = 0;
+ _private_tls_U32TO8(&trail[8], pt_length);
+ *(int *)&trail[12] = 0;
+
+ _private_tls_poly1305_update(&ctx, trail, 16);
+ _private_tls_poly1305_finish(&ctx, mac_tag);
+ if (memcmp(mac_tag, buf + header_size + pt_length, POLY1305_TAGLEN)) {
+ DEBUG_PRINT("INTEGRITY CHECK FAILED (msg length %i)\n", length);
+ DEBUG_DUMP_HEX_LABEL("POLY1305 TAG RECEIVED", buf + header_size + pt_length, POLY1305_TAGLEN);
+ DEBUG_DUMP_HEX_LABEL("POLY1305 TAG COMPUTED", mac_tag, POLY1305_TAGLEN);
+ TLS_FREE(pt);
+
+ // silently ignore packet for DTLS
+ if (context->dtls)
+ return header_size + length;
+
+ _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS);
+ _private_tls_write_packet(tls_build_alert(context, 1, bad_record_mac));
+ return TLS_INTEGRITY_FAILED;
+ }
+#endif
+ } else {
+ int err = _private_tls_crypto_decrypt(context, buf + header_size, pt, length);
+ if (err) {
+ TLS_FREE(pt);
+ DEBUG_PRINT("Decryption error %i\n", (int)err);
+ _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS);
+ return TLS_BROKEN_PACKET;
+ }
+ unsigned char padding_byte = pt[length - 1];
+ unsigned char padding = padding_byte + 1;
+
+ // poodle check
+ int padding_index = length - padding;
+ if (padding_index > 0) {
+ int i;
+ int limit = length - 1;
+ for (i = length - padding; i < limit; i++) {
+ if (pt[i] != padding_byte) {
+ TLS_FREE(pt);
+ DEBUG_PRINT("BROKEN PACKET (POODLE ?)\n");
+ _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS);
+ _private_tls_write_packet(tls_build_alert(context, 1, decrypt_error));
+ return TLS_BROKEN_PACKET;
+ }
+ }
+ }
+
+ unsigned int decrypted_length = length;
+ if (padding < decrypted_length)
+ decrypted_length -= padding;
+
+ DEBUG_DUMP_HEX_LABEL("decrypted", pt, decrypted_length);
+ ptr = pt;
+#ifdef TLS_LEGACY_SUPPORT
+ if ((context->version != TLS_V10) && (decrypted_length > TLS_AES_IV_LENGTH)) {
+ decrypted_length -= TLS_AES_IV_LENGTH;
+ ptr += TLS_AES_IV_LENGTH;
+ }
+#else
+ if (decrypted_length > TLS_AES_IV_LENGTH) {
+ decrypted_length -= TLS_AES_IV_LENGTH;
+ ptr += TLS_AES_IV_LENGTH;
+ }
+#endif
+ length = decrypted_length;
+
+ unsigned int mac_size = _private_tls_mac_length(context);
+ if ((length < mac_size) || (!mac_size)) {
+ TLS_FREE(pt);
+ DEBUG_PRINT("BROKEN PACKET\n");
+ _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS);
+ _private_tls_write_packet(tls_build_alert(context, 1, decrypt_error));
+ return TLS_BROKEN_PACKET;
+ }
+
+ length -= mac_size;
+
+ const unsigned char *message_hmac = &ptr[length];
+ unsigned char hmac_out[TLS_MAX_MAC_SIZE];
+ unsigned char temp_buf[5];
+ memcpy(temp_buf, buf, 3);
+ *(unsigned short *)(temp_buf + 3) = htons(length);
+ unsigned int hmac_out_len = _private_tls_hmac_message(0, context, temp_buf, 5, ptr, length, hmac_out, mac_size, dtls_sequence_number);
+ if ((hmac_out_len != mac_size) || (memcmp(message_hmac, hmac_out, mac_size))) {
+ DEBUG_PRINT("INTEGRITY CHECK FAILED (msg length %i)\n", length);
+ DEBUG_DUMP_HEX_LABEL("HMAC RECEIVED", message_hmac, mac_size);
+ DEBUG_DUMP_HEX_LABEL("HMAC COMPUTED", hmac_out, hmac_out_len);
+ TLS_FREE(pt);
+
+ // silently ignore packet for DTLS
+ if (context->dtls)
+ return header_size + length;
+
+ _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS);
+ _private_tls_write_packet(tls_build_alert(context, 1, bad_record_mac));
+
+ return TLS_INTEGRITY_FAILED;
+ }
+ }
+ }
+ if (context->dtls) {
+ context->dtls_epoch_remote = dtls_epoch;
+ context->remote_sequence_number = dtls_sequence_number & 0xFFFFFFFFFFFFLL;
+ } else
+ context->remote_sequence_number ++;
+
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ if (/*(context->connection_status == 2) && */(type == TLS_APPLICATION_DATA) && (context->crypto.created)) {
+ do {
+ length--;
+ type = ptr[length];
+ } while (!type);
+ }
+ }
+#endif
+ switch (type) {
+ // application data
+ case TLS_APPLICATION_DATA:
+ if (context->connection_status != 0xFF) {
+ DEBUG_PRINT("UNEXPECTED APPLICATION DATA MESSAGE\n");
+ payload_res = TLS_UNEXPECTED_MESSAGE;
+ _private_tls_write_packet(tls_build_alert(context, 1, unexpected_message));
+ } else {
+ DEBUG_PRINT("APPLICATION DATA MESSAGE (TLS VERSION: %x):\n", (int)context->version);
+ DEBUG_DUMP(ptr, length);
+ DEBUG_PRINT("\n");
+ _private_tls_write_app_data(context, ptr, length);
+ }
+ break;
+ // handshake
+ case TLS_HANDSHAKE:
+ DEBUG_PRINT("HANDSHAKE MESSAGE\n");
+ payload_res = tls_parse_payload(context, ptr, length, certificate_verify);
+ break;
+ // change cipher spec
+ case TLS_CHANGE_CIPHER:
+ context->dtls_epoch_remote ++;
+ if ((context->connection_status != 2) && (!context->dtls)) {
+#ifdef WITH_TLS_13
+ if (context->connection_status == 4) {
+ DEBUG_PRINT("IGNORING CHANGE CIPHER SPEC MESSAGE (HELLO RETRY REQUEST)\n");
+ break;
+ }
+#endif
+ DEBUG_PRINT("UNEXPECTED CHANGE CIPHER SPEC MESSAGE (%i)\n", context->connection_status);
+ _private_tls_write_packet(tls_build_alert(context, 1, unexpected_message));
+ payload_res = TLS_UNEXPECTED_MESSAGE;
+ } else {
+ DEBUG_PRINT("CHANGE CIPHER SPEC MESSAGE\n");
+ context->cipher_spec_set = 1;
+ // reset sequence numbers
+ context->remote_sequence_number = 0;
+ }
+#ifdef WITH_TLS_13
+ if (!context->is_server)
+ _private_tls13_key(context, 1);
+#endif
+ break;
+ // alert
+ case TLS_ALERT:
+ DEBUG_PRINT("ALERT MESSAGE\n");
+ if (length >= 2) {
+ DEBUG_DUMP_HEX(ptr, length);
+ int level = ptr[0];
+ int code = ptr[1];
+ if (level == TLS_ALERT_CRITICAL) {
+ context->critical_error = 1;
+ res = TLS_ERROR_ALERT;
+ }
+ context->error_code = code;
+ }
+ break;
+ default:
+ DEBUG_PRINT("NOT UNDERSTOOD MESSAGE TYPE: %x\n", (int)type);
+ TLS_FREE(pt);
+ return TLS_NOT_UNDERSTOOD;
+ }
+ TLS_FREE(pt);
+
+ if (payload_res < 0)
+ return payload_res;
+
+ if (res > 0)
+ return header_size + length;
+
+ return res;
+}
+
+unsigned int asn1_get_len(const unsigned char *buffer, int buf_len, unsigned int *octets) {
+ *octets = 0;
+
+ if (buf_len < 1)
+ return 0;
+
+ unsigned char size = buffer[0];
+ int i;
+ if (size & 0x80) {
+ *octets = size & 0x7F;
+ if ((int)*octets > buf_len - 1)
+ return 0;
+ // max 32 bits
+ unsigned int ref_octets = *octets;
+ if (*octets > 4)
+ ref_octets = 4;
+ if ((int)*octets > buf_len -1)
+ return 0;
+ unsigned int long_size = 0;
+ unsigned int coef = 1;
+
+ for (i = ref_octets; i > 0; i--) {
+ long_size += buffer[i] * coef;
+ coef *= 0x100;
+ }
+ ++*octets;
+ return long_size;
+ }
+ ++*octets;
+ return size;
+}
+
+void print_index(const unsigned int *fields) {
+ int i = 0;
+ while (fields[i]) {
+ if (i)
+ DEBUG_PRINT(".");
+ DEBUG_PRINT("%i", fields[i]);
+ i++;
+ }
+ while (i < 6) {
+ DEBUG_PRINT(" ");
+ i++;
+ }
+}
+
+int _is_field(const unsigned int *fields, const unsigned int *prefix) {
+ int i = 0;
+ while (prefix[i]) {
+ if (fields[i] != prefix[i])
+ return 0;
+ i++;
+ }
+ return 1;
+}
+
+int _private_tls_hash_len(int algorithm) {
+ switch (algorithm) {
+ case TLS_RSA_SIGN_MD5:
+ return 16;
+ case TLS_RSA_SIGN_SHA1:
+ return 20;
+ case TLS_RSA_SIGN_SHA256:
+ case TLS_ECDSA_SIGN_SHA256:
+ return 32;
+ case TLS_RSA_SIGN_SHA384:
+ return 48;
+ case TLS_RSA_SIGN_SHA512:
+ return 64;
+ }
+ return 0;
+}
+
+unsigned char *_private_tls_compute_hash(int algorithm, const unsigned char *message, unsigned int message_len) {
+ unsigned char *hash = NULL;
+ if ((!message) || (!message_len))
+ return hash;
+ int err;
+ hash_state state;
+ switch (algorithm) {
+ case TLS_RSA_SIGN_MD5:
+ DEBUG_PRINT("SIGN MD5\n");
+ hash = (unsigned char *)TLS_MALLOC(16);
+ if (!hash)
+ return NULL;
+
+ err = md5_init(&state);
+ if (!err) {
+ err = md5_process(&state, message, message_len);
+ if (!err)
+ err = md5_done(&state, hash);
+ }
+ break;
+ case TLS_RSA_SIGN_SHA1:
+ DEBUG_PRINT("SIGN SHA1\n");
+ hash = (unsigned char *)TLS_MALLOC(20);
+ if (!hash)
+ return NULL;
+
+ err = sha1_init(&state);
+ if (!err) {
+ err = sha1_process(&state, message, message_len);
+ if (!err)
+ err = sha1_done(&state, hash);
+ }
+ break;
+ case TLS_RSA_SIGN_SHA256:
+ case TLS_ECDSA_SIGN_SHA256:
+ DEBUG_PRINT("SIGN SHA256\n");
+ hash = (unsigned char *)TLS_MALLOC(32);
+ if (!hash)
+ return NULL;
+
+ err = sha256_init(&state);
+ if (!err) {
+ err = sha256_process(&state, message, message_len);
+ if (!err)
+ err = sha256_done(&state, hash);
+ }
+ break;
+ case TLS_RSA_SIGN_SHA384:
+ DEBUG_PRINT("SIGN SHA384\n");
+ hash = (unsigned char *)TLS_MALLOC(48);
+ if (!hash)
+ return NULL;
+
+ err = sha384_init(&state);
+ if (!err) {
+ err = sha384_process(&state, message, message_len);
+ if (!err)
+ err = sha384_done(&state, hash);
+ }
+ break;
+ case TLS_RSA_SIGN_SHA512:
+ DEBUG_PRINT("SIGN SHA512\n");
+ hash = (unsigned char *)TLS_MALLOC(64);
+ if (!hash)
+ return NULL;
+
+ err = sha512_init(&state);
+ if (!err) {
+ err = sha512_process(&state, message, message_len);
+ if (!err)
+ err = sha512_done(&state, hash);
+ }
+ break;
+ default:
+ DEBUG_PRINT("UNKNOWN SIGNATURE ALGORITHM\n");
+ }
+ return hash;
+}
+
+int tls_certificate_verify_signature(struct TLSCertificate *cert, struct TLSCertificate *parent) {
+ if ((!cert) || (!parent) || (!cert->sign_key) || (!cert->fingerprint) || (!cert->sign_len) || (!parent->der_bytes) || (!parent->der_len)) {
+ DEBUG_PRINT("CANNOT VERIFY SIGNATURE");
+ return 0;
+ }
+ tls_init();
+ int hash_len = _private_tls_hash_len(cert->algorithm);
+ if (hash_len <= 0)
+ return 0;
+
+ int hash_index = -1;
+ switch (cert->algorithm) {
+ case TLS_RSA_SIGN_MD5:
+ hash_index = find_hash("md5");
+ break;
+ case TLS_RSA_SIGN_SHA1:
+ hash_index = find_hash("sha1");
+ break;
+ case TLS_RSA_SIGN_SHA256:
+ case TLS_ECDSA_SIGN_SHA256:
+ hash_index = find_hash("sha256");
+ break;
+ case TLS_RSA_SIGN_SHA384:
+ hash_index = find_hash("sha384");
+ break;
+ case TLS_RSA_SIGN_SHA512:
+ hash_index = find_hash("sha512");
+ break;
+ default:
+ DEBUG_PRINT("UNKNOWN SIGNATURE ALGORITHM\n");
+ return 0;
+ }
+#ifdef TLS_ECDSA_SUPPORTED
+ if (cert->algorithm == TLS_ECDSA_SIGN_SHA256) {
+ ecc_key key;
+ int err = ecc_import(parent->der_bytes, parent->der_len, &key);
+ if (err) {
+ DEBUG_PRINT("Error importing ECC certificate (code: %i)\n", err);
+ DEBUG_DUMP_HEX_LABEL("CERTIFICATE", parent->der_bytes, parent->der_len);
+ return 0;
+ }
+ int ecc_stat = 0;
+ unsigned char *signature = cert->sign_key;
+ int signature_len = cert->sign_len;
+ if (!signature[0]) {
+ signature++;
+ signature_len--;
+ }
+ err = ecc_verify_hash(signature, signature_len, cert->fingerprint, hash_len, &ecc_stat, &key);
+ ecc_free(&key);
+ if (err) {
+ DEBUG_PRINT("ECC HASH VERIFY ERROR %i\n", err);
+ return 0;
+ }
+ DEBUG_PRINT("ECC CERTIFICATE VALIDATION: %i\n", ecc_stat);
+ return ecc_stat;
+ }
+#endif
+
+ rsa_key key;
+ int err = rsa_import(parent->der_bytes, parent->der_len, &key);
+ if (err) {
+ DEBUG_PRINT("Error importing RSA certificate (code: %i)\n", err);
+ DEBUG_DUMP_HEX_LABEL("CERTIFICATE", parent->der_bytes, parent->der_len);
+ return 0;
+ }
+ int rsa_stat = 0;
+ unsigned char *signature = cert->sign_key;
+ int signature_len = cert->sign_len;
+ if (!signature[0]) {
+ signature++;
+ signature_len--;
+ }
+ err = rsa_verify_hash_ex(signature, signature_len, cert->fingerprint, hash_len, LTC_PKCS_1_V1_5, hash_index, hash_len, &rsa_stat, &key);
+ rsa_free(&key);
+ if (err) {
+ DEBUG_PRINT("HASH VERIFY ERROR %i\n", err);
+ return 0;
+ }
+ DEBUG_PRINT("CERTIFICATE VALIDATION: %i\n", rsa_stat);
+ return rsa_stat;
+}
+
+int tls_certificate_chain_is_valid(struct TLSCertificate **certificates, int len) {
+ if ((!certificates) || (!len))
+ return bad_certificate;
+
+ int i;
+ len--;
+
+ // expired certificate or not yet valid ?
+ if (tls_certificate_is_valid(certificates[0]))
+ return bad_certificate;
+
+ // check
+ for (i = 0; i < len; i++) {
+ // certificate in chain is expired ?
+ if (tls_certificate_is_valid(certificates[i+1]))
+ return bad_certificate;
+ if (!tls_certificate_verify_signature(certificates[i], certificates[i+1]))
+ return bad_certificate;
+ }
+ return 0;
+}
+
+int tls_certificate_chain_is_valid_root(struct TLSContext *context, struct TLSCertificate **certificates, int len) {
+ if ((!certificates) || (!len) || (!context->root_certificates) || (!context->root_count))
+ return bad_certificate;
+ int i;
+ unsigned int j;
+ for (i = 0; i < len; i++) {
+ for (j = 0; j < context->root_count; j++) {
+ // check if root certificate expired
+ if (tls_certificate_is_valid(context->root_certificates[j]))
+ continue;
+ // if any root validates any certificate in the chain, then is root validated
+ if (tls_certificate_verify_signature(certificates[i], context->root_certificates[j]))
+ return 0;
+ }
+ }
+ return bad_certificate;
+}
+
+int _private_is_oid(struct _private_OID_chain *ref_chain, const unsigned char *looked_oid, int looked_oid_len) {
+ while (ref_chain) {
+ if (ref_chain->oid) {
+ if (_is_oid2(ref_chain->oid, looked_oid, 16, looked_oid_len))
+ return 1;
+ }
+ ref_chain = (struct _private_OID_chain *)ref_chain->top;
+ }
+ return 0;
+}
+
+int _private_asn1_parse(struct TLSContext *context, struct TLSCertificate *cert, const unsigned char *buffer, unsigned int size, int level, unsigned int *fields, unsigned char *has_key, int client_cert, unsigned char *top_oid, struct _private_OID_chain *chain) {
+ struct _private_OID_chain local_chain;
+ local_chain.top = chain;
+ unsigned int pos = 0;
+ // X.690
+ int idx = 0;
+ unsigned char oid[16];
+ memset(oid, 0, 16);
+ local_chain.oid = oid;
+ if (has_key)
+ *has_key = 0;
+ unsigned char local_has_key = 0;
+ const unsigned char *cert_data = NULL;
+ unsigned int cert_len = 0;
+ while (pos < size) {
+ unsigned int start_pos = pos;
+ CHECK_SIZE(2, size - pos, TLS_NEED_MORE_DATA)
+ unsigned char first = buffer[pos++];
+ unsigned char type = first & 0x1F;
+ unsigned char constructed = first & 0x20;
+ unsigned char element_class = first >> 6;
+ unsigned int octets = 0;
+ unsigned int temp;
+ idx++;
+ if (level <= TLS_ASN1_MAXLEVEL)
+ fields[level - 1] = idx;
+ unsigned int length = asn1_get_len((unsigned char *)&buffer[pos], size - pos, &octets);
+ if ((octets > 4) || (octets > size - pos)) {
+ DEBUG_PRINT("CANNOT READ CERTIFICATE\n");
+ return pos;
+ }
+ pos += octets;
+ CHECK_SIZE(length, size - pos, TLS_NEED_MORE_DATA)
+ //DEBUG_PRINT("FIRST: %x => %x (%i)\n", (int)first, (int)type, length);
+ // sequence
+ //DEBUG_PRINT("%2i: ", level);
+#ifdef DEBUG
+ DEBUG_INDEX(fields);
+ int i1;
+ for (i1 = 1; i1 < level; i1++)
+ DEBUG_PRINT(" ");
+#endif
+
+ if ((length) && (constructed)) {
+ switch (type) {
+ case 0x03:
+ DEBUG_PRINT("CONSTRUCTED BITSTREAM\n");
+ break;
+ case 0x10:
+ DEBUG_PRINT("SEQUENCE\n");
+ if ((level == 2) && (idx == 1)) {
+ cert_len = length + (pos - start_pos);
+ cert_data = &buffer[start_pos];
+ }
+ // private key on server or public key on client
+ if ((!cert->version) && (_is_field(fields, priv_der_id))) {
+ TLS_FREE(cert->der_bytes);
+ temp = length + (pos - start_pos);
+ cert->der_bytes = (unsigned char *)TLS_MALLOC(temp);
+ if (cert->der_bytes) {
+ memcpy(cert->der_bytes, &buffer[start_pos], temp);
+ cert->der_len = temp;
+ } else
+ cert->der_len = 0;
+ }
+ break;
+ case 0x11:
+ DEBUG_PRINT("EMBEDDED PDV\n");
+ break;
+ case 0x00:
+ if (element_class == 0x02) {
+ DEBUG_PRINT("CONTEXT-SPECIFIC\n");
+ break;
+ }
+ default:
+ DEBUG_PRINT("CONSTRUCT TYPE %02X\n", (int)type);
+ }
+ local_has_key = 0;
+ _private_asn1_parse(context, cert, &buffer[pos], length, level + 1, fields, &local_has_key, client_cert, top_oid, &local_chain);
+ if ((((local_has_key) && (context) && ((!context->is_server) || (client_cert))) || (!context)) && (_is_field(fields, pk_id))) {
+ TLS_FREE(cert->der_bytes);
+ temp = length + (pos - start_pos);
+ cert->der_bytes = (unsigned char *)TLS_MALLOC(temp);
+ if (cert->der_bytes) {
+ memcpy(cert->der_bytes, &buffer[start_pos], temp);
+ cert->der_len = temp;
+ } else
+ cert->der_len = 0;
+ }
+ } else {
+ switch (type) {
+ case 0x00:
+ // end of content
+ DEBUG_PRINT("END OF CONTENT\n");
+ return pos;
+ break;
+ case 0x01:
+ // boolean
+ temp = buffer[pos];
+ DEBUG_PRINT("BOOLEAN: %i\n", temp);
+ break;
+ case 0x02:
+ // integer
+ if (_is_field(fields, pk_id)) {
+ if (has_key)
+ *has_key = 1;
+
+ if (idx == 1)
+ tls_certificate_set_key(cert, &buffer[pos], length);
+ else
+ if (idx == 2)
+ tls_certificate_set_exponent(cert, &buffer[pos], length);
+ } else
+ if (_is_field(fields, serial_id))
+ tls_certificate_set_serial(cert, &buffer[pos], length);
+ if (_is_field(fields, version_id)) {
+ if (length == 1)
+ cert->version = buffer[pos];
+#ifdef TLS_X509_V1_SUPPORT
+ else
+ cert->version = 0;
+ idx++;
+#endif
+ }
+ if (level >= 2) {
+ unsigned int fields_temp[3];
+ fields_temp[0] = fields[level - 2];
+ fields_temp[1] = fields[level - 1];
+ fields_temp[2] = 0;
+ if (_is_field(fields_temp, priv_id))
+ tls_certificate_set_priv(cert, &buffer[pos], length);
+ }
+ DEBUG_PRINT("INTEGER(%i): ", length);
+ DEBUG_DUMP_HEX(&buffer[pos], length);
+ if ((chain) && (length > 2)) {
+ if (_private_is_oid(chain, san_oid, sizeof(san_oid) - 1)) {
+ cert->san = (unsigned char **)TLS_REALLOC(cert->san, sizeof(unsigned char *) * (cert->san_length + 1));
+ if (cert->san) {
+ cert->san[cert->san_length] = NULL;
+ tls_certificate_set_copy(&cert->san[cert->san_length], &buffer[pos], length);
+ DEBUG_PRINT(" => SUBJECT ALTERNATIVE NAME: %s", cert->san[cert->san_length ]);
+ cert->san_length++;
+ } else
+ cert->san_length = 0;
+ }
+ }
+ DEBUG_PRINT("\n");
+ break;
+ case 0x03:
+ if (_is_field(fields, pk_id)) {
+ if (has_key)
+ *has_key = 1;
+ }
+ // bitstream
+ DEBUG_PRINT("BITSTREAM(%i): ", length);
+ DEBUG_DUMP_HEX(&buffer[pos], length);
+ DEBUG_PRINT("\n");
+ if (_is_field(fields, sign_id)) {
+ tls_certificate_set_sign_key(cert, &buffer[pos], length);
+ } else
+ if ((cert->ec_algorithm) && (_is_field(fields, pk_id))) {
+ tls_certificate_set_key(cert, &buffer[pos], length);
+ } else {
+ if ((buffer[pos] == 0x00) && (length > 256))
+ _private_asn1_parse(context, cert, &buffer[pos]+1, length - 1, level + 1, fields, &local_has_key, client_cert, top_oid, &local_chain);
+ else
+ _private_asn1_parse(context, cert, &buffer[pos], length, level + 1, fields, &local_has_key, client_cert, top_oid, &local_chain);
+#ifdef TLS_FORWARD_SECRECY
+ #ifdef TLS_ECDSA_SUPPORTED
+ if (top_oid) {
+ if (_is_oid2(top_oid, TLS_EC_prime256v1_OID, sizeof(oid), sizeof(TLS_EC_prime256v1) - 1)) {
+ cert->ec_algorithm = secp256r1.iana;
+ } else
+ if (_is_oid2(top_oid, TLS_EC_secp224r1_OID, sizeof(oid), sizeof(TLS_EC_secp224r1_OID) - 1)) {
+ cert->ec_algorithm = secp224r1.iana;
+ } else
+ if (_is_oid2(top_oid, TLS_EC_secp384r1_OID, sizeof(oid), sizeof(TLS_EC_secp384r1_OID) - 1)) {
+ cert->ec_algorithm = secp384r1.iana;
+ } else
+ if (_is_oid2(top_oid, TLS_EC_secp521r1_OID, sizeof(oid), sizeof(TLS_EC_secp521r1_OID) - 1)) {
+ cert->ec_algorithm = secp521r1.iana;
+ }
+ if ((cert->ec_algorithm) && (!cert->pk))
+ tls_certificate_set_key(cert, &buffer[pos], length);
+ }
+ #endif
+#endif
+ }
+ break;
+ case 0x04:
+ if ((top_oid) && (_is_field(fields, ecc_priv_id)) && (!cert->priv)) {
+ DEBUG_PRINT("BINARY STRING(%i): ", length);
+ DEBUG_DUMP_HEX(&buffer[pos], length);
+ DEBUG_PRINT("\n");
+ tls_certificate_set_priv(cert, &buffer[pos], length);
+ } else
+ _private_asn1_parse(context, cert, &buffer[pos], length, level + 1, fields, &local_has_key, client_cert, top_oid, &local_chain);
+ break;
+ case 0x05:
+ DEBUG_PRINT("NULL\n");
+ break;
+ case 0x06:
+ // object identifier
+ if (_is_field(fields, pk_id)) {
+#ifdef TLS_ECDSA_SUPPORTED
+ if ((length == 8) || (length == 5))
+ tls_certificate_set_algorithm(context, &cert->ec_algorithm, &buffer[pos], length);
+ else
+#endif
+ tls_certificate_set_algorithm(context, &cert->key_algorithm, &buffer[pos], length);
+ }
+ if (_is_field(fields, algorithm_id))
+ tls_certificate_set_algorithm(context, &cert->algorithm, &buffer[pos], length);
+
+ DEBUG_PRINT("OBJECT IDENTIFIER(%i): ", length);
+ DEBUG_DUMP_HEX(&buffer[pos], length);
+ DEBUG_PRINT("\n");
+ // check previous oid
+ if (_is_oid2(oid, ocsp_oid, 16, sizeof(ocsp_oid) - 1))
+ tls_certificate_set_copy(&cert->ocsp, &buffer[pos], length);
+
+ if (length < 16)
+ memcpy(oid, &buffer[pos], length);
+ else
+ memcpy(oid, &buffer[pos], 16);
+ if (top_oid)
+ memcpy(top_oid, oid, 16);
+ break;
+ case 0x09:
+ DEBUG_PRINT("REAL NUMBER(%i): ", length);
+ DEBUG_DUMP_HEX(&buffer[pos], length);
+ DEBUG_PRINT("\n");
+ break;
+ case 0x17:
+ // utc time
+ DEBUG_PRINT("UTC TIME: [");
+ DEBUG_DUMP(&buffer[pos], length);
+ DEBUG_PRINT("]\n");
+
+ if (_is_field(fields, validity_id)) {
+ if (idx == 1)
+ tls_certificate_set_copy_date(&cert->not_before, &buffer[pos], length);
+ else
+ tls_certificate_set_copy_date(&cert->not_after, &buffer[pos], length);
+ }
+ break;
+ case 0x18:
+ // generalized time
+ DEBUG_PRINT("GENERALIZED TIME: [");
+ DEBUG_DUMP(&buffer[pos], length);
+ DEBUG_PRINT("]\n");
+ break;
+ case 0x13:
+ // printable string
+ case 0x0C:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x19:
+ case 0x1A:
+ case 0x1B:
+ case 0x1C:
+ case 0x1D:
+ case 0x1E:
+ if (_is_field(fields, issurer_id)) {
+ if (_is_oid(oid, country_oid, 3))
+ tls_certificate_set_copy(&cert->issuer_country, &buffer[pos], length);
+ else
+ if (_is_oid(oid, state_oid, 3))
+ tls_certificate_set_copy(&cert->issuer_state, &buffer[pos], length);
+ else
+ if (_is_oid(oid, location_oid, 3))
+ tls_certificate_set_copy(&cert->issuer_location, &buffer[pos], length);
+ else
+ if (_is_oid(oid, entity_oid, 3))
+ tls_certificate_set_copy(&cert->issuer_entity, &buffer[pos], length);
+ else
+ if (_is_oid(oid, subject_oid, 3))
+ tls_certificate_set_copy(&cert->issuer_subject, &buffer[pos], length);
+ } else
+ if (_is_field(fields, owner_id)) {
+ if (_is_oid(oid, country_oid, 3))
+ tls_certificate_set_copy(&cert->country, &buffer[pos], length);
+ else
+ if (_is_oid(oid, state_oid, 3))
+ tls_certificate_set_copy(&cert->state, &buffer[pos], length);
+ else
+ if (_is_oid(oid, location_oid, 3))
+ tls_certificate_set_copy(&cert->location, &buffer[pos], length);
+ else
+ if (_is_oid(oid, entity_oid, 3))
+ tls_certificate_set_copy(&cert->entity, &buffer[pos], length);
+ else
+ if (_is_oid(oid, subject_oid, 3))
+ tls_certificate_set_copy(&cert->subject, &buffer[pos], length);
+ }
+ DEBUG_PRINT("STR: [");
+ DEBUG_DUMP(&buffer[pos], length);
+ DEBUG_PRINT("]\n");
+ break;
+ case 0x10:
+ DEBUG_PRINT("EMPTY SEQUENCE\n");
+ break;
+ case 0xA:
+ DEBUG_PRINT("ENUMERATED(%i): ", length);
+ DEBUG_DUMP_HEX(&buffer[pos], length);
+ DEBUG_PRINT("\n");
+ break;
+ default:
+ DEBUG_PRINT("========> NOT SUPPORTED %x\n", (int)type);
+ // not supported / needed
+ break;
+ }
+ }
+ pos += length;
+ }
+ if ((level == 2) && (cert->sign_key) && (cert->sign_len) && (cert_len) && (cert_data)) {
+ TLS_FREE(cert->fingerprint);
+ cert->fingerprint = _private_tls_compute_hash(cert->algorithm, cert_data, cert_len);
+#ifdef DEBUG
+ if (cert->fingerprint) {
+ DEBUG_DUMP_HEX_LABEL("FINGERPRINT", cert->fingerprint, _private_tls_hash_len(cert->algorithm));
+ }
+#endif
+ }
+ return pos;
+}
+
+struct TLSCertificate *asn1_parse(struct TLSContext *context, const unsigned char *buffer, unsigned int size, int client_cert) {
+ unsigned int fields[TLS_ASN1_MAXLEVEL];
+ memset(fields, 0, sizeof(int) * TLS_ASN1_MAXLEVEL);
+ struct TLSCertificate *cert = tls_create_certificate();
+ if (cert) {
+ if (client_cert < 0) {
+ client_cert = 0;
+ // private key
+ unsigned char top_oid[16];
+ memset(top_oid, 0, sizeof(top_oid));
+ _private_asn1_parse(context, cert, buffer, size, 1, fields, NULL, client_cert, top_oid, NULL);
+ } else
+ _private_asn1_parse(context, cert, buffer, size, 1, fields, NULL, client_cert, NULL, NULL);
+ }
+ return cert;
+}
+
+int tls_load_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size) {
+ if (!context)
+ return TLS_GENERIC_ERROR;
+
+ unsigned int len;
+ int idx = 0;
+ do {
+ unsigned char *data = tls_pem_decode(pem_buffer, pem_size, idx++, &len);
+ if ((!data) || (!len))
+ break;
+ struct TLSCertificate *cert = asn1_parse(context, data, len, 0);
+ if (cert) {
+ if ((cert->version == 2)
+#ifdef TLS_X509_V1_SUPPORT
+ || (cert->version == 0)
+#endif
+ ) {
+ TLS_FREE(cert->der_bytes);
+ cert->der_bytes = data;
+ cert->der_len = len;
+ data = NULL;
+ if (cert->priv) {
+ DEBUG_PRINT("WARNING - parse error (private key encountered in certificate)\n");
+ TLS_FREE(cert->priv);
+ cert->priv = NULL;
+ cert->priv_len = 0;
+ }
+ if (context->is_server) {
+ context->certificates = (struct TLSCertificate **)TLS_REALLOC(context->certificates, (context->certificates_count + 1) * sizeof(struct TLSCertificate *));
+ context->certificates[context->certificates_count] = cert;
+ context->certificates_count++;
+ DEBUG_PRINT("Loaded certificate: %i\n", (int)context->certificates_count);
+ } else {
+ context->client_certificates = (struct TLSCertificate **)TLS_REALLOC(context->client_certificates, (context->client_certificates_count + 1) * sizeof(struct TLSCertificate *));
+ context->client_certificates[context->client_certificates_count] = cert;
+ context->client_certificates_count++;
+ DEBUG_PRINT("Loaded client certificate: %i\n", (int)context->client_certificates_count);
+ }
+ } else {
+ DEBUG_PRINT("WARNING - certificate version error (v%i)\n", (int)cert->version);
+ tls_destroy_certificate(cert);
+ }
+ }
+ TLS_FREE(data);
+ } while (1);
+
+ if (context->is_server)
+ return context->certificates_count;
+
+ return context->client_certificates_count;
+}
+
+int tls_load_private_key(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size) {
+ if (!context)
+ return TLS_GENERIC_ERROR;
+
+ unsigned int len;
+ int idx = 0;
+ do {
+ unsigned char *data = tls_pem_decode(pem_buffer, pem_size, idx++, &len);
+ if ((!data) || (!len))
+ break;
+ struct TLSCertificate *cert = asn1_parse(context, data, len, -1);
+ if (cert) {
+ if (!cert->der_len) {
+ TLS_FREE(cert->der_bytes);
+ cert->der_bytes = data;
+ cert->der_len = len;
+ } else
+ TLS_FREE(data);
+ if ((cert) && (cert->priv) && (cert->priv_len)) {
+#ifdef TLS_ECDSA_SUPPORTED
+ if (cert->ec_algorithm) {
+ DEBUG_PRINT("Loaded ECC private key\n");
+ if (context->ec_private_key)
+ tls_destroy_certificate(context->ec_private_key);
+ context->ec_private_key = cert;
+ return 1;
+ } else
+#endif
+ {
+ DEBUG_PRINT("Loaded private key\n");
+ if (context->private_key)
+ tls_destroy_certificate(context->private_key);
+ context->private_key = cert;
+ return 1;
+ }
+ }
+ tls_destroy_certificate(cert);
+ } else
+ TLS_FREE(data);
+ } while (1);
+ return 0;
+}
+
+int tls_clear_certificates(struct TLSContext *context) {
+ unsigned int i;
+ if ((!context) || (!context->is_server) || (context->is_child))
+ return TLS_GENERIC_ERROR;
+
+ if (context->root_certificates) {
+ for (i = 0; i < context->root_count; i++)
+ tls_destroy_certificate(context->root_certificates[i]);
+ }
+ context->root_certificates = NULL;
+ context->root_count = 0;
+ if (context->private_key)
+ tls_destroy_certificate(context->private_key);
+ context->private_key = NULL;
+#ifdef TLS_ECDSA_SUPPORTED
+ if (context->ec_private_key)
+ tls_destroy_certificate(context->ec_private_key);
+ context->ec_private_key = NULL;
+#endif
+ TLS_FREE(context->certificates);
+ context->certificates = NULL;
+ context->certificates_count = 0;
+ return 0;
+}
+
+#ifdef WITH_TLS_13
+struct TLSPacket *tls_build_certificate_verify(struct TLSContext *context) {
+ struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0);
+ //certificate verify
+ tls_packet_uint8(packet, 0x0F);
+ unsigned int size_offset = packet->len;
+ tls_packet_uint24(packet, 0);
+
+ unsigned char out[TLS_MAX_RSA_KEY];
+#ifdef TLS_ECDSA_SUPPORTED
+ unsigned long out_len = TLS_MAX_RSA_KEY;
+#endif
+
+ unsigned char signing_data[TLS_MAX_HASH_SIZE + 98];
+ int signing_data_len;
+
+ // first 64 bytes to 0x20 (32)
+ memset(signing_data, 0x20, 64);
+ // context string 33 bytes
+ if (context->is_server)
+ memcpy(signing_data + 64, "TLS 1.3, server CertificateVerify", 33);
+ else
+ memcpy(signing_data + 64, "TLS 1.3, client CertificateVerify", 33);
+ // a single 0 byte separator
+ signing_data[97] = 0;
+ signing_data_len = 98;
+
+ signing_data_len += _private_tls_get_hash(context, signing_data + 98);
+ DEBUG_DUMP_HEX_LABEL("verify data", signing_data, signing_data_len);
+ int hash_algorithm = sha256;
+#ifdef TLS_ECDSA_SUPPORTED
+ if (tls_is_ecdsa(context)) {
+ switch (context->ec_private_key->ec_algorithm) {
+ case 23:
+ // secp256r1 + sha256
+ tls_packet_uint16(packet, 0x0403);
+ break;
+ case 24:
+ // secp384r1 + sha384
+ tls_packet_uint16(packet, 0x0503);
+ hash_algorithm = sha384;
+ break;
+ case 25:
+ // secp521r1 + sha512
+ tls_packet_uint16(packet, 0x0603);
+ hash_algorithm = sha512;
+ break;
+ default:
+ DEBUG_PRINT("UNSUPPORTED CURVE (SIGNING)\n");
+ packet->broken = 1;
+ return packet;
+ }
+ } else
+#endif
+ {
+ tls_packet_uint16(packet, 0x0804);
+ }
+
+ int packet_size = 2;
+#ifdef TLS_ECDSA_SUPPORTED
+ if (tls_is_ecdsa(context)) {
+ if (_private_tls_sign_ecdsa(context, hash_algorithm, signing_data, signing_data_len, out, &out_len) == 1) {
+ DEBUG_PRINT("ECDSA signing OK! (ECDSA, length %lu)\n", out_len);
+ tls_packet_uint16(packet, out_len);
+ tls_packet_append(packet, out, out_len);
+ packet_size += out_len + 2;
+ }
+ } else
+#endif
+ if (_private_tls_sign_rsa(context, hash_algorithm, signing_data, signing_data_len, out, &out_len) == 1) {
+ DEBUG_PRINT("RSA signing OK! (length %lu)\n", out_len);
+ tls_packet_uint16(packet, out_len);
+ tls_packet_append(packet, out, out_len);
+ packet_size += out_len + 2;
+ }
+ packet->buf[size_offset] = packet_size / 0x10000;
+ packet_size %= 0x10000;
+ packet->buf[size_offset + 1] = packet_size / 0x100;
+ packet_size %= 0x100;
+ packet->buf[size_offset + 2] = packet_size;
+
+ tls_packet_update(packet);
+ return packet;
+}
+#endif
+
+struct TLSPacket *tls_build_certificate(struct TLSContext *context) {
+ int i;
+ unsigned int all_certificate_size = 0;
+ int certificates_count;
+ struct TLSCertificate **certificates;
+ if (context->is_server) {
+ certificates_count = context->certificates_count;
+ certificates = context->certificates;
+ } else {
+ certificates_count = context->client_certificates_count;
+ certificates = context->client_certificates;
+ }
+ int delta = 3;
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13))
+ delta = 5;
+#endif
+#ifdef TLS_ECDSA_SUPPORTED
+ int is_ecdsa = tls_is_ecdsa(context);
+ if (is_ecdsa) {
+ for (i = 0; i < certificates_count; i++) {
+ struct TLSCertificate *cert = certificates[i];
+ if ((cert) && (cert->der_len) && (cert->ec_algorithm))
+ all_certificate_size += cert->der_len + delta;
+ }
+ if (!all_certificate_size) {
+ for (i = 0; i < certificates_count; i++) {
+ struct TLSCertificate *cert = certificates[i];
+ if ((cert) && (cert->der_len))
+ all_certificate_size += cert->der_len + delta;
+ }
+ }
+ } else {
+ for (i = 0; i < certificates_count; i++) {
+ struct TLSCertificate *cert = certificates[i];
+ if ((cert) && (cert->der_len) && (!cert->ec_algorithm))
+ all_certificate_size += cert->der_len + delta;
+ }
+ }
+#else
+ for (i = 0; i < certificates_count; i++) {
+ struct TLSCertificate *cert = certificates[i];
+ if ((cert) && (cert->der_len))
+ all_certificate_size += cert->der_len + delta;
+ }
+#endif
+ if (!all_certificate_size) {
+ DEBUG_PRINT("NO CERTIFICATE SET\n");
+ }
+ struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0);
+ tls_packet_uint8(packet, 0x0B);
+ if (all_certificate_size) {
+#ifdef WITH_TLS_13
+ // context
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ tls_packet_uint24(packet, all_certificate_size + 4);
+ tls_packet_uint8(packet, 0);
+ } else
+#endif
+ tls_packet_uint24(packet, all_certificate_size + 3);
+
+ if (context->dtls)
+ _private_dtls_handshake_data(context, packet, all_certificate_size + 3);
+
+ tls_packet_uint24(packet, all_certificate_size);
+ for (i = 0; i < certificates_count; i++) {
+ struct TLSCertificate *cert = certificates[i];
+ if ((cert) && (cert->der_len)) {
+#ifdef TLS_ECDSA_SUPPORTED
+ // is RSA certificate ?
+ if ((is_ecdsa) && (!cert->ec_algorithm))
+ continue;
+ // is ECC certificate ?
+ if ((!is_ecdsa) && (cert->ec_algorithm))
+ continue;
+#endif
+ // 2 times -> one certificate
+ tls_packet_uint24(packet, cert->der_len);
+ tls_packet_append(packet, cert->der_bytes, cert->der_len);
+#ifdef WITH_TLS_13
+ // extension
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13))
+ tls_packet_uint16(packet, 0);
+#endif
+ }
+ }
+ } else {
+ tls_packet_uint24(packet, all_certificate_size);
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13))
+ tls_packet_uint8(packet, 0);
+#endif
+
+ if (context->dtls)
+ _private_dtls_handshake_data(context, packet, all_certificate_size);
+ }
+ tls_packet_update(packet);
+ if (context->dtls)
+ context->dtls_seq++;
+ return packet;
+}
+
+#ifdef WITH_TLS_13
+struct TLSPacket *tls_build_encrypted_extensions(struct TLSContext *context) {
+ struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 3);
+ tls_packet_uint8(packet, 0x08);
+ if (context->negotiated_alpn) {
+ int alpn_negotiated_len = strlen(context->negotiated_alpn);
+ int alpn_len = alpn_negotiated_len + 1;
+
+ tls_packet_uint24(packet, alpn_len + 8);
+ tls_packet_uint16(packet, alpn_len + 6);
+ tls_packet_uint16(packet, 0x10);
+ tls_packet_uint16(packet, alpn_len + 2);
+ tls_packet_uint16(packet, alpn_len);
+
+ tls_packet_uint8(packet, alpn_negotiated_len);
+ tls_packet_append(packet, (unsigned char *)context->negotiated_alpn, alpn_negotiated_len);
+ } else {
+ tls_packet_uint24(packet, 2);
+ tls_packet_uint16(packet, 0);
+ }
+ tls_packet_update(packet);
+ return packet;
+}
+#endif
+
+struct TLSPacket *tls_build_finished(struct TLSContext *context) {
+ struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, TLS_MIN_FINISHED_OPAQUE_LEN + 64);
+ tls_packet_uint8(packet, 0x14);
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13))
+ tls_packet_uint24(packet, _private_tls_mac_length(context));
+ else
+#endif
+ tls_packet_uint24(packet, TLS_MIN_FINISHED_OPAQUE_LEN);
+ if (context->dtls)
+ _private_dtls_handshake_data(context, packet, TLS_MIN_FINISHED_OPAQUE_LEN);
+ // verify
+ unsigned char hash[TLS_MAX_HASH_SIZE];
+ unsigned long out_size = TLS_MIN_FINISHED_OPAQUE_LEN;
+#ifdef WITH_TLS_13
+ unsigned char out[TLS_MAX_HASH_SIZE];
+#else
+ unsigned char out[TLS_MIN_FINISHED_OPAQUE_LEN];
+#endif
+ unsigned int hash_len;
+
+ // server verifies client's message
+ if (context->is_server) {
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ hash_len = _private_tls_get_hash(context, hash);
+ if ((!context->finished_key) || (!hash_len)) {
+ DEBUG_PRINT("NO FINISHED KEY COMPUTED OR NO HANDSHAKE HASH\n");
+ packet->broken = 1;
+ return packet;
+ }
+
+ DEBUG_DUMP_HEX_LABEL("HS HASH", hash, hash_len);
+ DEBUG_DUMP_HEX_LABEL("HS FINISH", context->finished_key, hash_len);
+ DEBUG_DUMP_HEX_LABEL("HS REMOTE FINISH", context->remote_finished_key, hash_len);
+
+ out_size = hash_len;
+ hmac_state hmac;
+ hmac_init(&hmac, _private_tls_get_hash_idx(context), context->finished_key, hash_len);
+ hmac_process(&hmac, hash, hash_len);
+ hmac_done(&hmac, out, &out_size);
+ } else
+#endif
+ {
+ hash_len = _private_tls_done_hash(context, hash);
+ _private_tls_prf(context, out, TLS_MIN_FINISHED_OPAQUE_LEN, context->master_key, context->master_key_len, (unsigned char *)"server finished", 15, hash, hash_len, NULL, 0);
+ _private_tls_destroy_hash(context);
+ }
+ } else {
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ hash_len = _private_tls_get_hash(context, hash);
+ if ((!context->finished_key) || (!hash_len)) {
+ DEBUG_PRINT("NO FINISHED KEY COMPUTED OR NO HANDSHAKE HASH\n");
+ packet->broken = 1;
+ return packet;
+ }
+ DEBUG_DUMP_HEX_LABEL("HS HASH", hash, hash_len);
+ DEBUG_DUMP_HEX_LABEL("HS FINISH", context->finished_key, hash_len);
+ DEBUG_DUMP_HEX_LABEL("HS REMOTE FINISH", context->remote_finished_key, hash_len);
+
+ TLS_FREE(context->server_finished_hash);
+ context->server_finished_hash = (unsigned char *)TLS_MALLOC(hash_len);
+ if (context->server_finished_hash)
+ memcpy(context->server_finished_hash, hash, hash_len);
+
+ out_size = hash_len;
+ hmac_state hmac;
+ hmac_init(&hmac, _private_tls_get_hash_idx(context), context->finished_key, hash_len);
+ hmac_process(&hmac, hash, hash_len);
+ hmac_done(&hmac, out, &out_size);
+ } else {
+#endif
+ hash_len = _private_tls_get_hash(context, hash);
+ _private_tls_prf(context, out, TLS_MIN_FINISHED_OPAQUE_LEN, context->master_key, context->master_key_len, (unsigned char *)"client finished", 15, hash, hash_len, NULL, 0);
+#ifdef WITH_TLS_13
+ }
+#endif
+ }
+ tls_packet_append(packet, out, out_size);
+ tls_packet_update(packet);
+ DEBUG_DUMP_HEX_LABEL("VERIFY DATA", out, out_size);
+#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION
+ if (context->is_server) {
+ // concatenate client verify and server verify
+ context->verify_data = (unsigned char *)TLS_REALLOC(context->verify_data, out_size);
+ if (context->verify_data) {
+ memcpy(context->verify_data + context->verify_len, out, out_size);
+ context->verify_len += out_size;
+ } else
+ context->verify_len = 0;
+ } else {
+ TLS_FREE(context->verify_data);
+ context->verify_data = (unsigned char *)TLS_MALLOC(out_size);
+ if (context->verify_data) {
+ memcpy(context->verify_data, out, out_size);
+ context->verify_len = out_size;
+ }
+ }
+#endif
+ return packet;
+}
+
+struct TLSPacket *tls_build_change_cipher_spec(struct TLSContext *context) {
+ struct TLSPacket *packet = tls_create_packet(context, TLS_CHANGE_CIPHER, context->version, 64);
+ tls_packet_uint8(packet, 1);
+ tls_packet_update(packet);
+ context->local_sequence_number = 0;
+ return packet;
+}
+
+struct TLSPacket *tls_build_done(struct TLSContext *context) {
+ struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0);
+ tls_packet_uint8(packet, 0x0E);
+ tls_packet_uint24(packet, 0);
+ if (context->dtls) {
+ _private_dtls_handshake_data(context, packet, 0);
+ context->dtls_seq++;
+ }
+ tls_packet_update(packet);
+ return packet;
+}
+
+struct TLSPacket *tls_build_message(struct TLSContext *context, const unsigned char *data, unsigned int len) {
+ if ((!data) || (!len))
+ return 0;
+ struct TLSPacket *packet = tls_create_packet(context, TLS_APPLICATION_DATA, context->version, len);
+ tls_packet_append(packet, data, len);
+ tls_packet_update(packet);
+ return packet;
+}
+
+int tls_client_connect(struct TLSContext *context) {
+ if ((context->is_server) || (context->critical_error))
+ return TLS_UNEXPECTED_MESSAGE;
+
+ return _private_tls_write_packet(tls_build_hello(context, 0));
+}
+
+int tls_write(struct TLSContext *context, const unsigned char *data, unsigned int len) {
+ if (!context)
+ return TLS_GENERIC_ERROR;
+#ifdef TLS_12_FALSE_START
+ if ((context->connection_status != 0xFF) && ((context->is_server) || (context->version != TLS_V12) || (context->critical_error) || (!context->false_start)))
+ return TLS_UNEXPECTED_MESSAGE;
+#else
+ if (context->connection_status != 0xFF)
+ return TLS_UNEXPECTED_MESSAGE;
+#endif
+ if (len > TLS_MAXTLS_APP_SIZE)
+ len = TLS_MAXTLS_APP_SIZE;
+ int actually_written = _private_tls_write_packet(tls_build_message(context, data, len));
+ if (actually_written <= 0)
+ return actually_written;
+ return len;
+}
+
+struct TLSPacket *tls_build_alert(struct TLSContext *context, char critical, unsigned char code) {
+ struct TLSPacket *packet = tls_create_packet(context, TLS_ALERT, context->version, 0);
+ tls_packet_uint8(packet, critical ? TLS_ALERT_CRITICAL : TLS_ALERT_WARNING);
+ if (critical)
+ context->critical_error = 1;
+ tls_packet_uint8(packet, code);
+ tls_packet_update(packet);
+ return packet;
+}
+
+int _private_tls_read_from_file(const char *fname, void *buf, int max_len) {
+ FILE *f = fopen(fname, "rb");
+ if (f) {
+ int size = (int)fread(buf, 1, max_len, f);
+ fclose(f);
+ return size;
+ }
+ return 0;
+}
+
+int tls_connection_status(struct TLSContext *context) {
+ return context->connection_status;
+}
+
+int tls_consume_stream(struct TLSContext *context, const unsigned char *buf, int buf_len, tls_validation_function certificate_verify) {
+ if (!context)
+ return TLS_GENERIC_ERROR;
+
+ if (context->critical_error)
+ return TLS_BROKEN_CONNECTION;
+
+ if (buf_len <= 0) {
+ DEBUG_PRINT("tls_consume_stream called with buf_len %i\n", buf_len);
+ return 0;
+ }
+
+ if (!buf) {
+ DEBUG_PRINT("tls_consume_stream called NULL buffer\n");
+ context->critical_error = 1;
+ return TLS_NO_MEMORY;
+ }
+
+ unsigned int orig_len = context->message_buffer_len;
+ context->message_buffer_len += buf_len;
+ context->message_buffer = (unsigned char *)TLS_REALLOC(context->message_buffer, context->message_buffer_len);
+ if (!context->message_buffer) {
+ context->message_buffer_len = 0;
+ return TLS_NO_MEMORY;
+ }
+ memcpy(context->message_buffer + orig_len, buf, buf_len);
+ unsigned int index = 0;
+ unsigned int tls_buffer_len = context->message_buffer_len;
+ int err_flag = 0;
+
+ int tls_header_size;
+ int tls_size_offset;
+
+ if (context->dtls) {
+ tls_size_offset = 11;
+ tls_header_size = 13;
+ } else {
+ tls_size_offset = 3;
+ tls_header_size = 5;
+ }
+ while (tls_buffer_len >= tls_header_size) {
+ unsigned int length = ntohs(*(unsigned short *)&context->message_buffer[index + tls_size_offset]) + tls_header_size;
+ if (length > tls_buffer_len) {
+ DEBUG_PRINT("NEED DATA: %i/%i\n", length, tls_buffer_len);
+ break;
+ }
+ int parse_message = 1;
+ int consumed = 0;
+ if ((context->dtls) && (!context->cipher_spec_set)) {
+ // check fragmented!
+ unsigned char *buffer = &context->message_buffer[index];
+ if ((buffer[0] == TLS_HANDSHAKE) && (length > 13)) {
+ buffer += tls_header_size;
+
+ unsigned int data_length = buffer[1] * 0x10000 + buffer[2] * 0x100 + buffer[3];
+ unsigned int fragment_offset = buffer[6] * 0x10000 + buffer[7] * 0x100 + buffer[8];
+ unsigned int fragment_length = buffer[9] * 0x10000 + buffer[10] * 0x100 + buffer[11];
+
+ if ((data_length > DTLS_MAX_FRAGMENT_SIZE) || (fragment_offset + fragment_length > data_length)) {
+ DEBUG_PRINT("INVALID PACKET SIZE: %i, FRAGMENT OFFSET: %i, FRAGMENT LENGTH: %i\n");
+ return TLS_BROKEN_PACKET;
+ }
+
+ if (data_length != fragment_length) {
+ // fragmented!
+ if (!context->dtls_data->fragment) {
+ context->dtls_data->fragment = (struct DTLSFragment *)TLS_MALLOC(sizeof(struct DTLSFragment));
+ if (context->dtls_data->fragment)
+ memset(context->dtls_data->fragment, 0, sizeof(struct DTLSFragment));
+ }
+
+ if (!context->dtls_data->fragment)
+ return TLS_NO_MEMORY;
+
+ char *fragment_buffer = context->dtls_data->fragment->buffer;
+
+ fragment_buffer = (char *)TLS_REALLOC(fragment_buffer, data_length * sizeof(char *));
+ if (!fragment_buffer)
+ return TLS_NO_MEMORY;
+
+ memcpy(fragment_buffer + fragment_offset, &buffer[12], fragment_length);
+ context->dtls_data->fragment->buffer = fragment_buffer;
+ context->dtls_data->fragment->len = data_length;
+ context->dtls_data->fragment->written += fragment_length;
+
+ if (context->dtls_data->fragment->written != context->dtls_data->fragment->len) {
+ consumed = length;
+ parse_message = 0;
+ }
+ }
+ }
+ }
+ if (parse_message)
+ consumed = tls_parse_message(context, &context->message_buffer[index], length, certificate_verify);
+
+ DEBUG_PRINT("Consumed %i/%i bytes\n", consumed, tls_buffer_len);
+ if (consumed < 0) {
+ if (!context->critical_error)
+ context->critical_error = 1;
+ err_flag = consumed;
+ break;
+ }
+ index += length;
+ tls_buffer_len -= length;
+ if (context->critical_error) {
+ err_flag = TLS_BROKEN_CONNECTION;
+ break;
+ }
+ }
+ if (err_flag) {
+ DEBUG_PRINT("ERROR IN CONSUME: %i\n", err_flag);
+ context->message_buffer_len = 0;
+ TLS_FREE(context->message_buffer);
+ context->message_buffer = NULL;
+ return err_flag;
+ }
+ if (index) {
+ context->message_buffer_len -= index;
+ if (context->message_buffer_len) {
+ // no realloc here
+ memmove(context->message_buffer, context->message_buffer + index, context->message_buffer_len);
+ } else {
+ TLS_FREE(context->message_buffer);
+ context->message_buffer = NULL;
+ }
+ }
+ return index;
+}
+
+void tls_close_notify(struct TLSContext *context) {
+ if ((!context) || (context->critical_error))
+ return;
+ context->critical_error = 1;
+ DEBUG_PRINT("CLOSE\n");
+ _private_tls_write_packet(tls_build_alert(context, 0, close_notify));
+}
+
+void tls_alert(struct TLSContext *context, unsigned char critical, int code) {
+ if (!context)
+ return;
+ if ((!context->critical_error) && (critical))
+ context->critical_error = 1;
+ DEBUG_PRINT("ALERT\n");
+ _private_tls_write_packet(tls_build_alert(context, critical, code));
+}
+
+int tls_pending(struct TLSContext *context) {
+ if (!context->message_buffer)
+ return 0;
+ return context->message_buffer_len;
+}
+
+void tls_make_exportable(struct TLSContext *context, unsigned char exportable_flag) {
+ context->exportable = exportable_flag;
+ if (!exportable_flag) {
+ // zero the memory
+ if ((context->exportable_keys) && (context->exportable_size))
+ memset(context->exportable_keys, 0, context->exportable_size);
+ // free the memory, if alocated
+ TLS_FREE(context->exportable_keys);
+ context->exportable_size = 0;
+ }
+}
+
+int tls_export_context(struct TLSContext *context, unsigned char *buffer, unsigned int buf_len, unsigned char small_version) {
+ // only negotiated AND exportable connections may be exported
+ if ((!context) || (context->critical_error) || (context->connection_status != 0xFF) || (!context->exportable) || (!context->exportable_keys) || (!context->exportable_size) || (!context->crypto.created)) {
+ DEBUG_PRINT("CANNOT EXPORT CONTEXT %i\n", (int)context->connection_status);
+ return 0;
+ }
+
+ struct TLSPacket *packet = tls_create_packet(NULL, TLS_SERIALIZED_OBJECT, context->version, 0);
+ // export buffer version
+ tls_packet_uint8(packet, 0x01);
+ tls_packet_uint8(packet, context->connection_status);
+ tls_packet_uint16(packet, context->cipher);
+ if (context->is_child)
+ tls_packet_uint8(packet, 2);
+ else
+ tls_packet_uint8(packet, context->is_server);
+
+ if (context->crypto.created == 2) {
+ // aead
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ tls_packet_uint8(packet, TLS_13_AES_GCM_IV_LENGTH);
+ tls_packet_append(packet, context->crypto.ctx_local_mac.local_iv, TLS_13_AES_GCM_IV_LENGTH);
+ tls_packet_append(packet, context->crypto.ctx_remote_mac.remote_iv, TLS_13_AES_GCM_IV_LENGTH);
+ } else {
+#endif
+ tls_packet_uint8(packet, TLS_AES_GCM_IV_LENGTH);
+ tls_packet_append(packet, context->crypto.ctx_local_mac.local_aead_iv, TLS_AES_GCM_IV_LENGTH);
+ tls_packet_append(packet, context->crypto.ctx_remote_mac.remote_aead_iv, TLS_AES_GCM_IV_LENGTH);
+#ifdef WITH_TLS_13
+ }
+#endif
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ } else
+ if (context->crypto.created == 3) {
+ // ChaCha20
+ tls_packet_uint8(packet, TLS_CHACHA20_IV_LENGTH);
+ tls_packet_append(packet, context->crypto.ctx_local_mac.local_nonce, TLS_CHACHA20_IV_LENGTH);
+ tls_packet_append(packet, context->crypto.ctx_remote_mac.remote_nonce, TLS_CHACHA20_IV_LENGTH);
+#endif
+ } else {
+ unsigned char iv[TLS_AES_IV_LENGTH];
+ unsigned long len = TLS_AES_IV_LENGTH;
+
+ memset(iv, 0, TLS_AES_IV_LENGTH);
+ cbc_getiv(iv, &len, &context->crypto.ctx_local.aes_local);
+ tls_packet_uint8(packet, TLS_AES_IV_LENGTH);
+ tls_packet_append(packet, iv, len);
+
+ memset(iv, 0, TLS_AES_IV_LENGTH);
+ cbc_getiv(iv, &len, &context->crypto.ctx_remote.aes_remote);
+ tls_packet_append(packet, iv, TLS_AES_IV_LENGTH);
+ }
+
+ tls_packet_uint8(packet, context->exportable_size);
+ tls_packet_append(packet, context->exportable_keys, context->exportable_size);
+
+ if (context->crypto.created == 2) {
+ tls_packet_uint8(packet, 0);
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ } else
+ if (context->crypto.created == 3) {
+ // ChaCha20
+ tls_packet_uint8(packet, 0);
+ unsigned int i;
+ for (i = 0; i < 16; i++)
+ tls_packet_uint32(packet, context->crypto.ctx_local.chacha_local.input[i]);
+ for (i = 0; i < 16; i++)
+ tls_packet_uint32(packet, context->crypto.ctx_remote.chacha_remote.input[i]);
+ tls_packet_append(packet, context->crypto.ctx_local.chacha_local.ks, CHACHA_BLOCKLEN);
+ tls_packet_append(packet, context->crypto.ctx_remote.chacha_remote.ks, CHACHA_BLOCKLEN);
+#endif
+ } else {
+ unsigned char mac_length = (unsigned char)_private_tls_mac_length(context);
+ tls_packet_uint8(packet, mac_length);
+ tls_packet_append(packet, context->crypto.ctx_local_mac.local_mac, mac_length);
+ tls_packet_append(packet, context->crypto.ctx_remote_mac.remote_mac, mac_length);
+ }
+
+ if (small_version) {
+ tls_packet_uint16(packet, 0);
+ } else {
+ tls_packet_uint16(packet, context->master_key_len);
+ tls_packet_append(packet, context->master_key, context->master_key_len);
+ }
+
+ uint64_t sequence_number = htonll(context->local_sequence_number);
+ tls_packet_append(packet, (unsigned char *)&sequence_number, sizeof(uint64_t));
+ sequence_number = htonll(context->remote_sequence_number);
+ tls_packet_append(packet, (unsigned char *)&sequence_number, sizeof(uint64_t));
+
+ tls_packet_uint32(packet, context->tls_buffer_len);
+ tls_packet_append(packet, context->tls_buffer, context->tls_buffer_len);
+
+ tls_packet_uint32(packet, context->message_buffer_len);
+ tls_packet_append(packet, context->message_buffer, context->message_buffer_len);
+
+ tls_packet_uint32(packet, context->application_buffer_len);
+ tls_packet_append(packet, context->application_buffer, context->application_buffer_len);
+ tls_packet_uint8(packet, context->dtls);
+ if (context->dtls) {
+ tls_packet_uint16(packet, context->dtls_epoch_local);
+ tls_packet_uint16(packet, context->dtls_epoch_remote);
+ }
+ tls_packet_update(packet);
+ unsigned int size = packet->len;
+ if ((buffer) && (buf_len)) {
+ if (size > buf_len) {
+ tls_destroy_packet(packet);
+ DEBUG_PRINT("EXPORT BUFFER TO SMALL\n");
+ return (int)buf_len - (int)size;
+ }
+ memcpy(buffer, packet->buf, size);
+ }
+ tls_destroy_packet(packet);
+ return size;
+}
+
+struct TLSContext *tls_import_context(const unsigned char *buffer, unsigned int buf_len) {
+ if ((!buffer) || (buf_len < 64) || (buffer[0] != TLS_SERIALIZED_OBJECT) || (buffer[5] != 0x01)) {
+ DEBUG_PRINT("CANNOT IMPORT CONTEXT BUFFER\n");
+ return NULL;
+ }
+ // create a context object
+ struct TLSContext *context = tls_create_context(0, TLS_V12);
+ if (context) {
+ unsigned char temp[0xFF];
+ context->version = ntohs(*(unsigned short *)&buffer[1]);
+ unsigned short length = ntohs(*(unsigned short *)&buffer[3]);
+ if (length != buf_len - 5) {
+ DEBUG_PRINT("INVALID IMPORT BUFFER SIZE\n");
+ tls_destroy_context(context);
+ return NULL;
+ }
+ context->connection_status = buffer[6];
+ context->cipher = ntohs(*(unsigned short *)&buffer[7]);
+ unsigned char server = buffer[9];
+ if (server == 2) {
+ context->is_server = 1;
+ context->is_child = 1;
+ } else
+ context->is_server = server;
+
+ unsigned char local_iv[TLS_AES_IV_LENGTH];
+ unsigned char remote_iv[TLS_AES_IV_LENGTH];
+ unsigned char iv_len = buffer[10];
+ if (iv_len > TLS_AES_IV_LENGTH) {
+ DEBUG_PRINT("INVALID IV LENGTH\n");
+ tls_destroy_context(context);
+ return NULL;
+ }
+
+ // get the initialization vectors
+ int buf_pos = 11;
+ memcpy(local_iv, &buffer[buf_pos], iv_len);
+ buf_pos += iv_len;
+ memcpy(remote_iv, &buffer[buf_pos], iv_len);
+ buf_pos += iv_len;
+
+ unsigned char key_lengths = buffer[buf_pos++];
+ TLS_IMPORT_CHECK_SIZE(buf_pos, key_lengths, buf_len)
+ memcpy(temp, &buffer[buf_pos], key_lengths);
+ buf_pos += key_lengths;
+#ifdef TLS_REEXPORTABLE
+ context->exportable = 1;
+ context->exportable_keys = (unsigned char *)TLS_MALLOC(key_lengths);
+ memcpy(context->exportable_keys, temp, key_lengths);
+ context->exportable_size = key_lengths;
+#else
+ context->exportable = 0;
+#endif
+ int is_aead = _private_tls_is_aead(context);
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ if (is_aead == 2) {
+ // ChaCha20
+ if (iv_len > TLS_CHACHA20_IV_LENGTH)
+ iv_len = TLS_CHACHA20_IV_LENGTH;
+ memcpy(context->crypto.ctx_local_mac.local_nonce, local_iv, iv_len);
+ memcpy(context->crypto.ctx_remote_mac.remote_nonce, remote_iv, iv_len);
+ } else
+#endif
+ if (is_aead) {
+#ifdef WITH_TLS_13
+ if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ if (iv_len > TLS_13_AES_GCM_IV_LENGTH)
+ iv_len = TLS_13_AES_GCM_IV_LENGTH;
+ memcpy(context->crypto.ctx_local_mac.local_iv, local_iv, iv_len);
+ memcpy(context->crypto.ctx_remote_mac.remote_iv, remote_iv, iv_len);
+ } else {
+#endif
+ if (iv_len > TLS_AES_GCM_IV_LENGTH)
+ iv_len = TLS_AES_GCM_IV_LENGTH;
+ memcpy(context->crypto.ctx_local_mac.local_aead_iv, local_iv, iv_len);
+ memcpy(context->crypto.ctx_remote_mac.remote_aead_iv, remote_iv, iv_len);
+#ifdef WITH_TLS_13
+ }
+#endif
+ }
+ if (context->is_server) {
+ if (_private_tls_crypto_create(context, key_lengths / 2, temp, local_iv, temp + key_lengths / 2, remote_iv)) {
+ DEBUG_PRINT("ERROR CREATING KEY CONTEXT\n");
+ tls_destroy_context(context);
+ return NULL;
+ }
+ } else {
+ if (_private_tls_crypto_create(context, key_lengths / 2, temp + key_lengths / 2, remote_iv, temp, local_iv)) {
+ DEBUG_PRINT("ERROR CREATING KEY CONTEXT (CLIENT)\n");
+ tls_destroy_context(context);
+ return NULL;
+ }
+ }
+ memset(temp, 0, sizeof(temp));
+
+ unsigned char mac_length = buffer[buf_pos++];
+ if (mac_length > TLS_MAX_MAC_SIZE) {
+ DEBUG_PRINT("INVALID MAC SIZE\n");
+ tls_destroy_context(context);
+ return NULL;
+ }
+
+ if (mac_length) {
+ TLS_IMPORT_CHECK_SIZE(buf_pos, mac_length, buf_len)
+ memcpy(context->crypto.ctx_local_mac.local_mac, &buffer[buf_pos], mac_length);
+ buf_pos += mac_length;
+
+ TLS_IMPORT_CHECK_SIZE(buf_pos, mac_length, buf_len)
+ memcpy(context->crypto.ctx_remote_mac.remote_mac, &buffer[buf_pos], mac_length);
+ buf_pos += mac_length;
+ } else
+#ifdef TLS_WITH_CHACHA20_POLY1305
+ if (is_aead == 2) {
+ // ChaCha20
+ unsigned int i;
+ TLS_IMPORT_CHECK_SIZE(buf_pos, 128 + CHACHA_BLOCKLEN * 2, buf_len)
+ for (i = 0; i < 16; i++) {
+ context->crypto.ctx_local.chacha_local.input[i] = ntohl(*(unsigned int *)(buffer + buf_pos));
+ buf_pos += sizeof(unsigned int);
+ }
+ for (i = 0; i < 16; i++) {
+ context->crypto.ctx_remote.chacha_remote.input[i] = ntohl(*(unsigned int *)(buffer + buf_pos));
+ buf_pos += sizeof(unsigned int);
+ }
+ memcpy(context->crypto.ctx_local.chacha_local.ks, buffer + buf_pos, CHACHA_BLOCKLEN);
+ buf_pos += CHACHA_BLOCKLEN;
+ memcpy(context->crypto.ctx_remote.chacha_remote.ks, buffer + buf_pos, CHACHA_BLOCKLEN);
+ buf_pos += CHACHA_BLOCKLEN;
+ }
+#endif
+
+ TLS_IMPORT_CHECK_SIZE(buf_pos, 2, buf_len)
+ unsigned short master_key_len = ntohs(*(unsigned short *)(buffer + buf_pos));
+ buf_pos += 2;
+ if (master_key_len) {
+ TLS_IMPORT_CHECK_SIZE(buf_pos, master_key_len, buf_len)
+ context->master_key = (unsigned char *)TLS_MALLOC(master_key_len);
+ if (context->master_key) {
+ memcpy(context->master_key, &buffer[buf_pos], master_key_len);
+ context->master_key_len = master_key_len;
+ }
+ buf_pos += master_key_len;
+ }
+
+ TLS_IMPORT_CHECK_SIZE(buf_pos, 16, buf_len)
+
+ context->local_sequence_number = ntohll(*(uint64_t *)&buffer[buf_pos]);
+ buf_pos += 8;
+ context->remote_sequence_number = ntohll(*(uint64_t *)&buffer[buf_pos]);
+ buf_pos += 8;
+
+ TLS_IMPORT_CHECK_SIZE(buf_pos, 4, buf_len)
+ unsigned int tls_buffer_len = ntohl(*(unsigned int *)&buffer[buf_pos]);
+ buf_pos += 4;
+ TLS_IMPORT_CHECK_SIZE(buf_pos, tls_buffer_len, buf_len)
+ if (tls_buffer_len) {
+ context->tls_buffer = (unsigned char *)TLS_MALLOC(tls_buffer_len);
+ if (context->tls_buffer) {
+ memcpy(context->tls_buffer, &buffer[buf_pos], tls_buffer_len);
+ context->tls_buffer_len = tls_buffer_len;
+ }
+ buf_pos += tls_buffer_len;
+ }
+
+ TLS_IMPORT_CHECK_SIZE(buf_pos, 4, buf_len)
+ unsigned int message_buffer_len = ntohl(*(unsigned int *)&buffer[buf_pos]);
+ buf_pos += 4;
+ TLS_IMPORT_CHECK_SIZE(buf_pos, message_buffer_len, buf_len)
+ if (message_buffer_len) {
+ context->message_buffer = (unsigned char *)TLS_MALLOC(message_buffer_len);
+ if (context->message_buffer) {
+ memcpy(context->message_buffer, &buffer[buf_pos], message_buffer_len);
+ context->message_buffer_len = message_buffer_len;
+ }
+ buf_pos += message_buffer_len;
+ }
+
+ TLS_IMPORT_CHECK_SIZE(buf_pos, 4, buf_len)
+ unsigned int application_buffer_len = ntohl(*(unsigned int *)&buffer[buf_pos]);
+ buf_pos += 4;
+ context->cipher_spec_set = 1;
+ TLS_IMPORT_CHECK_SIZE(buf_pos, application_buffer_len, buf_len)
+ if (application_buffer_len) {
+ context->application_buffer = (unsigned char *)TLS_MALLOC(application_buffer_len);
+ if (context->application_buffer) {
+ memcpy(context->application_buffer, &buffer[buf_pos], application_buffer_len);
+ context->application_buffer_len = application_buffer_len;
+ }
+ buf_pos += application_buffer_len;
+ }
+ TLS_IMPORT_CHECK_SIZE(buf_pos, 1, buf_len)
+ context->dtls = buffer[buf_pos];
+ buf_pos++;
+ if (context->dtls) {
+ TLS_IMPORT_CHECK_SIZE(buf_pos, 4, buf_len)
+ context->dtls_epoch_local = ntohs(*(unsigned short *)&buffer[buf_pos]);
+ buf_pos += 2;
+ context->dtls_epoch_remote = ntohs(*(unsigned short *)&buffer[buf_pos]);
+ }
+ }
+ return context;
+}
+
+int tls_is_broken(struct TLSContext *context) {
+ if ((!context) || (context->critical_error))
+ return 1;
+ return 0;
+}
+
+int tls_request_client_certificate(struct TLSContext *context) {
+ if ((!context) || (!context->is_server))
+ return 0;
+
+ context->request_client_certificate = 1;
+ return 1;
+}
+
+int tls_client_verified(struct TLSContext *context) {
+ if ((!context) || (context->critical_error))
+ return 0;
+
+ return (context->client_verified == 1);
+}
+
+const char *tls_sni(struct TLSContext *context) {
+ if (!context)
+ return NULL;
+ return context->sni;
+}
+
+int tls_sni_nset(struct TLSContext *context, const char *sni, unsigned int len)
+{
+ if ((!context) || (context->is_server) || (context->critical_error) || (context->connection_status != 0))
+ return 0;
+ TLS_FREE(context->sni);
+ context->sni = NULL;
+ if (sni && len > 0) {
+ context->sni = (char *)TLS_MALLOC(len + 1);
+ if (context->sni) {
+ context->sni[len] = 0;
+ memcpy(context->sni, sni, len);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int tls_sni_set(struct TLSContext *context, const char *sni) {
+ if (!context || !sni)
+ return 0;
+ return tls_sni_nset(context, sni, strlen(sni));
+}
+
+int tls_srtp_set(struct TLSContext *context) {
+ if ((!context) || (!context->dtls))
+ return TLS_GENERIC_ERROR;
+ context->dtls = 4;
+ return 0;
+}
+
+int tls_srtp_key(struct TLSContext *context, unsigned char *buffer) {
+ if ((!context->master_key) || (!context->master_key_len))
+ return TLS_GENERIC_ERROR;
+
+ unsigned char material[(SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_SALT_LEN) * 2];
+
+ if (context->is_server)
+ _private_tls_prf(context, material, sizeof(material), context->master_key, context->master_key_len, (unsigned char *)"EXTRACTOR-dtls_srtp", 19, context->remote_random, TLS_SERVER_RANDOM_SIZE, context->local_random, TLS_CLIENT_RANDOM_SIZE);
+ else
+ _private_tls_prf(context, material, sizeof(material), context->master_key, context->master_key_len, (unsigned char *)"EXTRACTOR-dtls_srtp", 19, context->local_random, TLS_SERVER_RANDOM_SIZE, context->remote_random, TLS_CLIENT_RANDOM_SIZE);
+
+ if (buffer)
+ memcpy(buffer, material, sizeof(material));
+
+ DEBUG_DUMP_HEX_LABEL("USING MASTER KEY", context->master_key, context->master_key_len);
+ return sizeof(material);
+}
+
+int tls_is_stun(const unsigned char *msg, int len) {
+ if ((!msg) || (len < 20))
+ return 0;
+
+ if ((msg[4] != 0x21) || (msg[5] != 0x12) || (msg[6] != 0xa4) || (msg[7] != 0x42))
+ return 0;
+
+ return 1;
+}
+
+uint32_t _private_tls_crc32(const unsigned char *s, int n) {
+ uint32_t crc = 0xFFFFFFFF;
+ int i;
+ int j;
+
+ for (i = 0; i < n; i++) {
+ char ch = s[i];
+ for (j = 0; j < 8; j ++) {
+ uint32_t b = (ch ^ crc) & 1;
+ crc >>= 1;
+ if (b)
+ crc=crc^0xEDB88320;
+ ch >>= 1;
+ }
+ }
+ return ~crc;
+}
+
+int tls_stun_parse(unsigned char *msg, int len, char *pwd, int pwd_len, unsigned char is_ipv6, unsigned char *addr, unsigned int port, unsigned char *response_buffer) {
+ // not a stun message?
+ if ((!msg) || (len < 20)) {
+ DEBUG_PRINT("INVALID STUN PACKET\n");
+ return TLS_GENERIC_ERROR;
+ }
+
+ if ((msg[4] != 0x21) || (msg[5] != 0x12) || (msg[6] != 0xa4) || (msg[7] != 0x42)) {
+ DEBUG_PRINT("INVALID STUN PACKET (INVALID MAGIC COOKIE)\n");
+ return TLS_GENERIC_ERROR;
+ }
+
+ int addr_len = 4;
+ if (is_ipv6)
+ addr_len = 16;
+
+ unsigned char *stun_message = msg;
+
+ unsigned short type = ntohs(*(unsigned short *)msg);
+ int msg_len = ntohs(*(unsigned short *)&msg[2]);
+
+ if (msg_len > len - 20)
+ return -1;
+
+ const unsigned char *magic_cookie = &msg[4];
+ const unsigned char *transaction_id = &msg[8];
+
+ const unsigned char *attributes = &msg[20];
+
+ switch (type) {
+ case 0x0001:
+ break;
+ case 0x0101:
+ // ignore
+ return 0;
+ default:
+ DEBUG_PRINT("UNSUPPORTED MESSAGE TYPE %x\n", (int)type);
+ return TLS_FEATURE_NOT_SUPPORTED;
+ }
+
+ msg += 20;
+
+ unsigned char hash[20];
+ unsigned long hash_len = 20;
+
+ unsigned char md5_hash[16];
+
+ hmac_state hmac;
+ hash_state md5_state;
+
+ memset(hash, 0, sizeof(hash));
+ int stun_message_len = 20;
+
+ unsigned char secret[16];
+
+ char key[0x4CE];
+
+ unsigned char *username = NULL;
+ int username_len = 0;
+
+ unsigned char *realm = NULL;
+ int realm_len = 0;
+
+ unsigned char *nonce = NULL;
+ int nonce_len = 0;
+
+ char *ptr;
+
+ int validated = 0;
+
+ uint32_t priority = 0;
+
+ while (msg_len >= 4) {
+ unsigned short attr_type = ntohs(*(unsigned short *)msg);
+ int attr_len = ntohs(*(unsigned short *)&msg[2]);
+ msg += 4;
+ msg_len -= 4;
+
+ if (attr_len > msg_len)
+ return TLS_GENERIC_ERROR;
+ DEBUG_PRINT("STUN ATTR 0x%04X\n", (int)attr_type);
+ unsigned short temp;
+ switch (attr_type) {
+ case 0x0001:
+ // MAPPED-ADDRESS
+ break;
+ case 0x0006:
+ // USERNAME
+ if (attr_len > 513)
+ return TLS_BROKEN_PACKET;
+
+ username = msg;
+ username_len = attr_len;
+ break;
+ case 0x0008:
+ // MESSAGE-INTEGRITY
+ if ((attr_len != 20) || (!username) || (!username_len))
+ return -1;
+
+ tls_init();
+
+ // HMAC is computed on message of size including MESSAGE-INTEGRITY, but not including fingerprint (or other post-MESSAGE-INTEGRITY extensions)
+ temp = *(unsigned short *)&stun_message[2];
+ *(unsigned short *)&stun_message[2] = htons(stun_message_len + attr_len + 4 - 20);
+
+ if ((realm) && (realm_len > 0)) {
+ ptr = key;
+ memcpy(ptr, username, username_len);
+ ptr += username_len;
+
+ *ptr = ':';
+ ptr ++;
+
+ if ((realm) && (realm_len > 0)) {
+ memcpy(ptr, realm, realm_len);
+ ptr += username_len;
+ *ptr = ':';
+ ptr ++;
+ }
+
+ memcpy(ptr, pwd, pwd_len);
+ ptr += pwd_len;
+
+ *ptr = 0;
+
+
+ DEBUG_PRINT("KEY: %s\n", key);
+
+ md5_init(&md5_state);
+ md5_process(&md5_state, (unsigned char *)key, strlen(key));
+ md5_done(&md5_state, md5_hash);
+
+ DEBUG_DUMP_HEX_LABEL("HASH", md5_hash, 16);
+
+ hmac_init(&hmac, find_hash("sha1"), md5_hash, 16);
+ } else
+ hmac_init(&hmac, find_hash("sha1"), (unsigned char *)pwd, pwd_len);
+
+
+ hmac_process(&hmac, stun_message, stun_message_len);
+ hmac_done(&hmac, hash, &hash_len);
+
+ *(unsigned short *)&stun_message[2] = temp;
+
+ if (memcmp(msg, hash, 16)) {
+ DEBUG_PRINT("MESSAGE-INTEGRITY check failed\n");
+ return TLS_INTEGRITY_FAILED;
+ }
+ validated = 1;
+ break;
+ case 0x0009:
+ // ERROR-CODE
+ break;
+ case 0x000A:
+ // UNKNOWN-ATTRIBUTES
+ break;
+ case 0x0014:
+ // REALM
+ if (attr_len > 763)
+ return TLS_BROKEN_PACKET;
+ realm = msg;
+ realm_len = attr_len;
+ break;
+ case 0x0015:
+ // NONCE
+ if (attr_len > 763)
+ return TLS_BROKEN_PACKET;
+ nonce = msg;
+ nonce_len = attr_len;
+ break;
+ case 0x0020:
+ // XOR-MAPPED-ADDRESS
+ break;
+ case 0x0024:
+ // PRIORITY
+ if (attr_len != 4)
+ return TLS_BROKEN_PACKET;
+ uint32_t priority;
+ memcpy(&priority, msg, sizeof(priority));
+ priority = ntohl(priority);
+ break;
+ }
+
+ while (attr_len % 4)
+ attr_len ++;
+
+ msg_len -= attr_len;
+ msg += attr_len;
+ stun_message_len += attr_len + 4;
+ }
+ if (!validated)
+ return TLS_GENERIC_ERROR;
+
+ if (response_buffer) {
+ response_buffer[0] = 0x01;
+ response_buffer[1] = 0x01;
+
+ // size
+ response_buffer[2] = 0x00;
+ response_buffer[3] = 0x00;
+
+ response_buffer[4] = 0x21;
+ response_buffer[5] = 0x12;
+ response_buffer[6] = 0xa4;
+ response_buffer[7] = 0x42;
+
+ // transaction ID
+ memcpy(response_buffer + 8, stun_message + 8, 12);
+
+ // XOR-MAPPED-ADDRESS
+ response_buffer[20] = 0x00;
+ response_buffer[21] = 0x20;
+
+ *(unsigned short *)&response_buffer[22] = htons(addr_len + 4);
+
+ response_buffer[24] = 0x00;
+ if (is_ipv6)
+ response_buffer[25] = 0x02;
+ else
+ response_buffer[25] = 0x01;
+
+ *(unsigned short *)&response_buffer[26] = htons(port);
+
+ response_buffer[26] ^= response_buffer[4];
+ response_buffer[27] ^= response_buffer[5];
+
+ memcpy(response_buffer + 28, addr, addr_len);
+
+ int i;
+ for (i = 0; i < addr_len; i ++)
+ response_buffer[28 + i] ^= response_buffer[4 + i % 4];
+
+ int buffer_index = 28 + addr_len;
+
+ // padding
+ while (buffer_index % 4) {
+ response_buffer[buffer_index] = 0x00;
+ buffer_index ++;
+ response_buffer[22] ++;
+ }
+
+ // must be computed before to be included in hmac!!!
+ *(unsigned short *)&response_buffer[2] = htons(buffer_index + 4);
+
+ hmac_init(&hmac, find_hash("sha1"), (unsigned char *)pwd, pwd_len);
+ hmac_process(&hmac, response_buffer, buffer_index);
+ hmac_done(&hmac, response_buffer + buffer_index + 4, &hash_len);
+
+ // hmac
+ response_buffer[buffer_index] = 0x00;
+ response_buffer[buffer_index + 1] = 0x08;
+ response_buffer[buffer_index + 2] = 0x00;
+ response_buffer[buffer_index + 3] = 0x14;
+
+ buffer_index += 24;
+
+ response_buffer[buffer_index ++] = 0x80;
+ response_buffer[buffer_index ++] = 0x28;
+ response_buffer[buffer_index ++] = 0x00;
+ response_buffer[buffer_index ++] = 0x04;
+
+ *(unsigned short *)&response_buffer[2] = htons(buffer_index - 16);
+
+ uint32_t fingerprint = _private_tls_crc32(response_buffer, buffer_index - 4) ^ 0x5354554e;
+ *(uint32_t *)&response_buffer[buffer_index] = htonl(fingerprint);
+
+ buffer_index += 4;
+
+ DEBUG_DUMP_HEX_LABEL("STUN RESPONSE>>>>>>>>", response_buffer, buffer_index);
+
+ return buffer_index;
+ }
+ return 0;
+}
+
+int tls_stun_build(unsigned char transaction_id[12], char *username, int username_len, char *pwd, int pwd_len, unsigned char *msg) {
+ if (!msg)
+ return 0;
+
+ *(unsigned short *)msg = htons(0x0001);
+
+ msg[4] = 0x21;
+ msg[5] = 0x12;
+ msg[6] = 0xa4;
+ msg[7] = 0x42;
+
+ memcpy(msg + 8, transaction_id, 12);
+
+ unsigned char *ptr = msg + 20;
+
+ int len = 20;
+ if ((username) && (username_len > 0) && (username_len <= 513)) {
+ *(unsigned short *)&msg[20] = htons(0x0006);
+ *(unsigned short *)&msg[22] = htons(username_len);
+
+ len += 4;
+
+ memcpy(msg + len, username, username_len);
+ len += username_len;
+
+ while (len % 4)
+ msg[len ++] = 0;
+ }
+
+ *(unsigned short *)&msg[len] = htons(0x0025);
+ *(unsigned short *)&msg[len + 2] = htons(0);
+
+ len += 4;
+
+
+ *(unsigned short *)&msg[len] = htons(0x0008);
+ *(unsigned short *)&msg[len + 2] = htons(20);
+
+ len += 24;
+
+ *(unsigned short *)&msg[2] = htons(len - 20);
+
+ tls_init();
+
+ hmac_state hmac;
+ unsigned long hash_len = 20;
+
+ hmac_init(&hmac, find_hash("sha1"), (unsigned char *)pwd, pwd_len);
+ hmac_process(&hmac, msg, len - 24);
+ hmac_done(&hmac, msg + len - 20, &hash_len);
+
+ msg[len ++] = 0x80;
+ msg[len ++] = 0x28;
+ msg[len ++] = 0x00;
+ msg[len ++] = 0x04;
+
+ *(unsigned short *)&msg[2] = htons(len - 16);
+
+ uint32_t fingerprint = _private_tls_crc32(msg, len - 4) ^ 0x5354554e;
+ *(uint32_t *)&msg[len] = htonl(fingerprint);
+
+ len += 4;
+
+ return len;
+}
+
+int tls_cert_fingerprint(const char *pem_data, int pem_size, char *buffer, unsigned int buf_len) {
+ unsigned int len = 0;
+ if ((!buffer) || (!buf_len))
+ return TLS_GENERIC_ERROR;
+
+ unsigned char *data = tls_pem_decode((const unsigned char *)pem_data, pem_size, 0, &len);
+ if (!data)
+ return TLS_GENERIC_ERROR;
+
+ unsigned char hash[32];
+
+ hash_state state;
+
+ sha256_init(&state);
+ sha256_process(&state, data, len);
+ sha256_done(&state, hash);
+
+ TLS_FREE(data);
+
+ int i;
+ buffer[0] = 0;
+ for (i = 0; i < 32; i++) {
+ if (buf_len <= 1)
+ break;
+ if (i) {
+ snprintf(buffer, buf_len, ":");
+ buffer ++;
+ buf_len --;
+ }
+ if (buf_len <= 2)
+ break;
+ snprintf(buffer, buf_len, "%02X", (unsigned int)hash[i]);
+ buffer += 2;
+ buf_len -= 2;
+ }
+ return 0;
+}
+
+int tls_load_root_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size) {
+ if (!context)
+ return TLS_GENERIC_ERROR;
+
+ unsigned int len;
+ int idx = 0;
+
+ do {
+ unsigned char *data = tls_pem_decode(pem_buffer, pem_size, idx++, &len);
+ if ((!data) || (!len))
+ break;
+ struct TLSCertificate *cert = asn1_parse(NULL, data, len, 0);
+ if (cert) {
+ if ((cert->version == 2)
+#ifdef TLS_X509_V1_SUPPORT
+ || (cert->version == 0)
+#endif
+ ) {
+ if (cert->priv) {
+ DEBUG_PRINT("WARNING - parse error (private key encountered in certificate)\n");
+ TLS_FREE(cert->priv);
+ cert->priv = NULL;
+ cert->priv_len = 0;
+ }
+ context->root_certificates = (struct TLSCertificate **)TLS_REALLOC(context->root_certificates, (context->root_count + 1) * sizeof(struct TLSCertificate *));
+ if (!context->root_certificates) {
+ context->root_count = 0;
+ return TLS_GENERIC_ERROR;
+ }
+ context->root_certificates[context->root_count] = cert;
+ context->root_count++;
+ DEBUG_PRINT("Loaded certificate: %i\n", (int)context->root_count);
+ } else {
+ DEBUG_PRINT("WARNING - certificate version error (v%i)\n", (int)cert->version);
+ tls_destroy_certificate(cert);
+ }
+ }
+ TLS_FREE(data);
+ } while (1);
+ return context->root_count;
+}
+
+int tls_default_verify(struct TLSContext *context, struct TLSCertificate **certificate_chain, int len) {
+ int i;
+ int err;
+
+ if (certificate_chain) {
+ for (i = 0; i < len; i++) {
+ struct TLSCertificate *certificate = certificate_chain[i];
+ // check validity date
+ err = tls_certificate_is_valid(certificate);
+ if (err)
+ return err;
+ }
+ }
+ // check if chain is valid
+ err = tls_certificate_chain_is_valid(certificate_chain, len);
+ if (err)
+ return err;
+
+ // check certificate subject
+ if ((!context->is_server) && (context->sni) && (len > 0) && (certificate_chain)) {
+ err = tls_certificate_valid_subject(certificate_chain[0], context->sni);
+ if (err)
+ return err;
+ }
+
+ err = tls_certificate_chain_is_valid_root(context, certificate_chain, len);
+ if (err)
+ return err;
+
+ DEBUG_PRINT("Certificate OK\n");
+ return no_error;
+}
+
+int tls_unmake_ktls(struct TLSContext *context, int socket) {
+#ifdef WITH_KTLS
+ struct tls12_crypto_info_aes_gcm_128 crypto_info;
+ socklen_t crypt_info_size = sizeof(crypto_info);
+ if (getsockopt(socket, SOL_TLS, TLS_TX, &crypto_info, &crypt_info_size)) {
+ DEBUG_PRINT("ERROR IN getsockopt\n");
+ return TLS_GENERIC_ERROR;
+ }
+ memcpy(&context->local_sequence_number, crypto_info.rec_seq, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE);
+ context->local_sequence_number = ntohll(context->local_sequence_number);
+#ifdef TLS_RX
+ crypt_info_size = sizeof(crypto_info);
+ if (getsockopt(socket, SOL_TLS, TLS_RX, &crypto_info, &crypt_info_size)) {
+ DEBUG_PRINT("ERROR IN getsockopt\n");
+ return TLS_GENERIC_ERROR;
+ }
+ memcpy(&context->remote_sequence_number, crypto_info.rec_seq, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE);
+ context->remote_sequence_number = ntohll(context->remote_sequence_number);
+#endif
+ return 0;
+#endif
+ DEBUG_PRINT("TLSe COMPILED WITHOUT kTLS SUPPORT\n");
+ return TLS_FEATURE_NOT_SUPPORTED;
+}
+
+int tls_make_ktls(struct TLSContext *context, int socket) {
+ if ((!context) || (context->critical_error) || (context->connection_status != 0xFF) || (!context->crypto.created)) {
+ DEBUG_PRINT("CANNOT SWITCH TO kTLS\n");
+ return TLS_GENERIC_ERROR;
+ }
+ if ((!context->exportable) || (!context->exportable_keys)) {
+ DEBUG_PRINT("KEY MUST BE EXPORTABLE TO BE ABLE TO USE kTLS\n");
+ return TLS_GENERIC_ERROR;
+ }
+ if ((context->version != TLS_V12) && (context->version != DTLS_V12) && (context->version != TLS_V13) && (context->version != DTLS_V13)) {
+ DEBUG_PRINT("kTLS IS SUPPORTED ONLY FOR TLS >= 1.2 AND DTLS >= 1.2\n");
+ return TLS_FEATURE_NOT_SUPPORTED;
+ }
+ switch (context->cipher) {
+ case TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case TLS_AES_128_GCM_SHA256:
+ break;
+ default:
+ DEBUG_PRINT("CIPHER UNSUPPORTED: kTLS SUPPORTS ONLY AES 128 GCM CIPHERS\n");
+ return TLS_FEATURE_NOT_SUPPORTED;
+ }
+#ifdef WITH_KTLS
+ if (context->exportable_size < TLS_CIPHER_AES_GCM_128_KEY_SIZE * 2) {
+ DEBUG_PRINT("INVALID KEY SIZE\n");
+ return TLS_GENERIC_ERROR;
+ }
+ int err;
+ struct tls12_crypto_info_aes_gcm_128 crypto_info;
+ crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_128;
+ uint64_t local_sequence_number = htonll(context->local_sequence_number);
+
+ if ((context->version == TLS_V12) || (context->version == DTLS_V12)) {
+ crypto_info.info.version = TLS_1_2_VERSION;
+ memcpy(crypto_info.iv, &local_sequence_number, TLS_CIPHER_AES_GCM_128_IV_SIZE);
+ memcpy(crypto_info.rec_seq, &local_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE);
+ memcpy(crypto_info.key, context->exportable_keys, TLS_CIPHER_AES_GCM_128_KEY_SIZE);
+ memcpy(crypto_info.salt, context->crypto.ctx_local_mac.local_aead_iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE);
+ } else if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ crypto_info.info.version = TLS_1_3_VERSION;
+ memcpy(crypto_info.iv, context->crypto.ctx_local_mac.local_iv + 4, TLS_CIPHER_AES_GCM_128_IV_SIZE);
+ memcpy(crypto_info.rec_seq, &local_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE);
+ memcpy(crypto_info.key, context->exportable_keys, TLS_CIPHER_AES_GCM_128_KEY_SIZE);
+ memcpy(crypto_info.salt, context->crypto.ctx_local_mac.local_iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE);
+ }
+
+ err = setsockopt(socket, SOL_TCP, TCP_ULP, "tls", sizeof("tls"));
+ if (err)
+ return err;
+
+#ifdef TLS_RX
+ // kernel 4.17 adds TLS_RX support
+ struct tls12_crypto_info_aes_gcm_128 crypto_info_read;
+
+ crypto_info_read.info.cipher_type = TLS_CIPHER_AES_GCM_128;
+
+ uint64_t remote_sequence_number = htonll(context->remote_sequence_number);
+
+ if ((context->version == TLS_V12) || (context->version == DTLS_V12)) {
+ crypto_info_read.info.version = TLS_1_2_VERSION;
+ memcpy(crypto_info_read.iv, &remote_sequence_number, TLS_CIPHER_AES_GCM_128_IV_SIZE);
+ memcpy(crypto_info_read.rec_seq, &remote_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE);
+ memcpy(crypto_info_read.key, context->exportable_keys + TLS_CIPHER_AES_GCM_128_KEY_SIZE, TLS_CIPHER_AES_GCM_128_KEY_SIZE);
+ memcpy(crypto_info_read.salt, context->crypto.ctx_remote_mac.remote_aead_iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE);
+ } else if ((context->version == TLS_V13) || (context->version == DTLS_V13)) {
+ crypto_info_read.info.version = TLS_1_3_VERSION;
+ memcpy(crypto_info_read.iv, context->crypto.ctx_remote_mac.remote_iv + 4, TLS_CIPHER_AES_GCM_128_IV_SIZE);
+ memcpy(crypto_info_read.rec_seq, &remote_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE);
+ memcpy(crypto_info_read.key, context->exportable_keys + TLS_CIPHER_AES_GCM_128_KEY_SIZE, TLS_CIPHER_AES_GCM_128_KEY_SIZE);
+ memcpy(crypto_info_read.salt, context->crypto.ctx_remote_mac.remote_iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE);
+ }
+
+ err = setsockopt(socket, SOL_TLS, TLS_RX, &crypto_info_read, sizeof(crypto_info_read));
+ if (err)
+ return err;
+#endif
+ return setsockopt(socket, SOL_TLS, TLS_TX, &crypto_info, sizeof(crypto_info));
+#else
+ DEBUG_PRINT("TLSe COMPILED WITHOUT kTLS SUPPORT\n");
+ return TLS_FEATURE_NOT_SUPPORTED;
+#endif
+}
+
+#ifdef DEBUG
+void tls_print_certificate(const char *fname) {
+ unsigned char buf[0xFFFF];
+ char out_buf[0xFFFF];
+ int size = _private_tls_read_from_file(fname, buf, 0xFFFF);
+ if (size > 0) {
+ int idx = 0;
+ unsigned int len;
+ do {
+ unsigned char *data;
+ if (buf[0] == '-') {
+ data = tls_pem_decode(buf, size, idx++, &len);
+ } else {
+ data = buf;
+ len = size;
+ }
+ if ((!data) || (!len))
+ return;
+ struct TLSCertificate *cert = asn1_parse(NULL, data, len, -1);
+ if (data != buf)
+ TLS_FREE(data);
+ if (cert) {
+ fprintf(stderr, "%s", tls_certificate_to_string(cert, out_buf, 0xFFFF));
+ tls_destroy_certificate(cert);
+ }
+ if (data == buf)
+ break;
+ } while (1);
+ }
+}
+#endif
+
+int tls_remote_error(struct TLSContext *context) {
+ if (!context)
+ return TLS_GENERIC_ERROR;
+
+ return context->error_code;
+}
+
+struct TLSRTCPeerConnection *tls_peerconnection_context(unsigned char active, tls_validation_function certificate_verify, void *userdata) {
+ const char pwd_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_/";
+ struct TLSRTCPeerConnection *channel = (struct TLSRTCPeerConnection *)TLS_MALLOC(sizeof(struct TLSRTCPeerConnection));
+ if (channel) {
+ memset(channel, 0, sizeof(struct TLSRTCPeerConnection));
+ tls_random(channel->stun_transcation_id, 12);
+
+ unsigned char buffer[32];
+ tls_random(buffer, 32);
+
+ int i;
+ for (i = 0; i < 4; i ++)
+ channel->local_user[i] = 'A' + buffer[i] % 25;
+ channel->local_user[4] = 0;
+
+ for (i = 0; i < 24; i ++)
+ channel->local_pwd[i] = pwd_chars[buffer[i + 4] % (sizeof(pwd_chars) - 1)];
+
+ channel->local_pwd[24] = 0;
+
+ channel->certificate_verify = certificate_verify;
+ channel->active = active;
+ channel->userdata = userdata;
+ }
+
+ return channel;
+}
+
+struct TLSRTCPeerConnection *tls_peerconnection_duplicate(struct TLSRTCPeerConnection *channel, void *userdata) {
+ if ((!channel) || (channel->active) || (!channel->context))
+ return NULL;
+
+ struct TLSRTCPeerConnection *clone = tls_peerconnection_context(0, channel->certificate_verify, userdata);
+ clone->context = tls_accept(channel->context);
+ tls_srtp_set(clone->context);
+ tls_add_alpn(clone->context, "webrtc");
+
+ return clone;
+}
+
+struct TLSContext *tls_peerconnection_dtls_context(struct TLSRTCPeerConnection *channel) {
+ if (!channel)
+ return NULL;
+
+ return channel->context;
+}
+
+int tls_peerconnection_remote_credentials(struct TLSRTCPeerConnection *channel, char *remote_username, int remote_username_len, char *remote_pwd, int remote_pwd_len, char *remote_fingerprint, int remote_fingerprint_len) {
+ if (!channel)
+ return TLS_GENERIC_ERROR;
+
+ if (channel->remote_user) {
+ TLS_FREE(channel->remote_user);
+ channel->remote_user = NULL;
+ channel->remote_user_len = 0;
+ }
+
+ if (channel->remote_pwd) {
+ TLS_FREE(channel->remote_pwd);
+ channel->remote_pwd = NULL;
+ channel->remote_pwd_len = 0;
+ }
+
+ if ((remote_username) && (remote_username_len > 0)) {
+ channel->remote_user = (unsigned char *)TLS_MALLOC(remote_username_len + 1);
+ if (!channel->remote_user)
+ return TLS_NO_MEMORY;
+
+ memcpy(channel->remote_user, remote_username, remote_username_len);
+ channel->remote_user[remote_username_len] = 0;
+ channel->remote_user_len = remote_username_len;
+ }
+
+ if ((remote_pwd) && (remote_pwd_len > 0)) {
+ channel->remote_pwd = (unsigned char *)TLS_MALLOC(remote_pwd_len + 1);
+ if (!channel->remote_pwd)
+ return TLS_NO_MEMORY;
+
+ memcpy(channel->remote_pwd, remote_pwd, remote_pwd_len);
+ channel->remote_pwd[remote_pwd_len] = 0;
+ channel->remote_pwd_len = remote_pwd_len;
+ }
+
+ if ((remote_fingerprint) && (remote_fingerprint_len > 0)) {
+ if (channel->context->dtls_data->remote_fingerprint)
+ TLS_FREE(channel->context->dtls_data->remote_fingerprint);
+
+ channel->context->dtls_data->remote_fingerprint = (char *)TLS_MALLOC(remote_fingerprint_len + 1);
+ if (!channel->context->dtls_data->remote_fingerprint)
+ return TLS_NO_MEMORY;
+
+ memcpy(channel->context->dtls_data->remote_fingerprint, remote_fingerprint, remote_fingerprint_len);
+ channel->context->dtls_data->remote_fingerprint[remote_fingerprint_len] = 0;
+ }
+
+ return 0;
+}
+
+const char *tls_peerconnection_local_pwd(struct TLSRTCPeerConnection *channel) {
+ if (!channel)
+ return NULL;
+
+ return channel->local_pwd;
+}
+
+const char *tls_peerconnection_local_username(struct TLSRTCPeerConnection *channel) {
+ if (!channel)
+ return NULL;
+
+ return channel->local_user;
+}
+
+void *tls_peerconnection_userdata(struct TLSRTCPeerConnection *channel) {
+ if (!channel)
+ return NULL;
+
+ return channel->userdata;
+}
+
+int tls_peerconnection_load_keys(struct TLSRTCPeerConnection *channel, const unsigned char *pem_pub_key, int pem_pub_key_size, const unsigned char *pem_priv_key, int pem_priv_key_size) {
+ if (!channel->context) {
+ channel->context = tls_create_context(!channel->active, DTLS_V12);
+ tls_srtp_set(channel->context);
+ tls_add_alpn(channel->context, "webrtc");
+
+ if (channel->context->is_server)
+ channel->context->request_client_certificate = 1;
+ }
+
+ if (tls_load_certificates(channel->context, pem_pub_key, pem_pub_key_size) < 0)
+ return TLS_UNSUPPORTED_CERTIFICATE;
+
+ if (tls_load_private_key(channel->context, pem_priv_key, pem_priv_key_size) < 0)
+ return TLS_UNSUPPORTED_CERTIFICATE;
+
+ return 0;
+}
+
+int _private_tls_peerconnection_buffer_add(struct TLSRTCPeerBuffer **use_buffer, const unsigned char *buf, int len) {
+ if ((!use_buffer) || (!buf) || (!len))
+ return TLS_GENERIC_ERROR;
+
+ struct TLSRTCPeerBuffer *buffer = (struct TLSRTCPeerBuffer *)TLS_MALLOC(sizeof(struct TLSRTCPeerBuffer));
+ if (!buffer)
+ return TLS_NO_MEMORY;
+
+ buffer->buf = (unsigned char *)TLS_MALLOC(len);
+ if (!buffer->buf) {
+ TLS_FREE(buffer);
+ return TLS_NO_MEMORY;
+ }
+
+ memcpy(buffer->buf, buf, len);
+ buffer->len = len;
+ buffer->next = NULL;
+
+ if (*use_buffer) {
+ struct TLSRTCPeerBuffer *next = *use_buffer;
+ while ((next) && (next->next))
+ next = (struct TLSRTCPeerBuffer *)next->next;
+ next->next = buffer;
+ } else
+ *use_buffer = buffer;
+
+ return len;
+}
+
+int tls_peerconnection_get_write_msg(struct TLSRTCPeerConnection *channel, unsigned char *buf) {
+ if ((!channel) || (!channel->write_buffer))
+ return 0;
+
+ struct TLSRTCPeerBuffer *buffer = channel->write_buffer;
+
+ int len = buffer->len;
+
+ if (!buf)
+ return len;
+
+ channel->write_buffer = (struct TLSRTCPeerBuffer *)buffer->next;
+
+ memcpy(buf, buffer->buf, buffer->len);
+
+ if (buffer->buf)
+ TLS_FREE(buffer->buf);
+ TLS_FREE(buffer);
+
+ return len;
+}
+
+int tls_peerconnection_get_read_msg(struct TLSRTCPeerConnection *channel, unsigned char *buf) {
+ if ((!channel) || (!channel->read_buffer))
+ return 0;
+
+ struct TLSRTCPeerBuffer *buffer = channel->read_buffer;
+
+ int len = buffer->len;
+
+ if (!buf)
+ return len;
+
+ channel->read_buffer = (struct TLSRTCPeerBuffer *)buffer->next;
+
+ memcpy(buf, buffer->buf, buffer->len);
+
+ if (buffer->buf)
+ TLS_FREE(buffer->buf);
+ TLS_FREE(buffer);
+
+ return len;
+}
+
+int tls_peerconnection_connect(struct TLSRTCPeerConnection *channel, tls_peerconnection_write_function write_function) {
+ if ((!channel) || (!channel->remote_pwd) || (!channel->remote_user))
+ return TLS_GENERIC_ERROR;
+
+ unsigned char msg[1024];
+ char full_user[1024];
+
+ snprintf(full_user, sizeof(full_user), "%s:%s", channel->remote_user, channel->local_user);
+
+ int len = tls_stun_build(channel->stun_transcation_id, full_user, strlen(full_user), (char *)channel->remote_pwd, channel->remote_pwd_len, msg);
+ if (len < 0)
+ return 0;
+
+ if (!write_function)
+ return _private_tls_peerconnection_buffer_add(&channel->write_buffer, msg, len);
+
+ return write_function(channel, msg, len);
+}
+
+void _private_dtls_ensure_keys(struct TLSRTCPeerConnection *channel) {
+#ifdef TLS_SRTP
+ if ((channel->remote_state != 3) && (tls_established(channel->context) == 1)) {
+ DEBUG_PRINT("******** HAVE REMOTE SRTP KEY ***********\n");
+ channel->remote_state = 3;
+
+ unsigned char key_buffer[(SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_SALT_LEN) * 2];
+ int key_size = tls_srtp_key(channel->context, key_buffer);
+
+ if (key_size > 0) {
+ DEBUG_DUMP_HEX_LABEL("SRTP KEY", key_buffer, key_size);
+
+ channel->srtp_local = srtp_init(SRTP_AES_CM, SRTP_AUTH_HMAC_SHA1);
+ channel->srtp_remote = srtp_init(SRTP_AES_CM, SRTP_AUTH_HMAC_SHA1);
+
+ unsigned char *localkey;
+ unsigned char *remotekey;
+ unsigned char *localsalt;
+ unsigned char *remotesalt;
+
+ if (channel->context->is_server) {
+ remotekey = key_buffer;
+ localkey = key_buffer + SRTP_MASTER_KEY_KEY_LEN;
+ remotesalt = key_buffer + SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_KEY_LEN;
+ localsalt = key_buffer + SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_SALT_LEN;
+ } else {
+ localkey = key_buffer;
+ remotekey = key_buffer + SRTP_MASTER_KEY_KEY_LEN;
+ localsalt = key_buffer + SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_KEY_LEN;
+ remotesalt = key_buffer + SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_SALT_LEN;
+ }
+
+ srtp_key(channel->srtp_local, localkey, 16, localsalt, 14, 80);
+ srtp_key(channel->srtp_remote, remotekey, 16, remotesalt, 14, 80);
+ }
+ }
+#endif
+}
+
+int tls_peerconnection_iterate(struct TLSRTCPeerConnection *channel, unsigned char *buf, int buf_len, unsigned char *addr, int port, unsigned char is_ipv6, tls_peerconnection_write_function write_function, int *validate_addr) {
+ if (validate_addr)
+ *validate_addr = 0;
+
+ if ((!channel) || (!buf) || (buf_len <= 0))
+ return 0;
+
+ int err;
+ struct TLSContext *context = NULL;
+ if (tls_is_stun(buf, buf_len)) {
+ DEBUG_PRINT("RECEIVED STUN PACKET\n");
+ unsigned char response_buffer[0x8000];
+ int len = tls_stun_parse(buf, buf_len, channel->local_pwd, strlen(channel->local_pwd), is_ipv6, addr, port, response_buffer);
+
+ if ((len >= 0) && (validate_addr))
+ *validate_addr = 1;
+
+ unsigned short type = ntohs(*(unsigned short *)buf);
+
+ if ((len <= 0) && (type != 0x0101))
+ return len;
+
+ if (!channel->remote_state) {
+ channel->remote_state = 1;
+
+ if (!channel->local_state)
+ tls_peerconnection_connect(channel, write_function);
+ }
+
+ if ((type == 0x0101) && (channel->remote_state == 1) && (channel->active)) {
+ err = tls_client_connect(channel->context);
+ if (err < 0)
+ return err;
+
+ context = channel->context;
+ }
+
+ if (len > 0) {
+ if (write_function)
+ err = write_function(channel, response_buffer, len);
+ else
+ err = _private_tls_peerconnection_buffer_add(&channel->write_buffer, response_buffer, len);
+
+ if (err <= 0)
+ return err;
+ }
+ context = channel->context;
+ if (context) {
+ unsigned int out_buffer_len = 0;
+ const unsigned char *out_buffer = tls_get_write_buffer(context, &out_buffer_len);
+ if ((out_buffer) && (out_buffer_len)) {
+ if (write_function)
+ err = write_function(channel, out_buffer, out_buffer_len);
+ else
+ err = _private_tls_peerconnection_buffer_add(&channel->write_buffer, out_buffer, out_buffer_len);
+ if (err > 0)
+ tls_buffer_clear(context);
+ }
+ }
+
+ return err;
+ }
+
+ if ((buf[0] >= 20) && (buf[0] <= 64)) {
+ DEBUG_PRINT("RECEIVED DTLS PACKET\n");
+ if (!channel->remote_state) {
+ DEBUG_PRINT("NO STUN ASSOCIATION, IGNORED DTLS PACKET\n");
+ return 0;
+ }
+ if (channel->remote_state == 1)
+ channel->remote_state = 2;
+
+ err = tls_consume_stream(channel->context, buf, buf_len, channel->certificate_verify);
+
+ unsigned int out_buffer_len = 0;
+ const unsigned char *out_buffer = tls_get_write_buffer(channel->context, &out_buffer_len);
+ if ((out_buffer) && (out_buffer_len)) {
+ if (write_function)
+ err = write_function(channel, out_buffer, out_buffer_len);
+ else
+ err = _private_tls_peerconnection_buffer_add(&channel->write_buffer, out_buffer, out_buffer_len);
+ if (err > 0)
+ tls_buffer_clear(channel->context);
+ }
+
+ if (err < 0)
+ return err;
+
+ _private_dtls_ensure_keys(channel);
+
+ return 0;
+ }
+
+ if ((buf[0] >= 128) && (buf[0] <= 191)) {
+ DEBUG_PRINT("RECEIVED RTP PACKET\n");
+#ifdef TLS_SRTP
+ if (channel->srtp_remote) {
+ DEBUG_DUMP_HEX_LABEL("SRTP", buf, buf_len);
+ if (buf_len > 12) {
+ unsigned char out[0x4000];
+ int out_buffer_len = sizeof(out) - 12;
+ int len = srtp_decrypt(channel->srtp_remote, 0, buf, 12, buf + 12, buf_len - 12, out + 12, &out_buffer_len);
+
+ if (len >= 0) {
+ memcpy(out, buf, 12);
+ DEBUG_DUMP_HEX_LABEL("RTP header", out, 12);
+ DEBUG_DUMP_HEX_LABEL("RTP payload", out + 12, out_buffer_len);
+ _private_tls_peerconnection_buffer_add(&channel->read_buffer, out, out_buffer_len + 12);
+ }
+ }
+ } else {
+ DEBUG_PRINT("DTLS-SRTP HANDSHAKE NOT YET ESTABLISHED\n");
+ }
+#endif
+ return 0;
+ }
+ return 0;
+}
+
+int tls_peerconnection_status(struct TLSRTCPeerConnection *channel) {
+ if (!channel)
+ return -1;
+
+ // 0 not connected
+ // 1 stun received
+ // 2 dtls received
+ // 3 srtp ready
+ // 4 closed
+
+ int status = channel->remote_state;
+ if (channel->context->critical_error)
+ status = 4;
+
+ return status;
+}
+
+void tls_destroy_peerconnection(struct TLSRTCPeerConnection *channel) {
+ if (!channel)
+ return;
+
+ if (channel->context)
+ tls_destroy_context(channel->context);
+
+ if (channel->remote_user)
+ TLS_FREE(channel->remote_user);
+ if (channel->remote_pwd)
+ TLS_FREE(channel->remote_pwd);
+
+#ifdef TLS_SRTP
+ if (channel->srtp_local)
+ srtp_destroy(channel->srtp_local);
+ if (channel->srtp_remote)
+ srtp_destroy(channel->srtp_remote);
+#endif
+
+ while (channel->write_buffer) {
+ struct TLSRTCPeerBuffer *next = (struct TLSRTCPeerBuffer *)channel->write_buffer->next;;
+ if (channel->write_buffer->buf)
+ TLS_FREE(channel->write_buffer->buf);
+ TLS_FREE(channel->write_buffer);
+ channel->write_buffer = next;
+ }
+
+ while (channel->read_buffer) {
+ struct TLSRTCPeerBuffer *next = (struct TLSRTCPeerBuffer *)channel->read_buffer->next;;
+ if (channel->read_buffer->buf)
+ TLS_FREE(channel->read_buffer->buf);
+ TLS_FREE(channel->read_buffer);
+ channel->read_buffer = next;
+ }
+
+ TLS_FREE(channel);
+}
+
+
+#ifdef SSL_COMPATIBLE_INTERFACE
+
+int SSL_library_init() {
+ // dummy function
+ return 1;
+}
+
+void SSL_load_error_strings() {
+ // dummy function
+}
+
+void OpenSSL_add_all_algorithms() {
+ // dummy function
+}
+
+void OpenSSL_add_all_ciphers() {
+ // dummy function
+}
+
+void OpenSSL_add_all_digests() {
+ // dummy function
+}
+
+void EVP_cleanup() {
+ // dummy function
+}
+
+int _tls_ssl_private_send_pending(int client_sock, struct TLSContext *context) {
+ unsigned int out_buffer_len = 0;
+ const unsigned char *out_buffer = tls_get_write_buffer(context, &out_buffer_len);
+ unsigned int out_buffer_index = 0;
+ int send_res = 0;
+ SOCKET_SEND_CALLBACK write_cb = NULL;
+ SSLUserData *ssl_data = (SSLUserData *)context->user_data;
+ if (ssl_data)
+ write_cb = (SOCKET_SEND_CALLBACK)ssl_data->send;
+ while ((out_buffer) && (out_buffer_len > 0)) {
+ int res;
+ if (write_cb)
+ res = write_cb(client_sock, (char *)&out_buffer[out_buffer_index], out_buffer_len, 0);
+ else
+ res = send(client_sock, (char *)&out_buffer[out_buffer_index], out_buffer_len, 0);
+ if (res <= 0) {
+ if ((!write_cb) && (res < 0)) {
+#ifdef _WIN32
+ if (WSAGetLastError() == WSAEWOULDBLOCK) {
+ context->tls_buffer_len = out_buffer_len;
+ memmove(context->tls_buffer, out_buffer + out_buffer_index, out_buffer_len);
+ return res;
+ }
+#else
+ if ((errno == EAGAIN) || (errno == EINTR)) {
+ context->tls_buffer_len = out_buffer_len;
+ memmove(context->tls_buffer, out_buffer + out_buffer_index, out_buffer_len);
+ return res;
+ }
+#endif
+ }
+ send_res = res;
+ break;
+ }
+ out_buffer_len -= res;
+ out_buffer_index += res;
+ send_res += res;
+ }
+ tls_buffer_clear(context);
+ return send_res;
+}
+
+struct TLSContext *SSL_new(struct TLSContext *context) {
+ return tls_accept(context);
+}
+
+int SSLv3_server_method() {
+ return 1;
+}
+
+int SSLv3_client_method() {
+ return 0;
+}
+
+int SSL_CTX_use_certificate_file(struct TLSContext *context, const char *filename, int dummy) {
+ // max 64k buffer
+ unsigned char buf[0xFFFF];
+ int size = _private_tls_read_from_file(filename, buf, sizeof(buf));
+ if (size > 0)
+ return tls_load_certificates(context, buf, size);
+ return size;
+}
+
+int SSL_CTX_use_PrivateKey_file(struct TLSContext *context, const char *filename, int dummy) {
+ unsigned char buf[0xFFFF];
+ int size = _private_tls_read_from_file(filename, buf, sizeof(buf));
+ if (size > 0)
+ return tls_load_private_key(context, buf, size);
+
+ return size;
+}
+
+int SSL_CTX_check_private_key(struct TLSContext *context) {
+ if ((!context) || (((!context->private_key) || (!context->private_key->der_bytes) || (!context->private_key->der_len))
+#ifdef TLS_ECDSA_SUPPORTED
+ && ((!context->ec_private_key) || (!context->ec_private_key->der_bytes) || (!context->ec_private_key->der_len))
+#endif
+ ))
+ return 0;
+ return 1;
+}
+
+struct TLSContext *SSL_CTX_new(int method) {
+#ifdef WITH_TLS_13
+ return tls_create_context(method, TLS_V13);
+#else
+ return tls_create_context(method, TLS_V12);
+#endif
+}
+
+void SSL_free(struct TLSContext *context) {
+ if (context) {
+ TLS_FREE(context->user_data);
+ tls_destroy_context(context);
+ }
+}
+
+void SSL_CTX_free(struct TLSContext *context) {
+ SSL_free(context);
+}
+
+int SSL_get_error(struct TLSContext *context, int ret) {
+ if (!context)
+ return TLS_GENERIC_ERROR;
+ return context->critical_error;
+}
+
+int SSL_set_fd(struct TLSContext *context, int socket) {
+ if (!context)
+ return 0;
+ SSLUserData *ssl_data = (SSLUserData *)context->user_data;
+ if (!ssl_data) {
+ ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData));
+ if (!ssl_data)
+ return TLS_NO_MEMORY;
+ memset(ssl_data, 0, sizeof(SSLUserData));
+ context->user_data = ssl_data;
+ }
+ ssl_data->fd = socket;
+ return 1;
+}
+
+void *SSL_set_userdata(struct TLSContext *context, void *data) {
+ if (!context)
+ return NULL;
+ SSLUserData *ssl_data = (SSLUserData *)context->user_data;
+ if (!ssl_data) {
+ ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData));
+ if (!ssl_data)
+ return NULL;
+ memset(ssl_data, 0, sizeof(SSLUserData));
+ context->user_data = ssl_data;
+ }
+ void *old_data = ssl_data->user_data;
+ ssl_data->user_data = data;
+ return old_data;
+}
+
+void *SSL_userdata(struct TLSContext *context) {
+ if (!context)
+ return NULL;
+ SSLUserData *ssl_data = (SSLUserData *)context->user_data;
+ if (!ssl_data)
+ return NULL;
+
+ return ssl_data->user_data;
+}
+
+int SSL_CTX_root_ca(struct TLSContext *context, const char *pem_filename) {
+ if (!context)
+ return TLS_GENERIC_ERROR;
+
+ int count = TLS_GENERIC_ERROR;
+ FILE *f = fopen(pem_filename, "rb");
+ if (f) {
+ fseek(f, 0, SEEK_END);
+ size_t size = (size_t)ftell(f);
+ fseek(f, 0, SEEK_SET);
+ if (size) {
+ unsigned char *buf = (unsigned char *)TLS_MALLOC(size + 1);
+ if (buf) {
+ buf[size] = 1;
+ if (fread(buf, 1, size, f) == size) {
+ count = tls_load_root_certificates(context, buf, size);
+ if (count > 0) {
+ SSLUserData *ssl_data = (SSLUserData *)context->user_data;
+ if (!ssl_data) {
+ ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData));
+ if (!ssl_data) {
+ fclose(f);
+ return TLS_NO_MEMORY;
+ }
+ memset(ssl_data, 0, sizeof(SSLUserData));
+ context->user_data = ssl_data;
+ }
+ if (!ssl_data->certificate_verify)
+ ssl_data->certificate_verify = tls_default_verify;
+ }
+ }
+ TLS_FREE(buf);
+ }
+ }
+ fclose(f);
+ }
+ return count;
+}
+
+void SSL_CTX_set_verify(struct TLSContext *context, int mode, tls_validation_function verify_callback) {
+ if (!context)
+ return;
+ SSLUserData *ssl_data = (SSLUserData *)context->user_data;
+ if (!ssl_data) {
+ ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData));
+ if (!ssl_data)
+ return;
+ memset(ssl_data, 0, sizeof(SSLUserData));
+ context->user_data = ssl_data;
+ }
+ if (mode == SSL_VERIFY_NONE)
+ ssl_data->certificate_verify = NULL;
+ else
+ ssl_data->certificate_verify = verify_callback;
+}
+
+int _private_tls_safe_read(struct TLSContext *context, void *buffer, int buf_size) {
+ SSLUserData *ssl_data = (SSLUserData *)context->user_data;
+ if ((!ssl_data) || (ssl_data->fd < 0))
+ return TLS_GENERIC_ERROR;
+
+ SOCKET_RECV_CALLBACK read_cb = (SOCKET_RECV_CALLBACK)ssl_data->recv;
+ if (read_cb)
+ return read_cb(ssl_data->fd, (char *)buffer, buf_size, 0);
+ return recv(ssl_data->fd, (char *)buffer, buf_size, 0);
+}
+
+int SSL_accept(struct TLSContext *context) {
+ if (!context)
+ return TLS_GENERIC_ERROR;
+ SSLUserData *ssl_data = (SSLUserData *)context->user_data;
+ if ((!ssl_data) || (ssl_data->fd < 0))
+ return TLS_GENERIC_ERROR;
+ if (tls_established(context))
+ return 1;
+ unsigned char client_message[0xFFFF];
+ // accept
+ int read_size = 0;
+ while ((read_size = _private_tls_safe_read(context, (char *)client_message, sizeof(client_message))) > 0) {
+ if (tls_consume_stream(context, client_message, read_size, ssl_data->certificate_verify) >= 0) {
+ int res = _tls_ssl_private_send_pending(ssl_data->fd, context);
+ if (res < 0)
+ return res;
+ }
+ if (tls_established(context))
+ return 1;
+ }
+ if (read_size <= 0)
+ return TLS_BROKEN_CONNECTION;
+ return 0;
+}
+
+int SSL_connect(struct TLSContext *context) {
+ if (!context)
+ return TLS_GENERIC_ERROR;
+ SSLUserData *ssl_data = (SSLUserData *)context->user_data;
+ if ((!ssl_data) || (ssl_data->fd < 0) || (context->critical_error))
+ return TLS_GENERIC_ERROR;
+ int res = tls_client_connect(context);
+ if (res < 0)
+ return res;
+ res = _tls_ssl_private_send_pending(ssl_data->fd, context);
+ if (res < 0)
+ return res;
+
+ int read_size;
+ unsigned char client_message[0xFFFF];
+
+ while ((read_size = _private_tls_safe_read(context, (char *)client_message, sizeof(client_message))) > 0) {
+ if (tls_consume_stream(context, client_message, read_size, ssl_data->certificate_verify) >= 0) {
+ res = _tls_ssl_private_send_pending(ssl_data->fd, context);
+ if (res < 0)
+ return res;
+ }
+ if (tls_established(context))
+ return 1;
+ if (context->critical_error)
+ return TLS_GENERIC_ERROR;
+ }
+ return read_size;
+}
+
+int SSL_shutdown(struct TLSContext *context) {
+ if (!context)
+ return TLS_GENERIC_ERROR;
+ SSLUserData *ssl_data = (SSLUserData *)context->user_data;
+ if ((!ssl_data) || (ssl_data->fd < 0))
+ return TLS_GENERIC_ERROR;
+
+ tls_close_notify(context);
+ return 0;
+}
+
+int SSL_write(struct TLSContext *context, const void *buf, unsigned int len) {
+ if (!context)
+ return TLS_GENERIC_ERROR;
+ SSLUserData *ssl_data = (SSLUserData *)context->user_data;
+ if ((!ssl_data) || (ssl_data->fd < 0))
+ return TLS_GENERIC_ERROR;
+
+ int written_size = tls_write(context, (const unsigned char *)buf, len);
+ if (written_size > 0) {
+ int res = _tls_ssl_private_send_pending(ssl_data->fd, context);
+ if (res <= 0)
+ return res;
+ }
+ return written_size;
+}
+
+int SSL_read(struct TLSContext *context, void *buf, unsigned int len) {
+ if (!context)
+ return TLS_GENERIC_ERROR;
+
+ if (context->application_buffer_len)
+ return tls_read(context, (unsigned char *)buf, len);
+
+ SSLUserData *ssl_data = (SSLUserData *)context->user_data;
+ if ((!ssl_data) || (ssl_data->fd < 0) || (context->critical_error))
+ return TLS_GENERIC_ERROR;
+ if (tls_established(context) != 1)
+ return TLS_GENERIC_ERROR;
+
+ unsigned char client_message[0xFFFF];
+ // accept
+ int read_size;
+ while ((!context->application_buffer_len) && ((read_size = _private_tls_safe_read(context, (char *)client_message, sizeof(client_message))) > 0)) {
+ if (tls_consume_stream(context, client_message, read_size, ssl_data->certificate_verify) > 0)
+ _tls_ssl_private_send_pending(ssl_data->fd, context);
+
+ if ((context->critical_error) && (!context->application_buffer_len))
+ return TLS_GENERIC_ERROR;
+ }
+ if ((read_size <= 0) && (!context->application_buffer_len))
+ return read_size;
+
+ return tls_read(context, (unsigned char *)buf, len);
+}
+
+int SSL_pending(struct TLSContext *context) {
+ if (!context)
+ return TLS_GENERIC_ERROR;
+ return context->application_buffer_len;
+}
+
+int SSL_set_io(struct TLSContext *context, void *recv_cb, void *send_cb) {
+ if (!context)
+ return TLS_GENERIC_ERROR;
+ SSLUserData *ssl_data = (SSLUserData *)context->user_data;
+ if (!ssl_data) {
+ ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData));
+ if (!ssl_data)
+ return TLS_NO_MEMORY;
+ memset(ssl_data, 0, sizeof(SSLUserData));
+ context->user_data = ssl_data;
+ }
+ ssl_data->recv = recv_cb;
+ ssl_data->send = send_cb;
+ return 0;
+}
+#endif // SSL_COMPATIBLE_INTERFACE
+
+
+#ifdef TLS_SRTP
+
+struct SRTPContext {
+ symmetric_CTR aes;
+ unsigned int salt[4];
+ unsigned char mac[TLS_SHA1_MAC_SIZE];
+
+ symmetric_CTR rtcp_aes;
+ unsigned int rtcp_salt[4];
+ unsigned char rtcp_mac[TLS_SHA1_MAC_SIZE];
+
+ unsigned int tag_size;
+ unsigned int roc;
+ unsigned short seq;
+
+ unsigned char mode;
+ unsigned char auth_mode;
+};
+
+struct SRTPContext *srtp_init(unsigned char mode, unsigned char auth_mode) {
+ struct SRTPContext *context = NULL;
+ tls_init();
+ switch (mode) {
+ case SRTP_NULL:
+ break;
+ case SRTP_AES_CM:
+ break;
+ default:
+ return NULL;
+ }
+
+ switch (auth_mode) {
+ case SRTP_AUTH_NULL:
+ break;
+ case SRTP_AUTH_HMAC_SHA1:
+ break;
+ default:
+ return NULL;
+ }
+ context = (struct SRTPContext *)TLS_MALLOC(sizeof(struct SRTPContext));
+ if (context) {
+ memset(context, 0, sizeof(struct SRTPContext));
+ context->mode = mode;
+ context->auth_mode = auth_mode;
+ }
+ return context;
+}
+
+static int _private_tls_srtp_key_derive(const void *key, int keylen, const void *salt, unsigned char label, void *out, int outlen) {
+ unsigned char iv[16];
+ memcpy(iv, salt, 14);
+ iv[14] = iv[15] = 0;
+ void *in = TLS_MALLOC(outlen);
+ if (!in)
+ return TLS_GENERIC_ERROR;
+ memset(in, 0, outlen);
+
+ iv[7] ^= label;
+
+ symmetric_CTR aes;
+
+ if (ctr_start(find_cipher("aes"), iv, (const unsigned char *)key, keylen, 0, CTR_COUNTER_BIG_ENDIAN, &aes))
+ return TLS_GENERIC_ERROR;
+
+ ctr_encrypt((unsigned char *)in, (unsigned char *)out, outlen, &aes);
+ TLS_FREE(in);
+ ctr_done(&aes);
+ return 0;
+}
+
+int srtp_key(struct SRTPContext *context, const void *key, int keylen, const void *salt, int saltlen, int tag_bits) {
+ if (!context)
+ return TLS_GENERIC_ERROR;
+ if (context->mode == SRTP_AES_CM) {
+ if ((saltlen < 14) || (keylen < 16))
+ return TLS_GENERIC_ERROR;
+ // key
+ unsigned char key_buf[16];
+ unsigned char iv[16];
+
+ memset(iv, 0, sizeof(iv));
+
+ if (_private_tls_srtp_key_derive(key, keylen, salt, 0, key_buf, sizeof(key_buf)))
+ return TLS_GENERIC_ERROR;
+
+ DEBUG_DUMP_HEX_LABEL("KEY", key_buf, 16)
+
+ if (_private_tls_srtp_key_derive(key, keylen, salt, 1, context->mac, sizeof(context->mac)))
+ return TLS_GENERIC_ERROR;
+
+ DEBUG_DUMP_HEX_LABEL("AUTH", context->mac, sizeof(context->mac))
+
+ memset(context->salt, 0, sizeof(context->salt));
+ if (_private_tls_srtp_key_derive(key, keylen, salt, 2, context->salt, 14))
+ return TLS_GENERIC_ERROR;
+
+ DEBUG_DUMP_HEX_LABEL("SALT", ((unsigned char *)context->salt), 14)
+
+ if (ctr_start(find_cipher("aes"), iv, key_buf, sizeof(key_buf), 0, CTR_COUNTER_BIG_ENDIAN, &context->aes))
+ return TLS_GENERIC_ERROR;
+
+ if (_private_tls_srtp_key_derive(key, keylen, salt, 3, key_buf, sizeof(key_buf)))
+ return TLS_GENERIC_ERROR;
+
+ DEBUG_DUMP_HEX_LABEL("RTCP KEY", key_buf, 16)
+
+ if (_private_tls_srtp_key_derive(key, keylen, salt, 4, context->rtcp_mac, sizeof(context->rtcp_mac)))
+ return TLS_GENERIC_ERROR;
+
+ DEBUG_DUMP_HEX_LABEL("RTCP AUTH", context->rtcp_mac, sizeof(context->rtcp_mac))
+
+ memset(context->rtcp_salt, 0, sizeof(context->rtcp_salt));
+ if (_private_tls_srtp_key_derive(key, keylen, salt, 5, context->rtcp_salt, 14))
+ return TLS_GENERIC_ERROR;
+
+ DEBUG_DUMP_HEX_LABEL("RTCP SALT", ((unsigned char *)context->rtcp_salt), 14)
+
+ memset(iv, 0, sizeof(iv));
+
+ if (ctr_start(find_cipher("aes"), iv, key_buf, sizeof(key_buf), 0, CTR_COUNTER_BIG_ENDIAN, &context->rtcp_aes))
+ return TLS_GENERIC_ERROR;
+ }
+ if (context->auth_mode)
+ context->tag_size = tag_bits / 8;
+ return 0;
+}
+
+int srtp_inline(struct SRTPContext *context, const char *b64, int tag_bits) {
+ char out_buffer[1024];
+
+ if (!b64)
+ return TLS_GENERIC_ERROR;
+
+ int len = strlen(b64);
+ if (len >= sizeof(out_buffer))
+ len = sizeof(out_buffer);
+ int size = _private_b64_decode(b64, len, (unsigned char *)out_buffer);
+ if (size <= 0)
+ return TLS_GENERIC_ERROR;
+ switch (context->mode) {
+ case SRTP_AES_CM:
+ if (size < 30)
+ return TLS_BROKEN_PACKET;
+ return srtp_key(context, out_buffer, 16, out_buffer + 16, 14, tag_bits);
+ }
+ return TLS_GENERIC_ERROR;
+}
+
+int srtp_encrypt(struct SRTPContext *context, unsigned char rtcp, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len) {
+ if ((!context) || (!out) || (!out_buffer_len) || (*out_buffer_len < payload_len))
+ return TLS_GENERIC_ERROR;
+
+ int out_len = payload_len;
+
+ unsigned short seq = 0;
+ unsigned int roc = context->roc;
+ unsigned int ssrc = 0;
+
+ if ((pt_header) && (pt_len >= 12)) {
+ seq = ntohs(*((unsigned short *)&pt_header[2]));
+ ssrc = ntohl(*((unsigned long *)&pt_header[8]));
+ }
+
+ if (seq < context->seq)
+ roc++;
+
+ unsigned int roc_be = htonl(roc);
+ if (context->mode) {
+ if (*out_buffer_len < out_len)
+ return TLS_NO_MEMORY;
+
+ unsigned int counter[4];
+ counter[0] = context->salt[0];
+ counter[1] = context->salt[1] ^ htonl (ssrc);
+ counter[2] = context->salt[2] ^ roc_be;
+ counter[3] = context->salt[3] ^ htonl (seq << 16);
+ if (rtcp) {
+ ctr_setiv((unsigned char *)&counter, 16, &context->rtcp_aes);
+ if (ctr_encrypt(payload, out, payload_len, &context->rtcp_aes))
+ return TLS_GENERIC_ERROR;
+ } else {
+ ctr_setiv((unsigned char *)&counter, 16, &context->aes);
+ if (ctr_encrypt(payload, out, payload_len, &context->aes))
+ return TLS_GENERIC_ERROR;
+ }
+ } else {
+ memcpy(out, payload, payload_len);
+ }
+
+ *out_buffer_len = out_len;
+
+ if (context->auth_mode == SRTP_AUTH_HMAC_SHA1) {
+ unsigned char digest_out[TLS_SHA1_MAC_SIZE];
+ unsigned long dlen = TLS_SHA1_MAC_SIZE;
+ hmac_state hmac;
+ int err;
+ if (rtcp)
+ err = hmac_init(&hmac, find_hash("sha1"), context->rtcp_mac, 20);
+ else
+ err = hmac_init(&hmac, find_hash("sha1"), context->mac, 20);
+ if (!err) {
+ if (pt_len)
+ err = hmac_process(&hmac, pt_header, pt_len);
+ if (out_len)
+ err = hmac_process(&hmac, out, payload_len);
+ err = hmac_process(&hmac, (unsigned char *)&roc_be, 4);
+ if (!err)
+ err = hmac_done(&hmac, digest_out, &dlen);
+ }
+ if (err)
+ return TLS_GENERIC_ERROR;
+ if (dlen > context->tag_size)
+ dlen = context->tag_size;
+
+ *out_buffer_len += dlen;
+ memcpy(out + out_len, digest_out, dlen);
+ }
+ context->roc = roc;
+ context->seq = seq;
+ return 0;
+}
+
+int srtp_decrypt(struct SRTPContext *context, unsigned char rtcp, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len) {
+ if ((!context) || (!out) || (!out_buffer_len) || (*out_buffer_len < payload_len) || (payload_len < context->tag_size) || (!pt_header) || ((pt_len < 12) && (!rtcp)) || ((pt_len < 8) && (rtcp)))
+ return TLS_GENERIC_ERROR;
+
+ int out_len = payload_len;
+
+ unsigned short seq = ntohs(*((unsigned short *)&pt_header[2]));
+ unsigned int roc = context->roc;
+ unsigned int ssrc = rtcp ? ntohl(*((unsigned long *)&pt_header[4])) : ntohl(*((unsigned long *)&pt_header[8]));
+
+ if (seq < context->seq)
+ roc++;
+
+ unsigned int roc_be = htonl(roc);
+ if (context->mode) {
+ unsigned int counter[4];
+ counter[0] = context->salt[0];
+ counter[1] = context->salt[1] ^ htonl (ssrc);
+ counter[2] = context->salt[2] ^ roc_be;
+ if (rtcp) {
+ uint32_t srtcp_index = ntohl(*(uint32_t *)&payload[payload_len - context->tag_size - 4]) & 0x7FFFFFFF;
+ counter[3] = context->salt[3] ^ htonl (srtcp_index);
+ // ((unsigned cscrhar *)payload)[payload_len - context->tag_size - 4] &= 0x7F;
+ // DEBUG_DUMP_HEX_LABEL("MODIFIED PACKET", payload, payload_len);
+ ctr_setiv((unsigned char *)&counter, 16, &context->rtcp_aes);
+ if (payload_len - context->tag_size - 4 < 0)
+ return TLS_GENERIC_ERROR;
+ if (ctr_decrypt(payload, out, payload_len - context->tag_size - 4, &context->rtcp_aes))
+ return TLS_GENERIC_ERROR;
+ } else {
+ counter[3] = context->salt[3] ^ htonl (seq << 16);
+ ctr_setiv((unsigned char *)&counter, 16, &context->aes);
+ if (ctr_decrypt(payload, out, payload_len - context->tag_size, &context->aes))
+ return TLS_GENERIC_ERROR;
+ }
+
+ if (context->auth_mode == SRTP_AUTH_HMAC_SHA1) {
+ unsigned char digest_out[TLS_SHA1_MAC_SIZE];
+ unsigned long dlen = TLS_SHA1_MAC_SIZE;
+ hmac_state hmac;
+ int err;
+ if (rtcp)
+ err = hmac_init(&hmac, find_hash("sha1"), context->rtcp_mac, sizeof(context->rtcp_mac));
+ else
+ err = hmac_init(&hmac, find_hash("sha1"), context->mac, sizeof(context->mac));
+ if (!err) {
+ if (pt_len)
+ err = hmac_process(&hmac, pt_header, pt_len);
+ if ((payload_len - context->tag_size) > 0)
+ err = hmac_process(&hmac, payload, payload_len - context->tag_size);
+ err = hmac_process(&hmac, (unsigned char *)&roc_be, 4);
+ if (!err)
+ err = hmac_done(&hmac, digest_out, &dlen);
+ }
+ if (err)
+ return TLS_GENERIC_ERROR;
+ if (dlen > context->tag_size)
+ dlen = context->tag_size;
+
+ if (memcmp(digest_out, payload + payload_len - context->tag_size, dlen)) {
+ DEBUG_DUMP_HEX_LABEL("SRTP INTEGRITY FAILED (computed)", digest_out, dlen);
+ DEBUG_DUMP_HEX_LABEL("SRTP INTEGRITY FAILED (expected)", payload + payload_len - context->tag_size, dlen);
+ return TLS_INTEGRITY_FAILED;
+ }
+ }
+ } else {
+ memcpy(out, payload, payload_len - context->tag_size);
+ }
+ context->seq = seq;
+ context->roc = roc;
+ *out_buffer_len = payload_len - context->tag_size;
+ return 0;
+}
+
+void srtp_destroy(struct SRTPContext *context) {
+ if (context) {
+ if (context->mode) {
+ ctr_done(&context->aes);
+ ctr_done(&context->rtcp_aes);
+ }
+
+ TLS_FREE(context);
+ }
+}
+
+struct SRTPContext *tls_peerconnection_srtp_local(struct TLSRTCPeerConnection *channel) {
+ if (!channel)
+ return NULL;
+
+ return channel->srtp_local;
+}
+
+struct SRTPContext *tls_peerconnection_srtp_remote(struct TLSRTCPeerConnection *channel) {
+ if (!channel)
+ return NULL;
+
+ return channel->srtp_remote;
+}
+
+int tls_peerconnection_encrypt(struct TLSRTCPeerConnection *channel, unsigned char rtcp, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len) {
+ return srtp_encrypt(channel->srtp_local, rtcp, pt_header, pt_len, payload, payload_len, out, out_buffer_len);
+}
+
+int tls_peerconnection_decrypt(struct TLSRTCPeerConnection *channel, unsigned char rtcp, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len) {
+ return srtp_decrypt(channel->srtp_remote, rtcp, pt_header, pt_len, payload, payload_len, out, out_buffer_len);
+}
+
+#endif // TLS_SRTP
+
+#endif // TLSE_C
+
+int main(){}
diff --git a/src/tlse/tlse.h b/src/tlse/tlse.h
new file mode 100644
index 0000000..8ceaf03
--- /dev/null
+++ b/src/tlse/tlse.h
@@ -0,0 +1,472 @@
+#ifndef TLSE_H
+#define TLSE_H
+
+// #define DEBUG
+
+// define TLS_LEGACY_SUPPORT to support TLS 1.1/1.0 (legacy)
+// legacy support it will use an additional 272 bytes / context
+#ifndef NO_TLS_LEGACY_SUPPORT
+#define TLS_LEGACY_SUPPORT
+#endif
+// SSL_* style blocking APIs
+#ifndef NO_SSL_COMPATIBLE_INTERFACE
+#define SSL_COMPATIBLE_INTERFACE
+#endif
+// support ChaCha20/Poly1305
+#if !defined(__BIG_ENDIAN__) && ((!defined(__BYTE_ORDER)) || (__BYTE_ORDER == __LITTLE_ENDIAN))
+ // not working on big endian machines
+ #ifndef NO_TLS_WITH_CHACHA20_POLY1305
+ #define TLS_WITH_CHACHA20_POLY1305
+ #endif
+#endif
+#ifndef NO_TLS_13
+#define WITH_TLS_13
+#endif
+// support forward secrecy (Diffie-Hellman ephemeral)
+#ifndef NO_TLS_FORWARD_SECRECY
+#define TLS_FORWARD_SECRECY
+#endif
+// support client-side ECDHE
+#ifndef NO_TLS_CLIENT_ECDHE
+#define TLS_CLIENT_ECDHE
+#endif
+// suport ecdsa
+#ifndef NO_TLS_ECDSA_SUPPORTED
+#define TLS_ECDSA_SUPPORTED
+#endif
+// suport ecdsa client-side
+#define TLS_CLIENT_ECDSA
+// TLS renegotiation is disabled by default (secured or not)
+// do not uncomment next line!
+// #define TLS_ACCEPT_SECURE_RENEGOTIATION
+// basic superficial X509v1 certificate support
+#ifndef NO_TLS_X509_V1_SUPPORT
+#define TLS_X509_V1_SUPPORT
+#endif
+
+// disable TLS_RSA_WITH_* ciphers
+#ifndef NO_TLS_ROBOT_MITIGATION
+#define TLS_ROBOT_MITIGATION
+#endif
+
+#define SSL_V30 0x0300
+#define TLS_V10 0x0301
+#define TLS_V11 0x0302
+#define TLS_V12 0x0303
+#define TLS_V13 0x0304
+#define DTLS_V10 0xFEFF
+#define DTLS_V12 0xFEFD
+#define DTLS_V13 0xFEFC
+
+#define TLS_NEED_MORE_DATA 0
+#define TLS_GENERIC_ERROR -1
+#define TLS_BROKEN_PACKET -2
+#define TLS_NOT_UNDERSTOOD -3
+#define TLS_NOT_SAFE -4
+#define TLS_NO_COMMON_CIPHER -5
+#define TLS_UNEXPECTED_MESSAGE -6
+#define TLS_CLOSE_CONNECTION -7
+#define TLS_COMPRESSION_NOT_SUPPORTED -8
+#define TLS_NO_MEMORY -9
+#define TLS_NOT_VERIFIED -10
+#define TLS_INTEGRITY_FAILED -11
+#define TLS_ERROR_ALERT -12
+#define TLS_BROKEN_CONNECTION -13
+#define TLS_BAD_CERTIFICATE -14
+#define TLS_UNSUPPORTED_CERTIFICATE -15
+#define TLS_NO_RENEGOTIATION -16
+#define TLS_FEATURE_NOT_SUPPORTED -17
+#define TLS_DECRYPTION_FAILED -20
+
+#define TLS_AES_128_GCM_SHA256 0x1301
+#define TLS_AES_256_GCM_SHA384 0x1302
+#define TLS_CHACHA20_POLY1305_SHA256 0x1303
+#define TLS_AES_128_CCM_SHA256 0x1304
+#define TLS_AES_128_CCM_8_SHA256 0x1305
+
+#define TLS_RSA_WITH_AES_128_CBC_SHA 0x002F
+#define TLS_RSA_WITH_AES_256_CBC_SHA 0x0035
+#define TLS_RSA_WITH_AES_128_CBC_SHA256 0x003C
+#define TLS_RSA_WITH_AES_256_CBC_SHA256 0x003D
+#define TLS_RSA_WITH_AES_128_GCM_SHA256 0x009C
+#define TLS_RSA_WITH_AES_256_GCM_SHA384 0x009D
+
+// forward secrecy
+#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x0033
+#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039
+#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067
+#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006B
+#define TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 0x009E
+#define TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 0x009F
+
+#define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA 0xC013
+#define TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA 0xC014
+#define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 0xC027
+#define TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 0xC02F
+#define TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 0xC030
+
+#define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA 0xC009
+#define TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA 0xC00A
+#define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 0xC023
+#define TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 0xC024
+#define TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 0xC02B
+#define TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 0xC02C
+
+#define TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA8
+#define TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA9
+#define TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCAA
+
+#define TLS_FALLBACK_SCSV 0x5600
+
+#define TLS_UNSUPPORTED_ALGORITHM 0x00
+#define TLS_RSA_SIGN_RSA 0x01
+#define TLS_RSA_SIGN_MD5 0x04
+#define TLS_RSA_SIGN_SHA1 0x05
+#define TLS_RSA_SIGN_SHA256 0x0B
+#define TLS_RSA_SIGN_SHA384 0x0C
+#define TLS_RSA_SIGN_SHA512 0x0D
+#define TLS_ECDSA_SIGN_SHA256 0x0E
+
+#define TLS_EC_PUBLIC_KEY 0x11
+#define TLS_EC_prime192v1 0x12
+#define TLS_EC_prime192v2 0x13
+#define TLS_EC_prime192v3 0x14
+#define TLS_EC_prime239v1 0x15
+#define TLS_EC_prime239v2 0x16
+#define TLS_EC_prime239v3 0x17
+#define TLS_EC_prime256v1 0x18
+#define TLS_EC_secp224r1 21
+#define TLS_EC_secp256r1 23
+#define TLS_EC_secp384r1 24
+#define TLS_EC_secp521r1 25
+
+#define TLS_ALERT_WARNING 0x01
+#define TLS_ALERT_CRITICAL 0x02
+
+#ifdef TLS_ROBOT_MITIGATION
+ #define TLS_CIPHERS_SIZE(n, mitigated) n * 2
+#else
+ #define TLS_CIPHERS_SIZE(n, mitigated) (n + mitigated) * 2
+#endif
+
+#define SRTP_AES128_CM_HMAC_SHA1_80 0x0001
+#define SRTP_AES128_CM_HMAC_SHA1_32 0x0002
+#define SRTP_NULL_HMAC_SHA1_80 0x0005
+#define SRTP_NULL_HMAC_SHA1_32 0x0006
+#define SRTP_AEAD_AES_128_GCM 0x0007
+#define SRTP_AEAD_AES_256_GCM 0x0008
+
+#define SRTP_NULL 0
+#define SRTP_AES_CM 1
+#define SRTP_AUTH_NULL 0
+#define SRTP_AUTH_HMAC_SHA1 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ close_notify = 0,
+ unexpected_message = 10,
+ bad_record_mac = 20,
+ decryption_failed_RESERVED = 21,
+ record_overflow = 22,
+ decompression_failure = 30,
+ handshake_failure = 40,
+ no_certificate_RESERVED = 41,
+ bad_certificate = 42,
+ unsupported_certificate = 43,
+ certificate_revoked = 44,
+ certificate_expired = 45,
+ certificate_unknown = 46,
+ illegal_parameter = 47,
+ unknown_ca = 48,
+ access_denied = 49,
+ decode_error = 50,
+ decrypt_error = 51,
+ export_restriction_RESERVED = 60,
+ protocol_version = 70,
+ insufficient_security = 71,
+ internal_error = 80,
+ inappropriate_fallback = 86,
+ user_canceled = 90,
+ no_renegotiation = 100,
+ unsupported_extension = 110,
+ no_error = 255
+} TLSAlertDescription;
+
+// forward declarations
+struct TLSPacket;
+struct TLSCertificate;
+struct TLSContext;
+struct ECCCurveParameters;
+typedef struct TLSContext TLS;
+typedef struct TLSCertificate Certificate;
+// webrtc datachannel
+struct TLSRTCPeerConnection;
+
+typedef int (*tls_validation_function)(struct TLSContext *context, struct TLSCertificate **certificate_chain, int len);
+
+/*
+ Global initialization. Optional, as it will be called automatically;
+ however, the initialization is not thread-safe, so if you intend to use TLSe
+ from multiple threads, you'll need to call tls_init() once, from a single thread,
+ before using the library.
+ */
+void tls_init();
+unsigned char *tls_pem_decode(const unsigned char *data_in, unsigned int input_length, int cert_index, unsigned int *output_len);
+struct TLSCertificate *tls_create_certificate();
+int tls_certificate_valid_subject(struct TLSCertificate *cert, const char *subject);
+int tls_certificate_valid_subject_name(const unsigned char *cert_subject, const char *subject);
+int tls_certificate_is_valid(struct TLSCertificate *cert);
+void tls_certificate_set_copy(unsigned char **member, const unsigned char *val, int len);
+void tls_certificate_set_copy_date(unsigned char **member, const unsigned char *val, int len);
+void tls_certificate_set_key(struct TLSCertificate *cert, const unsigned char *val, int len);
+void tls_certificate_set_priv(struct TLSCertificate *cert, const unsigned char *val, int len);
+void tls_certificate_set_sign_key(struct TLSCertificate *cert, const unsigned char *val, int len);
+char *tls_certificate_to_string(struct TLSCertificate *cert, char *buffer, int len);
+void tls_certificate_set_exponent(struct TLSCertificate *cert, const unsigned char *val, int len);
+void tls_certificate_set_serial(struct TLSCertificate *cert, const unsigned char *val, int len);
+void tls_certificate_set_algorithm(struct TLSContext *context, unsigned int *algorithm, const unsigned char *val, int len);
+void tls_destroy_certificate(struct TLSCertificate *cert);
+struct TLSPacket *tls_create_packet(struct TLSContext *context, unsigned char type, unsigned short version, int payload_size_hint);
+void tls_destroy_packet(struct TLSPacket *packet);
+void tls_packet_update(struct TLSPacket *packet);
+int tls_packet_append(struct TLSPacket *packet, const unsigned char *buf, unsigned int len);
+int tls_packet_uint8(struct TLSPacket *packet, unsigned char i);
+int tls_packet_uint16(struct TLSPacket *packet, unsigned short i);
+int tls_packet_uint32(struct TLSPacket *packet, unsigned int i);
+int tls_packet_uint24(struct TLSPacket *packet, unsigned int i);
+int tls_random(unsigned char *key, int len);
+
+/*
+ Get encrypted data to write, if any. Once you've sent all of it, call
+ tls_buffer_clear().
+ */
+const unsigned char *tls_get_write_buffer(struct TLSContext *context, unsigned int *outlen);
+
+void tls_buffer_clear(struct TLSContext *context);
+
+/* Returns 1 for established, 0 for not established yet, and -1 for a critical error. */
+int tls_established(struct TLSContext *context);
+
+/* Discards any unread decrypted data not consumed by tls_read(). */
+void tls_read_clear(struct TLSContext *context);
+
+/*
+ Reads any unread decrypted data (see tls_consume_stream). If you don't read all of it,
+ the remainder will be left in the internal buffers for next tls_read(). Returns -1 for
+ fatal error, 0 for no more data, or otherwise the number of bytes copied into the buffer
+ (up to a maximum of the given size).
+ */
+int tls_read(struct TLSContext *context, unsigned char *buf, unsigned int size);
+
+struct TLSContext *tls_create_context(unsigned char is_server, unsigned short version);
+const struct ECCCurveParameters *tls_set_curve(struct TLSContext *context, const struct ECCCurveParameters *curve);
+
+/* Create a context for a given client, from a server context. Returns NULL on error. */
+struct TLSContext *tls_accept(struct TLSContext *context);
+
+int tls_set_default_dhe_pg(struct TLSContext *context, const char *p_hex_str, const char *g_hex_str);
+void tls_destroy_context(struct TLSContext *context);
+int tls_cipher_supported(struct TLSContext *context, unsigned short cipher);
+int tls_cipher_is_fs(struct TLSContext *context, unsigned short cipher);
+int tls_choose_cipher(struct TLSContext *context, const unsigned char *buf, int buf_len, int *scsv_set);
+int tls_cipher_is_ephemeral(struct TLSContext *context);
+const char *tls_cipher_name(struct TLSContext *context);
+int tls_is_ecdsa(struct TLSContext *context);
+struct TLSPacket *tls_build_client_key_exchange(struct TLSContext *context);
+struct TLSPacket *tls_build_server_key_exchange(struct TLSContext *context, int method);
+struct TLSPacket *tls_build_hello(struct TLSContext *context, int tls13_downgrade);
+struct TLSPacket *tls_certificate_request(struct TLSContext *context);
+struct TLSPacket *tls_build_verify_request(struct TLSContext *context);
+int tls_parse_hello(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets, unsigned int *dtls_verified);
+int tls_parse_certificate(struct TLSContext *context, const unsigned char *buf, int buf_len, int is_client);
+int tls_parse_server_key_exchange(struct TLSContext *context, const unsigned char *buf, int buf_len);
+int tls_parse_client_key_exchange(struct TLSContext *context, const unsigned char *buf, int buf_len);
+int tls_parse_server_hello_done(struct TLSContext *context, const unsigned char *buf, int buf_len);
+int tls_parse_finished(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets);
+int tls_parse_verify(struct TLSContext *context, const unsigned char *buf, int buf_len);
+int tls_parse_payload(struct TLSContext *context, const unsigned char *buf, int buf_len, tls_validation_function certificate_verify);
+int tls_parse_message(struct TLSContext *context, unsigned char *buf, int buf_len, tls_validation_function certificate_verify);
+int tls_certificate_verify_signature(struct TLSCertificate *cert, struct TLSCertificate *parent);
+int tls_certificate_chain_is_valid(struct TLSCertificate **certificates, int len);
+int tls_certificate_chain_is_valid_root(struct TLSContext *context, struct TLSCertificate **certificates, int len);
+
+/*
+ Add a certificate or a certificate chain to the given context, in PEM form.
+ Returns a negative value (TLS_GENERIC_ERROR etc.) on error, 0 if there were no
+ certificates in the buffer, or the number of loaded certificates on success.
+ */
+int tls_load_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size);
+
+/*
+ Add a private key to the given context, in PEM form. Returns a negative value
+ (TLS_GENERIC_ERROR etc.) on error, 0 if there was no private key in the
+ buffer, or 1 on success.
+ */
+int tls_load_private_key(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size);
+struct TLSPacket *tls_build_certificate(struct TLSContext *context);
+struct TLSPacket *tls_build_finished(struct TLSContext *context);
+struct TLSPacket *tls_build_change_cipher_spec(struct TLSContext *context);
+struct TLSPacket *tls_build_done(struct TLSContext *context);
+struct TLSPacket *tls_build_message(struct TLSContext *context, const unsigned char *data, unsigned int len);
+int tls_client_connect(struct TLSContext *context);
+int tls_write(struct TLSContext *context, const unsigned char *data, unsigned int len);
+struct TLSPacket *tls_build_alert(struct TLSContext *context, char critical, unsigned char code);
+int tls_connection_status(struct TLSContext *context);
+
+/*
+ Process a given number of input bytes from a socket. If the other side just
+ presented a certificate and certificate_verify is not NULL, it will be called.
+
+ Returns 0 if there's no data ready yet, a negative value (see
+ TLS_GENERIC_ERROR etc.) for an error, or a positive value (the number of bytes
+ used from buf) if one or more complete TLS messages were received. The data
+ is copied into an internal buffer even if not all of it was consumed,
+ so you should not re-send it the next time.
+
+ Decrypted data, if any, should be read back with tls_read(). Can change the
+ status of tls_established(). If the library has anything to send back on the
+ socket (e.g. as part of the handshake), tls_get_write_buffer() will return
+ non-NULL.
+ */
+int tls_consume_stream(struct TLSContext *context, const unsigned char *buf, int buf_len, tls_validation_function certificate_verify);
+void tls_close_notify(struct TLSContext *context);
+void tls_alert(struct TLSContext *context, unsigned char critical, int code);
+
+/* Whether tls_consume_stream() has data in its buffer that is not processed yet. */
+int tls_pending(struct TLSContext *context);
+
+/*
+ Set the context as serializable or not. Must be called before negotiation.
+ Exportable contexts use a bit more memory, to be able to hold the keys.
+
+ Note that imported keys are not reexportable unless TLS_REEXPORTABLE is set.
+ */
+void tls_make_exportable(struct TLSContext *context, unsigned char exportable_flag);
+
+int tls_export_context(struct TLSContext *context, unsigned char *buffer, unsigned int buf_len, unsigned char small_version);
+struct TLSContext *tls_import_context(const unsigned char *buffer, unsigned int buf_len);
+int tls_is_broken(struct TLSContext *context);
+int tls_request_client_certificate(struct TLSContext *context);
+int tls_client_verified(struct TLSContext *context);
+const char *tls_sni(struct TLSContext *context);
+int tls_sni_set(struct TLSContext *context, const char *sni);
+int tls_sni_nset(struct TLSContext *context, const char *sni, unsigned int len);
+// set DTLS-SRTP mode for DTLS context
+int tls_srtp_set(struct TLSContext *context);
+int tls_srtp_key(struct TLSContext *context, unsigned char *buffer);
+
+int tls_stun_parse(unsigned char *msg, int len, char *pwd, int pwd_len, unsigned char is_ipv6, unsigned char *addr, unsigned int port, unsigned char *response_buffer);
+int tls_stun_build(unsigned char transaction_id[12], char *username, int username_len, char *pwd, int pwd_len, unsigned char *msg);
+int tls_is_stun(const unsigned char *msg, int len);
+
+typedef int (*tls_peerconnection_write_function)(struct TLSRTCPeerConnection *channel, const unsigned char *msg, int msg_len);
+
+struct TLSRTCPeerConnection *tls_peerconnection_context(unsigned char active, tls_validation_function certificate_verify, void *userdata);
+struct TLSRTCPeerConnection *tls_peerconnection_duplicate(struct TLSRTCPeerConnection *channel, void *userdata);
+struct TLSContext *tls_peerconnection_dtls_context(struct TLSRTCPeerConnection *channel);
+int tls_peerconnection_remote_credentials(struct TLSRTCPeerConnection *channel, char *remote_username, int remote_username_len, char *remote_pwd, int remote_pwd_len, char *remote_fingerprint, int remote_fingerprint_len);
+const char *tls_peerconnection_local_pwd(struct TLSRTCPeerConnection *channel);
+const char *tls_peerconnection_local_username(struct TLSRTCPeerConnection *channel);
+void *tls_peerconnection_userdata(struct TLSRTCPeerConnection *channel);
+int tls_peerconnection_load_keys(struct TLSRTCPeerConnection *channel, const unsigned char *pem_pub_key, int pem_pub_key_size, const unsigned char *pem_priv_key, int pem_priv_key_size);
+int tls_peerconnection_connect(struct TLSRTCPeerConnection *channel, tls_peerconnection_write_function write_function);
+int tls_peerconnection_iterate(struct TLSRTCPeerConnection *channel, unsigned char *buf, int buf_len, unsigned char *addr, int port, unsigned char is_ipv6, tls_peerconnection_write_function write_function, int *validate_addr);
+int tls_peerconnection_get_write_msg(struct TLSRTCPeerConnection *channel, unsigned char *buf);
+int tls_peerconnection_get_read_msg(struct TLSRTCPeerConnection *channel, unsigned char *buf);
+int tls_peerconnection_status(struct TLSRTCPeerConnection *channel);
+void tls_destroy_peerconnection(struct TLSRTCPeerConnection *channel);
+
+int tls_cert_fingerprint(const char *pem_data, int len, char *buffer, unsigned int buf_len);
+int tls_load_root_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size);
+int tls_default_verify(struct TLSContext *context, struct TLSCertificate **certificate_chain, int len);
+void tls_print_certificate(const char *fname);
+int tls_add_alpn(struct TLSContext *context, const char *alpn);
+int tls_alpn_contains(struct TLSContext *context, const char *alpn, unsigned char alpn_size);
+const char *tls_alpn(struct TLSContext *context);
+// useful when renewing certificates for servers, without the need to restart the server
+int tls_clear_certificates(struct TLSContext *context);
+int tls_make_ktls(struct TLSContext *context, int socket);
+int tls_unmake_ktls(struct TLSContext *context, int socket);
+/*
+ Creates a new DTLS random cookie secret to be used in HelloVerifyRequest (server-side).
+ It is recommended to call this function from time to time, to protect against some
+ DoS attacks.
+*/
+void dtls_reset_cookie_secret();
+
+int tls_remote_error(struct TLSContext *context);
+
+#ifdef SSL_COMPATIBLE_INTERFACE
+ #define SSL_SERVER_RSA_CERT 1
+ #define SSL_SERVER_RSA_KEY 2
+ typedef struct TLSContext SSL_CTX;
+ typedef struct TLSContext SSL;
+
+ #define SSL_FILETYPE_PEM 1
+ #define SSL_VERIFY_NONE 0
+ #define SSL_VERIFY_PEER 1
+ #define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 2
+ #define SSL_VERIFY_CLIENT_ONCE 3
+
+ typedef struct {
+ int fd;
+ tls_validation_function certificate_verify;
+ void *recv;
+ void *send;
+ void *user_data;
+ } SSLUserData;
+
+ int SSL_library_init();
+ void SSL_load_error_strings();
+ void OpenSSL_add_all_algorithms();
+ void OpenSSL_add_all_ciphers();
+ void OpenSSL_add_all_digests();
+ void EVP_cleanup();
+
+ int SSLv3_server_method();
+ int SSLv3_client_method();
+ struct TLSContext *SSL_new(struct TLSContext *context);
+ int SSL_CTX_use_certificate_file(struct TLSContext *context, const char *filename, int dummy);
+ int SSL_CTX_use_PrivateKey_file(struct TLSContext *context, const char *filename, int dummy);
+ int SSL_CTX_check_private_key(struct TLSContext *context);
+ struct TLSContext *SSL_CTX_new(int method);
+ void SSL_free(struct TLSContext *context);
+ void SSL_CTX_free(struct TLSContext *context);
+ int SSL_get_error(struct TLSContext *context, int ret);
+ int SSL_set_fd(struct TLSContext *context, int socket);
+ void *SSL_set_userdata(struct TLSContext *context, void *data);
+ void *SSL_userdata(struct TLSContext *context);
+ int SSL_CTX_root_ca(struct TLSContext *context, const char *pem_filename);
+ void SSL_CTX_set_verify(struct TLSContext *context, int mode, tls_validation_function verify_callback);
+ int SSL_accept(struct TLSContext *context);
+ int SSL_connect(struct TLSContext *context);
+ int SSL_shutdown(struct TLSContext *context);
+ int SSL_write(struct TLSContext *context, const void *buf, unsigned int len);
+ int SSL_read(struct TLSContext *context, void *buf, unsigned int len);
+ int SSL_pending(struct TLSContext *context);
+ int SSL_set_io(struct TLSContext *context, void *recv, void *send);
+#endif
+
+#ifdef TLS_SRTP
+ struct SRTPContext;
+ struct SRTPContext *srtp_init(unsigned char mode, unsigned char auth_mode);
+ int srtp_key(struct SRTPContext *context, const void *key, int keylen, const void *salt, int saltlen, int tag_bits);
+ int srtp_inline(struct SRTPContext *context, const char *b64, int tag_bits);
+ int srtp_encrypt(struct SRTPContext *context, unsigned char rtcp, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len);
+ int srtp_decrypt(struct SRTPContext *context, unsigned char rtcp, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len);
+ void srtp_destroy(struct SRTPContext *context);
+
+ struct SRTPContext *tls_peerconnection_srtp_local(struct TLSRTCPeerConnection *channel);
+ struct SRTPContext *tls_peerconnection_srtp_remote(struct TLSRTCPeerConnection *channel);
+ int tls_peerconnection_encrypt(struct TLSRTCPeerConnection *channel, unsigned char rtcp, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len);
+ int tls_peerconnection_decrypt(struct TLSRTCPeerConnection *channel, unsigned char rtcp, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len);
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif