/ python / pyoprf / __init__.py
__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])