module ECDSA::Format::PointOctetString

This module provides methods for converting between {Point} objects and “octet strings”, which are just strings with binary data in them that have the coordinates of the point. The point octet string format is defined in two sections of [SEC1](www.secg.org/collateral/sec1_final.pdf):

Public Class Methods

decode(string, group) click to toggle source

Converts an octet string to a {Point} in the specified group. Raises a {DecodeError} if the string is invalid for any reason.

This is equivalent to [ec_GFp_simple_oct2point](github.com/openssl/openssl/blob/a898936/crypto/ec/ecp_oct.c#L325) in OpenSSL.

@param string (String) @param group (Group) @return (Point)

# File lib/ecdsa/format/point_octet_string.rb, line 44
def self.decode(string, group)
  string = string.dup.force_encoding('BINARY')

  raise DecodeError, 'Point octet string is empty.' if string.empty?

  case string[0].ord
  when 0
    check_length string, 1
    return group.infinity
  when 2
    decode_compressed string, group, 0
  when 3
    decode_compressed string, group, 1
  when 4
    decode_uncompressed string, group
  else
    raise DecodeError, 'Unrecognized start byte for point octet string: 0x%x' % string[0].ord
  end
end
encode(point, opts = {}) click to toggle source

Converts a {Point} to an octet string.

@param point (Point) @param opts (Hash) @option opts :compression Set this option to true to generate a compressed

octet string, which only has one bit of the Y coordinate.
(Default: false)
# File lib/ecdsa/format/point_octet_string.rb, line 21
def self.encode(point, opts = {})
  return "\x00" if point.infinity?

  if opts[:compression]
    start_byte = point.y.even? ? "\x02" : "\x03"
    start_byte + FieldElementOctetString.encode(point.x, point.group.field)
  else
    "\x04" +
      FieldElementOctetString.encode(point.x, point.group.field) +
      FieldElementOctetString.encode(point.y, point.group.field)
  end
end

Private Class Methods

check_length(string, expected_length) click to toggle source
# File lib/ecdsa/format/point_octet_string.rb, line 100
def self.check_length(string, expected_length)
  if string.length != expected_length
    raise DecodeError, "Expected point octet string to be length #{expected_length} but it was #{string.length}."
  end
end
decode_compressed(string, group, y_lsb) click to toggle source
# File lib/ecdsa/format/point_octet_string.rb, line 66
def self.decode_compressed(string, group, y_lsb)
  expected_length = 1 + group.byte_length
  check_length string, expected_length

  x_string = string[1, group.byte_length]
  x = ECDSA::Format::FieldElementOctetString.decode x_string, group.field

  possible_ys = group.solve_for_y(x)
  y = possible_ys.find { |py| (py % 2) == y_lsb }
  raise DecodeError, 'Could not solve for y.' if y.nil?

  finish_decode x, y, group
end
decode_uncompressed(string, group) click to toggle source
# File lib/ecdsa/format/point_octet_string.rb, line 80
def self.decode_uncompressed(string, group)
  expected_length = 1 + 2 * group.byte_length
  check_length string, expected_length

  x_string = string[1, group.byte_length]
  y_string = string[1 + group.byte_length, group.byte_length]
  x = ECDSA::Format::FieldElementOctetString.decode x_string, group.field
  y = ECDSA::Format::FieldElementOctetString.decode y_string, group.field

  finish_decode x, y, group
end
finish_decode(x, y, group) click to toggle source
# File lib/ecdsa/format/point_octet_string.rb, line 92
def self.finish_decode(x, y, group)
  point = group.new_point [x, y]
  if !group.include? point
    raise DecodeError, "Decoded point does not satisfy curve equation: #{point.inspect}."
  end
  point
end