def y_from_x(self, x): """ Computes the y component corresponding to x. Since elliptic curves are symmetric about the x-axis, the x component (and sign) is all that is required to determine a point on the curve. Args: x (int): x component of the point. Returns: tuple: both possible y components of the point. """ rv = [] x_bn = ossl.int_to_bn(x) for y_bit in [0, 1]: # Create a new point ec_pt = c_void_p(ossl.lc.EC_POINT_new(self.os_group)) ossl.lc.EC_POINT_set_compressed_coordinates_GFp(self.os_group, ec_pt, x_bn, y_bit, c_void_p()) on_curve = ossl.lc.EC_POINT_is_on_curve(self.os_group, ec_pt, c_void_p()) if not on_curve: ossl.lc.EC_POINT_free(ec_pt) rv.append(None) continue # Get the y value _, y, _ = ossl.point_get_xy_ints(self.os_group, ec_pt) rv.append(y) ossl.lc.EC_POINT_free(ec_pt) ossl.lc.BN_free(x_bn) return rv
def y_from_x(self, x): """ Computes the y component corresponding to x. Since elliptic curves are symmetric about the x-axis, the x component (and sign) is all that is required to determine a point on the curve. Args: x (int): x component of the point. Returns: tuple: both possible y components of the point. """ rv = [] x_bn = ossl.int_to_bn(x) for y_bit in [0, 1]: # Create a new point ec_pt = c_void_p(ossl.lc.EC_POINT_new(self.os_group)) ossl.lc.EC_POINT_set_compressed_coordinates_GFp( self.os_group, ec_pt, x_bn, y_bit, c_void_p()) on_curve = ossl.lc.EC_POINT_is_on_curve(self.os_group, ec_pt, c_void_p()) if not on_curve: ossl.lc.EC_POINT_free(ec_pt) rv.append(None) continue # Get the y value _, y, _ = ossl.point_get_xy_ints(self.os_group, ec_pt) rv.append(y) ossl.lc.EC_POINT_free(ec_pt) ossl.lc.BN_free(x_bn) return rv
def _sign(self, message, private_key, do_hash=True, secret=None): # This function computes k, kinv and rp before sending into # OpenSSL ECDSA_do_sign_ex() rather than using ECDSA_do_sign() # directly. The reason for this is that as of the commit that # introduces this comment, OpenSSL only supports deterministic # signing nonces (RFC6979) in the master branch and not any # release. As a result, we cannot depend on callers having any # OpenSSL version capable of RFC6979 deterministic nonces. # Nevertheless, computation of kinv (from k) and rp is done # using OpenSSL primitives. ossl.lc.ERR_clear_error() hashed = self.hash_function(message).digest() if do_hash else message r = 0 s = 0 recovery_id = 0 key = ossl.new_key(self.curve_name, private_key) ctx = c_void_p(ossl.lc.BN_CTX_new()) ossl.lc.BN_CTX_start(ctx) order_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) k_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) kinv_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) px_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) r_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) ossl.lc.EC_GROUP_get_order(self.os_group, order_bn, ctx) while r == 0 or s == 0: k = self._nonce_rfc6979(private_key, hashed) if secret is None else secret ossl.int_to_bn(k, k_bn) ossl.lc.BN_mod_inverse(kinv_bn, k_bn, order_bn, ctx) p = c_void_p(ossl.lc.EC_POINT_new(self.os_group)) ossl.lc.EC_POINT_mul(self.os_group, p, k_bn, c_void_p(), c_void_p(), ctx) assert self.h == 1 px, py, _ = ossl.point_get_xy_ints(self.os_group, p) recovery_id = 2 if px > self.n else 0 recovery_id |= (py & 0x1) # Get r ossl.int_to_bn(px, px_bn) ossl.lc.BN_nnmod(r_bn, px_bn, order_bn, ctx) r = ossl.bn_to_int(r_bn) if r == 0: continue hashed_buf = c_char_p(hashed) sig = ossl.lc.ECDSA_do_sign_ex(hashed_buf, len(hashed), kinv_bn, r_bn, key) err = ossl.lc.ERR_peek_error() if err: err_buf = create_string_buffer(120) ossl.lc.ERR_error_string(err, err_buf) raise Exception("Problem when signing: %s" % err_buf.raw.decode()) sig_r = ossl.bn_to_int(sig.contents.r) sig_s = ossl.bn_to_int(sig.contents.s) if sig_r != r: raise ValueError("Didn't get the same r value.") s = sig_s ossl.lc.EC_KEY_free(key) ossl.lc.BN_CTX_end(ctx) ossl.lc.BN_CTX_free(ctx) return (Point(r, s), recovery_id)
def recover_public_key(self, message, signature, recovery_id=None): """ Recovers possibilities for the public key associated with the private key used to sign message and generate signature. Since there are multiple possibilities (two for curves with co-factor = 1), each possibility that successfully verifies the signature is returned. Args: message (bytes): The message that was signed. signature (ECPointAffine): The point representing the signature. recovery_id (int) (Optional): If provided, limits the valid x and y point to only that described by the recovery_id. Returns: list(ECPointAffine): List of points representing valid public keys that verify signature. """ r = signature.x s = signature.y ctx = c_void_p(ossl.lc.BN_CTX_new()) ossl.lc.BN_CTX_start(ctx) order_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) x_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) i_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) in_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) p_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) r_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) s_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) rinv_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) z_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) ossl.lc.EC_GROUP_get_order(self.os_group, order_bn, ctx) ossl.int_to_bn(self.p, p_bn) ossl.int_to_bn(r, r_bn) ossl.int_to_bn(s, s_bn) ossl.lc.BN_mod_inverse(rinv_bn, r_bn, order_bn, ctx) if recovery_id is not None: i_list = [recovery_id >> 1] k_list = [recovery_id & 0x1] else: i_list = range(2) k_list = range(2) rv = [] num_bytes = math.ceil(self.nlen / 8) z = int.from_bytes(self.hash_function(message).digest()[:num_bytes], 'big') ossl.int_to_bn(z, z_bn) zG = c_void_p(ossl.lc.EC_POINT_new(self.os_group)) sR = c_void_p(ossl.lc.EC_POINT_new(self.os_group)) temp = c_void_p(ossl.lc.EC_POINT_new(self.os_group)) pub_key = c_void_p(ossl.lc.EC_POINT_new(self.os_group)) Rn = c_void_p(ossl.lc.EC_POINT_new(self.os_group)) for i in i_list: ossl.int_to_bn(i, i_bn) ossl.lc.BN_mod_mul(in_bn, i_bn, order_bn, p_bn, ctx) ossl.lc.BN_mod_add(x_bn, r_bn, in_bn, p_bn, ctx) x = ossl.bn_to_int(x_bn) ys = self.y_from_x(x) for k in k_list: y = ys[k] if y & 0x1 != k: y = ys[k ^ 1] R = ossl.point_new_from_ints(self.os_group, r, y) ossl.lc.EC_POINT_mul(self.os_group, Rn, None, R, order_bn, ctx) if not ossl.lc.EC_POINT_is_at_infinity(self.os_group, Rn): continue ossl.lc.EC_POINT_mul(self.os_group, zG, z_bn, None, None, ctx) ossl.lc.EC_POINT_invert(self.os_group, zG, ctx) ossl.lc.EC_POINT_mul(self.os_group, sR, None, R, s_bn, ctx) ossl.lc.EC_POINT_add(self.os_group, temp, sR, zG, ctx) ossl.lc.EC_POINT_mul(self.os_group, pub_key, None, temp, rinv_bn, ctx) ossl.lc.EC_POINT_free(R) # Convert to ECPointAffine pub_x, pub_y, inf = ossl.point_get_xy_ints(self.os_group, pub_key) rv.append((ECPointAffine(self, pub_x, pub_y, inf), 2 * i + k)) ossl.lc.EC_POINT_free(zG) ossl.lc.EC_POINT_free(sR) ossl.lc.EC_POINT_free(temp) ossl.lc.EC_POINT_free(pub_key) ossl.lc.EC_POINT_free(Rn) ossl.lc.BN_CTX_end(ctx) ossl.lc.BN_CTX_free(ctx) return rv
def recover_public_key(self, message, signature, recovery_id=None): """ Recovers possibilities for the public key associated with the private key used to sign message and generate signature. Since there are multiple possibilities (two for curves with co-factor = 1), each possibility that successfully verifies the signature is returned. Args: message (bytes): The message that was signed. signature (ECPointAffine): The point representing the signature. recovery_id (int) (Optional): If provided, limits the valid x and y point to only that described by the recovery_id. Returns: list(ECPointAffine): List of points representing valid public keys that verify signature. """ r = signature.x s = signature.y ctx = c_void_p(ossl.lc.BN_CTX_new()) ossl.lc.BN_CTX_start(ctx) order_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) x_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) i_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) in_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) p_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) r_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) s_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) rinv_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) z_bn = c_void_p(ossl.lc.BN_CTX_get(ctx)) ossl.lc.EC_GROUP_get_order(self.os_group, order_bn, ctx) ossl.int_to_bn(self.p, p_bn) ossl.int_to_bn(r, r_bn) ossl.int_to_bn(s, s_bn) ossl.lc.BN_mod_inverse(rinv_bn, r_bn, order_bn, ctx) if recovery_id is not None: i_list = [recovery_id >> 1] k_list = [recovery_id & 0x1] else: i_list = range(2) k_list = range(2) rv = [] num_bytes = math.ceil(self.nlen / 8) z = int.from_bytes( self.hash_function(message).digest()[:num_bytes], 'big') ossl.int_to_bn(z, z_bn) zG = c_void_p(ossl.lc.EC_POINT_new(self.os_group)) sR = c_void_p(ossl.lc.EC_POINT_new(self.os_group)) temp = c_void_p(ossl.lc.EC_POINT_new(self.os_group)) pub_key = c_void_p(ossl.lc.EC_POINT_new(self.os_group)) Rn = c_void_p(ossl.lc.EC_POINT_new(self.os_group)) for i in i_list: ossl.int_to_bn(i, i_bn) ossl.lc.BN_mod_mul(in_bn, i_bn, order_bn, p_bn, ctx) ossl.lc.BN_mod_add(x_bn, r_bn, in_bn, p_bn, ctx) x = ossl.bn_to_int(x_bn) ys = self.y_from_x(x) for k in k_list: y = ys[k] if y & 0x1 != k: y = ys[k ^ 1] R = ossl.point_new_from_ints(self.os_group, r, y) ossl.lc.EC_POINT_mul(self.os_group, Rn, None, R, order_bn, ctx) if not ossl.lc.EC_POINT_is_at_infinity(self.os_group, Rn): continue ossl.lc.EC_POINT_mul(self.os_group, zG, z_bn, None, None, ctx) ossl.lc.EC_POINT_invert(self.os_group, zG, ctx) ossl.lc.EC_POINT_mul(self.os_group, sR, None, R, s_bn, ctx) ossl.lc.EC_POINT_add(self.os_group, temp, sR, zG, ctx) ossl.lc.EC_POINT_mul(self.os_group, pub_key, None, temp, rinv_bn, ctx) ossl.lc.EC_POINT_free(R) # Convert to ECPointAffine pub_x, pub_y, inf = ossl.point_get_xy_ints( self.os_group, pub_key) rv.append((ECPointAffine(self, pub_x, pub_y, inf), 2 * i + k)) ossl.lc.EC_POINT_free(zG) ossl.lc.EC_POINT_free(sR) ossl.lc.EC_POINT_free(temp) ossl.lc.EC_POINT_free(pub_key) ossl.lc.EC_POINT_free(Rn) ossl.lc.BN_CTX_end(ctx) ossl.lc.BN_CTX_free(ctx) return rv