def extract_coordinates(g, curve): """ Return the coordinates x and y as integers, regardless of the point format of string g. Second expected parameter is a CurveFp. """ p = curve.p() point_format = g[0] point = g[1:] if point_format == '\x04': point_len = len(point) if point_len % 2 != 0: raise Exception("Point length is not even.") x_bytes = point[:point_len>>1] x = pkcs_os2ip(x_bytes) % p y_bytes = point[point_len>>1:] y = pkcs_os2ip(y_bytes) % p elif point_format in ['\x02', '\x03']: x_bytes = point x = pkcs_os2ip(x_bytes) % p # perform the y coordinate computation with self.tls_ec y_square = (x*x*x + curve.a()*x + curve.b()) % p y = square_root_mod_prime(y_square, p) y_parity = ord(point_format) % 2 # \x02 means even, \x03 means odd if y % 2 != y_parity: y = -y % p else: raise Exception("Point starts with %s. This encoding " "is not recognized." % repr(point_format)) if not curve.contains_point(x, y): raise Exception("The point we extracted does not belong on the curve!") return x, y
def extract_coordinates(g, curve): """ Return the coordinates x and y as integers, regardless of the point format of string g. Second expected parameter is a CurveFp. """ p = curve.p() point_format = g[0] point = g[1:] if point_format == '\x04': point_len = len(point) if point_len % 2 != 0: raise Exception("Point length is not even.") x_bytes = point[:point_len >> 1] x = pkcs_os2ip(x_bytes) % p y_bytes = point[point_len >> 1:] y = pkcs_os2ip(y_bytes) % p elif point_format in ['\x02', '\x03']: x_bytes = point x = pkcs_os2ip(x_bytes) % p # perform the y coordinate computation with self.tls_ec y_square = (x * x * x + curve.a() * x + curve.b()) % p y = square_root_mod_prime(y_square, p) y_parity = ord(point_format) % 2 # \x02 means even, \x03 means odd if y % 2 != y_parity: y = -y % p else: raise Exception("Point starts with %s. This encoding " "is not recognized." % repr(point_format)) if not curve.contains_point(x, y): raise Exception("The point we extracted does not belong on the curve!") return x, y
def import_from_tuple(self, tup): # this is rarely used e, m, mLen = tup if isinstance(m, str): m = pkcs_os2ip(m) if isinstance(e, str): e = pkcs_os2ip(e) self.fill_and_store(modulus=m, pubExp=e) self.pem = self.pubkey.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) self.der = pem2der(self.pem)
def import_from_tuple(self, tup): # this is rarely used e, m, mLen = tup if isinstance(m, bytes): m = pkcs_os2ip(m) if isinstance(e, bytes): e = pkcs_os2ip(e) self.fill_and_store(modulus=m, pubExp=e) self.pem = self.pubkey.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) self.der = pem2der(self.pem)
def __init__(self, key=None, fixed_iv=None, nonce_explicit=None): """ 'key' and 'fixed_iv' are to be provided as strings, whereas the internal # noqa: E501 'nonce_explicit' is an integer (it is simpler for incrementation). /!\ The whole 'nonce' may be called IV in certain RFCs. """ self.ready = {"key": True, "fixed_iv": True, "nonce_explicit": True} if key is None: self.ready["key"] = False key = b"\0" * self.key_len if fixed_iv is None: self.ready["fixed_iv"] = False fixed_iv = b"\0" * self.fixed_iv_len if nonce_explicit is None: self.ready["nonce_explicit"] = False nonce_explicit = 0 if isinstance(nonce_explicit, str): nonce_explicit = pkcs_os2ip(nonce_explicit) # we use super() in order to avoid any deadlock with __setattr__ super(_AEADCipher, self).__setattr__("key", key) super(_AEADCipher, self).__setattr__("fixed_iv", fixed_iv) super(_AEADCipher, self).__setattr__("nonce_explicit", nonce_explicit) if hasattr(self, "pc_cls"): self._cipher = Cipher(self.pc_cls(key), self.pc_cls_mode(self._get_nonce()), backend=default_backend()) else: self._cipher = self.cipher_cls(key)
def __init__(self, key=None, salt=None, nonce_explicit=None): """ 'key' and 'salt' are to be provided as strings, whereas the internal 'nonce_explicit' is an integer (it is simpler for incrementation). """ self.ready = {"key": True, "salt": True, "nonce_explicit": True} if key is None: self.ready["key"] = False key = b"\0" * self.key_len if salt is None: self.ready["salt"] = False salt = b"\0" * self.salt_len if nonce_explicit is None: self.ready["nonce_explicit"] = False nonce_explicit = 0 if isinstance(nonce_explicit, str): nonce_explicit = pkcs_os2ip(nonce_explicit) # we use super() in order to avoid any deadlock with __setattr__ super(_AEADCipher, self).__setattr__("key", key) super(_AEADCipher, self).__setattr__("salt", salt) super(_AEADCipher, self).__setattr__("nonce_explicit", nonce_explicit) iv = salt + pkcs_i2osp(nonce_explicit, self.nonce_explicit_len) self._cipher = Cipher(self.pc_cls(key), self.pc_cls_mode(iv), backend=default_backend())
def register_pubkey(self): """ XXX Check that the pubkey received is in the group. """ p = pkcs_os2ip(self.dh_p) g = pkcs_os2ip(self.dh_g) pn = dh.DHParameterNumbers(p, g) y = pkcs_os2ip(self.dh_Ys) public_numbers = dh.DHPublicNumbers(y, pn) s = self.tls_session s.server_kx_pubkey = public_numbers.public_key(default_backend()) if not s.client_kx_ffdh_params: s.client_kx_ffdh_params = pn.parameters(default_backend())
def __init__(self, key=None, fixed_iv=None, nonce_explicit=None): """ 'key' and 'fixed_iv' are to be provided as strings, whereas the internal 'nonce_explicit' is an integer (it is simpler for incrementation). /!\ The whole 'nonce' may be called IV in certain RFCs. """ self.ready = {"key": True, "fixed_iv": True, "nonce_explicit": True} if key is None: self.ready["key"] = False key = b"\0" * self.key_len if fixed_iv is None: self.ready["fixed_iv"] = False fixed_iv = b"\0" * self.fixed_iv_len if nonce_explicit is None: self.ready["nonce_explicit"] = False nonce_explicit = 0 if isinstance(nonce_explicit, str): nonce_explicit = pkcs_os2ip(nonce_explicit) # we use super() in order to avoid any deadlock with __setattr__ super(_AEADCipher, self).__setattr__("key", key) super(_AEADCipher, self).__setattr__("fixed_iv", fixed_iv) super(_AEADCipher, self).__setattr__("nonce_explicit", nonce_explicit) if hasattr(self, "pc_cls"): self._cipher = Cipher(self.pc_cls(key), self.pc_cls_mode(self._get_nonce()), backend=default_backend()) else: self._cipher = self.cipher_cls(key)
def post_dissection(self, r): """ XXX Check that the pubkey received is in the group. """ #if self.dh_g and self.dh_p and self.dh_Ys: #XXX remove this, probably p = pkcs_os2ip(self.dh_p) g = pkcs_os2ip(self.dh_g) pn = dh.DHParameterNumbers(p, g) y = pkcs_os2ip(self.dh_Ys) public_numbers = dh.DHPublicNumbers(y, pn) s = self.tls_session s.server_kx_pubkey = public_numbers.public_key(default_backend()) if not s.client_kx_ffdh_params: s.client_kx_ffdh_params = pn.parameters(default_backend())
def import_curve(p, a, b, g, r, name="dummyName", oid=(1, 3, 132, 0, 0xff)): """ Create an ecdsa.curves.Curve from the usual parameters. Arguments may be either octet strings or integers, except g which we expect to be an octet string. """ if isinstance(p, str): p = pkcs_os2ip(p) if isinstance(a, str): a = pkcs_os2ip(a) if isinstance(b, str): b = pkcs_os2ip(b) if isinstance(r, str): r = pkcs_os2ip(r) curve = CurveFp(p, a, b) x, y = extract_coordinates(g, curve) generator = Point(curve, x, y, r) return Curve(name, curve, generator, oid)
def m2i(self, pkt, m): s = pkt.tls_session l = self.length_from(pkt) if s.prcs: cls = s.prcs.key_exchange.server_kx_msg_cls(m) if cls is None: return None, Raw(m[:l]) / Padding(m[l:]) return cls(m, tls_session=s) else: try: p = ServerDHParams(m, tls_session=s) if pkcs_os2ip(p.load[:2]) not in _tls_hash_sig: raise Exception return p except: cls = _tls_server_ecdh_cls_guess(m) p = cls(m, tls_session=s) if pkcs_os2ip(p.load[:2]) not in _tls_hash_sig: return None, Raw(m[:l]) / Padding(m[l:]) return p
def m2i(self, pkt, m): s = pkt.tls_session tmp_len = self.length_from(pkt) if s.prcs: cls = s.prcs.key_exchange.server_kx_msg_cls(m) if cls is None: return Raw(m[:tmp_len]) / Padding(m[tmp_len:]) return cls(m, tls_session=s) else: try: p = ServerDHParams(m, tls_session=s) if pkcs_os2ip(p.load[:2]) not in _tls_hash_sig: raise Exception return p except Exception: cls = _tls_server_ecdh_cls_guess(m) p = cls(m, tls_session=s) if pkcs_os2ip(p.load[:2]) not in _tls_hash_sig: return Raw(m[:tmp_len]) / Padding(m[tmp_len:]) return p
def auth_decrypt(self, A, C, seq_num=None, add_length=True): """ Decrypt the data and authenticate the associated data (i.e. A). If the verification fails, an AEADTagError is raised. It is the user's responsibility to catch it if deemed useful. If we lack the key, we raise a CipherError which contains the encrypted input. Note that we add the TLSCiphertext length to A although we're supposed to add the TLSCompressed length. Fortunately, they are the same, but the specifications actually messed up here. :'( The 'add_length' switch should always be True for TLS, but we provide it anyway (mostly for test cases, hum). The 'seq_num' should never be used here, it is only a safeguard needed because one cipher (ChaCha20Poly1305) using TLS 1.2 logic in record.py actually is a _AEADCipher_TLS13 (even though others are not). """ nonce_explicit_str, C, mac = (C[:self.nonce_explicit_len], C[self.nonce_explicit_len:-self.tag_len], C[-self.tag_len:]) if False in six.itervalues(self.ready): raise CipherError(nonce_explicit_str, C, mac) self.nonce_explicit = pkcs_os2ip(nonce_explicit_str) if add_length: A += struct.pack("!H", len(C)) if hasattr(self, "pc_cls"): self._cipher.mode._initialization_vector = self._get_nonce() self._cipher.mode._tag = mac decryptor = self._cipher.decryptor() decryptor.authenticate_additional_data(A) P = decryptor.update(C) try: decryptor.finalize() except InvalidTag: raise AEADTagError(nonce_explicit_str, P, mac) else: try: if isinstance(self._cipher, AESCCM): P = self._cipher.decrypt(self._get_nonce(), C + mac, A, tag_length=self.tag_len) else: P = self._cipher.decrypt(self._get_nonce(), C + mac, A) except InvalidTag: raise AEADTagError(nonce_explicit_str, "<unauthenticated data>", mac) return nonce_explicit_str, P, mac
def fill_missing(self): """ We do not want TLSServerKeyExchange.build() to overload and recompute things every time it is called. This method can be called specifically to have things filled in a smart fashion. Note that we do not expect default_params.g to be more than 0xff. """ s = self.tls_session default_params = _ffdh_groups['modp2048'][0].parameter_numbers() default_mLen = _ffdh_groups['modp2048'][1] if not self.dh_p: self.dh_p = pkcs_i2osp(default_params.p, default_mLen // 8) if self.dh_plen is None: self.dh_plen = len(self.dh_p) if not self.dh_g: self.dh_g = pkcs_i2osp(default_params.g, 1) if self.dh_glen is None: self.dh_glen = 1 p = pkcs_os2ip(self.dh_p) g = pkcs_os2ip(self.dh_g) real_params = dh.DHParameterNumbers(p, g).parameters(default_backend()) if not self.dh_Ys: s.server_kx_privkey = real_params.generate_private_key() pubkey = s.server_kx_privkey.public_key() y = pubkey.public_numbers().y self.dh_Ys = pkcs_i2osp(y, pubkey.key_size // 8) # else, we assume that the user wrote the server_kx_privkey by himself if self.dh_Yslen is None: self.dh_Yslen = len(self.dh_Ys) if not s.client_kx_ffdh_params: s.client_kx_ffdh_params = real_params
def fill_missing(self): """ We do not want TLSServerKeyExchange.build() to overload and recompute things everytime it is called. This method can be called specifically to have things filled in a smart fashion. Note that we do not expect default_params.g to be more than 0xff. """ s = self.tls_session default_params = _ffdh_groups['modp2048'][0].parameter_numbers() default_mLen = _ffdh_groups['modp2048'][1] if not self.dh_p: self.dh_p = pkcs_i2osp(default_params.p, default_mLen // 8) if self.dh_plen is None: self.dh_plen = len(self.dh_p) if not self.dh_g: self.dh_g = pkcs_i2osp(default_params.g, 1) if self.dh_glen is None: self.dh_glen = 1 p = pkcs_os2ip(self.dh_p) g = pkcs_os2ip(self.dh_g) real_params = dh.DHParameterNumbers(p, g).parameters(default_backend()) if not self.dh_Ys: s.server_kx_privkey = real_params.generate_private_key() pubkey = s.server_kx_privkey.public_key() y = pubkey.public_numbers().y self.dh_Ys = pkcs_i2osp(y, pubkey.key_size // 8) # else, we assume that the user wrote the server_kx_privkey by himself if self.dh_Yslen is None: self.dh_Yslen = len(self.dh_Ys) if not s.client_kx_ffdh_params: s.client_kx_ffdh_params = real_params
def __setattr__(self, name, val): if name == "key": if self._cipher is not None: if hasattr(self, "pc_cls"): self._cipher.algorithm.key = val else: self._cipher._key = val self.ready["key"] = True elif name == "fixed_iv": self.ready["fixed_iv"] = True elif name == "nonce_explicit": if isinstance(val, str): val = pkcs_os2ip(val) self.ready["nonce_explicit"] = True super(_AEADCipher, self).__setattr__(name, val)
def __setattr__(self, name, val): if name == "key": if self._cipher is not None: self._cipher.algorithm.key = val self.ready["key"] = True elif name == "salt": iv = val + pkcs_i2osp(self.nonce_explicit, self.nonce_explicit_len) if self._cipher is not None: self._cipher.mode._initialization_vector = iv self.ready["salt"] = True elif name == "nonce_explicit": if isinstance(val, str): val = pkcs_os2ip(val) iv = self.salt + pkcs_i2osp(val, self.nonce_explicit_len) if self._cipher is not None: self._cipher.mode._initialization_vector = iv self.ready["nonce_explicit"] = True super(_AEADCipher, self).__setattr__(name, val)
def post_dissection(self, m): """ First we update the client DHParams. Then, we try to update the server DHParams generated during Server*DHParams building, with the shared secret. Finally, we derive the session keys and update the context. """ s = self.tls_session if s.client_kx_ffdh_params: y = pkcs_os2ip(self.dh_Yc) param_numbers = s.client_kx_ffdh_params.parameter_numbers() public_numbers = dh.DHPublicNumbers(y, param_numbers) s.client_kx_pubkey = public_numbers.public_key(default_backend()) if s.server_kx_privkey and s.client_kx_pubkey: ZZ = s.server_kx_privkey.exchange(s.client_kx_pubkey) s.pre_master_secret = ZZ s.compute_ms_and_derive_keys()
def auth_decrypt(self, A, C, add_length=True): """ Decrypt the data and verify the authentication code (in this order). When additional data was authenticated, it has to be passed (as A). If the verification fails, an AEADTagError is raised. It is the user's responsibility to catch it if deemed useful. If we lack the key, we raise a CipherError which contains the encrypted input. Note that we add the TLSCiphertext length to A although we're supposed to add the TLSCompressed length. Fortunately, they are the same, but the specifications actually messed up here. :'( The 'add_length' switch should always be True for TLS, but we provide it anyway (mostly for test cases, hum). """ nonce_explicit_str, C, mac = (C[:self.nonce_explicit_len], C[self.nonce_explicit_len:-self.tag_len], C[-self.tag_len:]) if False in self.ready.itervalues(): raise CipherError, (nonce_explicit_str, C, mac) self.nonce_explicit = pkcs_os2ip(nonce_explicit_str) self._cipher.mode._tag = mac decryptor = self._cipher.decryptor() if add_length: A += struct.pack("!H", len(C)) decryptor.authenticate_additional_data(A) P = decryptor.update(C) try: decryptor.finalize() except InvalidTag: raise AEADTagError, (nonce_explicit_str, P, mac) return nonce_explicit_str, P, mac
def updateWith(self, privkey): self.privKey = pkcs_os2ip(privkey.privateKey.val) self.key = ecdsa.SigningKey.from_der(str(privkey)) self.vkey = self.key.get_verifying_key()