Updated DB_Helper by adding firebase methods.

This commit is contained in:
Batuhan Berk Başoğlu 2020-10-05 16:53:40 -04:00
parent 485cc3bbba
commit c82121d036
1810 changed files with 537281 additions and 1 deletions

View file

@ -0,0 +1,397 @@
#
# KDF.py : a collection of Key Derivation Functions
#
# Part of the Python Cryptography Toolkit
#
# ===================================================================
# The contents of this file are dedicated to the public domain. To
# the extent that dedication to the public domain is not available,
# everyone is granted a worldwide, perpetual, royalty-free,
# non-exclusive license to exercise all rights associated with the
# contents of this file for any purpose whatsoever.
# No rights are reserved.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# ===================================================================
"""This file contains a collection of standard key derivation functions.
A key derivation function derives one or more secondary secret keys from
one primary secret (a master key or a pass phrase).
This is typically done to insulate the secondary keys from each other,
to avoid that leakage of a secondary key compromises the security of the
master key, or to thwart attacks on pass phrases (e.g. via rainbow tables).
"""
import struct
from struct import unpack
from Crypto.Util.py3compat import *
from Crypto.Hash import SHA1, SHA256, HMAC, CMAC
from Crypto.Util.strxor import strxor
from Crypto.Util.number import size as bit_size, long_to_bytes, bytes_to_long
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
create_string_buffer,
get_raw_buffer)
from functools import reduce
_raw_salsa20_lib = load_pycryptodome_raw_lib("Crypto.Cipher._Salsa20",
"""
int Salsa20_8_core(const uint8_t *x, const uint8_t *y,
uint8_t *out);
uint32_t load_le_uint32(const uint8_t *in);
""")
def PBKDF1(password, salt, dkLen, count=1000, hashAlgo=None):
"""Derive one key from a password (or passphrase).
This function performs key derivation according an old version of
the PKCS#5 standard (v1.5).
This algorithm is called ``PBKDF1``. Even though it is still described
in the latest version of the PKCS#5 standard (version 2, or RFC2898),
newer applications should use the more secure and versatile `PBKDF2` instead.
:Parameters:
password : string
The secret password or pass phrase to generate the key from.
salt : byte string
An 8 byte string to use for better protection from dictionary attacks.
This value does not need to be kept secret, but it should be randomly
chosen for each derivation.
dkLen : integer
The length of the desired key. Default is 16 bytes, suitable for instance for `Crypto.Cipher.AES`.
count : integer
The number of iterations to carry out. It's recommended to use at least 1000.
hashAlgo : module
The hash algorithm to use, as a module or an object from the `Crypto.Hash` package.
The digest length must be no shorter than ``dkLen``.
The default algorithm is `SHA1`.
:Return: A byte string of length `dkLen` that can be used as key.
"""
if not hashAlgo:
hashAlgo = SHA1
password = tobytes(password)
pHash = hashAlgo.new(password+salt)
digest = pHash.digest_size
if dkLen>digest:
raise TypeError("Selected hash algorithm has a too short digest (%d bytes)." % digest)
if len(salt) != 8:
raise ValueError("Salt is not 8 bytes long (%d bytes instead)." % len(salt))
for i in range(count-1):
pHash = pHash.new(pHash.digest())
return pHash.digest()[:dkLen]
def PBKDF2(password, salt, dkLen=16, count=1000, prf=None):
"""Derive one or more keys from a password (or passphrase).
This function performs key derivation according to
the PKCS#5 standard (v2.0), by means of the ``PBKDF2`` algorithm.
:Parameters:
password : string
The secret password or pass phrase to generate the key from.
salt : string
A string to use for better protection from dictionary attacks.
This value does not need to be kept secret, but it should be randomly
chosen for each derivation. It is recommended to be at least 8 bytes long.
dkLen : integer
The cumulative length of the desired keys. Default is 16 bytes, suitable for instance for `Crypto.Cipher.AES`.
count : integer
The number of iterations to carry out. It's recommended to use at least 1000.
prf : callable
A pseudorandom function. It must be a function that returns a pseudorandom string
from two parameters: a secret and a salt. If not specified, HMAC-SHA1 is used.
:Return: A byte string of length `dkLen` that can be used as key material.
If you wanted multiple keys, just break up this string into segments of the desired length.
"""
password = tobytes(password)
if prf is None:
prf = lambda p,s: HMAC.new(p,s,SHA1).digest()
def link(s):
s[0], s[1] = s[1], prf(password, s[1])
return s[0]
key = b('')
i = 1
while len(key)<dkLen:
s = [ prf(password, salt + struct.pack(">I", i)) ] * 2
key += reduce(strxor, (link(s) for j in range(count)) )
i += 1
return key[:dkLen]
class _S2V(object):
"""String-to-vector PRF as defined in `RFC5297`_.
This class implements a pseudorandom function family
based on CMAC that takes as input a vector of strings.
.. _RFC5297: http://tools.ietf.org/html/rfc5297
"""
def __init__(self, key, ciphermod, cipher_params=None):
"""Initialize the S2V PRF.
:Parameters:
key : byte string
A secret that can be used as key for CMACs
based on ciphers from ``ciphermod``.
ciphermod : module
A block cipher module from `Crypto.Cipher`.
cipher_params : dictionary
A set of extra parameters to use to create a cipher instance.
"""
self._key = key
self._ciphermod = ciphermod
self._last_string = self._cache = bchr(0)*ciphermod.block_size
self._n_updates = ciphermod.block_size*8-1
if cipher_params is None:
self._cipher_params = {}
else:
self._cipher_params = dict(cipher_params)
@staticmethod
def new(key, ciphermod):
"""Create a new S2V PRF.
:Parameters:
key : byte string
A secret that can be used as key for CMACs
based on ciphers from ``ciphermod``.
ciphermod : module
A block cipher module from `Crypto.Cipher`.
"""
return _S2V(key, ciphermod)
def _double(self, bs):
doubled = bytes_to_long(bs)<<1
if bord(bs[0]) & 0x80:
doubled ^= 0x87
return long_to_bytes(doubled, len(bs))[-len(bs):]
def update(self, item):
"""Pass the next component of the vector.
The maximum number of components you can pass is equal to the block
length of the cipher (in bits) minus 1.
:Parameters:
item : byte string
The next component of the vector.
:Raise TypeError: when the limit on the number of components has been reached.
:Raise ValueError: when the component is empty
"""
if not item:
raise ValueError("A component cannot be empty")
if self._n_updates==0:
raise TypeError("Too many components passed to S2V")
self._n_updates -= 1
mac = CMAC.new(self._key,
msg=self._last_string,
ciphermod=self._ciphermod,
cipher_params=self._cipher_params)
self._cache = strxor(self._double(self._cache), mac.digest())
self._last_string = item
def derive(self):
""""Derive a secret from the vector of components.
:Return: a byte string, as long as the block length of the cipher.
"""
if len(self._last_string)>=16:
final = self._last_string[:-16] + strxor(self._last_string[-16:], self._cache)
else:
padded = (self._last_string + bchr(0x80)+ bchr(0)*15)[:16]
final = strxor(padded, self._double(self._cache))
mac = CMAC.new(self._key,
msg=final,
ciphermod=self._ciphermod,
cipher_params=self._cipher_params)
return mac.digest()
def HKDF(master, key_len, salt, hashmod, num_keys=1, context=None):
"""Derive one or more keys from a master secret using
the HMAC-based KDF defined in RFC5869_.
This KDF is not suitable for deriving keys from a password or for key
stretching. Use `PBKDF2` instead.
HKDF is a key derivation method approved by NIST in `SP 800 56C`__.
:Parameters:
master : byte string
The unguessable value used by the KDF to generate the other keys.
It must be a high-entropy secret, though not necessarily uniform.
It must not be a password.
salt : byte string
A non-secret, reusable value that strengthens the randomness
extraction step.
Ideally, it is as long as the digest size of the chosen hash.
If empty, a string of zeroes in used.
key_len : integer
The length in bytes of every derived key.
hashmod : module
A cryptographic hash algorithm from `Crypto.Hash`.
`Crypto.Hash.SHA512` is a good choice.
num_keys : integer
The number of keys to derive. Every key is ``key_len`` bytes long.
The maximum cumulative length of all keys is
255 times the digest size.
context : byte string
Optional identifier describing what the keys are used for.
:Return: A byte string or a tuple of byte strings.
.. _RFC5869: http://tools.ietf.org/html/rfc5869
.. __: http://csrc.nist.gov/publications/nistpubs/800-56C/SP-800-56C.pdf
"""
output_len = key_len * num_keys
if output_len > (255 * hashmod.digest_size):
raise ValueError("Too much secret data to derive")
if not salt:
salt = bchr(0) * hashmod.digest_size
if context is None:
context = b("")
# Step 1: extract
hmac = HMAC.new(salt, master, digestmod=hashmod)
prk = hmac.digest()
# Step 2: expand
t = [b("")]
n = 1
tlen = 0
while tlen < output_len:
hmac = HMAC.new(prk, t[-1] + context + bchr(n), digestmod=hashmod)
t.append(hmac.digest())
tlen += hashmod.digest_size
n += 1
derived_output = b("").join(t)
if num_keys == 1:
return derived_output[:key_len]
kol = [derived_output[idx:idx + key_len]
for idx in range(0, output_len, key_len)]
return list(kol[:num_keys])
def _scryptBlockMix(blocks, len_blocks):
"""Hash function for ROMix."""
x = blocks[-1]
core = _raw_salsa20_lib.Salsa20_8_core
result = [ create_string_buffer(64) for _ in range(len(blocks)) ]
for i in range(len(blocks)):
core(x, blocks[i], result[i])
x = result[i]
return [result[i + j] for j in range(2)
for i in range(0, len_blocks, 2)]
def _scryptROMix(blocks, n):
"""Sequential memory-hard function for scrypt."""
x = [blocks[i:i + 64] for i in range(0, len(blocks), 64)]
len_x = len(x)
v = [None]*n
load_le_uint32 = _raw_salsa20_lib.load_le_uint32
for i in range(n):
v[i] = x
x = _scryptBlockMix(x, len_x)
for i in range(n):
j = load_le_uint32(x[-1]) & (n - 1)
t = [strxor(x[idx], v[j][idx]) for idx in range(len_x)]
x = _scryptBlockMix(t, len_x)
return b("").join([get_raw_buffer(y) for y in x])
def scrypt(password, salt, key_len, N, r, p, num_keys=1):
"""Derive one or more keys from a passphrase.
This function performs key derivation according to
the `scrypt`_ algorithm, introduced in Percival's paper
`"Stronger key derivation via sequential memory-hard functions"`__.
This implementation is based on the `RFC draft`__.
:Parameters:
password : string
The secret pass phrase to generate the keys from.
salt : string
A string to use for better protection from dictionary attacks.
This value does not need to be kept secret,
but it should be randomly chosen for each derivation.
It is recommended to be at least 8 bytes long.
key_len : integer
The length in bytes of every derived key.
N : integer
CPU/Memory cost parameter. It must be a power of 2 and less
than ``2**32``.
r : integer
Block size parameter.
p : integer
Parallelization parameter.
It must be no greater than ``(2**32-1)/(4r)``.
num_keys : integer
The number of keys to derive. Every key is ``key_len`` bytes long.
By default, only 1 key is generated.
The maximum cumulative length of all keys is ``(2**32-1)*32``
(that is, 128TB).
A good choice of parameters *(N, r , p)* was suggested
by Colin Percival in his `presentation in 2009`__:
- *(16384, 8, 1)* for interactive logins (<=100ms)
- *(1048576, 8, 1)* for file encryption (<=5s)
:Return: A byte string or a tuple of byte strings.
.. _scrypt: http://www.tarsnap.com/scrypt.html
.. __: http://www.tarsnap.com/scrypt/scrypt.pdf
.. __: http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-03
.. __: http://www.tarsnap.com/scrypt/scrypt-slides.pdf
"""
if 2 ** (bit_size(N) - 1) != N:
raise ValueError("N must be a power of 2")
if N >= 2 ** 32:
raise ValueError("N is too big")
if p > ((2 ** 32 - 1) * 32) // (128 * r):
raise ValueError("p or r are too big")
prf_hmac_sha256 = lambda p, s: HMAC.new(p, s, SHA256).digest()
blocks = PBKDF2(password, salt, p * 128 * r, 1, prf=prf_hmac_sha256)
blocks = b("").join([_scryptROMix(blocks[x:x + 128 * r], N)
for x in range(0, len(blocks), 128 * r)])
dk = PBKDF2(password, blocks, key_len * num_keys, 1,
prf=prf_hmac_sha256)
if num_keys == 1:
return dk
kol = [dk[idx:idx + key_len]
for idx in range(0, key_len * num_keys, key_len)]
return kol

View file

@ -0,0 +1,331 @@
#
# SecretSharing.py : distribute a secret amongst a group of participants
#
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""This file implements secret sharing protocols.
In a *(k, n)* secret sharing protocol, a honest dealer breaks a secret
into multiple shares that are distributed amongst *n* players.
The protocol guarantees that nobody can learn anything about the
secret, unless *k* players gather together to assemble their shares.
"""
from Crypto.Util.py3compat import *
from Crypto.Util import number
from Crypto.Util.number import long_to_bytes, bytes_to_long
from Crypto.Random import get_random_bytes as rng
def _mult_gf2(f1, f2):
"""Multiply two polynomials in GF(2)"""
# Ensure f2 is the smallest
if f2 > f1:
f1, f2 = f2, f1
z = 0
while f2:
if f2 & 1:
z ^= f1
f1 <<= 1
f2 >>= 1
return z
def _div_gf2(a, b):
"""
Compute division of polynomials over GF(2).
Given a and b, it finds two polynomials q and r such that:
a = b*q + r with deg(r)<deg(b)
"""
if (a < b):
return 0, a
deg = number.size
q = 0
r = a
d = deg(b)
while deg(r) >= d:
s = 1 << (deg(r) - d)
q ^= s
r ^= _mult_gf2(b, s)
return (q, r)
class _Element(object):
"""Element of GF(2^128) field"""
# The irreducible polynomial defining this field is 1+x+x^2+x^7+x^128
irr_poly = 1 + 2 + 4 + 128 + 2 ** 128
def __init__(self, encoded_value):
"""Initialize the element to a certain value.
The value passed as parameter is internally encoded as
a 128-bit integer, where each bit represents a polynomial
coefficient. The LSB is the constant coefficient.
"""
if isinstance(encoded_value, int):
self._value = encoded_value
elif len(encoded_value) == 16:
self._value = bytes_to_long(encoded_value)
else:
raise ValueError("The encoded value must be an integer or a 16 byte string")
def __int__(self):
"""Return the field element, encoded as a 128-bit integer."""
return self._value
def encode(self):
"""Return the field element, encoded as a 16 byte string."""
return long_to_bytes(self._value, 16)
def __mul__(self, factor):
f1 = self._value
f2 = factor._value
# Make sure that f2 is the smallest, to speed up the loop
if f2 > f1:
f1, f2 = f2, f1
if self.irr_poly in (f1, f2):
return _Element(0)
mask1 = 2 ** 128
v, z = f1, 0
while f2:
if f2 & 1:
z ^= v
v <<= 1
if v & mask1:
v ^= self.irr_poly
f2 >>= 1
return _Element(z)
def __add__(self, term):
return _Element(self._value ^ term._value)
def inverse(self):
"""Return the inverse of this element in GF(2^128)."""
# We use the Extended GCD algorithm
# http://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor
r0, r1 = self._value, self.irr_poly
s0, s1 = 1, 0
while r1 > 0:
q = _div_gf2(r0, r1)[0]
r0, r1 = r1, r0 ^ _mult_gf2(q, r1)
s0, s1 = s1, s0 ^ _mult_gf2(q, s1)
return _Element(s0)
class Shamir(object):
"""Shamir's secret sharing scheme.
This class implements the Shamir's secret sharing protocol
described in his original paper `"How to share a secret"`__.
All shares are points over a 2-dimensional curve. At least
*k* points (that is, shares) are required to reconstruct the curve,
and therefore the secret.
This implementation is primarilly meant to protect AES128 keys.
To that end, the secret is associated to a curve in
the field GF(2^128) defined by the irreducible polynomial
*x^128 + x^7 + x^2 + x + 1* (the same used in AES-GCM).
The shares are always 16 bytes long.
Data produced by this implementation are compatible to the popular
`ssss`_ tool if used with 128 bit security (parameter *"-s 128"*)
and no dispersion (parameter *"-D"*).
As an example, the following code shows how to protect a file meant
for 5 people, in such a way that 2 of the 5 are required to
reassemble it.
>>> from binascii import hexlify
>>> from Crypto.Cipher import AES
>>> from Crypto.Random import get_random_bytes
>>> from Crypto.Protocol.secret_sharing import Shamir
>>>
>>> key = get_random_bytes(16)
>>> shares = Shamir.split(2, 5, key)
>>> for idx, share in shares:
>>> print "Index #%d: %s" % (idx, hexlify(share))
>>>
>>> fi = open("clear_file.txt", "rb")
>>> fo = open("enc_file.txt", "wb")
>>>
>>> cipher = AES.new(key, AES.MODE_EAX)
>>> ct, tag = cipher.encrypt(fi.read()), cipher.digest()
>>> fo.write(nonce + tag + ct)
Each person can be given one share and the encrypted file.
When 2 people gather together with their shares, the can
decrypt the file:
>>> from binascii import unhexlify
>>> from Crypto.Cipher import AES
>>> from Crypto.Protocol.secret_sharing import Shamir
>>>
>>> shares = []
>>> for x in range(2):
>>> in_str = raw_input("Enter index and share separated by comma: ")
>>> idx, share = [ strip(s) for s in in_str.split(",") ]
>>> shares.append((idx, unhexlify(share)))
>>> key = Shamir.combine(shares)
>>>
>>> fi = open("enc_file.txt", "rb")
>>> nonce, tag = [ fi.read(16) for x in range(2) ]
>>> cipher = AES.new(key, AES.MODE_EAX, nonce)
>>> try:
>>> result = cipher.decrypt(fi.read())
>>> cipher.verify(tag)
>>> with open("clear_file2.txt", "wb") as fo:
>>> fo.write(result)
>>> except ValueError:
>>> print "The shares were incorrect"
:attention:
Reconstruction does not guarantee that the result is authentic.
In particular, a malicious participant in the scheme has the
ability to force an algebric transformation on the result by
manipulating her share.
It is important to use the scheme in combination with an
authentication mechanism (the EAX cipher mode in the example).
.. __: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.80.8910&rep=rep1&type=pdf
.. _ssss: http://point-at-infinity.org/ssss/
"""
@staticmethod
def split(k, n, secret):
"""Split a secret into *n* shares.
The secret can be reconstructed later when *k* shares
out of the original *n* are recombined. Each share
must be kept confidential to the person it was
assigned to.
Each share is associated to an index (starting from 1),
which must be presented when the secret is recombined.
:Parameters:
k : integer
The number of shares that must be present in order to reconstruct
the secret.
n : integer
The total number of shares to create (>*k*).
secret : byte string
The 16 byte string (e.g. the AES128 key) to split.
:Return:
*n* tuples, each containing the unique index (an integer) and
the share (a byte string, 16 bytes long) meant for a
participant.
"""
#
# We create a polynomial with random coefficients in GF(2^128):
#
# p(x) = \sum_{i=0}^{k-1} c_i * x^i
#
# c_0 is the encoded secret
#
coeffs = [_Element(rng(16)) for i in range(k - 1)]
coeffs.insert(0, _Element(secret))
# Each share is y_i = p(x_i) where x_i is the public index
# associated to each of the n users.
def make_share(user, coeffs):
share, x, idx = [_Element(p) for p in (0, 1, user)]
for coeff in coeffs:
share += coeff * x
x *= idx
return share.encode()
return [(i, make_share(i, coeffs)) for i in range(1, n + 1)]
@staticmethod
def combine(shares):
"""Recombine a secret, if enough shares are presented.
:Parameters:
shares : tuples
At least *k* tuples, each containin the index (an integer) and
the share (a byte string, 16 bytes long) that were assigned to
a participant.
:Return:
The original secret, as a byte string (16 bytes long).
"""
#
# Given k points (x,y), the interpolation polynomial of degree k-1 is:
#
# L(x) = \sum_{j=0}^{k-1} y_i * l_j(x)
#
# where:
#
# l_j(x) = \prod_{ \overset{0 \le m \le k-1}{m \ne j} }
# \frac{x - x_m}{x_j - x_m}
#
# However, in this case we are purely intersted in the constant
# coefficient of L(x).
#
shares = [[_Element(y) for y in x] for x in shares]
result = _Element(0)
k = len(shares)
for j in range(k):
x_j, y_j = shares[j]
coeff_0_l = _Element(0)
while not int(coeff_0_l):
coeff_0_l = _Element(rng(16))
inv = coeff_0_l.inverse()
for m in range(k):
x_m = shares[m][0]
if m != j:
t = x_m * (x_j + x_m).inverse()
coeff_0_l *= t
result += y_j * coeff_0_l * inv
return result.encode()

View file

@ -0,0 +1,43 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""Cryptographic protocols
Implements various cryptographic protocols. (Don't expect to find
network protocols here.)
Crypto.Protocol.KDF
A collection of standard key derivation functions.
Crypto.Protocol.SecretSharing
Distribute a secret amongst a group of participants.
"""
__all__ = ['KDF', 'SecretSharing']