class Acme::Client::JWK::ECDSA

def coordinates

def coordinates
  @coordinates ||= begin
    hex = public_key.to_bn.to_s(16)
    data_len = hex.length - 2
    hex_x = hex[2, data_len / 2]
    hex_y = hex[2 + data_len / 2, data_len / 2]
    {
      x: OpenSSL::BN.new([hex_x].pack('H*'), 2),
      y: OpenSSL::BN.new([hex_y].pack('H*'), 2)
    }
  end
end

def initialize(private_key)

Returns nothing.

private_key - A OpenSSL::PKey::EC instance.

Instantiate a new ECDSA JWK.
def initialize(private_key)
  unless private_key.is_a?(OpenSSL::PKey::EC)
    raise ArgumentError, 'private_key must be a OpenSSL::PKey::EC'
  end
  unless @curve_params = KNOWN_CURVES[private_key.group.curve_name]
    raise ArgumentError, 'Unknown EC curve'
  end
  @private_key = private_key
end

def jwa_alg

Returns a String.

The name of the algorithm as needed for the `alg` member of a JWS object.
def jwa_alg
  @curve_params[:jwa_alg]
end

def public_key

def public_key
  @private_key.public_key
end

def sign(message)

Returns a String signature.

message - A String message to sign.

Sign a message with the private key.
def sign(message)
  # DER encoded ASN.1 signature
  der = @private_key.sign(@curve_params[:digest].new, message)
  # ASN.1 SEQUENCE
  seq = OpenSSL::ASN1.decode(der)
  # ASN.1 INTs
  ints = seq.value
  # BigNumbers
  bns = ints.map(&:value)
  byte_size = (@private_key.group.degree + 7) / 8
  # Binary R/S values
  r, s = bns.map { |bn| bn.to_s(2).rjust(byte_size, "\x00") }
  # JWS wants raw R/S concatenated.
  [r, s].join
end

def to_h

Returns a Hash.

Get this JWK as a Hash for JSON serialization.
def to_h
  {
    crv: @curve_params[:jwa_crv],
    kty: 'EC',
    x: Acme::Client::Util.urlsafe_base64(coordinates[:x].to_s(2)),
    y: Acme::Client::Util.urlsafe_base64(coordinates[:y].to_s(2))
  }
end