note
	description: "Objects that ..."
	author: "Colin LeMahieu"
	date: "$Date: 2011-11-11 18:13:16 +0100 (ven., 11 nov. 2011) $"
	revision: "$Revision: 87787 $"
	quote: "Those who expect to reap the benefits of freedom, must, like men, undergo the fatigue of supporting it. - Thomas Paine"

class
	EC_POINT_FP

inherit
	EC_POINT
		redefine
			x,
			y,
			copy,
			opposite_value,
			product_value,
			twice_value,
			minus_value,
			plus_value
		end
	EC_CONSTANTS
		undefine
			is_equal,
			copy
		end
	STANDARD_CURVES
		undefine
			is_equal,
			copy
		end

create
	make_curve_x_y,
	make_from_bytes,
	make_infinity,
	make_sec_p112r1,
	make_sec_p112r2,
	make_sec_p128r1,
	make_sec_p128r2,
	make_sec_p160k1,
	make_sec_p160r1,
	make_sec_p160r2,
	make_sec_p192k1,
	make_sec_p192r1,
	make_sec_p224k1,
	make_sec_p224r1,
	make_sec_p256k1,
	make_sec_p256r1,
	make_sec_p384r1,
	make_sec_p521r1,
	make_p192,
	make_p224,
	make_p256,
	make_p384,
	make_p521

feature
	make_infinity
		do
			set_infinity
		end

feature -- SEC curves
	make_sec_p112r1
		do
			create x.make_p_x (sec_p112r1_gx)
			create y.make_p_x (sec_p112r1_gy)
		end

	make_sec_p112r2
		do
			create x.make_p_x (sec_p112r2_gx)
			create y.make_p_x (sec_p112r2_gy)
		end

	make_sec_p128r1
		do
			create x.make_p_x (sec_p128r1_gx)
			create y.make_p_x (sec_p128r1_gy)
		end

	make_sec_p128r2
		do
			create x.make_p_x (sec_p128r2_gx)
			create y.make_p_x (sec_p128r2_gy)
		end

	make_sec_p160k1
		do
			create x.make_p_x (sec_p160k1_gx)
			create y.make_p_x (sec_p160k1_gy)
		end

	make_sec_p160r1
		do
			create x.make_p_x (sec_p160r1_gx)
			create y.make_p_x (sec_p160r1_gy)
		end

	make_sec_p160r2
		do
			create x.make_p_x (sec_p160r2_gx)
			create y.make_p_x (sec_p160r2_gy)
		end

	make_sec_p192k1
		do
			create x.make_p_x (sec_p192k1_gx)
			create y.make_p_x (sec_p192k1_gy)
		end

	make_sec_p192r1
		do
			create x.make_p_x (sec_p192r1_gx)
			create y.make_p_x (sec_p192r1_gy)
		end

	make_sec_p224k1
		do
			create x.make_p_x (sec_p224k1_gx)
			create y.make_p_x (sec_p224k1_gy)
		end

	make_sec_p224r1
		do
			create x.make_p_x (sec_p224r1_gx)
			create y.make_p_x (sec_p224r1_gy)
		end

	make_sec_p256k1
		do
			create x.make_p_x (sec_p256k1_gx)
			create y.make_p_x (sec_p256k1_gy)
		end

	make_sec_p256r1
		do
			create x.make_p_x (sec_p256r1_gx)
			create y.make_p_x (sec_p256r1_gy)
		end

	make_sec_p384r1
		do
			create x.make_p_x (sec_p384r1_gx)
			create y.make_p_x (sec_p384r1_gy)
		end

	make_sec_p521r1
		do
			create x.make_p_x (sec_p521r1_gx)
			create y.make_p_x (sec_p521r1_gy)
		end

feature
	make_p192
		do
			create x.make_p_x (p192_gx)
			create y.make_p_x (p192_gy)
		end

	make_p224
		do
			create x.make_p_x (p224_gx)
			create y.make_p_x (p224_gy)
		end

	make_p256
		do
			create x.make_p_x (p256_gx)
			create y.make_p_x (p256_gy)
		end

	make_p384
		do
			create x.make_p_x (p384_gx)
			create y.make_p_x (p384_gy)
		end

	make_p521
		do
			create x.make_p_x (p521_gx)
			create y.make_p_x (p521_gy)
		end

	make_curve_x_y (x_a: EC_FIELD_ELEMENT_FP; y_a: EC_FIELD_ELEMENT_FP)
		do
			x := x_a
			y := y_a
		end

	make_from_bytes (encoded: SPECIAL [NATURAL_8] curve: EC_CURVE_FP)
			-- Decode a point on this curve from its ASN.1 encoding
			-- encodings are taken account of, including point compression for
			-- <code>F<sub>p</sub><code> (X9.62 s 4.2.1 pg 17).
			-- @return The decoded point.
	require
		first_byte_indicator: encoded [0] = 0x02 or encoded [0] = 0x3 or encoded [0] = 0x4
	do
		inspect
			encoded [0]
		when 0x02 then
			decodeCompressedPoint (encoded, 0, curve)
		when 0x03 then
			decodeCompressedPoint (encoded, 1, curve)
		when 0x04 then
			decodeUncompressedPoint (encoded)
		end
	end

feature
	x: EC_FIELD_ELEMENT_FP
	y: EC_FIELD_ELEMENT_FP

	copy (other: like Current)
		do
			x.copy (other.x)
			y.copy (other.y)
		end

feature

	set_infinity
		do
			create x.make_p_x (create {INTEGER_X}.default_create)
			create y.make_p_x (create {INTEGER_X}.default_create)
			infinity := True
		end

	to_byte_array_compressed (curve: EC_CURVE_FP): SPECIAL [NATURAL_8]
			-- Return a compressed encoded version of this point
		local
			x_array: SPECIAL [NATURAL_8]
		do
			x_array := x.x.as_fixed_width_byte_array (x.encoded_field_size (curve))
			create result.make_filled (0, x_array.count + 1)
			result.copy_data (x_array, 0, 1, x_array.count)
			result [0] := compressed_PC_byte (y.x)
		end

	to_byte_array_uncompressed (curve: EC_CURVE_FP): SPECIAL [NATURAL_8]
			-- Return an uncompressed encoded version of this point
		local
			x_array: SPECIAL [NATURAL_8]
			y_array: SPECIAL [NATURAL_8]
			p0: SPECIAL [NATURAL_8]
			qLength: INTEGER_32
		do
			qLength := x.encoded_field_size (curve)
			x_array := x.x.as_fixed_width_byte_array (qlength)
			y_array := y.x.as_fixed_width_byte_array (qLength)
			check
				x_array.capacity = qlength
				y_array.capacity = qlength
			end
			create p0.make_filled (0, x_array.capacity + y_array.capacity + 1)
			p0.copy_data (x_array, 0, x_array.upper, 1)
			p0.copy_data (y_array, 0, y_array.upper, x_array.capacity + 1)
			p0.put (0x04, 0)
			result := p0
		end

	plus_value (other: like Current; curve: EC_CURVE_FP): EC_POINT_FP
		do
			Result := Precursor (other, curve)
		end

	plus (other: like Current; curve: EC_CURVE_FP)
			-- Addition over FP
		local
			gamma: EC_FIELD_ELEMENT_FP
			x3: EC_FIELD_ELEMENT_FP
			y3: EC_FIELD_ELEMENT_FP
		do
			if
				infinity
			then
				copy (other)
			elseif
				other.infinity
			then
			elseif
				x ~ other.x
			then
				if
					y ~ other.y
				then
					copy (twice_value (curve))
				else
					set_infinity
				end
			else
				gamma := (other.y.minus_value (y, curve)).quotient_value (other.x.minus_value (x, curve), curve)
				x3 := (gamma.product_value (gamma, curve)).minus_value (x, curve).minus_value (other.x, curve)
				y3 := (gamma.product_value (x.minus_value (x3, curve), curve)).minus_value (y, curve)
				x := x3
				y := y3
			end
		end

	twice_value (curve: EC_CURVE_FP): EC_POINT_FP
		do
			Result := Precursor (curve)
		end

	twice (curve: EC_CURVE_FP)
			-- Return current * current over FP
		local
			two_element: EC_FIELD_ELEMENT_FP
			three_element: EC_FIELD_ELEMENT_FP
			gamma: EC_FIELD_ELEMENT_FP
			x3: EC_FIELD_ELEMENT_FP
			y3: EC_FIELD_ELEMENT_FP
		do
			if
				infinity
			then
			elseif
				y.x.is_zero
			then
				set_infinity
			else
				create two_element.make_p_x (two)
				create three_element.make_p_x (three)
				gamma := (((x.product_value (x, curve)).product_value (three_element, curve)).plus_value (curve.a, curve)).quotient_value (y.product_value (two_element, curve), curve)
				x3 := (gamma.product_value (gamma, curve)).minus_value (x.product_value (two_element, curve), curve)
				y3 := (gamma.product_value (x.minus_value (x3, curve), curve)).minus_value (y, curve)
				x := x3
				y := y3
			end
		end

	minus_value (other: like Current; curve: EC_CURVE_FP): EC_POINT_FP
		do
			Result := Precursor (other, curve)
		end

	minus (other: like Current; curve: EC_CURVE_FP)
		do
			if
				other.infinity
			then
			else
				plus (other.opposite_value (curve), curve)
			end
		end

	product_value (other: INTEGER_X; curve: EC_CURVE_FP): EC_POINT_FP
		do
			Result := Precursor (other, curve)
		end

	product (other: INTEGER_X; curve: EC_CURVE_FP)
			-- return current * k over FP
		local
			e: INTEGER_X
			h: INTEGER_X
			R: like Current
			i: INTEGER_32
		do
			if
				infinity
			then
			elseif
				other.is_zero
			then
				set_infinity
			else
				e := other
				h := e * three
				R := deep_twin
				from
					i := (h.bits - 2)
				until
					i <= 0
				loop
					R := r.twice_value (curve)
					if
						h.bit_test (i) and not e.bit_test (i)
					then
						r := r.plus_value (Current, curve)
					elseif
						not h.bit_test (i) and e.bit_test (i)
					then
						r := r.minus_value (Current, curve)
					end
					i := i - 1
				end
				copy (r)
			end
		end

	opposite_value (curve: EC_CURVE_FP): like Current
		do
			Result := Precursor (curve)
		end

	opposite (curve: EC_CURVE_FP)
		do
			y.opposite (curve)
		end

feature {NONE} -- support features
	ytilde_set (source: INTEGER_X): BOOLEAN
			-- Test the least significant bit, this is ytilde
			-- X9.62 4.2.1
		do
			result := source.bit_test (0)
		end

	compressed_PC_byte (source: INTEGER_X): NATURAL_8
			-- Return the PC byte depending on if ytilde is set
			-- X9.62 4.3.6
		do
			if
				ytilde_set (source)
			then
				result := 0x03
			else
				result := 0x02
			end
		end

feature {NONE}
	decodeCompressedPoint (encoded: SPECIAL [NATURAL_8] ytilde: INTEGER curve: EC_CURVE_FP)
			-- Decode a compressed point
		require
			encoded.lower = 0
		local
			i: SPECIAL [NATURAL_8]
			x_new: EC_FIELD_ELEMENT_FP
			alpha: EC_FIELD_ELEMENT_FP
			beta: EC_FIELD_ELEMENT_FP
			x_int: INTEGER_X
			bit0: INTEGER
			q_minus_beta: EC_FIELD_ELEMENT_FP
		do
			create i.make_filled (0, encoded.count - 1)
			i.copy_data (encoded, 1, 0, i.count)
			create x_int.make_from_bytes (i, i.lower, i.upper)
			create x_new.make_p_x (x_int)
			alpha := (x_new.product_value (x_new.square_value (curve).plus_value (curve.a, curve), curve)).plus_value (curve.b, curve)
			beta := alpha.sqrt (curve)
			if
				beta.x.bit_test (0)
			then
				bit0 := 1
			else
				bit0 := 0
			end
			if
				bit0 = ytilde
			then
				make_curve_x_y (x_new, beta)
			else
				create q_minus_beta.make_p_x (curve.q - beta.x)
				make_curve_x_y (x_new, q_minus_beta)
			end
		end

	decodeUncompressedPoint (encoded: SPECIAL [NATURAL_8])
			-- Decode an uncompressed point
		require
			encoded_not_split_even: (encoded.count \\ 2) = 1
		local
			xEnc: SPECIAL [NATURAL_8]
			yEnc: SPECIAL [NATURAL_8]
			x_new: EC_FIELD_ELEMENT_FP
			y_new: EC_FIELD_ELEMENT_FP
		do
			create xEnc.make_filled (0, (encoded.capacity - 1) // 2)
			create yEnc.make_filled (0, (encoded.capacity - 1) // 2)
			encoded.copy_data (xEnc, 1, 0, xEnc.capacity)
			encoded.copy_data (yEnc, xEnc.capacity, 0, yEnc.capacity)
			create x_new.make_p_x (create {INTEGER_X}.make_from_bytes (xEnc, xEnc.lower, xEnc.upper))
			create y_new.make_p_x (create {INTEGER_X}.make_from_bytes (yEnc, yEnc.lower, yEnc.upper))
			x := x_new
			y := y_new
		end
end
