# Regarding, RFC 7748, Section 5.2, X448, second vector, the given input # u-coordinate is not part of a valid point on the Montgomery form of Curve448. # I suggest replacing the point with a valid one: (2^447 + 100)*G # Execute at: https://sagecell.sagemath.org/ # Curve448 parameters, as per RFC 7748, Section 4.2 # Galois Field F = GF(2^448 - 2^224 - 1) # Montgomery form parameters A = 156326 B = 1 # --- AUXILIARY FUNCTIONS --- def is_valid_point(u): # Check if the point satisfies the curve equation v^2 = u^3 + Au^2 + u in the field F u3_plus_Au2_plus_u = u^3 + A*u^2 + u # Check if this is a quadratic residue in F, meaning there exists a v^2 return u3_plus_Au2_plus_u.is_square() rawslicer = lambda x, k : [x[k*i:k*(i+1)] for i in range(len(x)//k)] stringslicer = lambda x, k : ''.join(rawslicer(x, k)) little2big = lambda x : stringslicer(x, 2) def little(x): if len(x) % 2: x = '0' + x return ''.join(rawslicer(x, 2)[::-1]) def fixscalar(d): d >>= 2 d <<= 2 d |= 1 << 447 return d # Generator's u-coordinate G = F(5) # First test vector, from Section 5.2, X448 FIRST = F(382239910814107330116229961234899377031416365240571325148346555922438025162094455820962429142971339584360034337310079791515452463053830) FIRSTSCALAR = F(fixscalar(599189175373896402783756016145213256157230856085026129926891459468622403380588640249457727683869421921443004045221642549886377526240828)) FIRSTOUTPUT = 'ce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239fe14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f' FIRSTOUTPUT = int.from_bytes(bytes.fromhex(FIRSTOUTPUT), byteorder='little') # Second test vector SECOND = F(622761797758325444462922068431234180649590390024811299761625153767228042600197997696167956134770744996690267634159427999832340166786063) SECONDSCALAR = F(fixscalar(633254335906970592779259481534862372382525155252028961056404001332122152890562527156973881968934311400345568203929409663925541994577184)) # Prove my point assert is_valid_point(G) assert is_valid_point(FIRST) assert not is_valid_point(SECOND) # Curve implementation ec = EllipticCurve(F, [0,A,0,B,0]) assert (FIRSTSCALAR * ec.lift_x(FIRST))[0] == FIRSTOUTPUT # Proposed point PROPOSED = fixscalar(100) * ec.lift_x(G) PROPOSED = PROPOSED[0] # Get only u assert is_valid_point(PROPOSED) # Proposed output, keeping the same d out = SECONDSCALAR * ec.lift_x(PROPOSED) out = out[0] # Only u # Encodings stru = str(PROPOSED) hexu = little(hex(PROPOSED)[2:]) hexout = little(hex(out)[2:]) # Just in case assert (SECONDSCALAR * ec.lift_x(PROPOSED))[0] == out assert int.from_bytes(bytes.fromhex(hexu), byteorder='little') == PROPOSED assert int.from_bytes(bytes.fromhex(hexout), byteorder='little') == out print(' Input u-coordinate:') print(' '+'\n '.join(rawslicer(hexu, len(hexu)//2))) print(' Input u-coordinate as a number (base 10):') print(' '+'\n '.join(rawslicer(stru, len(stru)//3))) print(' Output u-coordinate:') print(' '+'\n '.join(rawslicer(hexout, len(hexout)//2)))