openssl icon indicating copy to clipboard operation
openssl copied to clipboard

`OpenSSL::PKey::EC::Point` should be able to get and set affine coordinates

Open rickmark opened this issue 3 years ago • 4 comments

It is possible using a little magic to create a point with a raw x and y:

   def self.from_compressed_point(group, x, is_even)
      x_value = x.to_s(2).rjust(32, "\x00")
      prefix = is_even ? "\x02" : "\x03"

      encoded = OpenSSL::BN.new [ prefix, x_value ].join, 2
      OpenSSL::PKey::EC::Point.new(group, encoded)
    end

One can similarly use some magic to get uncompressed points and turn them into two OpenSSL::BN instances

It would be better if we supported this without using compressed point form magic, like so (RBS signature with hypothetical overload):

class OpenSSL::PKey::EC::Point
  # Since the OpenSSL operation returns both
  def affine_coordinates -> [ OpenSSL::BN, OpenSSL::BN ]
  
  # Convenance methods for above
  attr_reader :x, :y -> OpenSSL::BN

  #  Choice 1 - Using Bool for Y
  def initialize : (group: OpenSSL::PKey::EC::Group, x: OpenSSL::BN, even: Boolean) -> OpenSSL::PKey::EC::Point

  # Choice 2 - Using a symbol for Y
  def initialize : (group: OpenSSL::PKey::EC::Group, x: OpenSSL::BN, y:[ :even, :odd ]) -> OpenSSL::PKey::EC::Point

  # Using X and Y directly
  def initialize : (group: OpenSSL::PKey::EC::Group, x: OpenSSL::BN, y: OpenSSL::BN) -> OpenSSL::PKey::EC::Point
end

I've intentionally omitted mutating the X and Y of a point after creation. Because all of these have a different aridity they will not conflict. Similarly adding Jacobian would create an aridity == 4.

The big question is for choice 1 or 2 when creating an X and which Y value.

rickmark avatar Apr 02 '21 05:04 rickmark

Doh - maybe its best to have it be "odd" as the boolean. Since 0 == false == even and 1 == true == odd?

rickmark avatar Apr 02 '21 05:04 rickmark

Yes, this is a good feature to have.

  #  Choice 1 - Using Bool for Y
  def initialize : (group: OpenSSL::PKey::EC::Group, x: OpenSSL::BN, even: Boolean) -> OpenSSL::PKey::EC::Point

  # Choice 2 - Using a symbol for Y
  def initialize : (group: OpenSSL::PKey::EC::Group, x: OpenSSL::BN, y:[ :even, :odd ]) -> OpenSSL::PKey::EC::Point

SEC 1 refers to the compressed value as ỹP = 0 or ỹP = 1 and OpenSSL's man page calls it y_bit, seemingly because it indicates the different property in GF(p) group vs in GF(2m). I got the feeling that the Ruby interface also should take the integer 0 or 1, but I'm no expert.

In any case, I'd like to have this as a separate method from EC::Point.new (something like from_compressed_coordinates), since determining what the argument means by checking type isn't very Ruby-like.

rhenium avatar Apr 02 '21 12:04 rhenium

I got the feeling that the Ruby interface also should take the integer 0 or 1, but I'm no expert.

I'd like to avoid Integer here - because using :even and :odd prevent accidents where one gets a point at (x, 1) where it was intending to resolve compression. So even if its a separate from_compressed_coordinates(x : BN, y_bit: Symbol) and a from_coordinates(x: BN, y: BN) as class methods I'm good with it. These can just be helpers to encode the proper prefix and call the usual initializer to reduce the "magic" being done in the ruby bindings...

Operation becomes: For Uncompressed Coordinates: Convert X and Y to byte string Widen X and Y BN to the degree Call new with "\x04" + X + Y

For Compressed Coordinates Convert X to byte string Widen X to degree Prefix with "\x02" or "\x03" and append X Call new with above

rickmark avatar Apr 02 '21 20:04 rickmark

I found the bit is sometimes called odd or even in a GF(p) group's context, but I couldn't be sure if the word choice is appropriate in a GF(2m) group's context, too. The encoding seems more complex there, it's not taken directly from the right-most bit of y value but yx^-1.

rhenium avatar Apr 04 '21 13:04 rhenium