def recombine_secret_from_samir_shares(shares: list) -> bytes:
    """Permits to reconstruct a key which has its secret shared
    into `shares_count` shares thanks to a list of `shares`

    :param shares: list of k full-length shares (k being exactly the threshold of this shared secret)

    :return: the key reconstructed as bytes"""

    shares_per_secret = []  # List of lists of same-index 16-bytes shares

    assert len(set(share[0] for share in shares)) == len(
        shares)  # All shares have unique idx

    for share in shares:
        idx, secret = share
        chunks = split_as_chunks(secret, chunk_size=16, must_pad=False)
        shares_per_secret.append([(idx, chunk) for chunk in chunks])

    assert (len(set(len(chunks) for chunks in shares_per_secret)) == 1
            )  # Same-length lists

    all_chunk_shares = list(zip(*shares_per_secret))

    chunks = []
    for chunk_shares in all_chunk_shares:
        chunk = _recombine_128b_shares_into_bytestring(chunk_shares)
        chunks.append(chunk)

    secret = recombine_chunks(chunks,
                              chunk_size=SHAMIR_CHUNK_LENGTH,
                              must_unpad=True)

    return secret
def test_split_as_chunks_and_recombine():

    bytestring = get_random_bytes(100)

    chunks = split_as_chunks(bytestring, chunk_size=25, must_pad=True)
    assert all(len(x) == 25 for x in chunks)
    result = recombine_chunks(chunks, chunk_size=25, must_unpad=True)
    assert result == bytestring

    chunks = split_as_chunks(bytestring, chunk_size=22, must_pad=True)
    assert all(len(x) == 22 for x in chunks)
    result = recombine_chunks(chunks, chunk_size=22, must_unpad=True)
    assert result == bytestring

    chunks = split_as_chunks(bytestring, chunk_size=25, must_pad=False)
    assert all(len(x) == 25 for x in chunks)
    result = recombine_chunks(chunks, chunk_size=25, must_unpad=False)
    assert result == bytestring

    with pytest.raises(ValueError, match="size multiple of chunk_size"):
        split_as_chunks(bytestring, chunk_size=22, must_pad=False)

    chunks = split_as_chunks(
        bytestring, chunk_size=22, must_pad=False, accept_incomplete_chunk=True
    )
    assert not all(len(x) == 22 for x in chunks)
    result = recombine_chunks(chunks, chunk_size=22, must_unpad=False)
    assert result == bytestring
def split_bytestring_as_shamir_shares(secret: bytes, *, shares_count: int,
                                      threshold_count: int) -> list:
    """Generate a shared secret of `shares_count` subkeys, with `threshold_count`
        of them required to recompute the initial `bytestring`.

        :param secret: bytestring to separate as shares, whatever its length
        :param shares_count: the number of shares to be created for the secret
        :param threshold_count: the minimal number of shares needed to recombine the key

        :return: list of full bytestring shares"""

    if not shares_count:
        raise ValueError("Shares count must be strictly positive")

    if threshold_count > shares_count:
        raise ValueError(
            "Threshold count %s can't be higher than shared count %s" %
            (threshold_count, shares_count))

    all_chunk_shares = []  # List of lists of related 16-bytes shares

    # Split the secret into tuples of 16 bytes exactly (after padding)
    chunks = split_as_chunks(secret,
                             chunk_size=SHAMIR_CHUNK_LENGTH,
                             must_pad=True)

    # Separate each chunk into share
    for chunk in chunks:
        shares = _split_128b_bytestring_into_shares(chunk, shares_count,
                                                    threshold_count)
        all_chunk_shares.append(shares)
        del shares

    full_shares = []

    for idx in range(shares_count):
        assert all(
            chunk_share[idx][0] == idx + 1 for chunk_share in
            all_chunk_shares)  # By construction, share indices start at 1
        idx_shares = (chunk_share[idx][1] for chunk_share in all_chunk_shares)
        complete_share = b"".join(idx_shares)
        full_shares.append((idx + 1, complete_share))

    return full_shares
Exemplo n.º 4
0
def _encrypt_via_rsa_oaep(plaintext: bytes, key: RSA.RsaKey) -> dict:
    """Encrypt a bytestring with PKCS#1 RSA OAEP (asymmetric algo).

    :param plaintext: the bytes to cipher
    :param key: public RSA key

    :return: a dict with field `digest_list`, containing bytestring chunks of variable width."""
    _check_asymmetric_key_length_bits(key.size_in_bits())

    cipher = PKCS1_OAEP.new(key=key, hashAlgo=RSA_OAEP_HASHER)
    chunks = split_as_chunks(plaintext,
                             chunk_size=RSA_OAEP_CHUNKS_SIZE,
                             must_pad=False,
                             accept_incomplete_chunk=True)

    encrypted_chunks = []
    for chunk in chunks:
        encrypted_chunk = cipher.encrypt(chunk)
        encrypted_chunks.append(encrypted_chunk)
    return dict(digest_list=encrypted_chunks)