├── .gitignore ├── .travis.yml ├── LICENSE ├── Readme.md ├── native ├── CMakeLists.txt ├── compile-and-install.sh ├── crtecelgamal.c ├── crtecelgamal.h ├── crtecelgamal_jni_wrapper.c ├── ecelgamal.c ├── ecelgamal.h ├── include │ └── uthash.h └── testing.cpp ├── pom.xml └── src ├── main ├── java │ └── ch │ │ └── ethz │ │ └── dsg │ │ └── ecelgamal │ │ └── ECElGamal.java └── resources │ └── META-INF │ └── lib │ ├── linux_64 │ └── libecelgamal-jni-wrapper.so │ └── osx_64 │ └── libecelgamal-jni-wrapper.dylib └── test └── java └── ECElGamalTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | out 4 | target 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | include: 3 | - language: C 4 | script: ./native/compile-and-install.sh && ./native/out/ecelgamal 5 | - language: java 6 | script: mvn package 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Lukas Burkhalter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # EC-ElGamal 2 | 3 | [![Build status](https://travis-ci.org/lubux/ecelgamal.svg?branch=master)](https://travis-ci.org/lubux/ecelgamal) 4 | 5 | This repository contains a C implementation of the additive homomorphic elliptic curve based EL-Gamal cryptographic scheme and a corresponding Java JNI wrapper. The elliptic curve operations of OpenSSL are used for the implementation. 6 | Ciphertexts can be added toghether such that the decrypted result corresponds to the sum of the plaintexts (i.e. p1 + p2 = Dec(Enc(p1) ++ Enc(p2)))) similar to the Paillier-cryptosystem. 7 | 8 | 9 | ## Content 10 | The *native* folder contains the C implementation of the scheme and the *src* folder contains the Java wrapper library. 11 | 12 | ### Prerequisites 13 | Make sure you have the following installed: 14 | * [CMake](https://cmake.org/) 15 | * [OpenSSL](http://www.openssl.org/source/) 16 | * [Maven](https://maven.apache.org/) 17 | * Java JDK 1.8 18 | 19 | ### C Library 20 | The C library contains two versions of EC-Elgamal, a basic version and a Chinese Remainder Thereom (CRT) based optimized version, as introduced by [Pilatus](http://www.vs.inf.ethz.ch/publ/papers/mshafagh_SenSys17_Pilatus.pdf). The library builds with cmake. To compile the library and run the benchmark run: 21 | 22 | ``` 23 | cd native 24 | cmake . 25 | make 26 | ./out/ecelgamal 27 | ``` 28 | 29 | 30 | ### Java Wrapper 31 | The Java library wraps the CRT based EC-ElGamal scheme implemented in C in a Java class with the JNI. The Java wrapper contains a prebuilt version of the library for linux64 and darwin64 (src/main/resources). 32 | To build the jar package, maven is required. The following command builds the library: 33 | 34 | ``` 35 | mvn package 36 | ``` 37 | Here an example on how to use the library. 38 | ```java 39 | ECElGamal.CRTParams params32 = ECElGamal.getDefault32BitParams(); 40 | ECElGamal.ECElGamalKey key32 = ECElGamal.generateNewKey(params32) 41 | ECElGamal.ECElGamalCiphertext cipher1,cipher2; 42 | int val1 = 2, val2 = -3; 43 | cipher1 = ECElGamal.encrypt(BigInteger.valueOf(val1), key32); 44 | cipher2 = ECElGamal.encrypt(BigInteger.valueOf(val2), key32); 45 | cipher1 = ECElGamal.add(cipher1, cipher2); 46 | int decriptedVal = ECElGamal.decrypt32(cipher1, key32); 47 | assertEquals(val1 + val2, decriptedVal); 48 | ``` 49 | 50 | ## Performance 51 | On a 256-bit curve and with an AWS EC2 micro instance in ms. (OpenSSL 1.0.2g) 52 | ``` 53 | Plain EC-ElGamal 32-bit integers 54 | Avg ENC Time 0.238853ms Avg DEC Time 275.811 ms 55 | 56 | CRT optimized EC-ElGamal 32-bit integers 57 | Avg ENC Time 0.61461ms Avg DEC Time 0.339739ms 58 | 59 | CRT optimized EC-ElGamal 64-bit integers 60 | Avg ENC Time 1.00067ms Avg DEC Time 0.560923ms 61 | ``` 62 | 63 | ## Security 64 | This library is for academic purposes, gives no security guarantees and may contain implementation vulnerabilities. 65 | -------------------------------------------------------------------------------- /native/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(ecelgamal) 3 | 4 | set(DO_STATIC FALSE) 5 | set(PATH_STATIC /usr/local/Cellar/openssl@1.1/1.1.0g_1) 6 | 7 | find_package(Java REQUIRED) 8 | find_package(JNI REQUIRED) 9 | 10 | if(${DO_STATIC}) 11 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include ${PATH_STATIC}/include) 12 | set(NEW_SSL_1 ${PATH_STATIC}/lib/libssl.a) 13 | set(NEW_SSL_2 ${PATH_STATIC}/lib/libcrypto.a) 14 | else() 15 | find_package(OpenSSL REQUIRED) 16 | message(${OPENSSL_VERSION}) 17 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include) 18 | set(NEW_SSL_1 crypto) 19 | set(NEW_SSL_2 ssl) 20 | endif() 21 | 22 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/out) 23 | 24 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/out) 25 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/out) 26 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -std=c++11") 27 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") 28 | 29 | add_executable(ecelgamal testing.cpp ecelgamal.h ecelgamal.c crtecelgamal.c crtecelgamal.h) 30 | add_library(ecelgamal-lib SHARED ecelgamal.h ecelgamal.c crtecelgamal.c crtecelgamal.h) 31 | 32 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include ${JNI_INCLUDE_DIRS}) 33 | add_library(ecelgamal-jni-wrapper SHARED crtecelgamal_jni_wrapper.c ecelgamal.h ecelgamal.c) 34 | 35 | 36 | target_link_libraries(ecelgamal ${NEW_SSL_1} ${NEW_SSL_2}) 37 | target_link_libraries(ecelgamal-jni-wrapper ${NEW_SSL_1} ${NEW_SSL_2}) 38 | target_link_libraries(ecelgamal-lib ${NEW_SSL_1} ${NEW_SSL_2}) 39 | -------------------------------------------------------------------------------- /native/compile-and-install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | LOCAL_PATH=$(cd -P -- "$(dirname -- "$0")" && pwd -P) 4 | CUR_PATH=$(pwd) 5 | 6 | cd $LOCAL_PATH 7 | 8 | set -e 9 | cmake . 10 | make 11 | 12 | FOLDER="" 13 | 14 | if [[ "$OSTYPE" == "linux-gnu" ]]; then 15 | FOLDER="linux_64" 16 | elif [[ "$OSTYPE" == "darwin"* ]]; then 17 | FOLDER="osx_64" 18 | elif [[ "$OSTYPE" == "cygwin" ]]; then 19 | FOLDER="windows_64" 20 | elif [[ "$OSTYPE" == "msys" ]]; then 21 | FOLDER="windows_64" 22 | elif [[ "$OSTYPE" == "freebsd"* ]]; then 23 | FOLDER="linux_64" 24 | else 25 | exit 1 26 | fi 27 | 28 | echo "OS detected $FOLDER" 29 | 30 | cp out/* ../src/main/resources/META-INF/lib/$FOLDER 31 | 32 | cd $CUR_PATH -------------------------------------------------------------------------------- /native/crtecelgamal.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Author: 6 | * Lukas Burkhalter 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived 19 | * from this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | #include "crtecelgamal.h" 36 | 37 | 38 | uint64_t small_integer_primes[3] = {1429L, 1931L, 1733L}; 39 | 40 | //uint64_t big_integer_primes[5] = {14347L, 14303L, 15761L, 11257L, 16267L}; 41 | uint64_t big_integer_primes[5] = {6607L, 8011L, 8171L, 8017L, 8111L}; 42 | 43 | int crt_params_generate(crtgamal_params_t params, int dbits, int num_primes) { 44 | BIGNUM *d; 45 | BIGNUM **ds; 46 | BN_CTX *ctx = BN_CTX_new(); 47 | int ok; 48 | ds = (BIGNUM **) malloc(num_primes * sizeof(BIGNUM *)); 49 | d = BN_new(); 50 | BN_set_word(d, 1); 51 | 52 | while (dbits * num_primes > BN_num_bits(d)) { 53 | for (int index=0; index< num_primes; index++) { 54 | ds[index] = BN_new(); 55 | do { 56 | ok = 1; 57 | BN_generate_prime_ex(ds[index], dbits, 0, NULL, NULL, NULL); 58 | for (int i=0; inumsplits = num_primes; 68 | params->prime = d; 69 | params->splits = ds; 70 | BN_CTX_free(ctx); 71 | return 0; 72 | } 73 | 74 | int create_params_from_default(crtgamal_params_t params, uint64_t *primes, int num_primes) { 75 | BIGNUM *d; 76 | BIGNUM **ds; 77 | BN_CTX *ctx = BN_CTX_new(); 78 | 79 | ds = (BIGNUM **) malloc(num_primes * sizeof(BIGNUM *)); 80 | d = BN_new(); 81 | BN_set_word(d, 1); 82 | for (int iter=0; iternumsplits = num_primes; 88 | params->prime = d; 89 | params->splits = ds; 90 | BN_CTX_free(ctx); 91 | return 0; 92 | } 93 | 94 | int crt_params_create_default(crtgamal_params_t params, char default_params_id) { 95 | 96 | if (default_params_id == DEFAULT_32_INTEGER_PARAMS) { 97 | create_params_from_default(params, small_integer_primes, 3); 98 | } else if (default_params_id == DEFAULT_64_INTEGER_PARAMS){ 99 | create_params_from_default(params, big_integer_primes, 5); 100 | } else { 101 | return -1; 102 | } 103 | return 0; 104 | } 105 | 106 | int crt_params_free(crtgamal_params_t params) { 107 | BN_free(params->prime); 108 | for (int iter=0; iter < params->numsplits; iter++) { 109 | BN_free(params->splits[iter]); 110 | } 111 | return 0; 112 | } 113 | 114 | int solve_crt(BIGNUM *res, int count, BIGNUM **numbers, BIGNUM **primes, BIGNUM *prime) { 115 | BN_CTX *ctx = BN_CTX_new(); 116 | BIGNUM *temp1, *temp2; 117 | 118 | BN_zero(res); 119 | temp1 = BN_new(); 120 | temp2 = BN_new(); 121 | 122 | for (int iter = 0; iterkey); 139 | crt_params_generate(keys->params, dbits, num_primes); 140 | return 0; 141 | } 142 | 143 | int crtgamal_generate_keys(crtgamal_key_t keys, crtgamal_params_t params) { 144 | gamal_generate_keys(keys->key); 145 | keys->params->splits = params->splits; 146 | keys->params->numsplits = params->numsplits; 147 | keys->params->prime = params->prime; 148 | return 0; 149 | } 150 | 151 | int crtgamal_keys_clear(crtgamal_key_t keys) { 152 | gamal_key_clear(keys->key); 153 | return 0; 154 | } 155 | 156 | int crtgamal_encrypt(crtgamal_ciphertext_t ciphertext, crtgamal_key_t key, dig_t plaintext) { 157 | int num_Splits = key->params->numsplits; 158 | BIGNUM *t1, *plain_bn; 159 | BN_CTX *ctx = BN_CTX_new(); 160 | t1 = BN_new(); 161 | plain_bn = BN_new(); 162 | BN_set_word(plain_bn, plaintext); 163 | 164 | crtgamal_ciphertext_new(ciphertext, num_Splits); 165 | for (int iter=0; iter < num_Splits; iter++) { 166 | BN_mod(t1, plain_bn, key->params->splits[iter], ctx); 167 | gamal_encrypt(ciphertext->ciphertexts[iter], key->key, BN_get_word(t1)); 168 | } 169 | 170 | BN_free(t1); 171 | BN_free(plain_bn); 172 | BN_CTX_free(ctx); 173 | return 0; 174 | } 175 | 176 | int crtgamal_decrypt(dig_t *res, crtgamal_key_t key, crtgamal_ciphertext_t ciphertext, bsgs_table_t table) { 177 | int num_Splits = key->params->numsplits; 178 | BIGNUM **plaintexts, *result; 179 | 180 | if (ciphertext->num_ciphertexts != key->params->numsplits) { 181 | return -1; 182 | } 183 | 184 | result = BN_new(); 185 | dig_t temp_res; 186 | plaintexts = malloc(sizeof(BIGNUM*) * num_Splits); 187 | 188 | for (int iter=0; iter < num_Splits; iter++) { 189 | gamal_decrypt(&temp_res, key->key, ciphertext->ciphertexts[iter], table); 190 | plaintexts[iter] = BN_new(); 191 | BN_set_word(plaintexts[iter], temp_res); 192 | } 193 | 194 | solve_crt(result, num_Splits, plaintexts, key->params->splits, key->params->prime); 195 | *res = BN_get_word(result); 196 | 197 | for (int iter=0; iter < num_Splits; iter++) { 198 | BN_free(plaintexts[iter]); 199 | } 200 | free(plaintexts); 201 | BN_free(result); 202 | return 0; 203 | } 204 | 205 | int crtgamal_add(crtgamal_ciphertext_t res, crtgamal_ciphertext_t ciphertext1, crtgamal_ciphertext_t ciphertext2) { 206 | int num_Splits = ciphertext1->num_ciphertexts; 207 | for (int iter=0; iter < num_Splits; iter++) { 208 | gamal_add(res->ciphertexts[iter], ciphertext1->ciphertexts[iter], ciphertext2->ciphertexts[iter]); 209 | } 210 | return 0; 211 | } 212 | 213 | int crtgamal_ciphertext_new(crtgamal_ciphertext_t ciphertext, int num_partitions) { 214 | ciphertext->num_ciphertexts = num_partitions; 215 | ciphertext->ciphertexts = malloc(sizeof(gamal_ciphertext_t) * num_partitions); 216 | return 0; 217 | } 218 | 219 | int crtgamal_ciphertext_free(crtgamal_ciphertext_t ciphertext) { 220 | for (int iter=0; iter < ciphertext->num_ciphertexts; iter++) { 221 | gamal_cipher_clear(ciphertext->ciphertexts[iter]); 222 | } 223 | free(ciphertext->ciphertexts); 224 | return 0; 225 | } 226 | 227 | int crtgamal_init(int curve_id) { 228 | gamal_init(curve_id); 229 | return 0; 230 | } 231 | 232 | int crtgamal_deinit() { 233 | gamal_deinit(); 234 | return 0; 235 | } 236 | 237 | 238 | void write_size_local(unsigned char *buffer, int size) { 239 | buffer[0] = (unsigned char) (size & 0xFF); 240 | } 241 | 242 | int read_size_local(unsigned char *buffer) { 243 | return (int) buffer[0]; 244 | } 245 | 246 | size_t crt_get_encoded_ciphertext_size(crtgamal_ciphertext_t ciphertext) { 247 | return (size_t) (gamal_get_point_compressed_size() * 2 * ciphertext->num_ciphertexts) + 1; 248 | } 249 | 250 | int crt_encode_ciphertext(unsigned char* buff, int size, crtgamal_ciphertext_t ciphertext) { 251 | EC_GROUP *group = gamal_get_current_group(); 252 | int len_point, len_temp; 253 | BN_CTX *ctx = BN_CTX_new(); 254 | unsigned char *cur_ptr = buff; 255 | 256 | if (ciphertext->num_ciphertexts < 1) 257 | return -1; 258 | 259 | len_point = gamal_get_point_compressed_size(); 260 | 261 | if (size < ((len_point * 2 * ciphertext->num_ciphertexts) + 1)) 262 | return -1; 263 | 264 | write_size_local(cur_ptr, ciphertext->num_ciphertexts); 265 | cur_ptr++; 266 | 267 | for (int iter=0; iter < ciphertext->num_ciphertexts; iter++) { 268 | len_temp = (int) EC_POINT_point2oct(group, ciphertext->ciphertexts[iter]->C1, 269 | POINT_CONVERSION_COMPRESSED, cur_ptr, (size_t) len_point, ctx); 270 | 271 | if (len_temp != len_point) 272 | return -1; 273 | cur_ptr += len_point; 274 | len_temp = (int) EC_POINT_point2oct(group, ciphertext->ciphertexts[iter]->C2, 275 | POINT_CONVERSION_COMPRESSED, cur_ptr, (size_t) len_point, ctx); 276 | 277 | if (len_temp != len_point) 278 | return -1; 279 | cur_ptr += len_point; 280 | } 281 | BN_CTX_free(ctx); 282 | return 0; 283 | } 284 | 285 | 286 | int crt_decode_ciphertext(crtgamal_ciphertext_t ciphertext, unsigned char* buff, int size) { 287 | BN_CTX *ctx = BN_CTX_new(); 288 | unsigned char *cur_ptr = buff; 289 | EC_GROUP *group = gamal_get_current_group(); 290 | int len_point, num_partitions; 291 | 292 | if (size < 1) 293 | return -1; 294 | 295 | len_point = gamal_get_point_compressed_size(); 296 | num_partitions = read_size_local(cur_ptr); 297 | cur_ptr++; 298 | 299 | if (size < ((len_point * 2 * num_partitions) + 1)) 300 | return -1; 301 | 302 | crtgamal_ciphertext_new(ciphertext, num_partitions); 303 | 304 | for (int iter=0; iter < ciphertext->num_ciphertexts; iter++) { 305 | gamal_cipher_new(ciphertext->ciphertexts[iter]); 306 | 307 | EC_POINT_oct2point(group, ciphertext->ciphertexts[iter]->C1, cur_ptr, (size_t) len_point, ctx); 308 | cur_ptr += len_point; 309 | 310 | EC_POINT_oct2point(group, ciphertext->ciphertexts[iter]->C2, cur_ptr, (size_t) len_point, ctx); 311 | cur_ptr += len_point; 312 | } 313 | BN_CTX_free(ctx); 314 | return 0; 315 | } 316 | -------------------------------------------------------------------------------- /native/crtecelgamal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Author: 6 | * Lukas Burkhalter 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived 19 | * from this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | #ifndef ECELGAMAL_CRTECELGAMAL_H 36 | #define ECELGAMAL_CRTECELGAMAL_H 37 | 38 | #include "ecelgamal.h" 39 | 40 | #define DEFAULT_32_INTEGER_PARAMS 1 41 | #define DEFAULT_64_INTEGER_PARAMS 2 42 | 43 | struct crt_params { 44 | int numsplits; 45 | BIGNUM **splits; 46 | BIGNUM *prime; 47 | }; 48 | typedef struct crt_params *crtgamal_params_ptr; 49 | typedef struct crt_params crtgamal_params_t[1]; 50 | 51 | /** 52 | * Generates new CRT-params 53 | * @param params 54 | * @param dbits the number of bits per prime 55 | * @param num_primes the number of CRT-Partitions 56 | * @return 57 | */ 58 | int crt_params_generate(crtgamal_params_t params, int dbits, int num_primes); 59 | 60 | /** 61 | * Initializes the CRT-params with default parameters 62 | * @param params 63 | * @param default_params_id (ex. DEFAULT_32_INTEGER_PARAMS, DEFAULT_64_INTEGER_PARAMS) 64 | * @return 65 | */ 66 | int crt_params_create_default(crtgamal_params_t params, char default_params_id); 67 | int crt_params_free(crtgamal_params_t params); 68 | 69 | struct crtgamal_key { 70 | crtgamal_params_t params; 71 | gamal_key_t key; 72 | }; 73 | typedef struct crtgamal_key *crtgamal_key_ptr; 74 | typedef struct crtgamal_key crtgamal_key_t[1]; 75 | 76 | struct crtgamal_ciphertext { 77 | int num_ciphertexts; 78 | gamal_ciphertext_t *ciphertexts; 79 | }; 80 | typedef struct crtgamal_ciphertext *crtgamal_ciphertext_ptr; 81 | typedef struct crtgamal_ciphertext crtgamal_ciphertext_t[1]; 82 | 83 | size_t crt_get_encoded_ciphertext_size(crtgamal_ciphertext_t ciphertext); 84 | int crt_encode_ciphertext(unsigned char* buff, int size, crtgamal_ciphertext_t ciphertext); 85 | int crt_decode_ciphertext(crtgamal_ciphertext_t ciphertext, unsigned char* buff, int size); 86 | 87 | int crtgamal_ciphertext_new(crtgamal_ciphertext_t ciphertext, int num_partitions); 88 | int crtgamal_ciphertext_free(crtgamal_ciphertext_t ciphertext); 89 | 90 | int crtgamal_generate_keys_with_params(crtgamal_key_t keys, int dbits, int num_primes); 91 | int crtgamal_generate_keys(crtgamal_key_t keys, crtgamal_params_t params); 92 | int crtgamal_keys_clear(crtgamal_key_t keys); 93 | 94 | /** 95 | * Encrypts and Integer with additative homomorphic EC-ELGamal with CRT-optimizations 96 | * @param ciphertext 97 | * @param key 98 | * @param plaintext 99 | * @return 100 | */ 101 | int crtgamal_encrypt(crtgamal_ciphertext_t ciphertext, crtgamal_key_t key, dig_t plaintext); 102 | 103 | /** 104 | * Decrypts a CRT-EC-Elgamal ciphertext 105 | * @param res the rsulting Integer 106 | * @param key 107 | * @param ciphertext 108 | * @param table if NULL bruteforce is used 109 | * @return 110 | */ 111 | int crtgamal_decrypt(dig_t *res, crtgamal_key_t key, crtgamal_ciphertext_t ciphertext, bsgs_table_t table); 112 | 113 | /** 114 | * Adds two CRT-EC-ElGamal ciphertexts and stores the result in res. 115 | * @param res the resulting ciphertext 116 | * @param ciphertext1 117 | * @param ciphertext2 118 | * @return 119 | */ 120 | int crtgamal_add(crtgamal_ciphertext_t res, crtgamal_ciphertext_t ciphertext1, crtgamal_ciphertext_t ciphertext2); 121 | 122 | /** 123 | * Initializes the library with the given elliptic curve 124 | * @param curve_id (ex. DEFAULT_CURVE, CURVE_256_SEC) 125 | * @return 126 | */ 127 | int crtgamal_init(int curve_id); 128 | 129 | /** 130 | * Deinitializes the library and frees memory 131 | * @return 132 | */ 133 | int crtgamal_deinit(); 134 | 135 | #endif //ECELGAMAL_CRTECELGAMAL_H 136 | -------------------------------------------------------------------------------- /native/crtecelgamal_jni_wrapper.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Author: 6 | * Lukas Burkhalter 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived 19 | * from this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | #include 36 | #include "ecelgamal.h" 37 | 38 | 39 | jbyteArray as_byte_array(JNIEnv *env, unsigned char *buf, int len) { 40 | jbyteArray array = (*env)->NewByteArray(env, len); 41 | (*env)->SetByteArrayRegion(env, array, 0, len, (jbyte *) buf); 42 | return array; 43 | } 44 | 45 | unsigned char *as_unsigned_char_array(JNIEnv *env, jbyteArray array, int *len) { 46 | *len = (*env)->GetArrayLength(env, array); 47 | return (unsigned char *) (*env)->GetByteArrayElements(env, array, 0); 48 | } 49 | 50 | void free_jvm_char_array(JNIEnv *env, jbyteArray array, unsigned char * buff) { 51 | (*env)->ReleaseByteArrayElements(env, array, (jbyte *) buff, 0); 52 | } 53 | 54 | 55 | void get_key(JNIEnv *env, gamal_key_t key, jbyteArray array) { 56 | int buff_len; 57 | unsigned char *buffer = as_unsigned_char_array(env, array, &buff_len); 58 | decode_key(key, buffer, buff_len); 59 | free_jvm_char_array(env, array, buffer); 60 | } 61 | 62 | void get_cipher(JNIEnv *env, gamal_ciphertext_t cipher, jbyteArray array) { 63 | int buff_len; 64 | unsigned char *buffer = as_unsigned_char_array(env, array, &buff_len); 65 | decode_ciphertext(cipher, buffer, buff_len); 66 | free_jvm_char_array(env, array, buffer); 67 | } 68 | 69 | bsgs_table_t *table = NULL; 70 | 71 | jint Java_ch_ethz_dsg_ecelgamal_ECElGamal_initEcElGamal(JNIEnv *env, 72 | jobject javaThis, jint curve_id) { 73 | return (jint) gamal_init((int) curve_id); 74 | } 75 | 76 | jint Java_ch_ethz_dsg_ecelgamal_ECElGamal_deinitECElGamal(JNIEnv *env, 77 | jobject javaThis) { 78 | if (table != NULL) { 79 | gamal_free_bsgs_table(*table); 80 | free(table); 81 | table = NULL; 82 | } 83 | return (jint) gamal_deinit(); 84 | } 85 | 86 | jbyteArray Java_ch_ethz_dsg_ecelgamal_ECElGamal_generateKey(JNIEnv *env, jobject javaThis) { 87 | gamal_key_t key; 88 | unsigned char *buffer; 89 | size_t key_size; 90 | jbyteArray res; 91 | 92 | gamal_generate_keys(key); 93 | key_size = get_encoded_key_size(key, 0); 94 | buffer = (unsigned char *) malloc(key_size); 95 | encode_key(buffer, (int) key_size, key, 0); 96 | res = as_byte_array(env, buffer, (int) key_size); 97 | 98 | free(buffer); 99 | gamal_key_clear(key); 100 | return res; 101 | } 102 | 103 | jbyteArray Java_ch_ethz_dsg_ecelgamal_ECElGamal_encrypt(JNIEnv *env, 104 | jobject javaThis, jlong value, 105 | jbyteArray key_oct) { 106 | gamal_key_t key; 107 | gamal_ciphertext_t ciphertext; 108 | unsigned char *buffer; 109 | size_t cipher_size; 110 | jbyteArray res; 111 | 112 | get_key(env, key, key_oct); 113 | gamal_encrypt(ciphertext, key, (dig_t) value); 114 | 115 | cipher_size = get_encoded_ciphertext_size(ciphertext); 116 | buffer = (unsigned char *) malloc(cipher_size); 117 | encode_ciphertext(buffer, (int) cipher_size, ciphertext); 118 | res = as_byte_array(env, buffer, (int) cipher_size); 119 | 120 | free(buffer); 121 | gamal_key_clear(key); 122 | gamal_cipher_clear(ciphertext); 123 | return res; 124 | } 125 | 126 | jlong Java_ch_ethz_dsg_ecelgamal_ECElGamal_decrypt(JNIEnv *env, jobject javaThis, jbyteArray ciphertext_oct, 127 | jbyteArray key_oct, jboolean use_bsgs) { 128 | gamal_key_t key; 129 | gamal_ciphertext_t ciphertext; 130 | dig_t value = 0; 131 | 132 | get_key(env, key, key_oct); 133 | get_cipher(env, ciphertext, ciphertext_oct); 134 | 135 | if (use_bsgs) { 136 | if (table == NULL) { 137 | table = malloc(sizeof(bsgs_table_t)); 138 | gamal_init_bsgs_table(*table, 1L<<16); 139 | } 140 | 141 | if(gamal_decrypt(&value, key, ciphertext, *table)) { 142 | (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/Exception"), "Error on decryption"); 143 | } 144 | } else { 145 | if(gamal_decrypt(&value, key, ciphertext, NULL)) { 146 | (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/Exception"), "Error on decryption"); 147 | } 148 | } 149 | 150 | gamal_key_clear(key); 151 | gamal_cipher_clear(ciphertext); 152 | return (jlong) value; 153 | } 154 | 155 | jbyteArray Java_ch_ethz_dsg_ecelgamal_ECElGamal_homAdd(JNIEnv *env, jobject javaThis, jbyteArray ciphertext_1_oct, 156 | jbyteArray ciphertext_2_oct) { 157 | gamal_ciphertext_t ciphertext1, ciphertext2; 158 | unsigned char *buffer; 159 | size_t cipher_size; 160 | jbyteArray res; 161 | 162 | get_cipher(env, ciphertext1, ciphertext_1_oct); 163 | get_cipher(env, ciphertext2, ciphertext_2_oct); 164 | 165 | gamal_add(ciphertext1, ciphertext1, ciphertext2); 166 | 167 | cipher_size = get_encoded_ciphertext_size(ciphertext1); 168 | buffer = (unsigned char *) malloc((size_t) cipher_size); 169 | encode_ciphertext(buffer, (int) cipher_size, ciphertext1); 170 | res = as_byte_array(env, buffer, (int) cipher_size); 171 | 172 | free(buffer); 173 | gamal_cipher_clear(ciphertext1); 174 | gamal_cipher_clear(ciphertext2); 175 | return res; 176 | } 177 | 178 | jint Java_ch_ethz_dsg_ecelgamal_ECElGamal_initBsgsTable(JNIEnv *env, jobject javaThis, 179 | jint table_size) { 180 | table = malloc(sizeof(bsgs_table_t)); 181 | gamal_init_bsgs_table(*table, (dig_t) table_size); 182 | return 0; 183 | } 184 | 185 | jint Java_ch_ethz_dsg_ecelgamal_ECElGamal_getPointSize(JNIEnv *env, jobject javaThis) { 186 | return gamal_get_point_compressed_size(); 187 | } -------------------------------------------------------------------------------- /native/ecelgamal.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Author: 6 | * Lukas Burkhalter 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived 19 | * from this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | #include "ecelgamal.h" 36 | 37 | #define KEY_UNCOMPRESSED 0 38 | #define KEY_COMPRESSED 1 39 | #define KEY_PUBLIC 2 40 | 41 | 42 | EC_POINT *multiply_constant(const EC_POINT *in, const BIGNUM *n, EC_GROUP *curve_group) { 43 | EC_POINT *res; 44 | BIGNUM *bn1; 45 | BN_CTX *ctx; 46 | 47 | bn1 = BN_new(); 48 | ctx = BN_CTX_new(); 49 | BN_zero(bn1); 50 | res = EC_POINT_new(curve_group); 51 | EC_POINT_mul(curve_group, res, bn1, in, n, ctx); 52 | 53 | BN_free(bn1); 54 | BN_CTX_free(ctx); 55 | return res; 56 | } 57 | 58 | EC_POINT *multiply_generator(const BIGNUM *n, EC_GROUP *curve_group) { 59 | return multiply_constant(EC_GROUP_get0_generator(curve_group), n, curve_group); 60 | } 61 | 62 | 63 | char *point_to_string(EC_GROUP *curve_group, const EC_POINT *point) { 64 | BN_CTX *ctx; 65 | char *s; 66 | point_conversion_form_t form = POINT_CONVERSION_COMPRESSED; 67 | ctx = BN_CTX_new(); 68 | s = EC_POINT_point2hex(curve_group, point, form, ctx); 69 | BN_CTX_free(ctx); 70 | return s; 71 | } 72 | 73 | int add_value_to_table(bsgs_table_t table, EC_POINT *point, uint32_t value) { 74 | unsigned char* point_key; 75 | BN_CTX *ctx = BN_CTX_new(); 76 | size_t point_size = EC_POINT_point2oct(table->group, point, POINT_CONVERSION_COMPRESSED, NULL, 0, ctx); 77 | bsgs_hash_table_entry_t * new_entry = (bsgs_hash_table_entry_t *) malloc(sizeof(bsgs_hash_table_entry_t)); 78 | point_key = (unsigned char*) malloc(point_size); 79 | EC_POINT_point2oct(table->group, point, POINT_CONVERSION_COMPRESSED, point_key, point_size, ctx); 80 | 81 | new_entry->key = point_key; 82 | new_entry->value = value; 83 | HASH_ADD_KEYPTR(hh, table->table, point_key, point_size, new_entry); 84 | BN_CTX_free(ctx); 85 | return 0; 86 | } 87 | 88 | int bsgs_table_init(EC_GROUP *curve_group, bsgs_table_t table, dig_t t_size) { 89 | dig_t count = 0; 90 | BIGNUM *bn_size; 91 | EC_POINT *cur_point; 92 | const EC_POINT *gen; 93 | BN_CTX *ctx = BN_CTX_new(); 94 | 95 | gen = EC_GROUP_get0_generator(curve_group); 96 | table->table = NULL; 97 | table->group = curve_group; 98 | 99 | //set Table metadata 100 | bn_size = BN_new(); 101 | BN_set_word(bn_size, (BN_ULONG) t_size); 102 | table->tablesize = t_size; 103 | table->mG = multiply_constant(gen, bn_size, curve_group); 104 | BN_set_negative(bn_size, 1); 105 | table->mG_inv = multiply_constant(gen, bn_size, curve_group); 106 | BN_free(bn_size); 107 | 108 | 109 | gen = EC_GROUP_get0_generator(curve_group); 110 | cur_point = EC_POINT_new(curve_group); 111 | EC_POINT_set_to_infinity(curve_group, cur_point); 112 | for (; count <= t_size; count++) { 113 | add_value_to_table(table, cur_point, count); 114 | EC_POINT_add(curve_group, cur_point, cur_point, gen, ctx); 115 | } 116 | EC_POINT_free(cur_point); 117 | BN_CTX_free(ctx); 118 | return 0; 119 | } 120 | 121 | size_t bsgs_table_get_size(bsgs_table_t bsgs_table) { 122 | return 0; 123 | } 124 | 125 | int bsgs_table_free(bsgs_table_t bsgs_table) { 126 | bsgs_hash_table_entry_t *tmp, *current; 127 | HASH_ITER(hh, bsgs_table->table, current, tmp) { 128 | HASH_DEL(bsgs_table->table, current); 129 | free(current->key); 130 | free(current); 131 | } 132 | EC_POINT_free(bsgs_table->mG); 133 | EC_POINT_free(bsgs_table->mG_inv); 134 | return 0; 135 | } 136 | 137 | int get_power_from_table(uint64_t *power, bsgs_table_t bsgs_table, EC_POINT *lookup_point) { 138 | unsigned char* point_key; 139 | BN_CTX *ctx = BN_CTX_new(); 140 | size_t point_size = EC_POINT_point2oct(bsgs_table->group, lookup_point, POINT_CONVERSION_COMPRESSED, NULL, 0, ctx); 141 | bsgs_hash_table_entry_t * entry; 142 | point_key = (unsigned char*) malloc(point_size); 143 | EC_POINT_point2oct(bsgs_table->group, lookup_point, POINT_CONVERSION_COMPRESSED, point_key, point_size, ctx); 144 | 145 | HASH_FIND_BIN(bsgs_table->table, point_key, point_size, entry); 146 | BN_CTX_free(ctx); 147 | free(point_key); 148 | 149 | if (entry == NULL) 150 | return -1; 151 | *power = (uint64_t) entry->value; 152 | return 0; 153 | } 154 | 155 | 156 | int solve_ecdlp_bsgs(bsgs_table_t bsgs_table, uint64_t *result, EC_POINT *M, int64_t maxIt) { 157 | uint64_t j = 0, i = 0; 158 | int ok; 159 | EC_GROUP *curve_group = bsgs_table->group; 160 | EC_POINT *curPoint = EC_POINT_dup(M, curve_group); 161 | EC_POINT *curPointNeg = EC_POINT_dup(M, curve_group); 162 | BN_CTX *ctx = BN_CTX_new(); 163 | 164 | while (i <= maxIt) { 165 | ok = get_power_from_table(&j, bsgs_table, curPoint); 166 | if (ok == 0) { 167 | *result = i * bsgs_table->tablesize + j; 168 | break; 169 | } 170 | EC_POINT_add(curve_group, curPoint, curPoint, bsgs_table->mG_inv, ctx); 171 | i = i + 1; 172 | } 173 | 174 | if (i > maxIt) { 175 | return -1; 176 | } 177 | 178 | EC_POINT_free(curPoint); 179 | EC_POINT_free(curPointNeg); 180 | BN_CTX_free(ctx); 181 | return 0; 182 | } 183 | 184 | // Finds the value x with brute force s.t. M=xG 185 | int solve_dlog_brute(EC_GROUP *curve_group, EC_POINT *M, uint64_t *x, dig_t max_it) { 186 | EC_POINT *cur; 187 | const EC_POINT *G; 188 | uint64_t max, x_local = 1; 189 | BN_CTX *ctx = BN_CTX_new(); 190 | max = (int64_t) max_it; 191 | 192 | cur = EC_POINT_new(curve_group); 193 | G = EC_GROUP_get0_generator(curve_group); 194 | EC_POINT_set_to_infinity(curve_group, cur); 195 | 196 | if (EC_POINT_is_at_infinity(curve_group, M)) { 197 | *x = 0; 198 | return 0; 199 | } else { 200 | for (; x_local < max; (*x) = x_local++) { 201 | EC_POINT_add(curve_group, cur, cur, G, ctx); 202 | if (EC_POINT_cmp(curve_group, cur, M, ctx) == 0) { 203 | break; 204 | } 205 | } 206 | *x = x_local; 207 | } 208 | EC_POINT_free(cur); 209 | BN_CTX_free(ctx); 210 | return 0; 211 | } 212 | 213 | // API IMPLEMENTATION 214 | 215 | //the ec group used 216 | EC_GROUP *init_group = NULL; 217 | 218 | int gamal_init(int curve_id) { 219 | init_group = EC_GROUP_new_by_curve_name(curve_id); 220 | return 0; 221 | } 222 | 223 | int gamal_deinit() { 224 | if (init_group != NULL) { 225 | EC_GROUP_free(init_group); 226 | init_group = NULL; 227 | } 228 | return 0; 229 | } 230 | 231 | 232 | int gamal_init_bsgs_table(bsgs_table_t table, dig_t size) { 233 | return bsgs_table_init(init_group, table, size); 234 | } 235 | 236 | 237 | int gamal_free_bsgs_table(bsgs_table_t table) { 238 | bsgs_table_free(table); 239 | return 0; 240 | } 241 | 242 | int gamal_key_clear(gamal_key_t key) { 243 | EC_POINT_clear_free(key->Y); 244 | if (!key->is_public) { 245 | BN_clear_free(key->secret); 246 | } 247 | return 0; 248 | } 249 | 250 | int gamal_key_to_public(gamal_key_t pub, gamal_key_t priv) { 251 | pub->is_public = 1; 252 | pub->Y = EC_POINT_dup(priv->Y, init_group); 253 | return 0; 254 | } 255 | 256 | int gamal_cipher_clear(gamal_ciphertext_t cipher) { 257 | EC_POINT_clear_free(cipher->C1); 258 | EC_POINT_clear_free(cipher->C2); 259 | return 0; 260 | } 261 | 262 | int gamal_cipher_new(gamal_ciphertext_t cipher) { 263 | cipher->C1 = EC_POINT_new(init_group); 264 | cipher->C2 = EC_POINT_new(init_group); 265 | return 0; 266 | } 267 | 268 | int gamal_generate_keys(gamal_key_t keys) { 269 | BN_CTX *ctx = BN_CTX_new(); 270 | BIGNUM *ord, *key; 271 | 272 | ord = BN_new(); 273 | key = BN_new(); 274 | keys->Y = EC_POINT_new(init_group); 275 | 276 | EC_GROUP_get_order(init_group, ord, ctx); 277 | 278 | BN_rand_range(key, ord); 279 | keys->is_public = 0; 280 | keys->Y = multiply_generator(key, init_group); 281 | keys->secret = key; 282 | BN_free(ord); 283 | BN_CTX_free(ctx); 284 | return 0; 285 | } 286 | 287 | int gamal_encrypt(gamal_ciphertext_t ciphertext, gamal_key_t key, dig_t plaintext) { 288 | BIGNUM *bn_plain, *ord, *rand; 289 | BN_CTX *ctx = BN_CTX_new(); 290 | 291 | bn_plain = BN_new(); 292 | ord = BN_new(); 293 | rand = BN_new(); 294 | ciphertext->C2 = EC_POINT_new(init_group); 295 | 296 | EC_GROUP_get_order(init_group, ord, ctx); 297 | BN_rand_range(rand, ord); 298 | 299 | BN_set_word(bn_plain, plaintext); 300 | 301 | ciphertext->C1 = multiply_generator(rand, init_group); 302 | EC_POINT_mul(init_group, ciphertext->C2, bn_plain, key->Y, rand, ctx); 303 | 304 | BN_clear_free(rand); 305 | BN_free(ord); 306 | BN_free(bn_plain); 307 | BN_CTX_free(ctx); 308 | return 0; 309 | } 310 | 311 | // if table == NULL use bruteforce 312 | int gamal_decrypt(dig_t *res, gamal_key_t key, gamal_ciphertext_t ciphertext, bsgs_table_t table) { 313 | EC_POINT *M; 314 | uint64_t plaintext; 315 | BN_CTX *ctx = BN_CTX_new(); 316 | 317 | 318 | M = multiply_constant(ciphertext->C1, key->secret, init_group); 319 | EC_POINT_invert(init_group, M, ctx); 320 | EC_POINT_add(init_group, M, ciphertext->C2, M, ctx); 321 | 322 | if (table != NULL) { 323 | solve_ecdlp_bsgs(table, &plaintext, M, 1L << MAX_BITS); 324 | } else { 325 | solve_dlog_brute(init_group, M, &plaintext, 1L << MAX_BITS); 326 | } 327 | *res = (dig_t) plaintext; 328 | 329 | BN_CTX_free(ctx); 330 | EC_POINT_clear_free(M); 331 | return 0; 332 | } 333 | 334 | 335 | int gamal_add(gamal_ciphertext_t res, gamal_ciphertext_t ciphertext1, gamal_ciphertext_t ciphertext2) { 336 | BN_CTX *ctx = BN_CTX_new(); 337 | EC_POINT_add(init_group, res->C1, ciphertext1->C1, ciphertext2->C1, ctx); 338 | EC_POINT_add(init_group, res->C2, ciphertext1->C2, ciphertext2->C2, ctx); 339 | BN_CTX_free(ctx); 340 | return 0; 341 | } 342 | 343 | EC_GROUP *gamal_get_current_group() { 344 | return init_group; 345 | } 346 | 347 | int gamal_get_point_compressed_size() { 348 | BN_CTX *ctx = BN_CTX_new(); 349 | int res = (int) EC_POINT_point2oct(init_group, EC_GROUP_get0_generator(init_group), 350 | POINT_CONVERSION_COMPRESSED, NULL, 0, ctx); 351 | BN_CTX_free(ctx); 352 | return res; 353 | } 354 | 355 | 356 | // ENCODING + DECODING 357 | 358 | void write_size(unsigned char *buffer, size_t size) { 359 | buffer[0] = (unsigned char) ((size >> 8) & 0xFF); 360 | buffer[1] = (unsigned char) (size & 0xFF); 361 | } 362 | 363 | size_t read_size(unsigned char *buffer) { 364 | return ((uint8_t) buffer[0] << 8) | ((uint8_t) buffer[1]); 365 | } 366 | 367 | size_t get_encoded_ciphertext_size(gamal_ciphertext_t ciphertext) { 368 | return (size_t) gamal_get_point_compressed_size()*2; 369 | } 370 | 371 | int encode_ciphertext(unsigned char *buff, int size, gamal_ciphertext_t ciphertext) { 372 | unsigned char *cur_ptr = buff; 373 | size_t len_point, tmp; 374 | BN_CTX *ctx = BN_CTX_new(); 375 | len_point = (size_t) gamal_get_point_compressed_size(); 376 | if (size < (len_point * 2)) 377 | return -1; 378 | tmp = EC_POINT_point2oct(init_group, ciphertext->C1, POINT_CONVERSION_COMPRESSED, cur_ptr, len_point, ctx); 379 | cur_ptr += len_point; 380 | if (tmp != len_point) 381 | return -1; 382 | tmp = EC_POINT_point2oct(init_group, ciphertext->C2, POINT_CONVERSION_COMPRESSED, cur_ptr, len_point, ctx); 383 | if (tmp != len_point) 384 | return -1; 385 | BN_CTX_free(ctx); 386 | return 0; 387 | } 388 | 389 | int decode_ciphertext(gamal_ciphertext_t ciphertext, unsigned char *buff, int size) { 390 | size_t len_point; 391 | BN_CTX *ctx = BN_CTX_new(); 392 | unsigned char *cur_ptr = buff; 393 | len_point = (size_t) gamal_get_point_compressed_size(); 394 | if (size < len_point*2) 395 | return -1; 396 | 397 | ciphertext->C1 = EC_POINT_new(init_group); 398 | EC_POINT_oct2point(init_group, ciphertext->C1, cur_ptr, len_point, ctx); 399 | cur_ptr += len_point; 400 | 401 | ciphertext->C2 = EC_POINT_new(init_group); 402 | EC_POINT_oct2point(init_group, ciphertext->C2, cur_ptr, len_point, ctx); 403 | 404 | BN_CTX_free(ctx); 405 | return 0; 406 | } 407 | 408 | size_t get_encoded_key_size(gamal_key_t key, int compressed) { 409 | size_t size = 1; 410 | BN_CTX *ctx = BN_CTX_new(); 411 | if(!key->is_public) { 412 | if (compressed) 413 | size += BN_num_bytes(key->secret); 414 | else 415 | size += BN_num_bytes(key->secret) + 416 | EC_POINT_point2oct(init_group, key->Y, POINT_CONVERSION_COMPRESSED, NULL, 0, ctx); 417 | } else { 418 | size += EC_POINT_point2oct(init_group, key->Y, POINT_CONVERSION_COMPRESSED, NULL, 0, ctx); 419 | } 420 | BN_CTX_free(ctx); 421 | return size; 422 | } 423 | int encode_key(unsigned char *buff, int size, gamal_key_t key, int compressed) { 424 | size_t len_point; 425 | unsigned char *cur_ptr = buff; 426 | size_t size_data; 427 | BN_CTX *ctx = BN_CTX_new(); 428 | 429 | len_point = (size_t) gamal_get_point_compressed_size(); 430 | 431 | if (size < 3) 432 | return -1; 433 | 434 | if (key->is_public) { 435 | buff[0] = KEY_PUBLIC; 436 | } else { 437 | if (compressed) 438 | buff[0] = KEY_COMPRESSED; 439 | else 440 | buff[0] = KEY_UNCOMPRESSED; 441 | 442 | } 443 | 444 | cur_ptr++; 445 | if (key->is_public) { 446 | size_data = len_point; 447 | } else { 448 | if (compressed) 449 | size_data = (size_t) BN_num_bytes(key->secret); 450 | else 451 | size_data = (size_t) BN_num_bytes(key->secret) + len_point; 452 | 453 | } 454 | 455 | if (size < 1 + size_data) 456 | return -1; 457 | 458 | if (key->is_public) { 459 | EC_POINT_point2oct(init_group, key->Y, POINT_CONVERSION_COMPRESSED, cur_ptr, size_data, ctx); 460 | } else { 461 | if (compressed) { 462 | BN_bn2bin(key->secret, cur_ptr); 463 | } else { 464 | EC_POINT_point2oct(init_group, key->Y, POINT_CONVERSION_COMPRESSED, cur_ptr, len_point, ctx); 465 | cur_ptr += len_point; 466 | BN_bn2bin(key->secret, cur_ptr); 467 | } 468 | } 469 | BN_CTX_free(ctx); 470 | return 0; 471 | } 472 | int decode_key(gamal_key_t key, unsigned char* buff, int size) { 473 | size_t len_point; 474 | char is_pub; 475 | int is_compressed = 0, decode_id = 0; 476 | unsigned char *cur_ptr = buff; 477 | size_t size_data; 478 | BN_CTX *ctx = BN_CTX_new(); 479 | 480 | len_point = (size_t) gamal_get_point_compressed_size(); 481 | 482 | if (size < 3) 483 | return -1; 484 | 485 | decode_id = (int) buff[0]; 486 | 487 | if (decode_id == KEY_COMPRESSED) 488 | is_compressed = 1; 489 | 490 | if (decode_id == KEY_PUBLIC) 491 | is_pub = 1; 492 | else 493 | is_pub = 0; 494 | 495 | key->secret = BN_new(); 496 | cur_ptr++; 497 | key->is_public = is_pub; 498 | if (key->is_public) { 499 | size_data = len_point; 500 | } else { 501 | size_data = (size_t) size - 1; 502 | } 503 | 504 | if (size < 1 + size_data) 505 | return -1; 506 | if (is_pub) { 507 | key->Y = EC_POINT_new(init_group); 508 | EC_POINT_oct2point(init_group, key->Y, cur_ptr, size_data, ctx); 509 | } else { 510 | if (is_compressed) { 511 | BN_bin2bn(cur_ptr, (int) size_data, key->secret); 512 | key->Y = multiply_generator(key->secret, init_group); 513 | } else { 514 | key->Y = EC_POINT_new(init_group); 515 | EC_POINT_oct2point(init_group, key->Y, cur_ptr, len_point, ctx); 516 | cur_ptr += len_point; 517 | BN_bin2bn(cur_ptr, (int) size_data - (int) len_point, key->secret); 518 | } 519 | } 520 | BN_CTX_free(ctx); 521 | return 0; 522 | } 523 | 524 | 525 | 526 | -------------------------------------------------------------------------------- /native/ecelgamal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Author: 6 | * Lukas Burkhalter 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived 19 | * from this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | #ifndef ECELGAMAL_ECELGAMAL_H 36 | #define ECELGAMAL_ECELGAMAL_H 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include "uthash.h" 43 | 44 | #define DEFAULT_CURVE NID_X9_62_prime192v1 45 | #define CURVE_256_SEC NID_X9_62_prime256v1 46 | 47 | #define MAX_BITS 32 48 | 49 | struct gamal_key { 50 | char is_public; 51 | EC_POINT *Y; 52 | BIGNUM *secret; 53 | }; 54 | 55 | typedef struct gamal_key *gamal_key_ptr; 56 | typedef struct gamal_key gamal_key_t[1]; 57 | typedef uint64_t dig_t; 58 | 59 | size_t get_encoded_key_size(gamal_key_t key, int compressed); 60 | int encode_key(unsigned char* buff, int size, gamal_key_t key, int compressed); 61 | int decode_key(gamal_key_t key, unsigned char* buff, int size); 62 | 63 | struct gamal_ciphertext { 64 | EC_POINT *C1; 65 | EC_POINT *C2; 66 | }; 67 | 68 | typedef struct gamal_ciphertext *gamal_ciphertext_ptr; 69 | typedef struct gamal_ciphertext gamal_ciphertext_t[1]; 70 | 71 | size_t get_encoded_ciphertext_size(gamal_ciphertext_t ciphertext); 72 | int encode_ciphertext(unsigned char* buff, int size, gamal_ciphertext_t ciphertext); 73 | int decode_ciphertext(gamal_ciphertext_t ciphertext, unsigned char* buff, int size); 74 | 75 | typedef struct bsgs_hash_table_entry { 76 | unsigned char *key; 77 | uint32_t value; 78 | UT_hash_handle hh; 79 | } bsgs_hash_table_entry_t; 80 | 81 | struct bsgs_table_s { 82 | bsgs_hash_table_entry_t *table; 83 | EC_POINT *mG, *mG_inv; 84 | EC_GROUP *group; 85 | dig_t tablesize; 86 | }; 87 | typedef struct bsgs_table_s *bsgs_table_ptr; 88 | typedef struct bsgs_table_s bsgs_table_t[1]; 89 | 90 | /** 91 | * Inits the library with the given curve 92 | */ 93 | int gamal_init(int curve_id); 94 | 95 | /** 96 | * Deinits the library 97 | * @return 98 | */ 99 | int gamal_deinit(); 100 | 101 | /** 102 | * Returns the EC_Group (elliptic curve group) struct if initialized 103 | */ 104 | EC_GROUP *gamal_get_current_group(); 105 | 106 | /** 107 | * Returns the encded size of an EC-Point in this group. 108 | */ 109 | int gamal_get_point_compressed_size(); 110 | 111 | /** 112 | * Inititlaizes the baby-step-giant-step table. 113 | * @param the table 114 | * @param the number of elemnts to store in the table 115 | * @return 116 | */ 117 | int gamal_init_bsgs_table(bsgs_table_t table, dig_t size); 118 | 119 | /** 120 | * Frees the memory of the table 121 | * @param table 122 | * @return 123 | */ 124 | int gamal_free_bsgs_table(bsgs_table_t table); 125 | 126 | int gamal_key_clear(gamal_key_t keys); 127 | int gamal_key_to_public(gamal_key_t pub, gamal_key_t priv); 128 | int gamal_cipher_clear(gamal_ciphertext_t cipher); 129 | int gamal_cipher_new(gamal_ciphertext_t cipher); 130 | 131 | /** 132 | * Generates an EC-Elgamal key pair 133 | * @param keys the EC-ElGamal keypair 134 | * @return 135 | */ 136 | int gamal_generate_keys(gamal_key_t keys); 137 | 138 | /** 139 | * Encrypts an Integer with additadive homomorphic EC-ElGamal 140 | * @param ciphertext 141 | * @param key 142 | * @param plaintext 143 | * @return 144 | */ 145 | int gamal_encrypt(gamal_ciphertext_t ciphertext, gamal_key_t key, dig_t plaintext); 146 | 147 | /** 148 | * Decrypts an EC-Elgamal ciphertext 149 | * @param res the resulting plaintext integer 150 | * @param key 151 | * @param ciphertext 152 | * @param table if NULL bruteforce is used 153 | * @return 154 | */ 155 | int gamal_decrypt(dig_t *res, gamal_key_t key, gamal_ciphertext_t ciphertext, bsgs_table_t table); 156 | 157 | /** 158 | * Adds two EC-Elgamal ciphertext and stores it in res. 159 | * @param res the resulting ciphertext 160 | * @param ciphertext1 161 | * @param ciphertext2 162 | * @return 163 | */ 164 | int gamal_add(gamal_ciphertext_t res, gamal_ciphertext_t ciphertext1, gamal_ciphertext_t ciphertext2); 165 | 166 | #endif //ECELGAMAL_ECELGAMAL_H 167 | -------------------------------------------------------------------------------- /native/include/uthash.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2017, Troy D. Hanson http://troydhanson.github.com/uthash/ 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 12 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 13 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 14 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 15 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifndef UTHASH_H 25 | #define UTHASH_H 26 | 27 | #define UTHASH_VERSION 2.0.2 28 | 29 | #include /* memcmp, memset, strlen */ 30 | #include /* ptrdiff_t */ 31 | #include /* exit */ 32 | 33 | /* These macros use decltype or the earlier __typeof GNU extension. 34 | As decltype is only available in newer compilers (VS2010 or gcc 4.3+ 35 | when compiling c++ source) this code uses whatever method is needed 36 | or, for VS2008 where neither is available, uses casting workarounds. */ 37 | #if !defined(DECLTYPE) && !defined(NO_DECLTYPE) 38 | #if defined(_MSC_VER) /* MS compiler */ 39 | #if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ 40 | #define DECLTYPE(x) (decltype(x)) 41 | #else /* VS2008 or older (or VS2010 in C mode) */ 42 | #define NO_DECLTYPE 43 | #endif 44 | #elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) 45 | #define NO_DECLTYPE 46 | #else /* GNU, Sun and other compilers */ 47 | #define DECLTYPE(x) (__typeof(x)) 48 | #endif 49 | #endif 50 | 51 | #ifdef NO_DECLTYPE 52 | #define DECLTYPE(x) 53 | #define DECLTYPE_ASSIGN(dst,src) \ 54 | do { \ 55 | char **_da_dst = (char**)(&(dst)); \ 56 | *_da_dst = (char*)(src); \ 57 | } while (0) 58 | #else 59 | #define DECLTYPE_ASSIGN(dst,src) \ 60 | do { \ 61 | (dst) = DECLTYPE(dst)(src); \ 62 | } while (0) 63 | #endif 64 | 65 | /* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ 66 | #if defined(_WIN32) 67 | #if defined(_MSC_VER) && _MSC_VER >= 1600 68 | #include 69 | #elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__) 70 | #include 71 | #else 72 | typedef unsigned int uint32_t; 73 | typedef unsigned char uint8_t; 74 | #endif 75 | #elif defined(__GNUC__) && !defined(__VXWORKS__) 76 | #include 77 | #else 78 | typedef unsigned int uint32_t; 79 | typedef unsigned char uint8_t; 80 | #endif 81 | 82 | #ifndef uthash_malloc 83 | #define uthash_malloc(sz) malloc(sz) /* malloc fcn */ 84 | #endif 85 | #ifndef uthash_free 86 | #define uthash_free(ptr,sz) free(ptr) /* free fcn */ 87 | #endif 88 | #ifndef uthash_bzero 89 | #define uthash_bzero(a,n) memset(a,'\0',n) 90 | #endif 91 | #ifndef uthash_memcmp 92 | #define uthash_memcmp(a,b,n) memcmp(a,b,n) 93 | #endif 94 | #ifndef uthash_strlen 95 | #define uthash_strlen(s) strlen(s) 96 | #endif 97 | 98 | #ifndef uthash_noexpand_fyi 99 | #define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ 100 | #endif 101 | #ifndef uthash_expand_fyi 102 | #define uthash_expand_fyi(tbl) /* can be defined to log expands */ 103 | #endif 104 | 105 | #ifndef HASH_NONFATAL_OOM 106 | #define HASH_NONFATAL_OOM 0 107 | #endif 108 | 109 | #if HASH_NONFATAL_OOM 110 | /* malloc failures can be recovered from */ 111 | 112 | #ifndef uthash_nonfatal_oom 113 | #define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ 114 | #endif 115 | 116 | #define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) 117 | #define IF_HASH_NONFATAL_OOM(x) x 118 | 119 | #else 120 | /* malloc failures result in lost memory, hash tables are unusable */ 121 | 122 | #ifndef uthash_fatal 123 | #define uthash_fatal(msg) exit(-1) /* fatal OOM error */ 124 | #endif 125 | 126 | #define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") 127 | #define IF_HASH_NONFATAL_OOM(x) 128 | 129 | #endif 130 | 131 | /* initial number of buckets */ 132 | #define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ 133 | #define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ 134 | #define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ 135 | 136 | /* calculate the element whose hash handle address is hhp */ 137 | #define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) 138 | /* calculate the hash handle from element address elp */ 139 | #define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho))) 140 | 141 | #define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ 142 | do { \ 143 | struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ 144 | unsigned _hd_bkt; \ 145 | HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ 146 | (head)->hh.tbl->buckets[_hd_bkt].count++; \ 147 | _hd_hh_item->hh_next = NULL; \ 148 | _hd_hh_item->hh_prev = NULL; \ 149 | } while (0) 150 | 151 | #define HASH_VALUE(keyptr,keylen,hashv) \ 152 | do { \ 153 | HASH_FCN(keyptr, keylen, hashv); \ 154 | } while (0) 155 | 156 | #define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ 157 | do { \ 158 | (out) = NULL; \ 159 | if (head) { \ 160 | unsigned _hf_bkt; \ 161 | HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ 162 | if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ 163 | HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ 164 | } \ 165 | } \ 166 | } while (0) 167 | 168 | #define HASH_FIND(hh,head,keyptr,keylen,out) \ 169 | do { \ 170 | unsigned _hf_hashv; \ 171 | HASH_VALUE(keyptr, keylen, _hf_hashv); \ 172 | HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ 173 | } while (0) 174 | 175 | #ifdef HASH_BLOOM 176 | #define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) 177 | #define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) 178 | #define HASH_BLOOM_MAKE(tbl,oomed) \ 179 | do { \ 180 | (tbl)->bloom_nbits = HASH_BLOOM; \ 181 | (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ 182 | if (!(tbl)->bloom_bv) { \ 183 | HASH_RECORD_OOM(oomed); \ 184 | } else { \ 185 | uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ 186 | (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ 187 | } \ 188 | } while (0) 189 | 190 | #define HASH_BLOOM_FREE(tbl) \ 191 | do { \ 192 | uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ 193 | } while (0) 194 | 195 | #define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) 196 | #define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) 197 | 198 | #define HASH_BLOOM_ADD(tbl,hashv) \ 199 | HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) 200 | 201 | #define HASH_BLOOM_TEST(tbl,hashv) \ 202 | HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) 203 | 204 | #else 205 | #define HASH_BLOOM_MAKE(tbl,oomed) 206 | #define HASH_BLOOM_FREE(tbl) 207 | #define HASH_BLOOM_ADD(tbl,hashv) 208 | #define HASH_BLOOM_TEST(tbl,hashv) (1) 209 | #define HASH_BLOOM_BYTELEN 0U 210 | #endif 211 | 212 | #define HASH_MAKE_TABLE(hh,head,oomed) \ 213 | do { \ 214 | (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ 215 | if (!(head)->hh.tbl) { \ 216 | HASH_RECORD_OOM(oomed); \ 217 | } else { \ 218 | uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ 219 | (head)->hh.tbl->tail = &((head)->hh); \ 220 | (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ 221 | (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ 222 | (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ 223 | (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ 224 | HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ 225 | (head)->hh.tbl->signature = HASH_SIGNATURE; \ 226 | if (!(head)->hh.tbl->buckets) { \ 227 | HASH_RECORD_OOM(oomed); \ 228 | uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ 229 | } else { \ 230 | uthash_bzero((head)->hh.tbl->buckets, \ 231 | HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ 232 | HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ 233 | IF_HASH_NONFATAL_OOM( \ 234 | if (oomed) { \ 235 | uthash_free((head)->hh.tbl->buckets, \ 236 | HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ 237 | uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ 238 | } \ 239 | ) \ 240 | } \ 241 | } \ 242 | } while (0) 243 | 244 | #define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ 245 | do { \ 246 | (replaced) = NULL; \ 247 | HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ 248 | if (replaced) { \ 249 | HASH_DELETE(hh, head, replaced); \ 250 | } \ 251 | HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ 252 | } while (0) 253 | 254 | #define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ 255 | do { \ 256 | (replaced) = NULL; \ 257 | HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ 258 | if (replaced) { \ 259 | HASH_DELETE(hh, head, replaced); \ 260 | } \ 261 | HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ 262 | } while (0) 263 | 264 | #define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ 265 | do { \ 266 | unsigned _hr_hashv; \ 267 | HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ 268 | HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ 269 | } while (0) 270 | 271 | #define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ 272 | do { \ 273 | unsigned _hr_hashv; \ 274 | HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ 275 | HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ 276 | } while (0) 277 | 278 | #define HASH_APPEND_LIST(hh, head, add) \ 279 | do { \ 280 | (add)->hh.next = NULL; \ 281 | (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ 282 | (head)->hh.tbl->tail->next = (add); \ 283 | (head)->hh.tbl->tail = &((add)->hh); \ 284 | } while (0) 285 | 286 | #define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ 287 | do { \ 288 | do { \ 289 | if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ 290 | break; \ 291 | } \ 292 | } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ 293 | } while (0) 294 | 295 | #ifdef NO_DECLTYPE 296 | #undef HASH_AKBI_INNER_LOOP 297 | #define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ 298 | do { \ 299 | char *_hs_saved_head = (char*)(head); \ 300 | do { \ 301 | DECLTYPE_ASSIGN(head, _hs_iter); \ 302 | if (cmpfcn(head, add) > 0) { \ 303 | DECLTYPE_ASSIGN(head, _hs_saved_head); \ 304 | break; \ 305 | } \ 306 | DECLTYPE_ASSIGN(head, _hs_saved_head); \ 307 | } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ 308 | } while (0) 309 | #endif 310 | 311 | #if HASH_NONFATAL_OOM 312 | 313 | #define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ 314 | do { \ 315 | if (!(oomed)) { \ 316 | unsigned _ha_bkt; \ 317 | (head)->hh.tbl->num_items++; \ 318 | HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ 319 | HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ 320 | if (oomed) { \ 321 | HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ 322 | HASH_DELETE_HH(hh, head, &(add)->hh); \ 323 | (add)->hh.tbl = NULL; \ 324 | uthash_nonfatal_oom(add); \ 325 | } else { \ 326 | HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ 327 | HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ 328 | } \ 329 | } else { \ 330 | (add)->hh.tbl = NULL; \ 331 | uthash_nonfatal_oom(add); \ 332 | } \ 333 | } while (0) 334 | 335 | #else 336 | 337 | #define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ 338 | do { \ 339 | unsigned _ha_bkt; \ 340 | (head)->hh.tbl->num_items++; \ 341 | HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ 342 | HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ 343 | HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ 344 | HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ 345 | } while (0) 346 | 347 | #endif 348 | 349 | 350 | #define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ 351 | do { \ 352 | IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ 353 | (add)->hh.hashv = (hashval); \ 354 | (add)->hh.key = (char*) (keyptr); \ 355 | (add)->hh.keylen = (unsigned) (keylen_in); \ 356 | if (!(head)) { \ 357 | (add)->hh.next = NULL; \ 358 | (add)->hh.prev = NULL; \ 359 | HASH_MAKE_TABLE(hh, add, _ha_oomed); \ 360 | IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ 361 | (head) = (add); \ 362 | IF_HASH_NONFATAL_OOM( } ) \ 363 | } else { \ 364 | void *_hs_iter = (head); \ 365 | (add)->hh.tbl = (head)->hh.tbl; \ 366 | HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ 367 | if (_hs_iter) { \ 368 | (add)->hh.next = _hs_iter; \ 369 | if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ 370 | HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ 371 | } else { \ 372 | (head) = (add); \ 373 | } \ 374 | HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ 375 | } else { \ 376 | HASH_APPEND_LIST(hh, head, add); \ 377 | } \ 378 | } \ 379 | HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ 380 | HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ 381 | } while (0) 382 | 383 | #define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ 384 | do { \ 385 | unsigned _hs_hashv; \ 386 | HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ 387 | HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ 388 | } while (0) 389 | 390 | #define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ 391 | HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) 392 | 393 | #define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ 394 | HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) 395 | 396 | #define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ 397 | do { \ 398 | IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ 399 | (add)->hh.hashv = (hashval); \ 400 | (add)->hh.key = (char*) (keyptr); \ 401 | (add)->hh.keylen = (unsigned) (keylen_in); \ 402 | if (!(head)) { \ 403 | (add)->hh.next = NULL; \ 404 | (add)->hh.prev = NULL; \ 405 | HASH_MAKE_TABLE(hh, add, _ha_oomed); \ 406 | IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ 407 | (head) = (add); \ 408 | IF_HASH_NONFATAL_OOM( } ) \ 409 | } else { \ 410 | (add)->hh.tbl = (head)->hh.tbl; \ 411 | HASH_APPEND_LIST(hh, head, add); \ 412 | } \ 413 | HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ 414 | HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ 415 | } while (0) 416 | 417 | #define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ 418 | do { \ 419 | unsigned _ha_hashv; \ 420 | HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ 421 | HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ 422 | } while (0) 423 | 424 | #define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ 425 | HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) 426 | 427 | #define HASH_ADD(hh,head,fieldname,keylen_in,add) \ 428 | HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) 429 | 430 | #define HASH_TO_BKT(hashv,num_bkts,bkt) \ 431 | do { \ 432 | bkt = ((hashv) & ((num_bkts) - 1U)); \ 433 | } while (0) 434 | 435 | /* delete "delptr" from the hash table. 436 | * "the usual" patch-up process for the app-order doubly-linked-list. 437 | * The use of _hd_hh_del below deserves special explanation. 438 | * These used to be expressed using (delptr) but that led to a bug 439 | * if someone used the same symbol for the head and deletee, like 440 | * HASH_DELETE(hh,users,users); 441 | * We want that to work, but by changing the head (users) below 442 | * we were forfeiting our ability to further refer to the deletee (users) 443 | * in the patch-up process. Solution: use scratch space to 444 | * copy the deletee pointer, then the latter references are via that 445 | * scratch pointer rather than through the repointed (users) symbol. 446 | */ 447 | #define HASH_DELETE(hh,head,delptr) \ 448 | HASH_DELETE_HH(hh, head, &(delptr)->hh) 449 | 450 | #define HASH_DELETE_HH(hh,head,delptrhh) \ 451 | do { \ 452 | struct UT_hash_handle *_hd_hh_del = (delptrhh); \ 453 | if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ 454 | HASH_BLOOM_FREE((head)->hh.tbl); \ 455 | uthash_free((head)->hh.tbl->buckets, \ 456 | (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ 457 | uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ 458 | (head) = NULL; \ 459 | } else { \ 460 | unsigned _hd_bkt; \ 461 | if (_hd_hh_del == (head)->hh.tbl->tail) { \ 462 | (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ 463 | } \ 464 | if (_hd_hh_del->prev != NULL) { \ 465 | HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ 466 | } else { \ 467 | DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ 468 | } \ 469 | if (_hd_hh_del->next != NULL) { \ 470 | HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ 471 | } \ 472 | HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ 473 | HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ 474 | (head)->hh.tbl->num_items--; \ 475 | } \ 476 | HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ 477 | } while (0) 478 | 479 | /* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ 480 | #define HASH_FIND_STR(head,findstr,out) \ 481 | HASH_FIND(hh,head,findstr,(unsigned)uthash_strlen(findstr),out) 482 | #define HASH_ADD_STR(head,strfield,add) \ 483 | HASH_ADD(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add) 484 | #define HASH_REPLACE_STR(head,strfield,add,replaced) \ 485 | HASH_REPLACE(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add,replaced) 486 | #define HASH_FIND_INT(head,findint,out) \ 487 | HASH_FIND(hh,head,findint,sizeof(int),out) 488 | #define HASH_ADD_INT(head,intfield,add) \ 489 | HASH_ADD(hh,head,intfield,sizeof(int),add) 490 | #define HASH_REPLACE_INT(head,intfield,add,replaced) \ 491 | HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) 492 | #define HASH_FIND_PTR(head,findptr,out) \ 493 | HASH_FIND(hh,head,findptr,sizeof(void *),out) 494 | #define HASH_ADD_PTR(head,ptrfield,add) \ 495 | HASH_ADD(hh,head,ptrfield,sizeof(void *),add) 496 | #define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ 497 | HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) 498 | #define HASH_DEL(head,delptr) \ 499 | HASH_DELETE(hh,head,delptr) 500 | #define HASH_ADD_BIN(head,ptrfield,size,add) \ 501 | HASH_ADD(hh,head,ptrfield,size,add) 502 | #define HASH_FIND_BIN(head,findptr,size, out) \ 503 | HASH_FIND(hh,head,findptr,size,out) 504 | 505 | /* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. 506 | * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. 507 | */ 508 | #ifdef HASH_DEBUG 509 | #define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) 510 | #define HASH_FSCK(hh,head,where) \ 511 | do { \ 512 | struct UT_hash_handle *_thh; \ 513 | if (head) { \ 514 | unsigned _bkt_i; \ 515 | unsigned _count = 0; \ 516 | char *_prev; \ 517 | for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ 518 | unsigned _bkt_count = 0; \ 519 | _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ 520 | _prev = NULL; \ 521 | while (_thh) { \ 522 | if (_prev != (char*)(_thh->hh_prev)) { \ 523 | HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ 524 | (where), (void*)_thh->hh_prev, (void*)_prev); \ 525 | } \ 526 | _bkt_count++; \ 527 | _prev = (char*)(_thh); \ 528 | _thh = _thh->hh_next; \ 529 | } \ 530 | _count += _bkt_count; \ 531 | if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ 532 | HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ 533 | (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ 534 | } \ 535 | } \ 536 | if (_count != (head)->hh.tbl->num_items) { \ 537 | HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ 538 | (where), (head)->hh.tbl->num_items, _count); \ 539 | } \ 540 | _count = 0; \ 541 | _prev = NULL; \ 542 | _thh = &(head)->hh; \ 543 | while (_thh) { \ 544 | _count++; \ 545 | if (_prev != (char*)_thh->prev) { \ 546 | HASH_OOPS("%s: invalid prev %p, actual %p\n", \ 547 | (where), (void*)_thh->prev, (void*)_prev); \ 548 | } \ 549 | _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ 550 | _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ 551 | } \ 552 | if (_count != (head)->hh.tbl->num_items) { \ 553 | HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ 554 | (where), (head)->hh.tbl->num_items, _count); \ 555 | } \ 556 | } \ 557 | } while (0) 558 | #else 559 | #define HASH_FSCK(hh,head,where) 560 | #endif 561 | 562 | /* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to 563 | * the descriptor to which this macro is defined for tuning the hash function. 564 | * The app can #include to get the prototype for write(2). */ 565 | #ifdef HASH_EMIT_KEYS 566 | #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ 567 | do { \ 568 | unsigned _klen = fieldlen; \ 569 | write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ 570 | write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ 571 | } while (0) 572 | #else 573 | #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) 574 | #endif 575 | 576 | /* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ 577 | #ifdef HASH_FUNCTION 578 | #define HASH_FCN HASH_FUNCTION 579 | #else 580 | #define HASH_FCN HASH_JEN 581 | #endif 582 | 583 | /* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ 584 | #define HASH_BER(key,keylen,hashv) \ 585 | do { \ 586 | unsigned _hb_keylen = (unsigned)keylen; \ 587 | const unsigned char *_hb_key = (const unsigned char*)(key); \ 588 | (hashv) = 0; \ 589 | while (_hb_keylen-- != 0U) { \ 590 | (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ 591 | } \ 592 | } while (0) 593 | 594 | 595 | /* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at 596 | * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ 597 | #define HASH_SAX(key,keylen,hashv) \ 598 | do { \ 599 | unsigned _sx_i; \ 600 | const unsigned char *_hs_key = (const unsigned char*)(key); \ 601 | hashv = 0; \ 602 | for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ 603 | hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ 604 | } \ 605 | } while (0) 606 | /* FNV-1a variation */ 607 | #define HASH_FNV(key,keylen,hashv) \ 608 | do { \ 609 | unsigned _fn_i; \ 610 | const unsigned char *_hf_key = (const unsigned char*)(key); \ 611 | (hashv) = 2166136261U; \ 612 | for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ 613 | hashv = hashv ^ _hf_key[_fn_i]; \ 614 | hashv = hashv * 16777619U; \ 615 | } \ 616 | } while (0) 617 | 618 | #define HASH_OAT(key,keylen,hashv) \ 619 | do { \ 620 | unsigned _ho_i; \ 621 | const unsigned char *_ho_key=(const unsigned char*)(key); \ 622 | hashv = 0; \ 623 | for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ 624 | hashv += _ho_key[_ho_i]; \ 625 | hashv += (hashv << 10); \ 626 | hashv ^= (hashv >> 6); \ 627 | } \ 628 | hashv += (hashv << 3); \ 629 | hashv ^= (hashv >> 11); \ 630 | hashv += (hashv << 15); \ 631 | } while (0) 632 | 633 | #define HASH_JEN_MIX(a,b,c) \ 634 | do { \ 635 | a -= b; a -= c; a ^= ( c >> 13 ); \ 636 | b -= c; b -= a; b ^= ( a << 8 ); \ 637 | c -= a; c -= b; c ^= ( b >> 13 ); \ 638 | a -= b; a -= c; a ^= ( c >> 12 ); \ 639 | b -= c; b -= a; b ^= ( a << 16 ); \ 640 | c -= a; c -= b; c ^= ( b >> 5 ); \ 641 | a -= b; a -= c; a ^= ( c >> 3 ); \ 642 | b -= c; b -= a; b ^= ( a << 10 ); \ 643 | c -= a; c -= b; c ^= ( b >> 15 ); \ 644 | } while (0) 645 | 646 | #define HASH_JEN(key,keylen,hashv) \ 647 | do { \ 648 | unsigned _hj_i,_hj_j,_hj_k; \ 649 | unsigned const char *_hj_key=(unsigned const char*)(key); \ 650 | hashv = 0xfeedbeefu; \ 651 | _hj_i = _hj_j = 0x9e3779b9u; \ 652 | _hj_k = (unsigned)(keylen); \ 653 | while (_hj_k >= 12U) { \ 654 | _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ 655 | + ( (unsigned)_hj_key[2] << 16 ) \ 656 | + ( (unsigned)_hj_key[3] << 24 ) ); \ 657 | _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ 658 | + ( (unsigned)_hj_key[6] << 16 ) \ 659 | + ( (unsigned)_hj_key[7] << 24 ) ); \ 660 | hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ 661 | + ( (unsigned)_hj_key[10] << 16 ) \ 662 | + ( (unsigned)_hj_key[11] << 24 ) ); \ 663 | \ 664 | HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ 665 | \ 666 | _hj_key += 12; \ 667 | _hj_k -= 12U; \ 668 | } \ 669 | hashv += (unsigned)(keylen); \ 670 | switch ( _hj_k ) { \ 671 | case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ 672 | case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ 673 | case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ 674 | case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ 675 | case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ 676 | case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ 677 | case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ 678 | case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ 679 | case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ 680 | case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ 681 | case 1: _hj_i += _hj_key[0]; \ 682 | } \ 683 | HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ 684 | } while (0) 685 | 686 | /* The Paul Hsieh hash function */ 687 | #undef get16bits 688 | #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ 689 | || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) 690 | #define get16bits(d) (*((const uint16_t *) (d))) 691 | #endif 692 | 693 | #if !defined (get16bits) 694 | #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ 695 | +(uint32_t)(((const uint8_t *)(d))[0]) ) 696 | #endif 697 | #define HASH_SFH(key,keylen,hashv) \ 698 | do { \ 699 | unsigned const char *_sfh_key=(unsigned const char*)(key); \ 700 | uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ 701 | \ 702 | unsigned _sfh_rem = _sfh_len & 3U; \ 703 | _sfh_len >>= 2; \ 704 | hashv = 0xcafebabeu; \ 705 | \ 706 | /* Main loop */ \ 707 | for (;_sfh_len > 0U; _sfh_len--) { \ 708 | hashv += get16bits (_sfh_key); \ 709 | _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ 710 | hashv = (hashv << 16) ^ _sfh_tmp; \ 711 | _sfh_key += 2U*sizeof (uint16_t); \ 712 | hashv += hashv >> 11; \ 713 | } \ 714 | \ 715 | /* Handle end cases */ \ 716 | switch (_sfh_rem) { \ 717 | case 3: hashv += get16bits (_sfh_key); \ 718 | hashv ^= hashv << 16; \ 719 | hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ 720 | hashv += hashv >> 11; \ 721 | break; \ 722 | case 2: hashv += get16bits (_sfh_key); \ 723 | hashv ^= hashv << 11; \ 724 | hashv += hashv >> 17; \ 725 | break; \ 726 | case 1: hashv += *_sfh_key; \ 727 | hashv ^= hashv << 10; \ 728 | hashv += hashv >> 1; \ 729 | } \ 730 | \ 731 | /* Force "avalanching" of final 127 bits */ \ 732 | hashv ^= hashv << 3; \ 733 | hashv += hashv >> 5; \ 734 | hashv ^= hashv << 4; \ 735 | hashv += hashv >> 17; \ 736 | hashv ^= hashv << 25; \ 737 | hashv += hashv >> 6; \ 738 | } while (0) 739 | 740 | #ifdef HASH_USING_NO_STRICT_ALIASING 741 | /* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. 742 | * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. 743 | * MurmurHash uses the faster approach only on CPU's where we know it's safe. 744 | * 745 | * Note the preprocessor built-in defines can be emitted using: 746 | * 747 | * gcc -m64 -dM -E - < /dev/null (on gcc) 748 | * cc -## a.c (where a.c is a simple test file) (Sun Studio) 749 | */ 750 | #if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) 751 | #define MUR_GETBLOCK(p,i) p[i] 752 | #else /* non intel */ 753 | #define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL) 754 | #define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL) 755 | #define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL) 756 | #define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL) 757 | #define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) 758 | #if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) 759 | #define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) 760 | #define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) 761 | #define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) 762 | #else /* assume little endian non-intel */ 763 | #define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) 764 | #define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) 765 | #define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) 766 | #endif 767 | #define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ 768 | (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ 769 | (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ 770 | MUR_ONE_THREE(p)))) 771 | #endif 772 | #define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) 773 | #define MUR_FMIX(_h) \ 774 | do { \ 775 | _h ^= _h >> 16; \ 776 | _h *= 0x85ebca6bu; \ 777 | _h ^= _h >> 13; \ 778 | _h *= 0xc2b2ae35u; \ 779 | _h ^= _h >> 16; \ 780 | } while (0) 781 | 782 | #define HASH_MUR(key,keylen,hashv) \ 783 | do { \ 784 | const uint8_t *_mur_data = (const uint8_t*)(key); \ 785 | const int _mur_nblocks = (int)(keylen) / 4; \ 786 | uint32_t _mur_h1 = 0xf88D5353u; \ 787 | uint32_t _mur_c1 = 0xcc9e2d51u; \ 788 | uint32_t _mur_c2 = 0x1b873593u; \ 789 | uint32_t _mur_k1 = 0; \ 790 | const uint8_t *_mur_tail; \ 791 | const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \ 792 | int _mur_i; \ 793 | for (_mur_i = -_mur_nblocks; _mur_i != 0; _mur_i++) { \ 794 | _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ 795 | _mur_k1 *= _mur_c1; \ 796 | _mur_k1 = MUR_ROTL32(_mur_k1,15); \ 797 | _mur_k1 *= _mur_c2; \ 798 | \ 799 | _mur_h1 ^= _mur_k1; \ 800 | _mur_h1 = MUR_ROTL32(_mur_h1,13); \ 801 | _mur_h1 = (_mur_h1*5U) + 0xe6546b64u; \ 802 | } \ 803 | _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4)); \ 804 | _mur_k1=0; \ 805 | switch ((keylen) & 3U) { \ 806 | case 0: break; \ 807 | case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \ 808 | case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8; /* FALLTHROUGH */ \ 809 | case 1: _mur_k1 ^= (uint32_t)_mur_tail[0]; \ 810 | _mur_k1 *= _mur_c1; \ 811 | _mur_k1 = MUR_ROTL32(_mur_k1,15); \ 812 | _mur_k1 *= _mur_c2; \ 813 | _mur_h1 ^= _mur_k1; \ 814 | } \ 815 | _mur_h1 ^= (uint32_t)(keylen); \ 816 | MUR_FMIX(_mur_h1); \ 817 | hashv = _mur_h1; \ 818 | } while (0) 819 | #endif /* HASH_USING_NO_STRICT_ALIASING */ 820 | 821 | /* iterate over items in a known bucket to find desired item */ 822 | #define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ 823 | do { \ 824 | if ((head).hh_head != NULL) { \ 825 | DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ 826 | } else { \ 827 | (out) = NULL; \ 828 | } \ 829 | while ((out) != NULL) { \ 830 | if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ 831 | if (uthash_memcmp((out)->hh.key, keyptr, keylen_in) == 0) { \ 832 | break; \ 833 | } \ 834 | } \ 835 | if ((out)->hh.hh_next != NULL) { \ 836 | DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ 837 | } else { \ 838 | (out) = NULL; \ 839 | } \ 840 | } \ 841 | } while (0) 842 | 843 | /* add an item to a bucket */ 844 | #define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ 845 | do { \ 846 | UT_hash_bucket *_ha_head = &(head); \ 847 | _ha_head->count++; \ 848 | (addhh)->hh_next = _ha_head->hh_head; \ 849 | (addhh)->hh_prev = NULL; \ 850 | if (_ha_head->hh_head != NULL) { \ 851 | _ha_head->hh_head->hh_prev = (addhh); \ 852 | } \ 853 | _ha_head->hh_head = (addhh); \ 854 | if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ 855 | && !(addhh)->tbl->noexpand) { \ 856 | HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ 857 | IF_HASH_NONFATAL_OOM( \ 858 | if (oomed) { \ 859 | HASH_DEL_IN_BKT(head,addhh); \ 860 | } \ 861 | ) \ 862 | } \ 863 | } while (0) 864 | 865 | /* remove an item from a given bucket */ 866 | #define HASH_DEL_IN_BKT(head,delhh) \ 867 | do { \ 868 | UT_hash_bucket *_hd_head = &(head); \ 869 | _hd_head->count--; \ 870 | if (_hd_head->hh_head == (delhh)) { \ 871 | _hd_head->hh_head = (delhh)->hh_next; \ 872 | } \ 873 | if ((delhh)->hh_prev) { \ 874 | (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ 875 | } \ 876 | if ((delhh)->hh_next) { \ 877 | (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ 878 | } \ 879 | } while (0) 880 | 881 | /* Bucket expansion has the effect of doubling the number of buckets 882 | * and redistributing the items into the new buckets. Ideally the 883 | * items will distribute more or less evenly into the new buckets 884 | * (the extent to which this is true is a measure of the quality of 885 | * the hash function as it applies to the key domain). 886 | * 887 | * With the items distributed into more buckets, the chain length 888 | * (item count) in each bucket is reduced. Thus by expanding buckets 889 | * the hash keeps a bound on the chain length. This bounded chain 890 | * length is the essence of how a hash provides constant time lookup. 891 | * 892 | * The calculation of tbl->ideal_chain_maxlen below deserves some 893 | * explanation. First, keep in mind that we're calculating the ideal 894 | * maximum chain length based on the *new* (doubled) bucket count. 895 | * In fractions this is just n/b (n=number of items,b=new num buckets). 896 | * Since the ideal chain length is an integer, we want to calculate 897 | * ceil(n/b). We don't depend on floating point arithmetic in this 898 | * hash, so to calculate ceil(n/b) with integers we could write 899 | * 900 | * ceil(n/b) = (n/b) + ((n%b)?1:0) 901 | * 902 | * and in fact a previous version of this hash did just that. 903 | * But now we have improved things a bit by recognizing that b is 904 | * always a power of two. We keep its base 2 log handy (call it lb), 905 | * so now we can write this with a bit shift and logical AND: 906 | * 907 | * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) 908 | * 909 | */ 910 | #define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ 911 | do { \ 912 | unsigned _he_bkt; \ 913 | unsigned _he_bkt_i; \ 914 | struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ 915 | UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ 916 | _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ 917 | 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ 918 | if (!_he_new_buckets) { \ 919 | HASH_RECORD_OOM(oomed); \ 920 | } else { \ 921 | uthash_bzero(_he_new_buckets, \ 922 | 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ 923 | (tbl)->ideal_chain_maxlen = \ 924 | ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ 925 | ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ 926 | (tbl)->nonideal_items = 0; \ 927 | for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ 928 | _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ 929 | while (_he_thh != NULL) { \ 930 | _he_hh_nxt = _he_thh->hh_next; \ 931 | HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ 932 | _he_newbkt = &(_he_new_buckets[_he_bkt]); \ 933 | if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ 934 | (tbl)->nonideal_items++; \ 935 | _he_newbkt->expand_mult = _he_newbkt->count / (tbl)->ideal_chain_maxlen; \ 936 | } \ 937 | _he_thh->hh_prev = NULL; \ 938 | _he_thh->hh_next = _he_newbkt->hh_head; \ 939 | if (_he_newbkt->hh_head != NULL) { \ 940 | _he_newbkt->hh_head->hh_prev = _he_thh; \ 941 | } \ 942 | _he_newbkt->hh_head = _he_thh; \ 943 | _he_thh = _he_hh_nxt; \ 944 | } \ 945 | } \ 946 | uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ 947 | (tbl)->num_buckets *= 2U; \ 948 | (tbl)->log2_num_buckets++; \ 949 | (tbl)->buckets = _he_new_buckets; \ 950 | (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ 951 | ((tbl)->ineff_expands+1U) : 0U; \ 952 | if ((tbl)->ineff_expands > 1U) { \ 953 | (tbl)->noexpand = 1; \ 954 | uthash_noexpand_fyi(tbl); \ 955 | } \ 956 | uthash_expand_fyi(tbl); \ 957 | } \ 958 | } while (0) 959 | 960 | 961 | /* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ 962 | /* Note that HASH_SORT assumes the hash handle name to be hh. 963 | * HASH_SRT was added to allow the hash handle name to be passed in. */ 964 | #define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) 965 | #define HASH_SRT(hh,head,cmpfcn) \ 966 | do { \ 967 | unsigned _hs_i; \ 968 | unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ 969 | struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ 970 | if (head != NULL) { \ 971 | _hs_insize = 1; \ 972 | _hs_looping = 1; \ 973 | _hs_list = &((head)->hh); \ 974 | while (_hs_looping != 0U) { \ 975 | _hs_p = _hs_list; \ 976 | _hs_list = NULL; \ 977 | _hs_tail = NULL; \ 978 | _hs_nmerges = 0; \ 979 | while (_hs_p != NULL) { \ 980 | _hs_nmerges++; \ 981 | _hs_q = _hs_p; \ 982 | _hs_psize = 0; \ 983 | for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ 984 | _hs_psize++; \ 985 | _hs_q = ((_hs_q->next != NULL) ? \ 986 | HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ 987 | if (_hs_q == NULL) { \ 988 | break; \ 989 | } \ 990 | } \ 991 | _hs_qsize = _hs_insize; \ 992 | while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ 993 | if (_hs_psize == 0U) { \ 994 | _hs_e = _hs_q; \ 995 | _hs_q = ((_hs_q->next != NULL) ? \ 996 | HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ 997 | _hs_qsize--; \ 998 | } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ 999 | _hs_e = _hs_p; \ 1000 | if (_hs_p != NULL) { \ 1001 | _hs_p = ((_hs_p->next != NULL) ? \ 1002 | HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ 1003 | } \ 1004 | _hs_psize--; \ 1005 | } else if ((cmpfcn( \ 1006 | DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ 1007 | DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ 1008 | )) <= 0) { \ 1009 | _hs_e = _hs_p; \ 1010 | if (_hs_p != NULL) { \ 1011 | _hs_p = ((_hs_p->next != NULL) ? \ 1012 | HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ 1013 | } \ 1014 | _hs_psize--; \ 1015 | } else { \ 1016 | _hs_e = _hs_q; \ 1017 | _hs_q = ((_hs_q->next != NULL) ? \ 1018 | HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ 1019 | _hs_qsize--; \ 1020 | } \ 1021 | if ( _hs_tail != NULL ) { \ 1022 | _hs_tail->next = ((_hs_e != NULL) ? \ 1023 | ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ 1024 | } else { \ 1025 | _hs_list = _hs_e; \ 1026 | } \ 1027 | if (_hs_e != NULL) { \ 1028 | _hs_e->prev = ((_hs_tail != NULL) ? \ 1029 | ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ 1030 | } \ 1031 | _hs_tail = _hs_e; \ 1032 | } \ 1033 | _hs_p = _hs_q; \ 1034 | } \ 1035 | if (_hs_tail != NULL) { \ 1036 | _hs_tail->next = NULL; \ 1037 | } \ 1038 | if (_hs_nmerges <= 1U) { \ 1039 | _hs_looping = 0; \ 1040 | (head)->hh.tbl->tail = _hs_tail; \ 1041 | DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ 1042 | } \ 1043 | _hs_insize *= 2U; \ 1044 | } \ 1045 | HASH_FSCK(hh, head, "HASH_SRT"); \ 1046 | } \ 1047 | } while (0) 1048 | 1049 | /* This function selects items from one hash into another hash. 1050 | * The end result is that the selected items have dual presence 1051 | * in both hashes. There is no copy of the items made; rather 1052 | * they are added into the new hash through a secondary hash 1053 | * hash handle that must be present in the structure. */ 1054 | #define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ 1055 | do { \ 1056 | unsigned _src_bkt, _dst_bkt; \ 1057 | void *_last_elt = NULL, *_elt; \ 1058 | UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ 1059 | ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ 1060 | if ((src) != NULL) { \ 1061 | for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ 1062 | for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ 1063 | _src_hh != NULL; \ 1064 | _src_hh = _src_hh->hh_next) { \ 1065 | _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ 1066 | if (cond(_elt)) { \ 1067 | IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ 1068 | _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ 1069 | _dst_hh->key = _src_hh->key; \ 1070 | _dst_hh->keylen = _src_hh->keylen; \ 1071 | _dst_hh->hashv = _src_hh->hashv; \ 1072 | _dst_hh->prev = _last_elt; \ 1073 | _dst_hh->next = NULL; \ 1074 | if (_last_elt_hh != NULL) { \ 1075 | _last_elt_hh->next = _elt; \ 1076 | } \ 1077 | if ((dst) == NULL) { \ 1078 | DECLTYPE_ASSIGN(dst, _elt); \ 1079 | HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ 1080 | IF_HASH_NONFATAL_OOM( \ 1081 | if (_hs_oomed) { \ 1082 | uthash_nonfatal_oom(_elt); \ 1083 | (dst) = NULL; \ 1084 | continue; \ 1085 | } \ 1086 | ) \ 1087 | } else { \ 1088 | _dst_hh->tbl = (dst)->hh_dst.tbl; \ 1089 | } \ 1090 | HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ 1091 | HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ 1092 | (dst)->hh_dst.tbl->num_items++; \ 1093 | IF_HASH_NONFATAL_OOM( \ 1094 | if (_hs_oomed) { \ 1095 | HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ 1096 | HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ 1097 | _dst_hh->tbl = NULL; \ 1098 | uthash_nonfatal_oom(_elt); \ 1099 | continue; \ 1100 | } \ 1101 | ) \ 1102 | HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ 1103 | _last_elt = _elt; \ 1104 | _last_elt_hh = _dst_hh; \ 1105 | } \ 1106 | } \ 1107 | } \ 1108 | } \ 1109 | HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ 1110 | } while (0) 1111 | 1112 | #define HASH_CLEAR(hh,head) \ 1113 | do { \ 1114 | if ((head) != NULL) { \ 1115 | HASH_BLOOM_FREE((head)->hh.tbl); \ 1116 | uthash_free((head)->hh.tbl->buckets, \ 1117 | (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ 1118 | uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ 1119 | (head) = NULL; \ 1120 | } \ 1121 | } while (0) 1122 | 1123 | #define HASH_OVERHEAD(hh,head) \ 1124 | (((head) != NULL) ? ( \ 1125 | (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ 1126 | ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ 1127 | sizeof(UT_hash_table) + \ 1128 | (HASH_BLOOM_BYTELEN))) : 0U) 1129 | 1130 | #ifdef NO_DECLTYPE 1131 | #define HASH_ITER(hh,head,el,tmp) \ 1132 | for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ 1133 | (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) 1134 | #else 1135 | #define HASH_ITER(hh,head,el,tmp) \ 1136 | for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ 1137 | (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) 1138 | #endif 1139 | 1140 | /* obtain a count of items in the hash */ 1141 | #define HASH_COUNT(head) HASH_CNT(hh,head) 1142 | #define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) 1143 | 1144 | typedef struct UT_hash_bucket { 1145 | struct UT_hash_handle *hh_head; 1146 | unsigned count; 1147 | 1148 | /* expand_mult is normally set to 0. In this situation, the max chain length 1149 | * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If 1150 | * the bucket's chain exceeds this length, bucket expansion is triggered). 1151 | * However, setting expand_mult to a non-zero value delays bucket expansion 1152 | * (that would be triggered by additions to this particular bucket) 1153 | * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. 1154 | * (The multiplier is simply expand_mult+1). The whole idea of this 1155 | * multiplier is to reduce bucket expansions, since they are expensive, in 1156 | * situations where we know that a particular bucket tends to be overused. 1157 | * It is better to let its chain length grow to a longer yet-still-bounded 1158 | * value, than to do an O(n) bucket expansion too often. 1159 | */ 1160 | unsigned expand_mult; 1161 | 1162 | } UT_hash_bucket; 1163 | 1164 | /* random signature used only to find hash tables in external analysis */ 1165 | #define HASH_SIGNATURE 0xa0111fe1u 1166 | #define HASH_BLOOM_SIGNATURE 0xb12220f2u 1167 | 1168 | typedef struct UT_hash_table { 1169 | UT_hash_bucket *buckets; 1170 | unsigned num_buckets, log2_num_buckets; 1171 | unsigned num_items; 1172 | struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ 1173 | ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ 1174 | 1175 | /* in an ideal situation (all buckets used equally), no bucket would have 1176 | * more than ceil(#items/#buckets) items. that's the ideal chain length. */ 1177 | unsigned ideal_chain_maxlen; 1178 | 1179 | /* nonideal_items is the number of items in the hash whose chain position 1180 | * exceeds the ideal chain maxlen. these items pay the penalty for an uneven 1181 | * hash distribution; reaching them in a chain traversal takes >ideal steps */ 1182 | unsigned nonideal_items; 1183 | 1184 | /* ineffective expands occur when a bucket doubling was performed, but 1185 | * afterward, more than half the items in the hash had nonideal chain 1186 | * positions. If this happens on two consecutive expansions we inhibit any 1187 | * further expansion, as it's not helping; this happens when the hash 1188 | * function isn't a good fit for the key domain. When expansion is inhibited 1189 | * the hash will still work, albeit no longer in constant time. */ 1190 | unsigned ineff_expands, noexpand; 1191 | 1192 | uint32_t signature; /* used only to find hash tables in external analysis */ 1193 | #ifdef HASH_BLOOM 1194 | uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ 1195 | uint8_t *bloom_bv; 1196 | uint8_t bloom_nbits; 1197 | #endif 1198 | 1199 | } UT_hash_table; 1200 | 1201 | typedef struct UT_hash_handle { 1202 | struct UT_hash_table *tbl; 1203 | void *prev; /* prev element in app order */ 1204 | void *next; /* next element in app order */ 1205 | struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ 1206 | struct UT_hash_handle *hh_next; /* next hh in bucket order */ 1207 | void *key; /* ptr to enclosing struct's key */ 1208 | unsigned keylen; /* enclosing struct's key len */ 1209 | unsigned hashv; /* result of hash-fcn(key) */ 1210 | } UT_hash_handle; 1211 | 1212 | #endif /* UTHASH_H */ 1213 | -------------------------------------------------------------------------------- /native/testing.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Author: 6 | * Lukas Burkhalter 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived 19 | * from this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | 42 | extern "C" { 43 | #include "ecelgamal.h" 44 | #include "crtecelgamal.h" 45 | } 46 | 47 | using namespace std::chrono; 48 | 49 | int test1() { 50 | gamal_key_t key, key_decoded; 51 | gamal_ciphertext_t cipher, cipher_after; 52 | bsgs_table_t table; 53 | dig_t plain = 36435345, res, res2; 54 | unsigned char *buff; 55 | size_t size; 56 | 57 | gamal_init(DEFAULT_CURVE); 58 | 59 | gamal_generate_keys(key); 60 | gamal_encrypt(cipher, key, plain); 61 | 62 | std::cout << "key gen + enc ok" << std::endl; 63 | 64 | buff = (unsigned char *) malloc(get_encoded_ciphertext_size(cipher)); 65 | encode_ciphertext(buff, 100000, cipher); 66 | decode_ciphertext(cipher_after, buff, 1000000); 67 | free(buff); 68 | 69 | size = get_encoded_key_size(key, 0); 70 | buff = (unsigned char *) malloc(size); 71 | encode_key(buff, size, key, 0); 72 | decode_key(key_decoded, buff, size); 73 | 74 | 75 | gamal_init_bsgs_table(table, 1L << 16); 76 | 77 | gamal_decrypt(&res, key, cipher_after, table); 78 | 79 | std::cout << "Before: " << plain << " After: " << res << std::endl; 80 | gamal_free_bsgs_table(table); 81 | gamal_deinit(); 82 | return 0; 83 | } 84 | 85 | void bench_elgamal(int num_entries, int tablebits) { 86 | gamal_key_t key; 87 | gamal_ciphertext_t cipher; 88 | dig_t plain, after; 89 | bsgs_table_t table; 90 | srand (time(NULL)); 91 | double avg_enc = 0, avg_dec = 0; 92 | 93 | gamal_init(CURVE_256_SEC); 94 | gamal_generate_keys(key); 95 | gamal_init_bsgs_table(table, (dig_t) 1L << tablebits); 96 | 97 | for (int iter=0; iter(t2-t1).count(); 104 | 105 | avg_enc += enc_time; 106 | 107 | t1 = high_resolution_clock::now(); 108 | gamal_decrypt(&after, key, cipher, table); 109 | t2 = high_resolution_clock::now(); 110 | auto dec_time = duration_cast(t2-t1).count(); 111 | 112 | avg_dec += dec_time; 113 | std::cout << " ENC Time: " << enc_time / 1000000.0 << "ms DEC Time: " << dec_time / 1000000.0 << " ms" << std::endl; 114 | } 115 | avg_enc = avg_enc / num_entries; 116 | avg_dec = avg_dec / num_entries; 117 | std::cout << "Avg ENC Time " << avg_enc / 1000000.0 << "ms Avg DEC Time " << avg_dec / 1000000.0 << " ms" << std::endl; 118 | 119 | gamal_cipher_clear(cipher); 120 | gamal_key_clear(key); 121 | gamal_free_bsgs_table(table); 122 | gamal_deinit(); 123 | } 124 | 125 | void test2() { 126 | crtgamal_params_t params; 127 | crtgamal_key_t key, key_decoded; 128 | crtgamal_ciphertext_t cipher, cipher_after; 129 | bsgs_table_t table; 130 | dig_t plain = 36435345, res, res2; 131 | unsigned char *buff; 132 | size_t size, size_ciphertext; 133 | 134 | crtgamal_init(DEFAULT_CURVE); 135 | crt_params_create_default(params, DEFAULT_32_INTEGER_PARAMS); 136 | 137 | crtgamal_generate_keys(key, params); 138 | crtgamal_encrypt(cipher, key, plain); 139 | 140 | std::cout << "key gen + enc ok" << std::endl; 141 | 142 | size_ciphertext = crt_get_encoded_ciphertext_size(cipher); 143 | buff = (unsigned char *) malloc(size_ciphertext); 144 | crt_encode_ciphertext(buff, (int) size_ciphertext, cipher); 145 | crt_decode_ciphertext(cipher_after, buff, (int) size_ciphertext); 146 | free(buff); 147 | 148 | //size = get_encoded_key_size(key); 149 | //buff = (unsigned char *) malloc(size); 150 | //encode_key(buff, size, key); 151 | //decode_key(key_decoded, buff, size); 152 | 153 | 154 | gamal_init_bsgs_table(table, 1L << 8); 155 | 156 | crtgamal_decrypt(&res, key, cipher_after, table); 157 | 158 | std::cout << "Before: " << plain << " After: " << res << std::endl; 159 | gamal_free_bsgs_table(table); 160 | crt_params_free(params); 161 | crtgamal_ciphertext_free(cipher); 162 | crtgamal_ciphertext_free(cipher_after); 163 | gamal_deinit(); 164 | } 165 | 166 | void bench_crtelgamal(int num_entries, int tablebits, int plainbits) { 167 | crtgamal_key_t key; 168 | crtgamal_params_t params; 169 | crtgamal_ciphertext_t cipher; 170 | dig_t plain, after; 171 | bsgs_table_t table; 172 | srand (time(NULL)); 173 | double avg_enc = 0, avg_dec = 0; 174 | 175 | crtgamal_init(CURVE_256_SEC); 176 | if (plainbits>32) 177 | crt_params_create_default(params, DEFAULT_64_INTEGER_PARAMS); 178 | else 179 | crt_params_create_default(params, DEFAULT_32_INTEGER_PARAMS); 180 | crtgamal_generate_keys(key, params); 181 | gamal_init_bsgs_table(table, (dig_t) 1L << tablebits); 182 | 183 | for (int iter=0; iter(t2-t1).count(); 191 | 192 | avg_enc += enc_time; 193 | 194 | t1 = high_resolution_clock::now(); 195 | crtgamal_decrypt(&after, key, cipher, table); 196 | t2 = high_resolution_clock::now(); 197 | auto dec_time = duration_cast(t2-t1).count(); 198 | 199 | avg_dec += dec_time; 200 | std::cout << " ENC Time: " << enc_time / 1000000.0 << "ms DEC Time: " << dec_time / 1000000.0 << "ms " << std::endl; 201 | 202 | if (after != plain) 203 | std::cout << "ERROR" << std::endl; 204 | } 205 | avg_enc = avg_enc / num_entries; 206 | avg_dec = avg_dec / num_entries; 207 | std::cout << "Avg ENC Time " << avg_enc / 1000000.0 << "ms Avg DEC Time " << avg_dec / 1000000.0 << "ms " << std::endl; 208 | 209 | crtgamal_ciphertext_free(cipher); 210 | crtgamal_keys_clear(key); 211 | gamal_free_bsgs_table(table); 212 | gamal_deinit(); 213 | } 214 | 215 | int test() { 216 | gamal_key_t key, key_decoded; 217 | gamal_ciphertext_t cipher, cipher_after; 218 | bsgs_table_t table; 219 | dig_t plain = 36435345, res, res2; 220 | unsigned char *buff; 221 | size_t size; 222 | 223 | gamal_init(DEFAULT_CURVE); 224 | 225 | gamal_generate_keys(key); 226 | 227 | 228 | gamal_encrypt(cipher, key, plain); 229 | 230 | std::cout << "key gen + enc ok" << std::endl; 231 | 232 | buff = (unsigned char *) malloc(get_encoded_ciphertext_size(cipher)); 233 | high_resolution_clock::time_point t1 = high_resolution_clock::now(); 234 | encode_ciphertext(buff, 100000, cipher); 235 | high_resolution_clock::time_point t2 = high_resolution_clock::now(); 236 | auto encode_time = duration_cast(t2-t1).count(); 237 | std::cout << " ENCODE " << encode_time / 1000000.0 << std::endl; 238 | 239 | t1 = high_resolution_clock::now(); 240 | decode_ciphertext(cipher_after, buff, 1000000); 241 | t2 = high_resolution_clock::now(); 242 | auto decode_time = duration_cast(t2-t1).count(); 243 | std::cout << " DECODE " << decode_time / 1000000.0 << std::endl; 244 | 245 | free(buff); 246 | 247 | size = get_encoded_key_size(key, 0); 248 | buff = (unsigned char *) malloc(size); 249 | t1 = high_resolution_clock::now(); 250 | encode_key(buff, size, key, 0); 251 | t2 = high_resolution_clock::now(); 252 | auto key_encode_time = duration_cast(t2-t1).count(); 253 | std::cout << " ENCODE KEY " << key_encode_time / 1000000.0 << std::endl; 254 | 255 | t1 = high_resolution_clock::now(); 256 | decode_key(key_decoded, buff, size); 257 | t2 = high_resolution_clock::now(); 258 | auto key_decode_time = duration_cast(t2-t1).count(); 259 | std::cout << " DECODE KEY " << key_decode_time / 1000000.0 << std::endl; 260 | 261 | 262 | gamal_init_bsgs_table(table, 1L << 16); 263 | 264 | gamal_decrypt(&res, key_decoded, cipher_after, table); 265 | 266 | std::cout << "Before: " << plain << " After: " << res << std::endl; 267 | gamal_free_bsgs_table(table); 268 | gamal_deinit(); 269 | return 0; 270 | } 271 | 272 | int main() { 273 | std::cout << OPENSSL_VERSION_TEXT << std::endl; 274 | //test1(); 275 | //test2(); 276 | //test2(); 277 | std::cout << "Plain EC-ElGamal 32-bit integers" << std::endl; 278 | bench_elgamal(10, 16); 279 | std::cout << "CRT optimized EC-ElGamal 32-bit integers" << std::endl; 280 | bench_crtelgamal(1000, 16, 32); 281 | std::cout << "CRT optimized EC-ElGamal 64-bit integers" << std::endl; 282 | bench_crtelgamal(1000, 17, 64); 283 | return 0; 284 | } 285 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | ch.ethz.dsg.ecelgamal 8 | ecelgamal 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 16 | 1.8 17 | 1.8 18 | 19 | 20 | 21 | org.apache.maven.plugins 22 | maven-jar-plugin 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | junit 37 | junit 38 | RELEASE 39 | 40 | 41 | org.slf4j 42 | slf4j-nop 43 | 1.7.7 44 | 45 | 46 | org.scijava 47 | native-lib-loader 48 | 2.2.0 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/main/java/ch/ethz/dsg/ecelgamal/ECElGamal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Author: 6 | * Lukas Burkhalter 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived 19 | * from this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | package ch.ethz.dsg.ecelgamal; 36 | 37 | import java.math.BigInteger; 38 | import java.security.Key; 39 | import java.util.Arrays; 40 | import java.util.HashSet; 41 | import java.util.Random; 42 | 43 | import org.scijava.nativelib.NativeLibraryUtil; 44 | 45 | /** 46 | * Additive homomoprhic EC-El-Gamal wrapper around the native C implementation 47 | */ 48 | public class ECElGamal { 49 | 50 | public static int NID_X9_62_prime192v1 = 409; 51 | public static int NID_X9_62_prime239v1 = 412; 52 | public static int NID_X9_62_prime256v1 = 415; 53 | 54 | private static long[] default32BitParams = {1429L, 1931L, 1733L}; 55 | private static long[] default64BitParams = {6607L, 8011L, 8171L, 8017L, 8111L}; 56 | 57 | static { 58 | try { 59 | NativeLibraryUtil.loadNativeLibrary(ECElGamal.class, "ecelgamal-jni-wrapper"); 60 | } catch (Exception e) { 61 | e.printStackTrace(); 62 | } 63 | 64 | initEcElGamal(NID_X9_62_prime256v1); 65 | } 66 | 67 | public static synchronized void changeGroup(int newGroupID) { 68 | deinitECElGamal(); 69 | initEcElGamal(newGroupID); 70 | } 71 | 72 | private static BigInteger solveCRT(BigInteger[] nums, BigInteger[] ds, BigInteger d) { 73 | BigInteger res = BigInteger.ZERO; 74 | for (int index=0; index d.bitLength()) { 89 | HashSet before = new HashSet<>(numD); 90 | d = BigInteger.ONE; 91 | for (int index = 0; index < numD; index++) { 92 | BigInteger temp; 93 | do { 94 | temp = BigInteger.probablePrime(dBits, rand); 95 | } while (before.contains(temp)); 96 | before.add(temp); 97 | ds[index] = temp; 98 | d = d.multiply(temp); 99 | } 100 | 101 | } 102 | return new CRTParams(ds, d, dBits); 103 | } 104 | 105 | private static CRTParams generateParams(long[] primes, int numBits) { 106 | BigInteger[] ds = new BigInteger[primes.length]; 107 | BigInteger d = BigInteger.ONE; 108 | for (int iter=0; iter < primes.length; iter++) { 109 | ds[iter] = BigInteger.valueOf(primes[iter]); 110 | d = d.multiply(ds[iter]); 111 | } 112 | return new CRTParams(ds, d, numBits); 113 | } 114 | 115 | /** 116 | * Returns the default CRT-Params for 32-bit integers 117 | * @return CRT-pramams for 32-bit integers 118 | */ 119 | public static CRTParams getDefault32BitParams() { 120 | return generateParams(default32BitParams, 11); 121 | } 122 | 123 | /** 124 | * Returns the default CRT-Params for 64-bit integers 125 | * @return CRT-pramams for 64-bit integers 126 | */ 127 | public static CRTParams getDefault64BitParams() { 128 | return generateParams(default64BitParams, 13); 129 | } 130 | 131 | /** 132 | * Generates a new EC-ElGamal key-pair 133 | * @param params the crt-params to attach 134 | * @return the newly generated key 135 | */ 136 | public static ECElGamalKey generateNewKey(CRTParams params) { 137 | return new ECElGamalKey(generateKey(), params); 138 | } 139 | 140 | /** 141 | * Computes the ECElGamalKey based on an encoded key and the CRT-params to attach. 142 | * @param encodedKey an encoded version of the ECElGamalKey 143 | * @param params the global CRT-Params 144 | * @return an ECElGamalKey instance 145 | */ 146 | public static ECElGamalKey restoreKey(byte[] encodedKey, CRTParams params) { 147 | return new ECElGamalKey(encodedKey, params); 148 | } 149 | 150 | /** 151 | * Encrypts an integer with homomorphic EC-ElGamal 152 | * @param integer the integer to encrypt (!pay attention to the bit limits from the CRT-Params!) 153 | * @param key the ECElGamalKey key 154 | * @return the encrypted integer 155 | */ 156 | public static ECElGamalCiphertext encrypt(BigInteger integer, ECElGamalKey key) { 157 | byte[][] ciphertexts = new byte[key.params.ds.length][]; 158 | for(int iter=0; iter 0) { 190 | return res.subtract(key.getParams().d).intValue(); 191 | } 192 | return res.intValue(); 193 | } 194 | 195 | /** 196 | * Decrypts an ECElGamalCiphertext and returns the plaintext integer of type long. 197 | * Supports also negative integers. 198 | * @param ciphertext the EC-ElGamal ciphertext 199 | * @param key the the EC-ElGamal key 200 | * @return the plaintext value of type long 201 | */ 202 | public static long decrypt64(ECElGamalCiphertext ciphertext, ECElGamalKey key) { 203 | BigInteger res = decrypt(ciphertext, key); 204 | if (res.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) { 205 | return res.subtract(key.getParams().d).longValue(); 206 | } 207 | return res.longValue(); 208 | } 209 | 210 | /** 211 | * Adds two EC-El-Gamal ciphertexts and outputs the resulting ciphertext. 212 | * @param c1 first ciphertext 213 | * @param c2 second ciphertext 214 | * @return the resulting ciphertext of the addition 215 | */ 216 | public static ECElGamalCiphertext add(ECElGamalCiphertext c1, ECElGamalCiphertext c2) { 217 | byte[][] result = new byte[c1.getNumPartitions()][]; 218 | for(int iter=0; iter 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived 19 | * from this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | import ch.ethz.dsg.ecelgamal.ECElGamal; 36 | import org.junit.*; 37 | 38 | import java.math.BigInteger; 39 | import java.security.SecureRandom; 40 | import java.util.Random; 41 | 42 | import static junit.framework.TestCase.assertEquals; 43 | 44 | public class ECElGamalTest { 45 | 46 | private ECElGamal.CRTParams params32 = ECElGamal.getDefault32BitParams(); 47 | private ECElGamal.CRTParams params64 = ECElGamal.getDefault64BitParams(); 48 | ECElGamal.ECElGamalKey key32 = ECElGamal.generateNewKey(params32); 49 | ECElGamal.ECElGamalKey key64 = ECElGamal.generateNewKey(params64); 50 | 51 | Random rand = new Random(); 52 | 53 | @BeforeClass 54 | public static void init() { 55 | ECElGamal.initBsgsTable(1 << 14); 56 | } 57 | 58 | @AfterClass 59 | public static void deinit() { 60 | } 61 | 62 | @Test 63 | public void simple() { 64 | int val = 0; 65 | ECElGamal.ECElGamalCiphertext cipher = ECElGamal.encrypt(BigInteger.valueOf(val), key32); 66 | int decriptedVal = ECElGamal.decrypt32(cipher, key32); 67 | assertEquals(decriptedVal, val); 68 | } 69 | 70 | @Test 71 | public void simpleAdd() { 72 | ECElGamal.ECElGamalCiphertext cipher1,cipher2; 73 | int val1 = 2, val2 = -3; 74 | cipher1 = ECElGamal.encrypt(BigInteger.valueOf(val1), key32); 75 | cipher2 = ECElGamal.encrypt(BigInteger.valueOf(val2), key32); 76 | cipher1 = ECElGamal.add(cipher1, cipher2); 77 | int decriptedVal = ECElGamal.decrypt32(cipher1, key32); 78 | assertEquals(val1 + val2, decriptedVal); 79 | } 80 | 81 | @Test 82 | public void randTestInt() { 83 | int val1, val2; 84 | for (int i=0; i<100; i++) { 85 | val1 = rand.nextInt()/2; 86 | val2 = rand.nextInt()/2; 87 | 88 | ECElGamal.ECElGamalCiphertext cipher1, cipher2; 89 | 90 | cipher1 = ECElGamal.encrypt(BigInteger.valueOf(val1), key32); 91 | cipher2 = ECElGamal.encrypt(BigInteger.valueOf(val2), key32); 92 | cipher1 = ECElGamal.add(cipher1, cipher2); 93 | int decriptedVal = ECElGamal.decrypt32(cipher1, key32); 94 | assertEquals(val1 + val2, decriptedVal); 95 | System.out.println("ok " + i); 96 | } 97 | } 98 | 99 | @Test 100 | public void randTestLong() { 101 | long val1, val2; 102 | for (int i=0; i<100; i++) { 103 | val1 = rand.nextLong()/2; 104 | val2 = rand.nextLong()/2; 105 | 106 | ECElGamal.ECElGamalCiphertext cipher1, cipher2; 107 | 108 | cipher1 = ECElGamal.encrypt(BigInteger.valueOf(val1), key64); 109 | cipher2 = ECElGamal.encrypt(BigInteger.valueOf(val2), key64); 110 | cipher1 = ECElGamal.add(cipher1, cipher2); 111 | long decriptedVal = ECElGamal.decrypt64(cipher1, key64); 112 | assertEquals(val1 + val2, decriptedVal); 113 | System.out.println("ok " + i); 114 | } 115 | } 116 | 117 | 118 | 119 | private static double convertMS(long val) { 120 | return ((double) val) / 1000000.0; 121 | } 122 | 123 | @Test 124 | public void measureTime() { 125 | long val1, val2, timeadd; 126 | int add = 10; 127 | for (int i=0; i<100; i++) { 128 | val1 = rand.nextLong()/10000; 129 | val2 = rand.nextLong()/10000; 130 | 131 | ECElGamal.ECElGamalCiphertext cipher1, cipher2; 132 | 133 | long encrypt = System.nanoTime(); 134 | cipher1 = ECElGamal.encrypt(BigInteger.valueOf(val1), key64); 135 | encrypt = System.nanoTime() - encrypt; 136 | 137 | cipher2 = ECElGamal.encrypt(BigInteger.valueOf(val2), key64); 138 | long addTime = System.nanoTime(); 139 | for(int it=0; it