__init__.py
1 #!/usr/bin/env python 2 """ 3 Wrapper for liboprf library 4 5 SPDX-FileCopyrightText: 2023, Marsiske Stefan 6 SPDX-License-Identifier: LGPL-3.0-or-later 7 8 Copyright (c) 2023, Marsiske Stefan. 9 All rights reserved. 10 11 This file is part of liboprf. 12 13 liboprf is free software: you can redistribute it and/or 14 modify it under the terms of the GNU Lesser General Public License 15 as published by the Free Software Foundation, either version 3 of 16 the License, or (at your option) any later version. 17 18 liboprf is distributed in the hope that it will be 19 useful, but WITHOUT ANY WARRANTY; without even the implied 20 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 21 See the GNU Lesser General Public License for more details. 22 23 You should have received a copy of the GNU Lesser General Public License 24 along with liboprf. If not, see <http://www.gnu.org/licenses/>. 25 26 """ 27 28 import ctypes 29 import ctypes.util 30 import pysodium, os 31 import platform, sys 32 from typing import List, Tuple 33 from itertools import zip_longest 34 35 if "BYZANTINE_DKG" in os.environ: 36 liboprf = ctypes.cdll.LoadLibrary(os.environ['BYZANTINE_DKG']) 37 print("\x1b[1m\x1b[31mwarning: loading intentionally corrupting version of liboprf, only use this for testing!\x1b[0m", file=sys.stderr) 38 else: 39 liboprf = ctypes.cdll.LoadLibrary(ctypes.util.find_library('oprf') or ctypes.util.find_library('liboprf')) 40 41 if not liboprf._name: 42 raise ValueError('Unable to find liboprf') 43 44 def split_by_n(iterable, n): 45 return list(zip_longest(*[iter(iterable)]*n, fillvalue='')) 46 47 def __check(code): 48 if code != 0: 49 raise ValueError(f"error: {code}") 50 51 # (CFRG/IRTF) OPRF section 52 53 OPRF_BYTES=64 54 55 # This function generates an OPRF private key. 56 # 57 # This is almost the KeyGen OPRF function defined in the RFC: since 58 # this lib does not implement V oprf, we don't need a pubkey and so 59 # we don't bother with all that is related. 60 # 61 # @param [out] k - the per-user OPRF private key 62 # void oprf_KeyGen(uint8_t kU[crypto_core_ristretto255_SCALARBYTES]); 63 def keygen() -> bytes: 64 k = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_SCALARBYTES) 65 liboprf.oprf_KeyGen(k) 66 return k.raw 67 68 69 # This function converts input x into an element of the OPRF group, randomizes it 70 # by some scalar r, producing blinded, and outputs (r, blinded). 71 # 72 # This is the Blind OPRF function defined in the RFC. 73 # 74 # @param [in] x - the input value to blind 75 # @param [out] r - an OPRF scalar value used for randomization 76 # @param [out] blinded - a serialized OPRF group element, a byte array of fixed length, 77 # the blinded version of x, an input to oprf_Evaluate 78 # @return The function raises a ValueError if there is something wrong with the inputs. 79 # 80 #int oprf_Blind(const uint8_t *x, const uint16_t x_len, 81 # uint8_t r[crypto_core_ristretto255_SCALARBYTES], 82 # uint8_t blinded[crypto_core_ristretto255_BYTES]); 83 def blind(x: bytes) -> (bytes, bytes): 84 r = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_SCALARBYTES) 85 blinded = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_BYTES) 86 __check(liboprf.oprf_Blind(x, ctypes.c_size_t(len(x)), r, blinded)) 87 return r.raw, blinded.raw 88 89 90 # This function evaluates input element blinded using private key k, yielding output 91 # element Z. 92 # 93 # This is the Evaluate OPRF function defined in the RFC. 94 # 95 # @param [in] key - a private key - the output of keygen() 96 # @param [in] blinded - a serialized OPRF group element, a byte array 97 # of fixed length, an output of blind() 98 # @param [out] Z - a serialized OPRF group element, a byte array of fixed 99 # length, an input to oprf_Unblind 100 # @return The function raises a ValueError if there is something wrong with the inputs. 101 #int oprf_Evaluate(const uint8_t k[crypto_core_ristretto255_SCALARBYTES], 102 # const uint8_t blinded[crypto_core_ristretto255_BYTES], 103 # uint8_t Z[crypto_core_ristretto255_BYTES]); 104 def evaluate(key: bytes, blinded: bytes) -> bytes: 105 if len(key) != pysodium.crypto_core_ristretto255_SCALARBYTES: 106 raise ValueError("key has incorrect length") 107 if not isinstance(key, bytes): 108 raise ValueError("key is not of type bytes") 109 if len(blinded) != pysodium.crypto_core_ristretto255_BYTES: 110 raise ValueError("blinded param has incorrect length") 111 if not isinstance(blinded, bytes): 112 raise ValueError("blinded is not of type bytes") 113 Z = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_BYTES) 114 __check(liboprf.oprf_Evaluate(key, blinded, Z)) 115 return Z.raw 116 117 118 # This function removes random scalar r from Z, yielding output N. 119 # 120 # This is the Unblind OPRF function defined in the RFC. 121 # 122 # If you do not call finalize() on the result the output is equivalent 123 # to the OPRF protcol we refer to as HashDH - this protocol retains 124 # the algebraic structure of the value, and has weaker security 125 # guarantees, than the full 2HashDH which is equivalent to running 126 # finalize on the output of blind(). The hashDH variant is not 127 # explicitly specified by the CFRG/IRTF specification. This hashDH 128 # variant has one property that makes it interesting: it is an 129 # updateable OPRF - that is if the server updates their key, they can 130 # calculate a public delta value, that can be applied by the client to 131 # the output of blind() and the result will be as if the client and 132 # the server run the OPRF protocol with the original input and the new 133 # key. It is important to note that the delta value is not sensitive, 134 # and can be public. 135 # 136 # @param [in] r - an OPRF scalar value used for randomization in oprf_Blind 137 # @param [in] Z - a serialized OPRF group element, a byte array of fixed length, 138 # an output of oprf_Evaluate 139 # @param [out] N - a serialized OPRF group element with random scalar r removed, 140 # a byte array of fixed length, an input to oprf_Finalize 141 # @return The function raises a ValueError if there is something wrong with the inputs. 142 #int oprf_Unblind(const uint8_t r[crypto_core_ristretto255_SCALARBYTES], 143 # const uint8_t Z[crypto_core_ristretto255_BYTES], 144 # uint8_t N[crypto_core_ristretto255_BYTES]); 145 def unblind(r: bytes, Z: bytes) -> bytes: 146 if len(r) != pysodium.crypto_core_ristretto255_SCALARBYTES: 147 raise ValueError("param r has incorrect length") 148 if not isinstance(r, bytes): 149 raise ValueError("param r is not of type bytes") 150 if len(Z) != pysodium.crypto_core_ristretto255_BYTES: 151 raise ValueError("param Z has incorrect length") 152 if not isinstance(Z, bytes): 153 raise ValueError("param Z is not of type bytes") 154 N = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_BYTES) 155 __check(liboprf.oprf_Unblind(r, Z, N)) 156 return N.raw 157 158 # This function computes the OPRF output using input x, N, and domain 159 # separation tag info. 160 # 161 # This is the Finalize OPRF function defined in the RFC. 162 # 163 # @param [in] x - a value used to compute OPRF (the same value that 164 # was used as input to be blinded) 165 # @param [in] N - a serialized OPRF group element, a byte array of fixed length, 166 # an output of oprf_Unblind 167 # @param [out] y - an OPRF output 168 # @return The function raises a ValueError if there is something wrong with the inputs. 169 #int oprf_Finalize(const uint8_t *x, const uint16_t x_len, 170 # const uint8_t N[crypto_core_ristretto255_BYTES], 171 # uint8_t rwdU[OPRF_BYTES]); 172 def finalize(x: bytes, N: bytes) -> bytes: 173 if len(N) != pysodium.crypto_core_ristretto255_BYTES: 174 raise ValueError("param N has incorrect length") 175 if not isinstance(N, bytes): 176 raise ValueError("param N is not of type bytes") 177 y = ctypes.create_string_buffer(OPRF_BYTES) 178 __check(liboprf.oprf_Finalize(x, ctypes.c_size_t(len(x)), N, y)) 179 return y.raw 180 181 # This function combines unblind() and finalize() as a convenience 182 def unblind_finalize(r: bytes, Z: bytes, x: bytes) -> bytes: 183 return finalize(x, unblind(r,Z)) 184 185 # TOPRF section 186 187 TOPRF_Share_BYTES=pysodium.crypto_core_ristretto255_SCALARBYTES+1 188 TOPRF_Part_BYTES=pysodium.crypto_core_ristretto255_BYTES+1 189 190 # This function calculates a lagrange coefficient based on the index 191 # and the indexes of the other contributing shareholders. 192 # 193 # @param [in] index - the index of the shareholder whose lagrange 194 # coefficient we're calculating, must be greater than 0 195 # 196 # @param [in] peers - list of the shares that contribute to the reconstruction 197 # 198 # @param [out] result - the lagrange coefficient 199 #void coeff(const int index, const int peers_len, const uint8_t peers[peers_len], uint8_t result[crypto_scalarmult_ristretto255_SCALARBYTES]); 200 def coeff(index: int, peers: list) -> bytes: 201 if index < 1: raise ValueError("index must be positive integer") 202 if len(peers) < 2: raise ValueError("peers must be a list of at least 2 integers") 203 peers_len=ctypes.c_size_t(len(peers)) 204 c = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_SCALARBYTES) 205 liboprf.coeff(index, peers_len, peers, c) 206 return c.raw 207 208 209 # This function creates shares of secret in a (threshold, n) scheme 210 # over the curve ristretto255 211 # 212 # @param [in] secret - the scalar value to be secretly shared 213 # 214 # @param [in] n - the number of shares created 215 # 216 # @param [in] threshold - the threshold needed to reconstruct the secret 217 # 218 # @param [out] shares - n shares 219 # 220 # @return The function raises a ValueError if there is something wrong with the inputs. 221 #void toprf_create_shares(const uint8_t secret[crypto_core_ristretto255_SCALARBYTES], 222 # const uint8_t n, 223 # const uint8_t threshold, 224 # uint8_t shares[n][TOPRF_Share_BYTES]); 225 bytes_list_t = List[bytes] 226 def create_shares(secret: bytes, n: int, t: int) -> bytes_list_t: 227 if len(secret) != pysodium.crypto_core_ristretto255_SCALARBYTES: 228 raise ValueError("secret has incorrect length") 229 if not isinstance(secret, bytes): 230 raise ValueError("secret is not of type bytes") 231 if n < t: 232 raise ValueError("t cannot be bigger than n") 233 if t < 2: 234 raise ValueError("t must be bigger than 1") 235 shares = ctypes.create_string_buffer(n*TOPRF_Share_BYTES) 236 __check(liboprf.toprf_create_shares(secret, n, t, shares)) 237 return tuple([bytes(s) for s in split_by_n(shares.raw, TOPRF_Share_BYTES)]) 238 239 240 # This function recovers the secret in the exponent using lagrange interpolation 241 # over the curve ristretto255 242 # 243 # The shareholders are not aware if they are contributing to a 244 # threshold or non-threshold oprf evaluation, from their perspective 245 # nothing changes in this approach. 246 # 247 # @param [in] responses - is an array of shares (k_i) multiplied by a 248 # point (P) on the r255 curve 249 # 250 # @param [in] responses_len - the number of elements in the response array 251 # 252 # @param [out] result - the reconstructed value of P multipled by k 253 # 254 # @return The function raises a ValueError if there is something wrong with the inputs. 255 #int toprf_thresholdmult(const size_t response_len, 256 # const uint8_t responses[response_len][TOPRF_Part_BYTES], 257 # uint8_t result[crypto_scalarmult_ristretto255_BYTES]); 258 def thresholdmult(responses: bytes_list_t) -> bytes: 259 if len(responses) < 2: raise ValueError("responses must be a list of at least 2 integers") 260 if not all(isinstance(r,bytes) for r in responses): 261 raise ValueError("at least one of the responses is not of type bytes") 262 if not all(len(r)==TOPRF_Part_BYTES for r in responses): 263 raise ValueError("at least one of the responses is not of correct size") 264 responses_len=ctypes.c_size_t(len(responses)) 265 responses_buf = ctypes.create_string_buffer(b''.join(responses)) 266 result = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_BYTES) 267 __check(liboprf.toprf_thresholdmult(responses_len, responses_buf, result)) 268 return result.raw 269 270 271 # This function is the efficient threshold version of oprf_Evaluate. 272 # 273 # This function needs to know in advance the indexes of all the 274 # shares that will be combined later in the toprf_thresholdcombine() function. 275 # by doing so this reduces the total costs and distributes them to the shareholders. 276 # 277 # @param [in] k - a private key (for OPAQUE, this is kU, the user's 278 # OPRF private key) 279 # 280 # @param [in] blinded - a serialized OPRF group element, a byte array 281 # of fixed length, an output of oprf_Blind (for OPAQUE, this 282 # is the blinded pwdU, the user's password) 283 # 284 # @param [in] self - the index of the current shareholder 285 # 286 # @param [in] indexes - the indexes of the all the shareholders 287 # contributing to this oprf evaluation, 288 # 289 # @param [in] index_len - the length of the indexes array, 290 # 291 # @param [out] Z - a serialized OPRF group element, a byte array of fixed length, 292 # an input to oprf_Unblind 293 # 294 # @return The function raises a ValueError if there is something wrong with the inputs. 295 #int toprf_Evaluate(const uint8_t k[TOPRF_Share_BYTES], 296 # const uint8_t blinded[crypto_core_ristretto255_BYTES], 297 # const uint8_t self, const uint8_t *indexes, const uint16_t index_len, 298 # uint8_t Z[TOPRF_Part_BYTES]); 299 def threshold_evaluate(k: bytes, blinded: bytes, self: int, indexes: list) -> bytes: 300 if len(k) != TOPRF_Share_BYTES: 301 raise ValueError("param k has incorrect length") 302 if not isinstance(k, bytes): 303 raise ValueError("param k is not of type bytes") 304 if len(blinded) != pysodium.crypto_core_ristretto255_BYTES: 305 raise ValueError("blinded param has incorrect length") 306 if not isinstance(blinded, bytes): 307 raise ValueError("blinded is not of type bytes") 308 if(self>255 or self<1): 309 raise ValueError("self outside valid range") 310 if(not all(i>0 and i<256 for i in indexes)): 311 raise ValueError("index(es) outside valid range") 312 index_len=ctypes.c_uint16(len(indexes)) 313 314 indexes_buf=ctypes.create_string_buffer(bytes(indexes)) 315 Z = ctypes.create_string_buffer(TOPRF_Part_BYTES) 316 317 __check(liboprf.toprf_Evaluate(k, blinded, self, indexes_buf, index_len, Z)) 318 return Z.raw 319 320 # This function is combines the results of the toprf_Evaluate() 321 # function to recover the shared secret in the exponent. 322 # 323 # @param [in] responses - is an array of shares (k_i) multiplied by a point (P) on the r255 curve 324 # 325 # @param [in] responses_len - the number of elements in the response array 326 # 327 # @param [out] result - the reconstructed value of P multipled by k 328 # 329 # @return The function raises a ValueError if there is something wrong with the inputs. 330 #void toprf_thresholdcombine(const size_t response_len, 331 # const uint8_t _responses[response_len][TOPRF_Part_BYTES], 332 # uint8_t result[crypto_scalarmult_ristretto255_BYTES]); 333 def threshold_combine(responses: bytes_list_t) -> bytes: 334 if len(responses) < 2: raise ValueError("responses must be a list of at least 2 integers") 335 if not all(isinstance(r,bytes) for r in responses): 336 raise ValueError("at least one of the responses is not of type bytes") 337 if not all(len(r)==TOPRF_Part_BYTES for r in responses): 338 raise ValueError("at least one of the responses is not of correct size") 339 responses_len=ctypes.c_size_t(len(responses)) 340 responses_buf = ctypes.create_string_buffer(b''.join(responses)) 341 result = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_BYTES) 342 343 __check(liboprf.toprf_thresholdcombine(responses_len, responses_buf, result)) 344 return result.raw 345 346 #int toprf_3hashtdh(const uint8_t k[TOPRF_Share_BYTES], 347 # const uint8_t z[TOPRF_Share_BYTES], 348 # const uint8_t alpha[crypto_core_ristretto255_BYTES], 349 # const uint8_t *ssid_S, const uint16_t ssid_S_len, 350 # uint8_t beta[TOPRF_Part_BYTES]); 351 def _3hashtdh(k: bytes, z: bytes, alpha: bytes, ssid_S: bytes) -> bytes: 352 if len(k) != TOPRF_Share_BYTES: 353 raise ValueError("param k has incorrect length") 354 if not isinstance(k, bytes): 355 raise ValueError("param k is not of type bytes") 356 if len(z) != TOPRF_Share_BYTES: 357 raise ValueError("param z has incorrect length") 358 if not isinstance(z, bytes): 359 raise ValueError("param z is not of type bytes") 360 if len(alpha) != pysodium.crypto_core_ristretto255_BYTES: 361 raise ValueError("alpha param has incorrect length") 362 if not isinstance(alpha, bytes): 363 raise ValueError("alpha is not of type bytes") 364 if not isinstance(ssid_S, bytes): 365 raise ValueError("ssid_S is not of type bytes") 366 if len(ssid_S) > (1<<16)-1: 367 raise ValueError("ssid_S is too long") 368 369 ssid_S_len=ctypes.c_uint16(len(ssid_S)) 370 beta = ctypes.create_string_buffer(TOPRF_Part_BYTES) 371 __check(liboprf.toprf_3hashtdh(k, z, alpha, ssid_S, ssid_S_len, beta)) 372 return beta.raw 373 374 # todo documentation! 375 #int dkg_start(const uint8_t n, 376 # const uint8_t threshold, 377 # uint8_t commitment_hash[dkg_hash_BYTES], 378 # uint8_t commitments[dkg_commitment_BYTES(threshold)], 379 # TOPRF_Share shares[n]); 380 def dkg_start(n : int, t : int) -> (bytes, bytes, bytes_list_t): 381 if n < t: 382 raise ValueError("t cannot be bigger than n") 383 if t < 2: 384 raise ValueError("t must be bigger than 1") 385 shares = ctypes.create_string_buffer(n*TOPRF_Share_BYTES) 386 commitments = ctypes.create_string_buffer(t*pysodium.crypto_core_ristretto255_BYTES) 387 388 __check(liboprf.dkg_start(n, t, commitments, shares)) 389 390 shares = tuple([bytes(s) for s in split_by_n(shares.raw, TOPRF_Share_BYTES)]) 391 return commitments.raw, shares 392 393 #int dkg_verify_commitments(const uint8_t n, 394 # const uint8_t threshold, 395 # const uint8_t self, 396 # const uint8_t commitments[n][threshold*crypto_core_ristretto255_BYTES], 397 # const TOPRF_Share shares[n], 398 # uint8_t fails[n], 399 # uint8_t *fails_len); 400 def dkg_verify_commitments(n: int, t: int, self: int, 401 commitments : bytes_list_t, 402 shares: bytes_list_t) -> bytes: 403 if n < t: 404 raise ValueError("t cannot be bigger than n") 405 if t < 2: 406 raise ValueError("t must be bigger than 1") 407 if self < 1 or self > n: 408 raise ValueError("self must 1 <= self <= n") 409 if len(commitments) != n*t*pysodium.crypto_core_ristretto255_BYTES: 410 raise ValueError(f"signed_commitments must be {n*t*pysodium.crypto_core_ristretto255_BYTES} bytes is instead: {len(commitments)}") 411 shares = b''.join(shares) 412 if len(shares) != n*TOPRF_Share_BYTES: 413 raise ValueError(f"shares must be {TOPRF_Share_BYTES*n} bytes is instead {len(shares)}") 414 415 shares = ctypes.create_string_buffer(shares) 416 fails = ctypes.create_string_buffer(n) 417 fails_len = ctypes.c_uint8() 418 __check(liboprf.dkg_verify_commitments(n, t, self, 419 commitments, shares, 420 fails, ctypes.byref(fails_len))) 421 return fails[:fails_len.value] 422 423 #void dkg_finish(const uint8_t n, 424 # const TOPRF_Share shares[n], 425 # const uint8_t self, 426 # TOPRF_Share *xi); 427 def dkg_finish(n: int, shares: List[bytes], self: int, ) -> bytes: 428 if self < 1 or self > n: 429 raise ValueError("self must 1 <= self <= n") 430 shares = b''.join(shares) 431 if len(shares) != n*TOPRF_Share_BYTES: 432 raise ValueError(f"shares must be {TOPRF_Share_BYTES*n} bytes is instead {len(shares)}") 433 434 shares = ctypes.create_string_buffer(shares) 435 436 xi = ctypes.create_string_buffer(TOPRF_Share_BYTES) 437 xi[0]=self 438 439 liboprf.dkg_finish(n, shares, self, xi) 440 return xi.raw 441 442 #void dkg_reconstruct(const size_t response_len, 443 # const TOPRF_Share responses[response_len][2], 444 # uint8_t result[crypto_scalarmult_ristretto255_BYTES]); 445 def dkg_reconstruct(responses) -> bytes_list_t: 446 rlen = len(responses) 447 responses = ctypes.create_string_buffer(b''.join(responses)) 448 result = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_BYTES) 449 450 liboprf.dkg_reconstruct(rlen, responses, result) 451 return result.raw 452 453 tpdkg_sessionid_SIZE=32 454 tpdkg_msg0_SIZE = 179 # ( sizeof(TP_DKG_Message) \ 455 # + crypto_generichash_BYTES/*dst*/ \ 456 # + 2 /*n,t*/ \ 457 # + crypto_sign_PUBLICKEYBYTES /* tp_sign_pk */) 458 tpdkg_msg8_SIZE = 258 # (sizeof(TP_DKG_Message) /* header */ \ 459 # + noise_xk_handshake3_SIZE /* 4th&final noise handshake */ \ 460 # + sizeof(TOPRF_Share) /* msg: the noise_xk wrapped share */ \ 461 # + crypto_secretbox_xchacha20poly1305_MACBYTES /* mac of msg */ \ 462 # + crypto_auth_hmacsha256_BYTES /* key-committing mac over msg*/ ) 463 tpdkg_max_err_SIZE = 128 464 465 class TP_DKG_Cheater(ctypes.Structure): 466 _fields_ = [('step', ctypes.c_int), 467 ('error', ctypes.c_int), 468 ('peer', ctypes.c_uint8), 469 ('other_peer', ctypes.c_uint8), 470 ('invalid_index', ctypes.c_int), 471 ] 472 473 #int tpdkg_start_tp(TP_DKG_TPState *ctx, const uint64_t ts_epsilon, 474 # const uint8_t n, const uint8_t t, 475 # const char *proto_name, const size_t proto_name_len, 476 # const size_t msg0_len, TP_DKG_Message *msg0); 477 # 478 # also wraps conveniently: 479 # 480 # void tpdkg_tp_set_bufs(TP_DKG_TPState *ctx, 481 # uint8_t (*commitments)[][crypto_core_ristretto255_BYTES], 482 # uint16_t (*complaints)[], 483 # uint8_t (*suspicious)[], 484 # uint8_t (*tp_peers_sig_pks)[][crypto_sign_PUBLICKEYBYTES], 485 # uint8_t (*peer_lt_pks)[][crypto_sign_PUBLICKEYBYTES], 486 # uint64_t (*last_ts)[]); 487 def tpdkg_start_tp(n, t, ts_epsilon, proto_name, peer_lt_pks): 488 b = ctypes.create_string_buffer(liboprf.tpdkg_tpstate_size()+32) 489 b_addr = ctypes.addressof(b) 490 s_addr = b_addr + (b_addr % 32) 491 state = ctypes.c_void_p(s_addr) 492 if state.value % 32 != 0: 493 raise ValueError("cannot align at 32bytes the TP_DKG_TPState struct") 494 495 msg = ctypes.create_string_buffer(tpdkg_msg0_SIZE) 496 __check(liboprf.tpdkg_start_tp(state, ctypes.c_uint64(ts_epsilon), ctypes.c_uint8(n), ctypes.c_uint8(t), proto_name, ctypes.c_size_t(len(proto_name)), ctypes.c_size_t(len(msg.raw)), msg)) 497 498 peers_sig_pks = ctypes.create_string_buffer(n*pysodium.crypto_sign_PUBLICKEYBYTES) 499 commitments = ctypes.create_string_buffer(n*t*pysodium.crypto_core_ristretto255_BYTES) 500 complaints = ctypes.create_string_buffer(n*n*2) 501 noisy_shares = ctypes.create_string_buffer(n*n*tpdkg_msg8_SIZE) 502 cheaters = (TP_DKG_Cheater * (t*t - 1))() 503 peer_lt_pks = b''.join(peer_lt_pks) 504 last_ts = (ctypes.c_uint64 * n)() 505 506 liboprf.tpdkg_tp_set_bufs(state, 507 ctypes.byref(commitments), 508 ctypes.byref(complaints), 509 ctypes.byref(noisy_shares), 510 ctypes.byref(cheaters), 511 len(cheaters), 512 ctypes.byref(peers_sig_pks), 513 peer_lt_pks, 514 ctypes.byref(last_ts)) 515 516 # we need to keep these arrays around, otherwise the gc eats them up. 517 ctx = (state, cheaters, peers_sig_pks, commitments, complaints, noisy_shares, peer_lt_pks, last_ts, b) 518 519 return ctx, msg.raw 520 521 522 #size_t tpdkg_tp_input_size(const TP_DKG_TPState *ctx); 523 def tpdkg_tp_input_size(ctx): 524 return liboprf.tpdkg_tp_input_size(ctx[0]) 525 526 #int tpdkg_tp_input_sizes(const TP_DKG_TPState *ctx, size_t *sizes); 527 def tpdkg_tp_input_sizes(ctx): 528 sizes = (ctypes.c_size_t * tpdkg_tpstate_n(ctx))() 529 ret = liboprf.tpdkg_tp_input_sizes(ctx[0], ctypes.byref(sizes)) 530 return ret, [x for x in sizes] 531 532 #size_t tpdkg_tp_output_size(const TP_DKG_TPState *ctx); 533 def tpdkg_tp_output_size(ctx): 534 return liboprf.tpdkg_tp_output_size(ctx[0]) 535 536 #int tpdkg_tp_next(TP_DKG_TPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); 537 def tpdkg_tp_next(ctx, msg): 538 input_len = tpdkg_tp_input_size(ctx) 539 if len(msg) != input_len: raise ValueError(f"input msg is invalid size: {len(msg)}B must be: {input_len}B") 540 output_len = tpdkg_tp_output_size(ctx) 541 output = ctypes.create_string_buffer(output_len) 542 __check(liboprf.tpdkg_tp_next(ctx[0], msg, ctypes.c_size_t(input_len), output, ctypes.c_size_t(output_len))) 543 return output 544 545 #int tpdkg_tp_peer_msg(const TP_DKG_TPState *ctx, const uint8_t *base, const size_t base_size, const uint8_t peer, const uint8_t **msg, size_t *len); 546 def tpdkg_tp_peer_msg(ctx, base, peer): 547 msg = ctypes.POINTER(ctypes.c_char)() 548 size = ctypes.c_size_t() 549 __check(liboprf.tpdkg_tp_peer_msg(ctx[0], base, len(base.raw), peer, ctypes.byref(msg), ctypes.byref(size))) 550 msg = b''.join([msg[i] for i in range(size.value)]) 551 return msg 552 553 #int tpdkg_tp_not_done(const TP_DKG_TPState *tp); 554 def tpdkg_tp_not_done(ctx): 555 return liboprf.tpdkg_tp_not_done(ctx[0]) == 1 556 557 def tpdkg_get_cheaters(ctx): 558 cheats = [] 559 cheaters = set() 560 for i in range(tpdkg_tpstate_cheater_len(ctx)): 561 err = ctypes.create_string_buffer(tpdkg_max_err_SIZE) 562 p = liboprf.tpdkg_cheater_msg(ctypes.byref(ctx[1][i]), err, tpdkg_max_err_SIZE) 563 if 0 >= p > tpdkg_tpstate_n(ctx): 564 print(f"invalid cheater index: {p}, skipping this entry") 565 continue 566 cheaters.add(p) 567 cheats.append((p, err.raw[:err.raw.find(b'\x00')].decode('utf8'))) 568 return cheaters, cheats 569 570 liboprf.tpdkg_peerstate_n.restype = ctypes.c_uint8 571 def tpdkg_peerstate_n(ctx): 572 return liboprf.tpdkg_peerstate_n(ctx[0]) 573 liboprf.tpdkg_peerstate_t.restype = ctypes.c_uint8 574 def tpdkg_peerstate_t(ctx): 575 return liboprf.tpdkg_peerstate_t(ctx[0]) 576 liboprf.tpdkg_peerstate_sessionid.restype = ctypes.POINTER(ctypes.c_uint8) 577 def tpdkg_peerstate_sessionid(ctx): 578 ptr = liboprf.tpdkg_peerstate_sessionid(ctx[0]) 579 return bytes(ptr[i] for i in range(tpdkg_sessionid_SIZE)) 580 liboprf.tpdkg_peerstate_lt_sk.restype = ctypes.POINTER(ctypes.c_uint8) 581 def tpdkg_peerstate_lt_sk(ctx): 582 ptr = liboprf.tpdkg_peerstate_lt_sk(ctx[0]) 583 return bytes(ptr[i] for i in range(pysodium.crypto_sign_SECRETKEYBYTES)) 584 liboprf.tpdkg_peerstate_share.restype = ctypes.POINTER(ctypes.c_uint8) 585 def tpdkg_peerstate_share(ctx): 586 ptr = liboprf.tpdkg_peerstate_share(ctx[0]) 587 return bytes(ptr[i] for i in range(TOPRF_Share_BYTES)) 588 def tpdkg_peerstate_step(ctx): 589 return liboprf.tpdkg_peerstate_step(ctx[0]) 590 591 liboprf.tpdkg_tpstate_n.restype = ctypes.c_uint8 592 def tpdkg_tpstate_n(ctx): 593 return liboprf.tpdkg_tpstate_n(ctx[0]) 594 liboprf.tpdkg_tpstate_t.restype = ctypes.c_uint8 595 def tpdkg_tpstate_t(ctx): 596 return liboprf.tpdkg_tpstate_t(ctx[0]) 597 liboprf.tpdkg_tpstate_cheater_len.restype = ctypes.c_size_t 598 def tpdkg_tpstate_cheater_len(ctx): 599 return liboprf.tpdkg_tpstate_cheater_len(ctx[0]) 600 liboprf.tpdkg_tpstate_sessionid.restype = ctypes.POINTER(ctypes.c_uint8) 601 def tpdkg_tpstate_sessionid(ctx): 602 ptr = liboprf.tpdkg_tpstate_sessionid(ctx[0]) 603 return bytes(ptr[i] for i in range(tpdkg_sessionid_SIZE)) 604 def tpdkg_tpstate_step(ctx): 605 return liboprf.tpdkg_tpstate_step(ctx[0]) 606 607 #int tpdkg_start_peer(TP_DKG_PeerState *ctx, const uint64_t ts_epsilon, 608 # const uint8_t peer_lt_sk[crypto_sign_SECRETKEYBYTES], 609 # const TP_DKG_Message *msg0); 610 # 611 # also wraps conveniently 612 # 613 #void tpdkg_peer_set_bufs(TP_DKG_PeerState *ctx, 614 # uint8_t (*peers_sig_pks)[][crypto_sign_PUBLICKEYBYTES], 615 # uint8_t (*peers_noise_pks)[][crypto_scalarmult_BYTES], 616 # Noise_XK_session_t *(*noise_outs)[], 617 # Noise_XK_session_t *(*noise_ins)[], 618 # TOPRF_Share (*shares)[], 619 # TOPRF_Share (*xshares)[], 620 # uint8_t (*commitments)[][crypto_core_ristretto255_BYTES], 621 # uint16_t (*complaints)[], 622 # uint8_t (*my_complaints)[]); 623 def tpdkg_peer_start(ts_epsilon, peer_lt_sk, msg0): 624 b = ctypes.create_string_buffer(liboprf.tpdkg_peerstate_size()+32) 625 b_addr = ctypes.addressof(b) 626 s_addr = b_addr + (b_addr % 32) 627 state = ctypes.c_void_p(s_addr) 628 if state.value % 32 != 0: 629 raise ValueError("cannot align at 32bytes the TP_DKG_PeerState struct") 630 631 __check(liboprf.tpdkg_start_peer(state, ctypes.c_uint64(ts_epsilon), peer_lt_sk, msg0)) 632 633 n = tpdkg_peerstate_n([state]) 634 t = tpdkg_peerstate_t([state]) 635 636 peers_sig_pks = ctypes.create_string_buffer(b"peer_sig_pks", n * pysodium.crypto_sign_PUBLICKEYBYTES) 637 peers_noise_pks = ctypes.create_string_buffer(b"peer_noise_pks", n * pysodium.crypto_scalarmult_BYTES) 638 noise_outs = (ctypes.c_void_p * n)() 639 noise_ins = (ctypes.c_void_p * n)() 640 shares = ctypes.create_string_buffer(n * TOPRF_Share_BYTES) 641 xshares = ctypes.create_string_buffer(n * TOPRF_Share_BYTES) 642 commitments = ctypes.create_string_buffer(n * t * pysodium.crypto_core_ristretto255_BYTES) 643 complaints = ctypes.create_string_buffer(n * n * 2) 644 my_complaints = ctypes.create_string_buffer(n) 645 last_ts = (ctypes.c_uint64 * n)() 646 liboprf.tpdkg_peer_set_bufs(state, 647 ctypes.byref(peers_sig_pks), 648 ctypes.byref(peers_noise_pks), 649 noise_outs, 650 noise_ins, 651 ctypes.byref(shares), 652 ctypes.byref(xshares), 653 ctypes.byref(commitments), 654 ctypes.byref(complaints), 655 ctypes.byref(my_complaints), 656 ctypes.byref(last_ts)) 657 658 # we need to keep these arrays around, otherwise the gc eats them up. 659 ctx = (state, peers_sig_pks, peers_noise_pks, noise_outs, noise_ins, shares, xshares, commitments, complaints, my_complaints, b, last_ts) 660 return ctx 661 662 #size_t tpdkg_peer_input_size(const TP_DKG_PeerState *ctx); 663 def tpdkg_peer_input_size(ctx): 664 return liboprf.tpdkg_peer_input_size(ctx[0]) 665 666 #size_t tpdkg_peer_output_size(const TP_DKG_PeerState *ctx); 667 def tpdkg_peer_output_size(ctx): 668 return liboprf.tpdkg_peer_output_size(ctx[0]) 669 670 #int tpdkg_peer_next(TP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); 671 def tpdkg_peer_next(ctx, msg): 672 input_len = tpdkg_peer_input_size(ctx) 673 if len(msg) != input_len: raise ValueError(f"input msg is invalid size: {len(msg)}B must be: {input_len}B") 674 output_len = tpdkg_peer_output_size(ctx) 675 output = ctypes.create_string_buffer(output_len) 676 __check(liboprf.tpdkg_peer_next(ctx[0], msg, ctypes.c_size_t(input_len), output, ctypes.c_size_t(output_len))) 677 return output.raw 678 679 #int tpdkg_peer_not_done(const TP_DKG_PeerState *peer); 680 def tpdkg_peer_not_done(ctx): 681 return liboprf.tpdkg_peer_not_done(ctx[0]) == 1 682 683 #void tpdkg_peer_free(TP_DKG_PeerState *ctx); 684 def tpdkg_peer_free(ctx): 685 liboprf.tpdkg_peer_free(ctx[0]) 686 687 688 #int dkg_vss_reconstruct(const uint8_t t, 689 # const uint8_t x, 690 # const size_t shares_len, 691 # const TOPRF_Share shares[shares_len][2], 692 # const uint8_t commitments[shares_len][crypto_scalarmult_ristretto255_BYTES] 693 # uint8_t result[crypto_scalarmult_ristretto255_SCALARBYTES], 694 # uint8_t blind[crypto_scalarmult_ristretto255_SCALARBYTES]) { 695 def dkg_vss_reconstruct(n, t, x, shares, commitments = None): 696 if len(shares) < t: 697 raise ValueError(f"shares must be at least {TOPRF_Share_BYTES*2*n} bytes is instead {len(shares)}") 698 for i, s in enumerate(shares): 699 if len(s)!=TOPRF_Share_BYTES*2: 700 raise ValueError(f"share {i+1} has incorrect length: {len(s)}, must be {TOPRF_Share_BYTES*2}") 701 if commitments is not None: 702 if len(commitments) < t: 703 raise ValueError(f"commitments must be at least {pysodium.crypto_core_ristretto255_BYTES*t} bytes is instead {len(commitments)}") 704 for i, c in enumerate(commitments): 705 if len(c)!=pysodium.crypto_core_ristretto255_BYTES: 706 raise ValueError(f"commitment {i+1} has incorrect length: {len(c)}, must be {pysodium.crypto_core_ristretto255_BYTES}") 707 commitments = b''.join(commitments) 708 709 shares_len = ctypes.c_size_t(len(shares)) 710 shares = b''.join(shares) 711 712 result = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_SCALARBYTES) 713 blind = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_SCALARBYTES) 714 __check(liboprf.dkg_vss_reconstruct(ctypes.c_uint8(t), 715 ctypes.c_uint8(x), 716 shares_len, 717 shares, 718 commitments, 719 result, blind)) 720 return result.raw, blind.raw 721 722 sessionid_SIZE=32 723 tupdate_msg0_SIZE = 0xd1 # ( sizeof(TP_DKG_Message) \ 724 # + crypto_generichash_BYTES/*dst*/ \ 725 # + 2 /*n,t*/ \ 726 # + crypto_sign_PUBLICKEYBYTES /* tp_sign_pk */) 727 tupdate_max_err_SIZE = 128 728 tupdate_keyid_SIZE = 32 729 tupdate_commitment_HASHBYTES = 32 730 noise_xk_handshake3_SIZE = 64 731 732 class Cheater(ctypes.Structure): 733 _fields_ = [('step', ctypes.c_int), 734 ('error', ctypes.c_int), 735 ('peer', ctypes.c_uint8), 736 ('other_peer', ctypes.c_uint8), 737 ('invalid_index', ctypes.c_int), 738 ] 739 740 741 # int toprf_update_start_stp(TOPRF_Update_STPState *ctx, const uint64_t ts_epsilon, 742 # const uint8_t n, const uint8_t t, 743 # const char *proto_name, const size_t proto_name_len, 744 # const uint8_t keyid[toprf_keyid_SIZE], 745 # const uint8_t (*sig_pks)[][crypto_sign_PUBLICKEYBYTES], 746 # const uint8_t ltssk[crypto_sign_SECRETKEYBYTES], 747 # const size_t msg0_len, 748 # TOPRF_Update_Message *msg0); 749 # 750 # also wraps conveniently: 751 # 752 # void toprf_update_stp_set_bufs(TOPRF_Update_STPState *ctx, 753 # uint16_t p_complaints[], 754 # uint16_t y2_complaints[], 755 # TOPRF_Update_Cheater (*cheaters)[], const size_t cheater_max, 756 # uint8_t (*p_commitments_hashes)[][toprf_update_commitment_HASHBYTES], 757 # uint8_t (*p_share_macs)[][crypto_auth_hmacsha256_BYTES], 758 # uint8_t (*p_commitments)[][crypto_core_ristretto255_BYTES], 759 # uint8_t (*kc0_commitments)[][crypto_core_ristretto255_BYTES], 760 # uint8_t (*k0p_commitments)[][crypto_core_ristretto255_BYTES], 761 # uint8_t (*zk_challenge_commitments)[][3][crypto_scalarmult_ristretto255_SCALARBYTES], 762 # uint8_t (*zk_challenge_e_i)[][crypto_scalarmult_ristretto255_SCALARBYTES], 763 # uint8_t (*k0p_final_commitments)[][crypto_scalarmult_ristretto255_BYTES], 764 # uint64_t *last_ts); 765 766 def tupdate_start_stp(n, t, ts_epsilon, proto_name, sig_pks, keyid, ltssk): 767 dealers = (t-1)*2 + 1 768 769 if(len(keyid)!=tupdate_keyid_SIZE): raise ValueError(f"keyid has incorrect size, must be {tupdate_keyid_SIZE}") 770 if(len(sig_pks)!=n+1): raise ValueError(f"invalid number of long-term signature pubkeys ({len(sig_pks)}, must be equal n ({n+1})") 771 for i, k in enumerate(sig_pks): 772 if len(k) != pysodium.crypto_sign_PUBLICKEYBYTES: 773 raise ValueError(f"long-term signature pubkey #{i} has invalid length ({len(k)}) must be {pysodium.crypto_sign_PUBLICKEYBYTES}") 774 if len(ltssk) != pysodium.crypto_sign_SECRETKEYBYTES: 775 raise ValueError(f"long-term signature secret key of STP has invalid length ({len(ltssk)}) must be {pysodium.crypto_sign_SECRETKEYBYTES}") 776 777 b = ctypes.create_string_buffer(liboprf.toprf_update_stpstate_size()+32) 778 b_addr = ctypes.addressof(b) 779 s_addr = b_addr + (b_addr % 32) 780 state = ctypes.c_void_p(s_addr) 781 if state.value % 32 != 0: 782 raise ValueError("cannot align at 32bytes the TOPRF_Update_STPState struct") 783 784 sig_pks = ctypes.create_string_buffer(b''.join(sig_pks)) 785 786 msg = ctypes.create_string_buffer(tupdate_msg0_SIZE) 787 __check(liboprf.toprf_update_start_stp(state, ctypes.c_uint64(ts_epsilon), 788 ctypes.c_uint8(n), ctypes.c_uint8(t), 789 proto_name, ctypes.c_size_t(len(proto_name)), 790 keyid, ctypes.byref(sig_pks), ltssk, 791 ctypes.c_size_t(len(msg.raw)), msg)) 792 793 k0_commitments = ctypes.create_string_buffer(n*pysodium.crypto_core_ristretto255_BYTES) 794 p_complaints = (ctypes.c_uint16 * n*n)() 795 y2_complaints = (ctypes.c_uint16 * n*n)() 796 cheaters = (Cheater * (t*t - 1))() 797 p_commitments_hashes = ctypes.create_string_buffer(n*tupdate_commitment_HASHBYTES) 798 p_share_macs = ctypes.create_string_buffer(n*n*pysodium.crypto_auth_hmacsha256_BYTES) 799 p_commitments = ctypes.create_string_buffer(n*n*pysodium.crypto_core_ristretto255_BYTES) 800 k0p_commitments = ctypes.create_string_buffer(dealers*(n+1)*pysodium.crypto_core_ristretto255_BYTES) 801 zk_challenge_commitments = ctypes.create_string_buffer(dealers*2*3*pysodium.crypto_core_ristretto255_SCALARBYTES) 802 zk_challenge_e_i = ctypes.create_string_buffer(2*dealers*pysodium.crypto_core_ristretto255_SCALARBYTES) 803 k0p_final_commitments = ctypes.create_string_buffer(n*pysodium.crypto_core_ristretto255_BYTES) 804 last_ts = (ctypes.c_uint64 * n)() 805 806 liboprf.toprf_update_stp_set_bufs(state 807 # uint16_t p_complaints[], 808 ,p_complaints 809 # uint16_t x2_complaints[], uint16_t y2_complaints[], 810 ,y2_complaints 811 # TOPRF_Update_Cheater (*cheaters)[], const size_t cheater_max, 812 ,ctypes.byref(cheaters), ctypes.c_size_t(len(cheaters)) 813 # uint8_t (*p_commitments_hashes)[][toprf_update_commitment_HASHBYTES], 814 ,ctypes.byref(p_commitments_hashes) 815 # uint8_t (*p_share_macs)[][crypto_auth_hmacsha256_BYTES], 816 ,ctypes.byref(p_share_macs) 817 # uint8_t (*p_commitments)[][crypto_core_ristretto255_BYTES], 818 ,ctypes.byref(p_commitments) 819 # uint8_t (*kc0_commitments)[][crypto_core_ristretto255_BYTES], 820 ,ctypes.byref(k0_commitments) 821 # uint8_t (*k0p_commitments)[][crypto_core_ristretto255_BYTES], 822 ,ctypes.byref(k0p_commitments) 823 # uint8_t (*zk_challenge_commitments)[][3][crypto_scalarmult_ristretto255_SCALARBYTES], 824 ,ctypes.byref(zk_challenge_commitments) 825 # uint8_t (*zk_challenge_e_i)[][crypto_scalarmult_ristretto255_SCALARBYTES], 826 ,ctypes.byref(zk_challenge_e_i) 827 # uint8_t (*k0p_final_commitments)[][crypto_scalarmult_ristretto255_BYTES], 828 ,ctypes.byref(k0p_final_commitments) 829 # uint64_t *last_ts); 830 ,ctypes.byref(last_ts)) 831 832 # we need to keep these arrays around, otherwise the gc eats them up. 833 ctx = (state, cheaters, p_complaints, y2_complaints, 834 p_commitments_hashes, p_share_macs, 835 p_commitments, 836 k0_commitments, 837 k0p_commitments, 838 zk_challenge_commitments, zk_challenge_e_i, 839 k0p_final_commitments, 840 last_ts, sig_pks, b) 841 842 return ctx, msg.raw 843 844 845 #size_t tpdkg_tp_input_size(const TP_DKG_TPState *ctx); 846 def tupdate_stp_input_size(ctx): 847 return liboprf.toprf_update_stp_input_size(ctx[0]) 848 849 #int tpdkg_tp_input_sizes(const TP_DKG_TPState *ctx, size_t *sizes); 850 def tupdate_stp_input_sizes(ctx): 851 sizes = (ctypes.c_size_t * tpdkg_tpstate_n(ctx))() 852 ret = liboprf.toprf_update_stp_input_sizes(ctx[0], ctypes.byref(sizes)) 853 return ret, [x for x in sizes] 854 855 #size_t tpdkg_tp_output_size(const TP_DKG_TPState *ctx); 856 def tupdate_stp_output_size(ctx): 857 return liboprf.toprf_update_stp_output_size(ctx[0]) 858 859 #int tpdkg_tp_next(TP_DKG_TPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); 860 def tupdate_stp_next(ctx, msg): 861 input_len = tupdate_stp_input_size(ctx) 862 if len(msg) != input_len: raise ValueError(f"input msg is invalid size: {len(msg)}B must be: {input_len}B") 863 output_len = tupdate_stp_output_size(ctx) 864 output = ctypes.create_string_buffer(output_len) 865 __check(liboprf.toprf_update_stp_next(ctx[0], msg, ctypes.c_size_t(input_len), output, ctypes.c_size_t(output_len))) 866 return output 867 868 #int tpdkg_tp_peer_msg(const TP_DKG_TPState *ctx, const uint8_t *base, const size_t base_size, const uint8_t peer, const uint8_t **msg, size_t *len); 869 def tupdate_stp_peer_msg(ctx, base, peer): 870 msg = ctypes.POINTER(ctypes.c_char)() 871 size = ctypes.c_size_t() 872 __check(liboprf.toprf_update_stp_peer_msg(ctx[0], base, len(base.raw), peer, ctypes.byref(msg), ctypes.byref(size))) 873 msg = b''.join([msg[i] for i in range(size.value)]) 874 return msg 875 876 #int tpdkg_tp_not_done(const TP_DKG_TPState *tp); 877 def tupdate_stp_not_done(ctx): 878 return liboprf.toprf_update_stp_not_done(ctx[0]) == 1 879 880 #todo 881 #def tupdate_get_cheaters(ctx): 882 # cheats = [] 883 # cheaters = set() 884 # for i in range(tupdate_stpstate_cheater_len(ctx)): 885 # err = ctypes.create_string_buffer(tpdkg_max_err_SIZE) 886 # p = liboprf.toprf_update_cheater_msg(ctypes.byref(ctx[1][i]), err, tpdkg_max_err_SIZE) 887 # if 0 >= p > tpdkg_tpstate_n(ctx): 888 # print(f"invalid cheater index: {p}, skipping this entry") 889 # continue 890 # cheaters.add(p) 891 # cheats.append((p, err.raw[:err.raw.find(b'\x00')].decode('utf8'))) 892 # return cheaters, cheats 893 894 liboprf.toprf_update_peerstate_n.restype = ctypes.c_uint8 895 def tupdate_peerstate_n(ctx): 896 return liboprf.toprf_update_peerstate_n(ctx[0]) 897 liboprf.toprf_update_peerstate_t.restype = ctypes.c_uint8 898 def tupdate_peerstate_t(ctx): 899 return liboprf.toprf_update_peerstate_t(ctx[0]) 900 liboprf.toprf_update_peerstate_sessionid.restype = ctypes.POINTER(ctypes.c_uint8) 901 def tupdate_peerstate_sessionid(ctx): 902 ptr = liboprf.toprf_update_peerstate_sessionid(ctx[0]) 903 return bytes(ptr[i] for i in range(sessionid_SIZE)) 904 liboprf.toprf_update_peerstate_share.restype = ctypes.POINTER(ctypes.c_uint8) 905 def tupdate_peerstate_share(ctx): 906 ptr = liboprf.toprf_update_peerstate_share(ctx[0]) 907 return bytes(ptr[i] for i in range(TOPRF_Share_BYTES*2)) 908 liboprf.toprf_update_peerstate_commitment.restype = ctypes.POINTER(ctypes.c_uint8) 909 def tupdate_peerstate_commitment(ctx): 910 ptr = liboprf.toprf_update_peerstate_commitment(ctx[0]) 911 return bytes(ptr[i] for i in range(pysodium.crypto_core_ristretto255_BYTES)) 912 liboprf.toprf_update_peerstate_commitments.restype = ctypes.POINTER(ctypes.c_uint8) 913 def tupdate_peerstate_commitments(ctx): 914 ptr = liboprf.toprf_update_peerstate_commitments(ctx[0]) 915 return tuple(bytes(ptr[p*pysodium.crypto_core_ristretto255_BYTES:(p+1)*pysodium.crypto_core_ristretto255_BYTES]) for p in range(tupdate_peerstate_n(ctx))) 916 def tupdate_peerstate_step(ctx): 917 return liboprf.toprf_update_peerstate_step(ctx[0]) 918 919 liboprf.toprf_update_stpstate_n.restype = ctypes.c_uint8 920 def tupdate_stpstate_n(ctx): 921 return liboprf.toprf_update_stpstate_n(ctx[0]) 922 liboprf.toprf_update_stpstate_t.restype = ctypes.c_uint8 923 def tupdate_stpstate_t(ctx): 924 return liboprf.toprf_update_stpstate_t(ctx[0]) 925 liboprf.toprf_update_stpstate_cheater_len.restype = ctypes.c_size_t 926 def tupdate_stpstate_cheater_len(ctx): 927 return liboprf.toprf_update_stpstate_cheater_len(ctx[0]) 928 liboprf.toprf_update_stpstate_sessionid.restype = ctypes.POINTER(ctypes.c_uint8) 929 def tupdate_stpstate_sessionid(ctx): 930 ptr = liboprf.toprf_update_stpstate_sessionid(ctx[0]) 931 return bytes(ptr[i] for i in range(sessionid_SIZE)) 932 liboprf.toprf_update_stpstate_delta.restype = ctypes.POINTER(ctypes.c_uint8) 933 def tupdate_stpstate_delta(ctx): 934 ptr = liboprf.toprf_update_stpstate_delta(ctx[0]) 935 return bytes(ptr[i] for i in range(pysodium.crypto_core_ristretto255_BYTES)) 936 liboprf.toprf_update_stpstate_commitments.restype = ctypes.POINTER(ctypes.c_uint8) 937 def tupdate_stpstate_commitments(ctx): 938 ptr = liboprf.toprf_update_stpstate_commitments(ctx[0]) 939 return tuple(bytes(ptr[p*pysodium.crypto_core_ristretto255_BYTES:(p+1)*pysodium.crypto_core_ristretto255_BYTES]) for p in range(tupdate_stpstate_n(ctx))) 940 def tupdate_stpstate_step(ctx): 941 return liboprf.toprf_update_stpstate_step(ctx[0]) 942 943 # TOPRF_Update_Err toprf_update_start_peer(TOPRF_Update_PeerState *ctx, 944 # const uint64_t ts_epsilon, 945 # const uint8_t lt_sk[crypto_sign_SECRETKEYBYTES], 946 # const TOPRF_Update_Message *msg0, 947 # uint8_t keyid[toprf_keyid_SIZE], 948 # uint8_t stp_ltpk[crypto_sign_PUBLICKEYBYTES]); 949 def tupdate_peer_start(ts_epsilon, peer_lt_sk, noise_sk, msg0): 950 if len(peer_lt_sk) != pysodium.crypto_sign_SECRETKEYBYTES: 951 raise ValueError(f"peer long-term secret key has invalid size, must be {pysodium.crypto_sign_SECRETKEYBYTES}") 952 if len(noise_sk) != pysodium.crypto_scalarmult_SCALARBYTES: 953 raise ValueError(f"peer long-term secret noise key has invalid size, must be {pysodium.crypto_scalarmult_SCALARBYTES}") 954 955 b = ctypes.create_string_buffer(liboprf.toprf_update_peerstate_size()+32) 956 b_addr = ctypes.addressof(b) 957 s_addr = b_addr + (b_addr % 32) 958 state = ctypes.c_void_p(s_addr) 959 if state.value % 32 != 0: 960 raise ValueError("cannot align at 32bytes the TP_DKG_PeerState struct") 961 962 keyid = ctypes.create_string_buffer(tupdate_keyid_SIZE) 963 stp_ltpk = ctypes.create_string_buffer(pysodium.crypto_sign_PUBLICKEYBYTES) 964 965 __check(liboprf.toprf_update_start_peer(state, ctypes.c_uint64(ts_epsilon), peer_lt_sk, 966 noise_sk, 967 msg0, keyid, stp_ltpk)) 968 969 return (state, b), keyid.raw, stp_ltpk.raw 970 971 def tupdate_peer_set_bufs(ctx, n, t, index, sig_pks, noise_pks, k0 = None, k0_commitments = None): 972 dealers = (t-1)*2 + 1 973 if k0 is not None: 974 if len(k0) != TOPRF_Share_BYTES * 2: 975 raise ValueError(f"k0 has invalid size {len(k0)} must be {TOPRF_Share_BYTES * 2}") 976 if(k0[0]!=index or k0[TOPRF_Share_BYTES]!=index): 977 raise ValueError(f"k0 has a different index ({k0[0]} & {k0[TOPRF_Share_BYTES]} than provided: {index}") 978 if k0_commitments is None: 979 raise ValueError(f"must provide also commitments for k0") 980 if len(k0_commitments) < dealers: 981 raise ValueError(f"not enough dealers holding kc0 shares, need at least {dealers}") 982 for i, c in enumerate(k0_commitments): 983 if len(c) == pysodium.crypto_core_ristretto255_BYTES: continue 984 raise ValueError(f"k0 commitment #{i} has invalid length ({len(c)}) must be {pysodium.crypto_core_ristretto255_BYTES}") 985 k0_commitments = ctypes.create_string_buffer(b''.join(k0_commitments)) 986 987 if(len(sig_pks)!=n+1): raise ValueError(f"invalid number of long-term signature pubkeys ({len(sig_pks)}, must be equal n ({n+1})") 988 for i, k in enumerate(sig_pks): 989 if len(k) != pysodium.crypto_sign_PUBLICKEYBYTES: 990 raise ValueError(f"long-term signature pubkey #{i} has invalid length ({len(k)}) must be {pysodium.crypto_sign_PUBLICKEYBYTES}") 991 sig_pks = ctypes.create_string_buffer(b''.join(sig_pks)) 992 993 if(len(noise_pks)!=n): raise ValueError(f"invalid number of long-term noise pubkeys ({len(noise_pks)}, must be equal n ({n})") 994 for i, k in enumerate(noise_pks): 995 if len(k) != pysodium.crypto_scalarmult_BYTES: 996 raise ValueError(f"noise pubkey #{i} has invalid length ({len(k)}) must be {pysodium.crypto_scalarmult_BYTES}") 997 noise_pks = ctypes.create_string_buffer(b''.join(noise_pks)) 998 999 noise_outs = (ctypes.c_void_p * n)() 1000 noise_ins = (ctypes.c_void_p * n)() 1001 p_shares = ctypes.create_string_buffer(n * TOPRF_Share_BYTES * 2) 1002 p_commitments = ctypes.create_string_buffer(n * n * pysodium.crypto_core_ristretto255_BYTES) 1003 p_commitment_hashes = ctypes.create_string_buffer(n * tupdate_commitment_HASHBYTES) 1004 p_share_macs = ctypes.create_string_buffer(n * n * pysodium.crypto_auth_hmacsha256_BYTES) 1005 encrypted_shares = ctypes.create_string_buffer(n * (noise_xk_handshake3_SIZE + TOPRF_Share_BYTES * 2)) 1006 cheaters = (Cheater * (t*t - 1))() 1007 lambdas = ctypes.create_string_buffer(dealers * pysodium.crypto_core_ristretto255_SCALARBYTES) 1008 k0p_shares = ctypes.create_string_buffer(dealers * TOPRF_Share_BYTES * 2) 1009 k0p_commitments = ctypes.create_string_buffer(dealers * (n+1) * pysodium.crypto_core_ristretto255_BYTES) 1010 zk_challenge_nonce_commitments = ctypes.create_string_buffer(n * pysodium.crypto_core_ristretto255_BYTES) 1011 zk_challenge_nonces = ctypes.create_string_buffer(n * 2 * pysodium.crypto_core_ristretto255_SCALARBYTES) 1012 zk_challenge_commitments = ctypes.create_string_buffer(dealers * 3 * pysodium.crypto_core_ristretto255_SCALARBYTES) 1013 zk_challenge_e_i = ctypes.create_string_buffer(dealers * pysodium.crypto_core_ristretto255_SCALARBYTES) 1014 p_complaints = (ctypes.c_uint16 * n*n)() 1015 p_my_complaints = ctypes.create_string_buffer(n) 1016 last_ts = (ctypes.c_uint64 * n)() 1017 1018 # int toprf_update_peer_set_bufs(TOPRF_Update_PeerState *ctx, 1019 liboprf.toprf_update_peer_set_bufs(ctx[0] 1020 # const uint8_t self, 1021 ,ctypes.c_uint8(index) 1022 # const uint8_t n, const uint8_t t, 1023 ,ctypes.c_uint8(n), ctypes.c_uint8(t) 1024 # const TOPRF_Share k0[2], 1025 ,k0 1026 # uint8_t (*kc0_commitments)[][crypto_core_ristretto255_BYTES], 1027 ,ctypes.byref(k0_commitments) 1028 # const uint8_t (*sig_pks)[][], 1029 ,ctypes.byref(sig_pks) 1030 # uint8_t (*peers_noise_pks)[][crypto_scalarmult_BYTES], 1031 ,ctypes.byref(noise_pks) 1032 # Noise_XK_session_t *(*noise_outs)[], 1033 ,noise_outs 1034 # Noise_XK_session_t *(*noise_ins)[], 1035 ,noise_ins 1036 # TOPRF_Share (*p_shares)[][2], 1037 ,ctypes.byref(p_shares) 1038 # uint8_t (*p_commitments)[][crypto_core_ristretto255_BYTES], 1039 ,ctypes.byref(p_commitments) 1040 # uint8_t (*p_commitments_hashes)[][toprf_update_commitment_HASHBYTES], 1041 ,ctypes.byref(p_commitment_hashes) 1042 # uint8_t (*p_share_macs)[][crypto_auth_hmacsha256_BYTES], 1043 ,ctypes.byref(p_share_macs) 1044 # uint8_t (*encrypted_shares)[][noise_xk_handshake3_SIZE + toprf_update_encrypted_shares_SIZE*2], 1045 ,ctypes.byref(encrypted_shares) 1046 # TOPRF_Update_Cheater (*cheaters)[], const size_t cheater_max, 1047 ,ctypes.byref(cheaters), ctypes.c_size_t(len(cheaters)) 1048 # uint8_t (*lambdas)[][crypto_core_ristretto255_SCALARBYTES], 1049 ,ctypes.byref(lambdas) 1050 # TOPRF_Share (*k0p_shares)[][2], 1051 ,ctypes.byref(k0p_shares) 1052 # uint8_t (*k0p_commitments)[][crypto_core_ristretto255_BYTES], 1053 ,ctypes.byref(k0p_commitments) 1054 # uint8_t (*zk_challenge_nonce_commitments)[][crypto_scalarmult_ristretto255_BYTES], 1055 ,ctypes.byref(zk_challenge_nonce_commitments) 1056 # uint8_t (*zk_challenge_nonces)[][2][crypto_scalarmult_ristretto255_SCALARBYTES], 1057 ,ctypes.byref(zk_challenge_nonces) 1058 # uint8_t (*zk_challenge_commitments)[][3][crypto_scalarmult_ristretto255_SCALARBYTES], 1059 ,ctypes.byref(zk_challenge_commitments) 1060 # uint8_t (*zk_challenge_e_i)[][crypto_scalarmult_ristretto255_SCALARBYTES], 1061 ,ctypes.byref(zk_challenge_e_i) 1062 # uint16_t *p_complaints, 1063 ,p_complaints 1064 #uint8_t *my_p_complaints, 1065 ,p_my_complaints 1066 # uint64_t *last_ts); 1067 ,ctypes.byref(last_ts)) 1068 1069 # we need to keep these arrays around, otherwise the gc eats them up. 1070 ctx = (ctx[0], noise_pks, noise_outs, noise_ins, 1071 k0_commitments, sig_pks, 1072 p_shares, 1073 p_commitments, 1074 p_commitment_hashes, 1075 p_share_macs, 1076 encrypted_shares, 1077 cheaters, 1078 lambdas, 1079 k0p_shares, k0p_commitments, 1080 zk_challenge_nonce_commitments, zk_challenge_nonces, zk_challenge_commitments, zk_challenge_e_i, 1081 p_complaints, p_my_complaints, 1082 last_ts, ctx[1]) 1083 return ctx 1084 1085 #size_t toprf_update_peer_input_size(const TOPRF_Update_PeerState *ctx); 1086 def tupdate_peer_input_size(ctx): 1087 return liboprf.toprf_update_peer_input_size(ctx[0]) 1088 1089 #size_t toprf_update_peer_output_size(const TOPRF_Update_PeerState *ctx); 1090 def tupdate_peer_output_size(ctx): 1091 return liboprf.toprf_update_peer_output_size(ctx[0]) 1092 1093 #int toprf_update_peer_next(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); 1094 def tupdate_peer_next(ctx, msg): 1095 input_len = tupdate_peer_input_size(ctx) 1096 if len(msg) != input_len: raise ValueError(f"input msg is invalid size: {len(msg)}B must be: {input_len}B") 1097 output_len = tupdate_peer_output_size(ctx) 1098 output = ctypes.create_string_buffer(output_len) 1099 __check(liboprf.toprf_update_peer_next(ctx[0], msg, ctypes.c_size_t(input_len), output, ctypes.c_size_t(output_len))) 1100 return output.raw 1101 1102 #int toprf_update_peer_not_done(const TOPRF_Update_PeerState *peer); 1103 def tupdate_peer_not_done(ctx): 1104 return liboprf.toprf_update_peer_not_done(ctx[0]) == 1 1105 1106 #void toprf_update_peer_free(TOPRF_Update_PeerState *ctx); 1107 def tupdate_peer_free(ctx): 1108 liboprf.toprf_update_peer_free(ctx[0]) 1109 1110 stpdkg_msg0_SIZE = 0xb3 1111 stp_dkg_commitment_HASHBYTES = 32 1112 stp_dkg_max_err_SIZE = 128 1113 stp_dkg_sessionid_SIZE = 32 1114 stp_dkg_encrypted_share_SIZE = TOPRF_Share_BYTES * 2 + 16 #pysodium.crypto_secretbox_xchacha20poly1305_MACBYTES 1115 1116 def stp_dkg_start_stp(n, t, ts_epsilon, proto_name, sig_pks, ltssk): 1117 b = ctypes.create_string_buffer(liboprf.stp_dkg_stpstate_size()+32) 1118 b_addr = ctypes.addressof(b) 1119 s_addr = b_addr + (b_addr % 32) 1120 state = ctypes.c_void_p(s_addr) 1121 if state.value % 32 != 0: 1122 raise ValueError("cannot align at 32bytes the STP_DKG_STPState struct") 1123 1124 if(len(sig_pks)!=n+1): raise ValueError(f"invalid number of long-term signature pubkeys ({len(sig_pks)}, must be equal n ({n+1})") 1125 for i, k in enumerate(sig_pks): 1126 if len(k) != pysodium.crypto_sign_PUBLICKEYBYTES: 1127 raise ValueError(f"long-term signature pubkey #{i} has invalid length ({len(k)}) must be {pysodium.crypto_sign_PUBLICKEYBYTES}") 1128 if len(ltssk) != pysodium.crypto_sign_SECRETKEYBYTES: 1129 raise ValueError(f"long-term signature secret key of STP has invalid length ({len(ltssk)}) must be {pysodium.crypto_sign_SECRETKEYBYTES}") 1130 1131 msg = ctypes.create_string_buffer(stpdkg_msg0_SIZE) 1132 sig_pks = ctypes.create_string_buffer(b''.join(sig_pks)) 1133 1134 __check(liboprf.stp_dkg_start_stp(state, 1135 ctypes.c_uint64(ts_epsilon), 1136 ctypes.c_uint8(n), ctypes.c_uint8(t), 1137 proto_name, ctypes.c_size_t(len(proto_name)), 1138 ctypes.byref(sig_pks), 1139 ltssk, 1140 ctypes.c_size_t(len(msg.raw)), msg)) 1141 1142 commitment_hashes = ctypes.create_string_buffer(n*stp_dkg_commitment_HASHBYTES) 1143 share_macs = ctypes.create_string_buffer(n * n * pysodium.crypto_auth_hmacsha256_BYTES) 1144 commitments = ctypes.create_string_buffer(n*n*pysodium.crypto_core_ristretto255_BYTES) 1145 share_complaints = (ctypes.c_uint16 * n*n)() 1146 cheaters = (TP_DKG_Cheater * (t*t - 1))() 1147 last_ts = (ctypes.c_uint64 * n)() 1148 1149 liboprf.stp_dkg_stp_set_bufs(state, 1150 ctypes.byref(commitment_hashes), 1151 ctypes.byref(share_macs), 1152 ctypes.byref(commitments), 1153 ctypes.byref(share_complaints), 1154 ctypes.byref(cheaters), 1155 ctypes.c_size_t(len(cheaters)), 1156 ctypes.byref(last_ts)) 1157 1158 # we need to keep these arrays around, otherwise the gc eats them up. 1159 ctx = (state, cheaters, sig_pks, commitments, commitment_hashes, share_macs, share_complaints, last_ts, b) 1160 1161 return ctx, msg.raw 1162 1163 1164 #size_t stp_dkg_stp_input_size(const STP_DKG_STPState *ctx); 1165 def stp_dkg_stp_input_size(ctx): 1166 return liboprf.stp_dkg_stp_input_size(ctx[0]) 1167 1168 #int stp_dkg_stp_input_sizes(const STP_DKG_STPState *ctx, size_t *sizes); 1169 def stp_dkg_stp_input_sizes(ctx): 1170 sizes = (ctypes.c_size_t * stp_dkg_stpstate_n(ctx))() 1171 ret = liboprf.stp_dkg_stp_input_sizes(ctx[0], ctypes.byref(sizes)) 1172 return ret, [x for x in sizes] 1173 1174 #size_t stp_dkg_stp_output_size(const STP_DKG_STPState *ctx); 1175 def stp_dkg_stp_output_size(ctx): 1176 return liboprf.stp_dkg_stp_output_size(ctx[0]) 1177 1178 #int stp_dkg_stp_next(TP_DKG_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); 1179 def stp_dkg_stp_next(ctx, msg): 1180 input_len = stp_dkg_stp_input_size(ctx) 1181 if len(msg) != input_len: raise ValueError(f"input msg is invalid size: {len(msg)}B must be: {input_len}B") 1182 output_len = stp_dkg_stp_output_size(ctx) 1183 output = ctypes.create_string_buffer(output_len) 1184 __check(liboprf.stp_dkg_stp_next(ctx[0], msg, ctypes.c_size_t(input_len), output, ctypes.c_size_t(output_len))) 1185 return output 1186 1187 #int stp_dkg_stp_peer_msg(const STP_DKG_STPState *ctx, const uint8_t *base, const size_t base_size, const uint8_t peer, const uint8_t **msg, size_t *len); 1188 def stp_dkg_stp_peer_msg(ctx, base, peer): 1189 msg = ctypes.POINTER(ctypes.c_char)() 1190 size = ctypes.c_size_t() 1191 __check(liboprf.stp_dkg_stp_peer_msg(ctx[0], base, ctypes.c_size_t(len(base.raw)), peer, ctypes.byref(msg), ctypes.byref(size))) 1192 return msg[:size.value] 1193 1194 #int stp_dkg_stp_not_done(const STP_DKG_STPState *tp); 1195 def stp_dkg_stp_not_done(ctx): 1196 return liboprf.stp_dkg_stp_not_done(ctx[0]) == 1 1197 1198 def stp_dkg_get_cheaters(ctx): 1199 cheats = [] 1200 cheaters = set() 1201 for i in range(stp_dkg_stpstate_cheater_len(ctx)): 1202 err = ctypes.create_string_buffer(stp_dkg_max_err_SIZE) 1203 p = liboprf.stp_dkg_stp_cheater_msg(ctypes.byref(ctx[1][i]), err, stp_dkg_max_err_SIZE) 1204 if 0 >= p > stp_dkg_stpstate_n(ctx): 1205 print(f"invalid cheater index: {p}, skipping this entry") 1206 continue 1207 cheaters.add(p) 1208 cheats.append((p, err.raw[:err.raw.find(b'\x00')].decode('utf8'))) 1209 return cheaters, cheats 1210 1211 liboprf.stp_dkg_peerstate_n.restype = ctypes.c_uint8 1212 def stp_dkg_peerstate_n(ctx): 1213 return liboprf.stp_dkg_peerstate_n(ctx[0]) 1214 liboprf.stp_dkg_peerstate_t.restype = ctypes.c_uint8 1215 def stp_dkg_peerstate_t(ctx): 1216 return liboprf.stp_dkg_peerstate_t(ctx[0]) 1217 liboprf.stp_dkg_peerstate_sessionid.restype = ctypes.POINTER(ctypes.c_uint8) 1218 def stp_dkg_peerstate_sessionid(ctx): 1219 ptr = liboprf.stp_dkg_peerstate_sessionid(ctx[0]) 1220 return bytes(ptr[i] for i in range(stp_dkg_sessionid_SIZE)) 1221 liboprf.stp_dkg_peerstate_lt_sk.restype = ctypes.POINTER(ctypes.c_uint8) 1222 def stp_dkg_peerstate_lt_sk(ctx): 1223 ptr = liboprf.stp_dkg_peerstate_lt_sk(ctx[0]) 1224 return bytes(ptr[i] for i in range(pysodium.crypto_sign_SECRETKEYBYTES)) 1225 liboprf.stp_dkg_peerstate_share.restype = ctypes.POINTER(ctypes.c_uint8) 1226 def stp_dkg_peerstate_share(ctx): 1227 ptr = liboprf.stp_dkg_peerstate_share(ctx[0]) 1228 return bytes(ptr[i] for i in range(TOPRF_Share_BYTES*2)) 1229 liboprf.stp_dkg_peerstate_commitments.restype = ctypes.POINTER(ctypes.c_uint8) 1230 def stp_dkg_peerstate_commitments(ctx): 1231 ptr = liboprf.stp_dkg_peerstate_commitments(ctx[0]) 1232 return tuple(bytes(ptr[c*pysodium.crypto_core_ristretto255_BYTES+i] 1233 for i in range(pysodium.crypto_core_ristretto255_BYTES)) 1234 for c in range(stp_dkg_peerstate_n(ctx))) 1235 def stp_dkg_peerstate_step(ctx): 1236 return liboprf.stp_dkg_peerstate_step(ctx[0]) 1237 1238 liboprf.stp_dkg_stpstate_n.restype = ctypes.c_uint8 1239 def stp_dkg_stpstate_n(ctx): 1240 return liboprf.stp_dkg_stpstate_n(ctx[0]) 1241 liboprf.stp_dkg_stpstate_t.restype = ctypes.c_uint8 1242 def stp_dkg_stpstate_t(ctx): 1243 return liboprf.stp_dkg_stpstate_t(ctx[0]) 1244 liboprf.stp_dkg_stpstate_cheater_len.restype = ctypes.c_size_t 1245 def stp_dkg_stpstate_cheater_len(ctx): 1246 return liboprf.stp_dkg_stpstate_cheater_len(ctx[0]) 1247 liboprf.stp_dkg_stpstate_sessionid.restype = ctypes.POINTER(ctypes.c_uint8) 1248 def stp_dkg_stpstate_sessionid(ctx): 1249 ptr = liboprf.stp_dkg_stpstate_sessionid(ctx[0]) 1250 return bytes(ptr[i] for i in range(stp_dkg_sessionid_SIZE)) 1251 liboprf.stp_dkg_stpstate_commitments.restype = ctypes.POINTER(ctypes.c_uint8) 1252 def stp_dkg_stpstate_commitments(ctx): 1253 ptr = liboprf.stp_dkg_stpstate_commitments(ctx[0]) 1254 return tuple(bytes(ptr[c*pysodium.crypto_core_ristretto255_BYTES+i] 1255 for i in range(pysodium.crypto_core_ristretto255_BYTES)) 1256 for c in range(stp_dkg_stpstate_n(ctx))) 1257 def stp_dkg_stpstate_step(ctx): 1258 return liboprf.stp_dkg_stpstate_step(ctx[0]) 1259 1260 1261 #typedef int (*Keyloader_CB)(const uint8_t id[crypto_generichash_BYTES], 1262 # void *arg, 1263 # uint8_t sigpk[crypto_sign_PUBLICKEYBYTES], 1264 # uint8_t noise_pk[crypto_scalarmult_BYTES]); 1265 #@ctypes.CFUNCTYPE(c.c_int, c.POINTER(c.c_ubyte), c.POINTER(c.c_ubyte), c.POINTER(c.c_ubyte)) 1266 #def load_key(keyid, alpha, beta): 1267 # c.memmove(beta, beta_, len(beta_)) 1268 1269 # STP_DKG_Err stp_dkg_start_peer(STP_DKG_PeerState *ctx, 1270 # const uint64_t ts_epsilon, 1271 # const uint8_t lt_sk[crypto_sign_SECRETKEYBYTES], 1272 # const STP_DKG_Message *msg0, 1273 # uint8_t stp_ltpk[crypto_sign_PUBLICKEYBYTES]); 1274 # also conveniently wraps 1275 # int stp_dkg_peer_set_bufs(STP_DKG_PeerState *ctx, 1276 # uint8_t (*peerids)[][crypto_generichash_BYTES], 1277 # Keyloader_CB keyloader_cb, 1278 # void *keyloader_cb_arg, 1279 # uint8_t (*peers_sig_pks)[][crypto_sign_PUBLICKEYBYTES], 1280 # uint8_t (*peers_noise_pks)[][crypto_scalarmult_BYTES], 1281 # Noise_XK_session_t *(*noise_outs)[], 1282 # Noise_XK_session_t *(*noise_ins)[], 1283 # TOPRF_Share (*k_shares)[][2], 1284 # uint8_t (*encrypted_shares)[][noise_xk_handshake3_SIZE + stp_dkg_encrypted_share_SIZE], 1285 # uint8_t (*share_macs)[][crypto_auth_hmacsha256_BYTES], 1286 # uint8_t (*ki_commitments)[][crypto_core_ristretto255_BYTES], 1287 # uint8_t (*k_commitments)[][crypto_core_ristretto255_BYTES], 1288 # uint8_t (*commitments_hashes)[][stp_dkg_commitment_HASHBYTES], 1289 # STP_DKG_Cheater (*cheaters)[], const size_t cheater_max, 1290 # uint16_t *share_complaints, 1291 # uint8_t *my_share_complaints, 1292 # uint64_t *last_ts); 1293 def stp_dkg_peer_start(ts_epsilon, lt_sk, noise_sk, stp_ltpk, msg0, keyloader=None, keyloader_arg=None): 1294 b = ctypes.create_string_buffer(liboprf.stp_dkg_peerstate_size()+32) 1295 b_addr = ctypes.addressof(b) 1296 s_addr = b_addr + (b_addr % 32) 1297 state = ctypes.c_void_p(s_addr) 1298 if state.value % 32 != 0: 1299 raise ValueError("cannot align at 32bytes the STP_DKG_PeerState struct") 1300 1301 if len(lt_sk) != pysodium.crypto_sign_SECRETKEYBYTES: 1302 raise ValueError(f"long-term signature secret key of peer has invalid length ({len(lt_sk)}) must be {pysodium.crypto_sign_SECRETKEYBYTES}") 1303 1304 if len(noise_sk) != pysodium.crypto_scalarmult_SCALARBYTES: 1305 raise ValueError(f"long-term noise secret key of peer has invalid length ({len(noise_sk)}) must be {pysodium.crypto_scalarmult_SCALARBYTES}") 1306 1307 if len(stp_ltpk) != pysodium.crypto_sign_PUBLICKEYBYTES: 1308 raise ValueError(f"long-term signature public key of STP has invalid length ({len(stp_ltpk)}) must be {pysodium.crypto_sign_PUBLICKEYBYTES}") 1309 1310 __check(liboprf.stp_dkg_start_peer(state, ctypes.c_uint64(ts_epsilon), lt_sk, noise_sk, msg0, stp_ltpk)) 1311 1312 n = stp_dkg_peerstate_n([state]) 1313 t = stp_dkg_peerstate_t([state]) 1314 1315 peer_ids = ctypes.create_string_buffer(n * pysodium.crypto_generichash_BYTES) 1316 peers_sig_pks = ctypes.create_string_buffer((n+1) * pysodium.crypto_sign_PUBLICKEYBYTES) 1317 peers_noise_pks = ctypes.create_string_buffer(n * pysodium.crypto_scalarmult_BYTES) 1318 noise_outs = (ctypes.c_void_p * n)() 1319 noise_ins = (ctypes.c_void_p * n)() 1320 shares = ctypes.create_string_buffer(n * TOPRF_Share_BYTES*2) 1321 encrypted_shares = ctypes.create_string_buffer(n * (noise_xk_handshake3_SIZE + stp_dkg_encrypted_share_SIZE)) 1322 share_macs = ctypes.create_string_buffer(n * n * pysodium.crypto_auth_hmacsha256_BYTES) 1323 commitments = ctypes.create_string_buffer(n * n * pysodium.crypto_core_ristretto255_BYTES) 1324 k_commitments = ctypes.create_string_buffer(n * pysodium.crypto_core_ristretto255_BYTES) 1325 commitment_hashes = ctypes.create_string_buffer(n * tupdate_commitment_HASHBYTES) 1326 cheaters = (TP_DKG_Cheater * (t*t - 1))() 1327 complaints = (ctypes.c_uint16 * n*n)() 1328 my_complaints = ctypes.create_string_buffer(n) 1329 last_ts = (ctypes.c_uint64 * n)() 1330 1331 liboprf.stp_dkg_peer_set_bufs(state, 1332 ctypes.byref(peer_ids), 1333 #ctypes.byref(keyloader), 1334 keyloader, 1335 #ctypes.byref(keyloader_arg), 1336 keyloader_arg, 1337 ctypes.byref(peers_sig_pks), 1338 ctypes.byref(peers_noise_pks), 1339 noise_outs, 1340 noise_ins, 1341 ctypes.byref(shares), 1342 ctypes.byref(encrypted_shares), 1343 ctypes.byref(share_macs), 1344 ctypes.byref(commitments), 1345 ctypes.byref(k_commitments), 1346 ctypes.byref(commitment_hashes), 1347 ctypes.byref(cheaters), ctypes.c_size_t(len(cheaters)), 1348 ctypes.byref(complaints), 1349 ctypes.byref(my_complaints), 1350 ctypes.byref(last_ts)) 1351 1352 # we need to keep these arrays around, otherwise the gc eats them up. 1353 ctx = (state, peer_ids, peers_sig_pks, peers_noise_pks, noise_outs, noise_ins, shares, encrypted_shares, 1354 share_macs, commitments, k_commitments, commitment_hashes, cheaters, complaints, my_complaints, b, last_ts) 1355 return ctx 1356 1357 #size_t stp_dkg_peer_input_size(const STP_DKG_PeerState *ctx); 1358 def stp_dkg_peer_input_size(ctx): 1359 return liboprf.stp_dkg_peer_input_size(ctx[0]) 1360 1361 #size_t stp_dkg_peer_output_size(const STP_DKG_PeerState *ctx); 1362 def stp_dkg_peer_output_size(ctx): 1363 return liboprf.stp_dkg_peer_output_size(ctx[0]) 1364 1365 #int stp_dkg_peer_next(STP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); 1366 def stp_dkg_peer_next(ctx, msg): 1367 input_len = stp_dkg_peer_input_size(ctx) 1368 if len(msg) != input_len: raise ValueError(f"input msg is invalid size: {len(msg)}B must be: {input_len}B") 1369 output_len = stp_dkg_peer_output_size(ctx) 1370 output = ctypes.create_string_buffer(output_len) 1371 __check(liboprf.stp_dkg_peer_next(ctx[0], msg, ctypes.c_size_t(input_len), output, ctypes.c_size_t(output_len))) 1372 return output.raw 1373 1374 #int stp_dkg_peer_not_done(const STP_DKG_PeerState *peer); 1375 def stp_dkg_peer_not_done(ctx): 1376 return liboprf.stp_dkg_peer_not_done(ctx[0]) == 1 1377 1378 #void stp_dkg_peer_free(STP_DKG_PeerState *ctx); 1379 def stp_dkg_peer_free(ctx): 1380 liboprf.stp_dkg_peer_free(ctx[0])