def test_ell(P): Puc = deserialize(serialize(P, False)) Pc = deserialize(serialize(P, True)) assert point_eq(P, Puc), "%s\n%s\n%d %d" % (str(P), str(Puc), sgn0( P[1]), sgn0(Puc[1])) assert point_eq( P, Pc), "%s\n%s\n%d %d" % (str(P), str(Pc), sgn0(P[1]), sgn0(Pc[1]))
def _serialize_ell2(P, compressed): if not isinstance(P[0], Fq2): raise SerError("cannot serialize a point not on E2") if len(P) != 3: raise SerError("can only serialize Jacobian points") first_tag = 0xe0 if compressed else 0x60 # handle point at infinity if P[2] == 0: return first_tag.to_bytes( 1, "big") + b'\x00' * 47 + b'\xc0' + b'\x00' * (47 if compressed else 143) (x, y) = from_jacobian(P) if pow(y, 2) != _gx2(x): raise SerError("cannot serialize invalid point") x_str = _to_bytes_F2(x) x_str[0] = x_str[0] | first_tag if not compressed: x_str[48] = x_str[48] | 0x80 return struct.pack("=" + "B" * 192, *(x_str + _to_bytes_F2(y))) y_neg = sgn0(y) < 0 tag_bits = 0xa0 if y_neg else 0x80 x_str[48] = x_str[48] | tag_bits return struct.pack("=" + "B" * 96, *x_str)
def _deserialize_ell2(data, tag): assert len(data) > 48 (tag2, data[48]) = (data[48] >> 5, data[48] & 0x1f) if tag2 not in (4, 5, 6): raise DeserError("invalid tag2 for Ell2 point: %d" % tag2) if tag2 == 6: # point at infinity expected_len = 192 if tag == 3 else 96 if len(data) != expected_len: raise DeserError( "invalid point at infinity: length must be %d, got %d" % (expected_len, len(data))) if any(d != 0 for d in data): raise DeserError( "invalid: point at infinity must be all 0s other than tags") return (F2_zero, F2_one, F2_zero) if tag == 3: # uncompressed point on G2 if len(data) != 192: raise DeserError( "invalid uncompressed point: length must be 192, got %d" % len(data)) if tag2 == 5: raise DeserError("invalid uncompressed point: tag2 cannot be 5") x = _from_bytes_F2(data[:96]) y = _from_bytes_F2(data[96:]) if pow(y, 2) != _gx2(x): raise DeserError("invalid uncompressed point: not on curve") return (x, y, F2_one) if tag == 7: # compressed point on G2 if len(data) != 96: raise DeserError( "invalid compressed point: length must be 96, got %d" % len(data)) x = _from_bytes_F2(data) # recompute y gx = _gx2(x) y = sqrt_F2(gx) if y is None: raise DeserError("invalid compresesd point: g(x) is nonsquare") # fix sign of y y_neg = -1 if tag2 == 5 else 1 y = y_neg * sgn0(y) * y return (x, y, F2_one) raise DeserError("invalid tag/tag2 for Ell2 point: %d/%d" % (tag, tag2))
def _deserialize_help(sp, from_bytes, clen, g, sqrt_fn, zero, one): data = list(struct.unpack("=" + "B" * len(sp), sp)) (tag, data[0]) = (data[0] >> 5, data[0] & 0x1f) if tag in (0b001, 0b011, 0b111): raise DeserError("cannot deserialize value with invalid tag: %d" % tag) if tag == 0b000: # uncompressed point if len(data) != 2 * clen: raise DeserError( "invalid uncompresed point: length must be %d, got %d" % (2 * clen, len(data))) x = from_bytes(data[:clen]) y = from_bytes(data[clen:]) if pow(y, 2) != g(x): raise DeserError("invalid uncompressed point: not on curve") return (x, y, one) if tag in (0b010, 0b110): # point at infinity expected_len = 2 * clen if tag == 0b010 else clen if len(data) != expected_len: raise DeserError( "invalid point at infinity: length must be %d, got %d" % (expected_len, len(data))) if any(d != 0 for d in data): raise DeserError( "invalid point at infinity: must be all 0s other than tag") return (zero, one, zero) if tag in (0b100, 0b101): # compressed point if len(data) != clen: raise DeserError( "invalid compressed point: length must be %d, got %d" % (clen, len(data))) x = from_bytes(data) # recompute y gx = g(x) y = sqrt_fn(gx) if y is None or pow(y, 2) != gx: raise DeserError("invalid compressed point: g(x) is nonsquare") # fix sign of y y_neg = -1 if tag == 0b101 else 1 y = y_neg * sgn0(y) * y return (x, y, one) raise DeserError("invalid tag %d" % tag)
def osswu_help(t): assert isinstance(t, Fq) # first, compute X0(t), detecting and handling exceptional case num_den_common = xi_1**2 * t**4 + xi_1 * t**2 x0_num = EllP_b * (num_den_common + 1) x0_den = -EllP_a * num_den_common x0_den = EllP_a * xi_1 if x0_den == 0 else x0_den # g(X0(t)) gx0_den = pow(x0_den, 3) gx0_num = EllP_b * gx0_den gx0_num += EllP_a * x0_num * pow(x0_den, 2) gx0_num += pow(x0_num, 3) # try taking sqrt of g(X0(t)) # this uses the trick for combining division and sqrt from Section 5 of # Bernstein, Duif, Lange, Schwabe, and Yang, "High-speed high-security signatures." # J Crypt Eng 2(2):77--89, Sept. 2012. http://ed25519.cr.yp.to/ed25519-20110926.pdf tmp1 = gx0_num * gx0_den # u v tmp2 = tmp1 * pow(gx0_den, 2) # u v^3 sqrt_candidate = tmp1 * pow(tmp2, (p - 3) // 4) # did we find it? if sqrt_candidate**2 * gx0_den == gx0_num: # found sqrt(g(X0(t))). Force sign of y to equal sign of t (x_num, y) = (x0_num, sqrt_candidate) else: x1_num = xi_1 * t**2 * x0_num y1 = sqrt_candidate * t**3 * sqrt_mxi_1_cubed (x_num, y) = (x1_num, y1) # set sign of y equal to sign of t y = sgn0(y) * sgn0(t) * y assert sgn0(y) == sgn0(t) return (x_num * x0_den, y * pow(x0_den, 3), x0_den)
def osswu2_help(t): assert isinstance(t, Fq2) # first, compute X0(t), detecting and handling exceptional case num_den_common = xi_2**2 * t**4 + xi_2 * t**2 x0_num = Ell2p_b * (num_den_common + 1) x0_den = -Ell2p_a * num_den_common x0_den = Ell2p_a * xi_2 if x0_den == 0 else x0_den # compute num and den of g(X0(t)) gx0_den = pow(x0_den, 3) gx0_num = Ell2p_b * gx0_den gx0_num += Ell2p_a * x0_num * pow(x0_den, 2) gx0_num += pow(x0_num, 3) # try taking sqrt of g(X0(t)) # this uses the trick for combining division and sqrt from Section 5 of # Bernstein, Duif, Lange, Schwabe, and Yang, "High-speed high-security signatures." # J Crypt Eng 2(2):77--89, Sept. 2012. http://ed25519.cr.yp.to/ed25519-20110926.pdf tmp1 = pow(gx0_den, 7) # v^7 tmp2 = gx0_num * tmp1 # u v^7 tmp1 = tmp1 * tmp2 * gx0_den # u v^15 sqrt_candidate = tmp2 * pow(tmp1, (p**2 - 9) // 16) # check if g(X0(t)) is square and return the sqrt if so for root in roots_of_unity: y0 = sqrt_candidate * root if y0**2 * gx0_den == gx0_num: # found sqrt(g(X0(t))). force sign of y to equal sign of t y0 = sgn0(y0) * sgn0(t) * y0 assert sgn0(y0) == sgn0(t) return (x0_num * x0_den, y0 * pow(x0_den, 3), x0_den) # if we've gotten here, then g(X0(t)) is not square. convert srqt_candidate to sqrt(g(X1(t))) (x1_num, x1_den) = (xi_2 * t**2 * x0_num, x0_den) (gx1_num, gx1_den) = (xi_2**3 * t**6 * gx0_num, gx0_den) sqrt_candidate *= t**3 for eta in etas: y1 = eta * sqrt_candidate if y1**2 * gx1_den == gx1_num: # found sqrt(g(X1(t))). force sign of y to equal sign of t y1 = sgn0(y1) * sgn0(t) * y1 assert sgn0(y1) == sgn0(t) return (x1_num * x1_den, y1 * pow(x1_den, 3), x1_den) # if we got here, something is wrong raise RuntimeError("osswu2_help failed for unknown reasons")
def _deserialize_ell1(data, tag): if tag == 0: # uncompressed point if len(data) != 96: raise DeserError( "invalid uncompressed point: length must be 96, got %d" % len(data)) x = _from_bytes_F1(data[:48]) y = _from_bytes_F1(data[48:]) if pow(y, 2) != _gx1(x): raise DeserError("invalid uncompressed point: not on curve") return (x, y, F1_one) if tag in (2, 6): # point at infinity expected_len = 96 if tag == 2 else 48 if len(data) != expected_len: raise DeserError( "invalid point at infinity: length must be %d, got %d" % (expected_len, len(data))) if any(d != 0 for d in data): raise DeserError( "invalid: point at infinity must be all 0s other than tag") return (F1_zero, F1_one, F1_zero) if tag in (4, 5): # compressed point not at infinity if len(data) != 48: raise DeserError( "invalid compressed point: length must be 48, got %d" % len(data)) x = _from_bytes_F1(data) # recompute y gx = _gx1(x) y = pow(gx, (p + 1) // 4) if pow(y, 2) != gx: raise DeserError("invalid compressed point: g(x) is nonsquare") # fix sign of y y_neg = -1 if tag == 5 else 1 y = y_neg * sgn0(y) * y return (x, y, F1_one) raise DeserError("invalid tag for Ell1 point: %d" % tag)
def _serialize_help(P, compressed, to_bytes, clen, g): # point at infinity if P[2] == 0: if compressed: return b'\xc0' + b'\x00' * (clen - 1) return b'\x40' + b'\x00' * (2 * clen - 1) (x, y) = from_jacobian(P) if pow(y, 2) != g(x): raise SerError("cannot serialize invalid point") x_str = to_bytes(x) if not compressed: return struct.pack("=" + "B" * 2 * clen, *(x_str + to_bytes(y))) y_neg = sgn0(y) < 0 tag_bits = 0xa0 if y_neg else 0x80 x_str[0] = x_str[0] | tag_bits return struct.pack("=" + "B" * clen, *x_str)
def _serialize_ell1(P, compressed): if not isinstance(P[0], Fq): raise SerError("cannot serialize a point not on E1") if len(P) != 3: raise SerError("can only serialize Jacobian points") # handle point at infinity if P[2] == 0: if compressed: return b'\xc0' + b'\x00' * 47 return b'\x40' + b'\x00' * 95 (x, y) = from_jacobian(P) if pow(y, 2) != _gx1(x): raise SerError("cannot serialize invalid point") x_str = _to_bytes_F1(x) if not compressed: return struct.pack("=" + "B" * 96, *(x_str + _to_bytes_F1(y))) y_neg = sgn0(y) < 0 tag_bits = 0xa0 if y_neg else 0x80 x_str[0] = x_str[0] | tag_bits return struct.pack("=" + "B" * 48, *x_str)