def _import_openssh_private_ecc(data, password): from ._openssh import (import_openssh_private_generic, read_bytes, read_string, check_padding) ssh_name, decrypted = import_openssh_private_generic(data, password) name, decrypted = read_string(decrypted) if name not in _curves: raise UnsupportedEccFeature("Unsupported ECC curve %s" % name) curve = _curves[name] modulus_bytes = (curve.modulus_bits + 7) // 8 public_key, decrypted = read_bytes(decrypted) if bord(public_key[0]) != 4: raise ValueError("Only uncompressed OpenSSH EC keys are supported") if len(public_key) != 2 * modulus_bytes + 1: raise ValueError("Incorrect public key length") point_x = Integer.from_bytes(public_key[1:1 + modulus_bytes]) point_y = Integer.from_bytes(public_key[1 + modulus_bytes:]) point = EccPoint(point_x, point_y, curve=name) private_key, decrypted = read_bytes(decrypted) d = Integer.from_bytes(private_key) _, padded = read_string(decrypted) # Comment check_padding(padded) return EccKey(curve=name, d=d, point=point)
def byte_init(cls, key, bytestring): '''Returns a Ciphertext from a bytestring containing c1 and c2''' padded_size = int(np.round(key.p.size_in_bits() / 8)) assert (len(bytestring) == 2 * padded_size) c1 = Integer.from_bytes(bytestring[:padded_size]) c2 = Integer.from_bytes(bytestring[padded_size:]) return cls(key, c1, c2)
def deserialize_ElGamalPrivateKey(bytestring): '''constructs an ElGamal Public Key from a bytestring''' padded_size = len(bytestring) // 4 assert padded_size * 4 == len(bytestring), "Wrong bytestring length" p = Integer.from_bytes(bytestring[:padded_size]) g = Integer.from_bytes(bytestring[padded_size:2 * padded_size]) y = Integer.from_bytes(bytestring[2 * padded_size:3 * padded_size]) x = Integer.from_bytes(bytestring[3 * padded_size:]) return ElGamal.construct((p, g, y, x))
def _import_public_der(ec_point, curve_oid=None, curve_name=None): """Convert an encoded EC point into an EccKey object ec_point: byte string with the EC point (SEC1-encoded) curve_oid: string with the name the curve curve_name: string with the OID of the curve Either curve_id or curve_name must be specified """ for _curve_name, curve in _curves.items(): if curve_oid and curve.oid == curve_oid: break if curve_name == _curve_name: break else: if curve_oid: raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid) else: raise UnsupportedEccFeature("Unsupported ECC curve (%s)" % curve_name) # See 2.2 in RFC5480 and 2.3.3 in SEC1 # The first byte is: # - 0x02: compressed, only X-coordinate, Y-coordinate is even # - 0x03: compressed, only X-coordinate, Y-coordinate is odd # - 0x04: uncompressed, X-coordinate is followed by Y-coordinate # # PAI is in theory encoded as 0x00. modulus_bytes = curve.p.size_in_bytes() point_type = bord(ec_point[0]) # Uncompressed point if point_type == 0x04: if len(ec_point) != (1 + 2 * modulus_bytes): raise ValueError("Incorrect EC point length") x = Integer.from_bytes(ec_point[1:modulus_bytes + 1]) y = Integer.from_bytes(ec_point[modulus_bytes + 1:]) # Compressed point elif point_type in (0x02, 0x03): if len(ec_point) != (1 + modulus_bytes): raise ValueError("Incorrect EC point length") x = Integer.from_bytes(ec_point[1:]) # Right now, we only support Short Weierstrass curves y = (x**3 - x * 3 + curve.b).sqrt(curve.p) if point_type == 0x02 and y.is_odd(): y = curve.p - y if point_type == 0x03 and y.is_even(): y = curve.p - y else: raise ValueError("Incorrect EC point encoding") return construct(curve=_curve_name, point_x=x, point_y=y)
def test_several_lengths(self): prng = SHAKE128.new().update(b('Test')) for length in range(1, 100): base = Integer.from_bytes(prng.read(length)) modulus2 = Integer.from_bytes(prng.read(length)) | 1 exponent2 = Integer.from_bytes(prng.read(length)) expected = pow(base, exponent2, modulus2) result = monty_pow(base, exponent2, modulus2) self.assertEqual(result, expected)
def _generate_domain(L, randfunc): """Generate a new set of DSA domain parameters""" N = {1024: 160, 2048: 224, 3072: 256}.get(L) if N is None: raise ValueError("Invalid modulus length (%d)" % L) outlen = SHA256.digest_size * 8 n = (L + outlen - 1) // outlen - 1 # ceil(L/outlen) -1 b_ = L - 1 - (n * outlen) # Generate q (A.1.1.2) q = Integer(4) upper_bit = 1 << (N - 1) while test_probable_prime(q, randfunc) != PROBABLY_PRIME: seed = randfunc(64) U = Integer.from_bytes(SHA256.new(seed).digest()) & (upper_bit - 1) q = U | upper_bit | 1 assert (q.size_in_bits() == N) # Generate p (A.1.1.2) offset = 1 upper_bit = 1 << (L - 1) while True: V = [ SHA256.new(seed + Integer(offset + j).to_bytes()).digest() for j in range(n + 1) ] V = [Integer.from_bytes(v) for v in V] W = sum([V[i] * (1 << (i * outlen)) for i in range(n)], (V[n] & (1 << b_ - 1)) * (1 << (n * outlen))) X = Integer(W + upper_bit) # 2^{L-1} < X < 2^{L} assert (X.size_in_bits() == L) c = X % (q * 2) p = X - (c - 1) # 2q divides (p-1) if p.size_in_bits() == L and \ test_probable_prime(p, randfunc) == PROBABLY_PRIME: break offset += n + 1 # Generate g (A.2.3, index=1) e = (p - 1) // q for count in itertools.count(1): U = seed + b("ggen") + bchr(1) + Integer(count).to_bytes() W = Integer.from_bytes(SHA256.new(U).digest()) g = pow(W, e, p) if g != 1: break return (p, q, g, seed)
def verify(self, msg_hash, signature): """Verify that a certain DSS signature is authentic. This function checks if the party holding the private half of the key really signed the message. :Parameters: msg_hash : hash object The hash that was carried out over the message. This is an object belonging to the `Cryptodome.Hash` module. Under mode *'fips-186-3'*, the hash must be a FIPS approved secure hash (SHA-1 or a member of the SHA-2 family), of cryptographic strength appropriate for the DSA key. For instance, a 3072/256 DSA key can only be used in combination with SHA-512. signature : byte string The signature that needs to be validated. :Raise ValueError: If the signature is not authentic. """ if not self._valid_hash(msg_hash): raise ValueError("Hash does not belong to SHS") if self._encoding == 'binary': if len(signature) != (2 * self._order_bytes): raise ValueError("The signature is not authentic (length)") r_prime, s_prime = [ Integer.from_bytes(x) for x in (signature[:self._order_bytes], signature[self._order_bytes:]) ] else: try: der_seq = DerSequence().decode(signature) except (ValueError, IndexError): raise ValueError("The signature is not authentic (DER)") if len(der_seq) != 2 or not der_seq.hasOnlyInts(): raise ValueError( "The signature is not authentic (DER content)") r_prime, s_prime = der_seq[0], der_seq[1] if not (0 < r_prime < self._order) or not (0 < s_prime < self._order): raise ValueError("The signature is not authentic (d)") z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) result = self._key._verify(z, (r_prime, s_prime)) if not result: raise ValueError("The signature is not authentic") # Make PyCryptodome code to fail return False
def verify(self, msg_hash, signature): """Verify that a certain DSS signature is authentic. This function checks if the party holding the private half of the key really signed the message. :Parameters: msg_hash : hash object The hash that was carried out over the message. This is an object belonging to the `Cryptodome.Hash` module. Under mode *'fips-186-3'*, the hash must be a FIPS approved secure hash (SHA-1 or a member of the SHA-2 family), of cryptographic strength appropriate for the DSA key. For instance, a 3072/256 DSA key can only be used in combination with SHA-512. signature : byte string The signature that needs to be validated. :Raise ValueError: If the signature is not authentic. """ if not self._valid_hash(msg_hash): raise ValueError("Hash does not belong to SHS") if self._encoding == 'binary': if len(signature) != (2 * self._order_bytes): raise ValueError("The signature is not authentic (length)") r_prime, s_prime = [Integer.from_bytes(x) for x in (signature[:self._order_bytes], signature[self._order_bytes:])] else: try: der_seq = DerSequence().decode(signature) except (ValueError, IndexError): raise ValueError("The signature is not authentic (DER)") if len(der_seq) != 2 or not der_seq.hasOnlyInts(): raise ValueError("The signature is not authentic (DER content)") r_prime, s_prime = der_seq[0], der_seq[1] if not (0 < r_prime < self._order) or not (0 < s_prime < self._order): raise ValueError("The signature is not authentic (d)") z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) result = self._key._verify(z, (r_prime, s_prime)) if not result: raise ValueError("The signature is not authentic") # Make PyCryptodome code to fail return False
def _generate_domain(L, randfunc): """Generate a new set of DSA domain parameters""" N = { 1024:160, 2048:224, 3072:256 }.get(L) if N is None: raise ValueError("Invalid modulus length (%d)" % L) outlen = SHA256.digest_size * 8 n = (L + outlen - 1) // outlen - 1 # ceil(L/outlen) -1 b_ = L - 1 - (n * outlen) # Generate q (A.1.1.2) q = Integer(4) upper_bit = 1 << (N - 1) while test_probable_prime(q, randfunc) != PROBABLY_PRIME: seed = randfunc(64) U = Integer.from_bytes(SHA256.new(seed).digest()) & (upper_bit - 1) q = U | upper_bit | 1 assert(q.size_in_bits() == N) # Generate p (A.1.1.2) offset = 1 upper_bit = 1 << (L - 1) while True: V = [ SHA256.new(seed + Integer(offset + j).to_bytes()).digest() for j in iter_range(n + 1) ] V = [ Integer.from_bytes(v) for v in V ] W = sum([V[i] * (1 << (i * outlen)) for i in iter_range(n)], (V[n] & ((1 << b_) - 1)) * (1 << (n * outlen))) X = Integer(W + upper_bit) # 2^{L-1} < X < 2^{L} assert(X.size_in_bits() == L) c = X % (q * 2) p = X - (c - 1) # 2q divides (p-1) if p.size_in_bits() == L and \ test_probable_prime(p, randfunc) == PROBABLY_PRIME: break offset += n + 1 # Generate g (A.2.3, index=1) e = (p - 1) // q for count in itertools.count(1): U = seed + b"ggen" + bchr(1) + Integer(count).to_bytes() W = Integer.from_bytes(SHA256.new(U).digest()) g = pow(W, e, p) if g != 1: break return (p, q, g, seed)
def verify(self, msg_hash, signature): """Check if a certain (EC)DSA signature is authentic. :parameter msg_hash: The hash that was carried out over the message. This is an object belonging to the :mod:`Cryptodome.Hash` module. Under mode *'fips-186-3'*, the hash must be a FIPS approved secure hash (SHA-1 or a member of the SHA-2 family), of cryptographic strength appropriate for the DSA key. For instance, a 3072/256 DSA key can only be used in combination with SHA-512. :type msg_hash: hash object :parameter signature: The signature that needs to be validated :type signature: byte string :raise ValueError: if the signature is not authentic """ if not self._valid_hash(msg_hash): raise ValueError("Hash is not sufficiently strong") if self._encoding == 'binary': if len(signature) != (2 * self._order_bytes): raise ValueError("The signature is not authentic (length)") r_prime, s_prime = [ Integer.from_bytes(x) for x in (signature[:self._order_bytes], signature[self._order_bytes:]) ] else: try: der_seq = DerSequence().decode(signature, strict=True) except (ValueError, IndexError): raise ValueError("The signature is not authentic (DER)") if len(der_seq) != 2 or not der_seq.hasOnlyInts(): raise ValueError( "The signature is not authentic (DER content)") r_prime, s_prime = Integer(der_seq[0]), Integer(der_seq[1]) if not (0 < r_prime < self._order) or not (0 < s_prime < self._order): raise ValueError("The signature is not authentic (d)") z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) result = self._key._verify(z, (r_prime, s_prime)) if not result: raise ValueError("The signature is not authentic") # Make PyCryptodome code to fail return False
def _import_public_der(curve_name, publickey): # We only support P-256 named curves for now if curve_name != _curve.oid: raise ValueError("Unsupport curve") # ECPoint ::= OCTET STRING # We support only uncompressed points order_bytes = _curve.order.size_in_bytes() if len(publickey) != (1 + 2 * order_bytes) or bord(publickey[0]) != 4: raise ValueError("Only uncompressed points are supported") point_x = Integer.from_bytes(publickey[1:order_bytes+1]) point_y = Integer.from_bytes(publickey[order_bytes+1:]) return construct(curve="P-256", point_x=point_x, point_y=point_y)
def decode_hospital_shared_secrets_bytestring(key_public, ss_bytes, sketch_size=32): '''Decodes a bytestring of hospital shared secrets corresponding to an encrypted HLL bytestring from the transmit entry of hospital_round2a_hll''' padded_size = int(np.round(key_public.p.size_in_bits() / 8)) array_item_num = len(ss_bytes) // padded_size assert array_item_num * padded_size == len(ss_bytes) num_buckets = array_item_num // sketch_size assert num_buckets * sketch_size == array_item_num ss_array = [[] for _ in range(num_buckets)] pointer = 0 curr_bucket = 0 val = 0 while pointer < len(ss_bytes): ss = Integer.from_bytes(ss_bytes[pointer:pointer + padded_size]) ss_array[curr_bucket].append(ss) pointer = pointer + padded_size val = val + 1 if val == sketch_size: val = 0 curr_bucket = curr_bucket + 1 return ss_array
def verify(self, msg_hash, signature): """Check if a certain (EC)DSA signature is authentic. Args: msg_hash (hash object): The hash that was carried out over the message. This is an object belonging to the :mod:`Cryptodome.Hash` module. Under mode ``'fips-186-3'``, the hash must be a FIPS approved secure hash (SHA-2 or SHA-3). signature (``bytes``): The signature that needs to be validated. :raise ValueError: if the signature is not authentic """ if not self._valid_hash(msg_hash): raise ValueError("Hash is not sufficiently strong") if self._encoding == 'binary': if len(signature) != (2 * self._order_bytes): raise ValueError("The signature is not authentic (length)") r_prime, s_prime = [ Integer.from_bytes(x) for x in (signature[:self._order_bytes], signature[self._order_bytes:]) ] else: try: der_seq = DerSequence().decode(signature, strict=True) except (ValueError, IndexError): raise ValueError("The signature is not authentic (DER)") if len(der_seq) != 2 or not der_seq.hasOnlyInts(): raise ValueError( "The signature is not authentic (DER content)") r_prime, s_prime = Integer(der_seq[0]), Integer(der_seq[1]) if not (0 < r_prime < self._order) or not (0 < s_prime < self._order): raise ValueError("The signature is not authentic (d)") z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) result = self._key._verify(z, (r_prime, s_prime)) if not result: raise ValueError("The signature is not authentic") # Make PyCryptodome code to fail return False
def full_simulation_counts(key_template, hospital_counts): '''Runs a full simulation of an encrypted sum''' padded_size = int(np.round(key_template.p.size_in_bits() / 8)) num_hospitals = len(hospital_counts) round0a = [hospital_round0a(key_template) for _ in range(num_hospitals)] round0a_transmissions = [x['transmit'] for x in round0a] round0b = hub_round0b(key_template, round0a_transmissions) key_public = round0b['key_public'] hospital_keys = [x['private_key'] for x in round0a] round1a_start = time.perf_counter() round1a = [ encrypt(key_public, pow(Integer(4), Integer(int(count)), key_public.p)).export_val() for count in hospital_counts ] round1a_time = time.perf_counter() - round1a_start round1b_start = time.perf_counter() round1b = sum([CipherText.byte_init(key_public, x) for x in round1a]).export_val() round1b_time = time.perf_counter() - round1b_start round2a_start = time.perf_counter() round2a = [ shared_secret(hk, CipherText.byte_init(key_public, round1b)).to_bytes(padded_size) for hk in hospital_keys ] round2a_time = time.perf_counter() - round2a_start round2b_start = time.perf_counter() round2b_sec = Integer(1) encrypted_sum = CipherText.byte_init(key_public, round1b) for k in range(num_hospitals): round2b_sec = (round2b_sec * Integer.from_bytes(round2a[k])) % key_public.p s_inv = pow( round2b_sec, key_public.p - 2, key_public.p ) # modular multiplicative inverse https://stackoverflow.com/questions/4798654/modular-multiplicative-inverse-function-in-python decrypted_sum = (s_inv * encrypted_sum.c2) % key_public.p load_precomputed_discrete_log4_table( ) # using precomputation instead of naive method logged_sum = discrete_log4(key_public, decrypted_sum) round2b_time = time.perf_counter() - round2b_start ans_dict = {} ans_dict['round0a_time'] = sum(x['time'] for x in round0a) ans_dict['round0b_time'] = round0b['time'] ans_dict['round1a_time'] = round1a_time ans_dict['round1b_time'] = round1b_time ans_dict['round2a_time'] = round2a_time ans_dict['round2b_time'] = round2b_time return logged_sum, ans_dict
def _bits2int(self, bstr): """See 2.3.2 in RFC6979""" result = Integer.from_bytes(bstr) q_len = self._order.size_in_bits() b_len = len(bstr) * 8 if b_len > q_len: result >>= (b_len - q_len) return result
def _import_public_der(curve_oid, ec_point): """Convert an encoded EC point into an EccKey object curve_name: string with the OID of the curve ec_point: byte string with the EC point (not DER encoded) """ # We only support P-256 named curves for now if curve_oid != _curve.oid: raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid) # See 2.2 in RFC5480 and 2.3.3 in SEC1 # The first byte is: # - 0x02: compressed, only X-coordinate, Y-coordinate is even # - 0x03: compressed, only X-coordinate, Y-coordinate is odd # - 0x04: uncompressed, X-coordinate is followed by Y-coordinate # # PAI is in theory encoded as 0x00. order_bytes = _curve.order.size_in_bytes() point_type = bord(ec_point[0]) # Uncompressed point if point_type == 0x04: if len(ec_point) != (1 + 2 * order_bytes): raise ValueError("Incorrect EC point length") x = Integer.from_bytes(ec_point[1:order_bytes + 1]) y = Integer.from_bytes(ec_point[order_bytes + 1:]) # Compressed point elif point_type in (0x02, 0x3): if len(ec_point) != (1 + order_bytes): raise ValueError("Incorrect EC point length") x = Integer.from_bytes(ec_point[1:]) y = (x**3 - x * 3 + _curve.b).sqrt(_curve.p) # Short Weierstrass if point_type == 0x02 and y.is_odd(): y = _curve.p - y if point_type == 0x03 and y.is_even(): y = _curve.p - y else: raise ValueError("Incorrect EC point encoding") return construct(curve="P-256", point_x=x, point_y=y)
def _import_public_der(curve_oid, ec_point): """Convert an encoded EC point into an EccKey object curve_name: string with the OID of the curve ec_point: byte string with the EC point (not DER encoded) """ # We only support P-256 named curves for now if curve_oid != _curve.oid: raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid) # See 2.2 in RFC5480 and 2.3.3 in SEC1 # The first byte is: # - 0x02: compressed, only X-coordinate, Y-coordinate is even # - 0x03: compressed, only X-coordinate, Y-coordinate is odd # - 0x04: uncompressed, X-coordinate is followed by Y-coordinate # # PAI is in theory encoded as 0x00. order_bytes = _curve.order.size_in_bytes() point_type = bord(ec_point[0]) # Uncompressed point if point_type == 0x04: if len(ec_point) != (1 + 2 * order_bytes): raise ValueError("Incorrect EC point length") x = Integer.from_bytes(ec_point[1:order_bytes+1]) y = Integer.from_bytes(ec_point[order_bytes+1:]) # Compressed point elif point_type in (0x02, 0x3): if len(ec_point) != (1 + order_bytes): raise ValueError("Incorrect EC point length") x = Integer.from_bytes(ec_point[1:]) y = (x**3 - x*3 + _curve.b).sqrt(_curve.p) # Short Weierstrass if point_type == 0x02 and y.is_odd(): y = _curve.p - y if point_type == 0x03 and y.is_even(): y = _curve.p - y else: raise ValueError("Incorrect EC point encoding") return construct(curve="P-256", point_x=x, point_y=y)
def deserialize_ElGamalPublicKey(bytestring): '''constructs an ElGamal Public Key from a bytestring''' padded_size = len(bytestring) // 3 assert padded_size * 3 == len(bytestring), "Wrong bytestring length" p = Integer.from_bytes(bytestring[:padded_size]) g = Integer.from_bytes(bytestring[padded_size:2 * padded_size]) y = Integer.from_bytes(bytestring[2 * padded_size:]) return ElGamal.construct((p, g, y)) def export_val(self): '''Returns a bytestring.''' padded_size = int(np.round(key.p.size_in_bits() / 8)) return self.c1.to_bytes(padded_size) + self.c2.to_bytes(padded_size) @classmethod def byte_init(cls, key, bytestring): '''Returns a Ciphertext from a bytestring containing c1 and c2''' padded_size = int(np.round(key.p.size_in_bits() / 8)) assert (len(bytestring) == 2 * padded_size) c1 = Integer.from_bytes(bytestring[:padded_size]) c2 = Integer.from_bytes(bytestring[padded_size:]) return cls(key, c1, c2)
def _import_private_der(encoded, passphrase, curve_oid=None): # See RFC5915 https://tools.ietf.org/html/rfc5915 # # ECPrivateKey ::= SEQUENCE { # version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), # privateKey OCTET STRING, # parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, # publicKey [1] BIT STRING OPTIONAL # } private_key = DerSequence().decode(encoded, nr_elements=(3, 4)) if private_key[0] != 1: raise ValueError("Incorrect ECC private key version") try: parameters = DerObjectId(explicit=0).decode(private_key[2]).value if curve_oid is not None and parameters != curve_oid: raise ValueError("Curve mismatch") curve_oid = parameters except ValueError: pass if curve_oid is None: raise ValueError("No curve found") for curve_name, curve in _curves.items(): if curve.oid == curve_oid: break else: raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid) scalar_bytes = DerOctetString().decode(private_key[1]).payload modulus_bytes = curve.p.size_in_bytes() if len(scalar_bytes) != modulus_bytes: raise ValueError("Private key is too small") d = Integer.from_bytes(scalar_bytes) # Decode public key (if any) if len(private_key) == 4: public_key_enc = DerBitString(explicit=1).decode(private_key[3]).value public_key = _import_public_der(public_key_enc, curve_oid=curve_oid) point_x = public_key.pointQ.x point_y = public_key.pointQ.y else: point_x = point_y = None return construct(curve=curve_name, d=d, point_x=point_x, point_y=point_y)
def sign(self, msg_hash): """Produce the DSA/ECDSA signature of a message. :parameter msg_hash: The hash that was carried out over the message. The object belongs to the :mod:`Cryptodome.Hash` package. Under mode *'fips-186-3'*, the hash must be a FIPS approved secure hash (SHA-1 or a member of the SHA-2 family), of cryptographic strength appropriate for the DSA key. For instance, a 3072/256 DSA key can only be used in combination with SHA-512. :type msg_hash: hash object :return: The signature as a *byte string* :raise ValueError: if the hash algorithm is incompatible to the (EC)DSA key :raise TypeError: if the (EC)DSA key has no private half """ if not self._valid_hash(msg_hash): raise ValueError("Hash is not sufficiently strong") # Generate the nonce k (critical!) nonce = self._compute_nonce(msg_hash) # Perform signature using the raw API z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) sig_pair = self._key._sign(z, nonce) # Encode the signature into a single byte string if self._encoding == 'binary': output = b"".join([long_to_bytes(x, self._order_bytes) for x in sig_pair]) else: # Dss-sig ::= SEQUENCE { # r INTEGER, # s INTEGER # } # Ecdsa-Sig-Value ::= SEQUENCE { # r INTEGER, # s INTEGER # } output = DerSequence(sig_pair).encode() return output
def sign(self, msg_hash): """Compute the DSA/ECDSA signature of a message. Args: msg_hash (hash object): The hash that was carried out over the message. The object belongs to the :mod:`Cryptodome.Hash` package. Under mode ``'fips-186-3'``, the hash must be a FIPS approved secure hash (SHA-2 or SHA-3). :return: The signature as ``bytes`` :raise ValueError: if the hash algorithm is incompatible to the (EC)DSA key :raise TypeError: if the (EC)DSA key has no private half """ if not self._key.has_private(): raise TypeError("Private key is needed to sign") if not self._valid_hash(msg_hash): raise ValueError("Hash is not sufficiently strong") # Generate the nonce k (critical!) nonce = self._compute_nonce(msg_hash) # Perform signature using the raw API z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) sig_pair = self._key._sign(z, nonce) # Encode the signature into a single byte string if self._encoding == 'binary': output = b"".join( [long_to_bytes(x, self._order_bytes) for x in sig_pair]) else: # Dss-sig ::= SEQUENCE { # r INTEGER, # s INTEGER # } # Ecdsa-Sig-Value ::= SEQUENCE { # r INTEGER, # s INTEGER # } output = DerSequence(sig_pair).encode() return output
def sign(self, msg_hash): """Produce the DSS signature of a message. :Parameters: msg_hash : hash object The hash that was carried out over the message. The object belongs to the `Cryptodome.Hash` package. Under mode *'fips-186-3'*, the hash must be a FIPS approved secure hash (SHA-1 or a member of the SHA-2 family), of cryptographic strength appropriate for the DSA key. For instance, a 3072/256 DSA key can only be used in combination with SHA-512. :Return: The signature encoded as a byte string. :Raise ValueError: If the hash algorithm is incompatible to the DSA key. :Raise TypeError: If the DSA key has no private half. """ if not self._valid_hash(msg_hash): raise ValueError("Hash is not sufficiently strong") # Generate the nonce k (critical!) nonce = self._compute_nonce(msg_hash) # Perform signature using the raw API z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) sig_pair = self._key._sign(z, nonce) # Encode the signature into a single byte string if self._encoding == 'binary': output = b("").join([long_to_bytes(x, self._order_bytes) for x in sig_pair]) else: # Dss-sig ::= SEQUENCE { # r OCTET STRING, # s OCTET STRING # } output = DerSequence(sig_pair).encode() return output
def _sign_message(privkey, msg, k=None): """ create DSA signed message @arg privkey ... privatekey as DSA obj @arg msg ... message to sign @arg k ... override random k """ k = k or random.StrongRandom().randint(2, privkey.q - 1) # generate msg hash # sign the messages using privkey h = SHA1.new(msg).digest() # taken from pycryptodome # Perform signature using the raw API order_bytes = (Integer(privkey.q).size_in_bits() - 1) // 8 + 1 z = Integer.from_bytes(h[:order_bytes]) r, s = privkey._sign(z, k) return h, (r, s), privkey.publickey()
def _import_openssh_private_rsa(data, password): from ._openssh import (import_openssh_private_generic, read_bytes, read_string, check_padding) ssh_name, decrypted = import_openssh_private_generic(data, password) if ssh_name != "ssh-rsa": raise ValueError("This SSH key is not RSA") n, decrypted = read_bytes(decrypted) e, decrypted = read_bytes(decrypted) d, decrypted = read_bytes(decrypted) iqmp, decrypted = read_bytes(decrypted) p, decrypted = read_bytes(decrypted) q, decrypted = read_bytes(decrypted) _, padded = read_string(decrypted) # Comment check_padding(padded) build = [Integer.from_bytes(x) for x in (n, e, d, q, p, iqmp)] return construct(build)
def hospital_round0a(key_template, key_x=None): '''The partial key generation phase If key_x is set, we'll use that instead of a new random integer Returns a dictionary with: key_x: a new Elgamal private key secret share private_key: an ElGamal private key using key_x curr_y: a partial public key share transmit: a bytestring encoding of curr_y to send to hub time: amount of time elapsed for this function ''' start = time.perf_counter() if key_x: x = key_x else: x = Integer(randint(2, int(key_template.p - 1))) curr_y = pow(key_template.g, x, key_template.p) padded_size = int(np.round(key_template.p.size_in_bits() / 8)) curr_y_bytestring = curr_y.to_bytes(padded_size) private_key = construct_key(key_template, x) assert (private_key.y == curr_y) elapsed = time.perf_counter() - start assert (Integer.from_bytes(curr_y_bytestring) == curr_y) answer_dict = {} answer_dict['key_x'] = x answer_dict['key_x_bytestring'] = x.to_bytes(padded_size) answer_dict['private_key'] = private_key answer_dict['private_key_export'] = serialize_ElGamalPrivateKey( private_key) answer_dict['curr_y'] = curr_y answer_dict['transmit'] = curr_y_bytestring answer_dict['time'] = elapsed return answer_dict
def _import_private_der(encoded, passphrase, curve_name=None): # ECPrivateKey ::= SEQUENCE { # version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), # privateKey OCTET STRING, # parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, # publicKey [1] BIT STRING OPTIONAL # } private_key = DerSequence().decode(encoded, nr_elements=(3, 4)) if private_key[0] != 1: raise ValueError("Incorrect ECC private key version") try: curve_name = DerObjectId(explicit=0).decode(private_key[2]).value except ValueError: pass if curve_name != _curve.oid: raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_name) scalar_bytes = DerOctetString().decode(private_key[1]).payload order_bytes = _curve.order.size_in_bytes() if len(scalar_bytes) != order_bytes: raise ValueError("Private key is too small") d = Integer.from_bytes(scalar_bytes) # Decode public key (if any, it must be P-256) if len(private_key) == 4: public_key_enc = DerBitString(explicit=1).decode(private_key[3]).value public_key = _import_public_der(curve_name, public_key_enc) point_x = public_key.pointQ.x point_y = public_key.pointQ.y else: point_x = point_y = None return construct(curve="P-256", d=d, point_x=point_x, point_y=point_y)
def hub_round0b(key_template, hospital_public_shares): '''Generating the shared public key from a list of partial shares from the hospitals as bytestrings Returns a dictionary with: key_public: a new ElGamal public key time: amount of time elapsed for this function ''' start = time.perf_counter() key_y = Integer(1) for bytestring_y in hospital_public_shares: curr_y = Integer.from_bytes(bytestring_y) key_y = (curr_y * key_y) % key_template.p key_public = ElGamal.construct((key_template.p, key_template.g, key_y)) elapsed = time.perf_counter() - start answer_dict = {} answer_dict['key_public'] = key_public answer_dict['curr_y'] = key_y # answer_dict['transmit'] = key_y_bytestring answer_dict['transmit'] = serialize_ElGamalPublicKey(key_public) answer_dict['time'] = elapsed return answer_dict
def hash(msg): h_obj = SHA3_256.new() h_obj.update(msg.encode()) return Integer.from_bytes(h_obj.digest())
def import_key(extern_key, passphrase=None): """Import a DSA key (public or private). :Parameters: extern_key : (byte) string The DSA key to import. An DSA *public* key can be in any of the following formats: - X.509 certificate (binary or PEM format) - X.509 ``subjectPublicKeyInfo`` (binary or PEM) - OpenSSH (one line of text, see `RFC4253`_) A DSA *private* key can be in any of the following formats: - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM encoding) - OpenSSL/OpenSSH (binary or PEM) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. The private key may be encrypted by means of a certain pass phrase either at the PEM level or at the PKCS#8 level. passphrase : string In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. :Return: A DSA key object (`DsaKey`). :Raise ValueError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC4253: http://www.ietf.org/rfc/rfc4253.txt .. _PKCS#8: http://www.ietf.org/rfc/rfc5208.txt """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b('-----')): # This is probably a PEM encoded key (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _import_key_der(der, passphrase, None) if extern_key.startswith(b('ssh-dss ')): # This is probably a public OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) keyparts = [] while len(keystring) > 4: length = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + length]) keystring = keystring[4 + length:] if keyparts[0] == b("ssh-dss"): tup = [Integer.from_bytes(keyparts[x]) for x in (4, 3, 1, 2)] return construct(tup) if bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _import_key_der(extern_key, passphrase, None) raise ValueError("DSA key format is not supported")
def import_key(extern_key, passphrase=None): """Import an RSA key (public or private half), encoded in standard form. Args: extern_key (string or byte string): The RSA key to import. The following formats are supported for an RSA **public key**: - X.509 certificate (binary or PEM format) - X.509 ``subjectPublicKeyInfo`` DER SEQUENCE (binary or PEM encoding) - `PKCS#1`_ ``RSAPublicKey`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) The following formats are supported for an RSA **private key**: - PKCS#1 ``RSAPrivateKey`` DER SEQUENCE (binary or PEM encoding) - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. The private key may be encrypted by means of a certain pass phrase either at the PEM level or at the PKCS#8 level. passphrase (string): In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. Returns: An RSA key object (:class:`RsaKey`). Raises: ValueError/IndexError/TypeError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b('-----')): # This is probably a PEM encoded key. (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _import_keyDER(der, passphrase) if extern_key.startswith(b('ssh-rsa ')): # This is probably an OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) keyparts = [] while len(keystring) > 4: l = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + l]) keystring = keystring[4 + l:] e = Integer.from_bytes(keyparts[1]) n = Integer.from_bytes(keyparts[2]) return construct([n, e]) if bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _import_keyDER(extern_key, passphrase) raise ValueError("RSA key format is not supported")
def import_key(extern_key, passphrase=None): """Import a DSA key. Args: extern_key (string or byte string): The DSA key to import. The following formats are supported for a DSA **public** key: - X.509 certificate (binary DER or PEM) - X.509 ``subjectPublicKeyInfo`` (binary DER or PEM) - OpenSSH (ASCII one-liner, see `RFC4253`_) The following formats are supported for a DSA **private** key: - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM) - OpenSSL/OpenSSH custom format (binary or PEM) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. passphrase (string): In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. Encryption may be applied either at the `PKCS#8`_ or at the PEM level. Returns: :class:`DsaKey` : a DSA key object Raises: ValueError : when the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC4253: http://www.ietf.org/rfc/rfc4253.txt .. _PKCS#8: http://www.ietf.org/rfc/rfc5208.txt """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b'-----'): # This is probably a PEM encoded key (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _import_key_der(der, passphrase, None) if extern_key.startswith(b'ssh-dss '): # This is probably a public OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b' ')[1]) keyparts = [] while len(keystring) > 4: length = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + length]) keystring = keystring[4 + length:] if keyparts[0] == b"ssh-dss": tup = [Integer.from_bytes(keyparts[x]) for x in (4, 3, 1, 2)] return construct(tup) if bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _import_key_der(extern_key, passphrase, None) raise ValueError("DSA key format is not supported")
def import_key(extern_key, passphrase=None): """Import an RSA key (public or private). Args: extern_key (string or byte string): The RSA key to import. The following formats are supported for an RSA **public key**: - X.509 certificate (binary or PEM format) - X.509 ``subjectPublicKeyInfo`` DER SEQUENCE (binary or PEM encoding) - `PKCS#1`_ ``RSAPublicKey`` DER SEQUENCE (binary or PEM encoding) - An OpenSSH line (e.g. the content of ``~/.ssh/id_ecdsa``, ASCII) The following formats are supported for an RSA **private key**: - PKCS#1 ``RSAPrivateKey`` DER SEQUENCE (binary or PEM encoding) - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (text format, introduced in `OpenSSH 6.5`_) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. passphrase (string or byte string): For private keys only, the pass phrase that encrypts the key. Returns: An RSA key object (:class:`RsaKey`). Raises: ValueError/IndexError/TypeError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt .. _`OpenSSH 6.5`: https://flak.tedunangst.com/post/new-openssh-key-format-and-bcrypt-pbkdf """ from Cryptodome.IO import PEM extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b'-----BEGIN OPENSSH PRIVATE KEY'): text_encoded = tostr(extern_key) openssh_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase) result = _import_openssh_private_rsa(openssh_encoded, passphrase) return result if extern_key.startswith(b'-----'): # This is probably a PEM encoded key. (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _import_keyDER(der, passphrase) if extern_key.startswith(b'ssh-rsa '): # This is probably an OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b' ')[1]) keyparts = [] while len(keystring) > 4: length = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + length]) keystring = keystring[4 + length:] e = Integer.from_bytes(keyparts[1]) n = Integer.from_bytes(keyparts[2]) return construct([n, e]) if len(extern_key) > 0 and bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _import_keyDER(extern_key, passphrase) raise ValueError("RSA key format is not supported")
def import_key(extern_key, passphrase=None): """Import an RSA key (public or private half), encoded in standard form. :Parameter extern_key: The RSA key to import, encoded as a byte string. An RSA public key can be in any of the following formats: - X.509 certificate (binary or PEM format) - X.509 ``subjectPublicKeyInfo`` DER SEQUENCE (binary or PEM encoding) - `PKCS#1`_ ``RSAPublicKey`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) An RSA private key can be in any of the following formats: - PKCS#1 ``RSAPrivateKey`` DER SEQUENCE (binary or PEM encoding) - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. The private key may be encrypted by means of a certain pass phrase either at the PEM level or at the PKCS#8 level. :Type extern_key: string :Parameter passphrase: In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. :Type passphrase: string :Return: An RSA key object (`RsaKey`). :Raise ValueError/IndexError/TypeError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b('-----')): # This is probably a PEM encoded key. (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _import_keyDER(der, passphrase) if extern_key.startswith(b('ssh-rsa ')): # This is probably an OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) keyparts = [] while len(keystring) > 4: l = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + l]) keystring = keystring[4 + l:] e = Integer.from_bytes(keyparts[1]) n = Integer.from_bytes(keyparts[2]) return construct([n, e]) if bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _import_keyDER(extern_key, passphrase) raise ValueError("RSA key format is not supported")
def import_key(extern_key, passphrase=None): """Import a DSA key. Args: extern_key (string or byte string): The DSA key to import. The following formats are supported for a DSA **public** key: - X.509 certificate (binary DER or PEM) - X.509 ``subjectPublicKeyInfo`` (binary DER or PEM) - OpenSSH (ASCII one-liner, see `RFC4253`_) The following formats are supported for a DSA **private** key: - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM) - OpenSSL/OpenSSH custom format (binary or PEM) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. passphrase (string): In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. Encryption may be applied either at the `PKCS#8`_ or at the PEM level. Returns: :class:`DsaKey` : a DSA key object Raises: ValueError : when the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC4253: http://www.ietf.org/rfc/rfc4253.txt .. _PKCS#8: http://www.ietf.org/rfc/rfc5208.txt """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b('-----')): # This is probably a PEM encoded key (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _import_key_der(der, passphrase, None) if extern_key.startswith(b('ssh-dss ')): # This is probably a public OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) keyparts = [] while len(keystring) > 4: length = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + length]) keystring = keystring[4 + length:] if keyparts[0] == b("ssh-dss"): tup = [Integer.from_bytes(keyparts[x]) for x in (4, 3, 1, 2)] return construct(tup) if bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _import_key_der(extern_key, passphrase, None) raise ValueError("DSA key format is not supported")