class Sandal::Sig::ES

Base implementation of the ECDSA-SHA family of signature algorithms.

Attributes

name[R]

The JWA name of the algorithm.

Public Class Methods

decode_asn1_signature(signature) click to toggle source

Decodes an ASN.1 signature into a pair of BNs.

@param signature [String] The ASN.1 signature. @return [OpenSSL::BN, OpenSSL::BN] A pair of BNs.

# File lib/sandal/sig/es.rb, line 53
def self.decode_asn1_signature(signature)
  asn_seq = OpenSSL::ASN1.decode(signature)
  return asn_seq.value[0].value, asn_seq.value[1].value
end
decode_jws_signature(signature) click to toggle source

Decodes a JWS signature into a pair of BNs.

@param signature [String] The ASN.1 signature. @return [OpenSSL::BN, OpenSSL::BN] A pair of BNs.

# File lib/sandal/sig/es.rb, line 72
def self.decode_jws_signature(signature)
  n_length = signature.length / 2
  s_to_n = -> s { OpenSSL::BN.new(s.unpack("H*")[0], 16) }
  r = s_to_n.call(signature[0..(n_length - 1)])
  s = s_to_n.call(signature[n_length..-1])
  return r, s
end
encode_asn1_signature(r, s) click to toggle source

Encodes a pair of BNs into an ASN.1 signature.

@param r [OpenSSL::BN] The “r” value. @param s [OpenSSL::BN] The “s” value. @return [String] The ASN.1 signature.

# File lib/sandal/sig/es.rb, line 63
def self.encode_asn1_signature(r, s)
  items = [OpenSSL::ASN1::Integer.new(r), OpenSSL::ASN1::Integer.new(s)]
  OpenSSL::ASN1::Sequence.new(items).to_der
end
encode_jws_signature(r, s, prime_size) click to toggle source

Encodes a pair of BNs into a JWS signature.

@param r [OpenSSL::BN] The “r” value. @param s [OpenSSL::BN] The “s” value. @param prime_size [Integer] The size of the ECDSA primes. @return [String] The ASN.1 signature.

# File lib/sandal/sig/es.rb, line 86
def self.encode_jws_signature(r, s, prime_size)
  byte_count = (prime_size / 8.0).ceil
  n_to_s = -> n { [n.to_s(16)].pack("H*").rjust(byte_count, "\0") }
  n_to_s.call(r) + n_to_s.call(s)
end
new(name, sha_size, prime_size, key) click to toggle source

Creates a new instance; it’s probably easier to use one of the subclass constructors.

@oaram name [String] The JWA name of the algorithm. @param sha_size [Integer] The size of the SHA algorithm. @param prime_size [Integer] The size of the ECDSA primes. @param key [OpenSSL::PKey::EC] The key to use for signing (private) or validation (public).

# File lib/sandal/sig/es.rb, line 19
def initialize(name, sha_size, prime_size, key)
  @name = name
  @digest = OpenSSL::Digest.new("sha#{sha_size}")
  @prime_size = prime_size
  @key = key
end

Public Instance Methods

sign(payload) click to toggle source

Signs a payload and returns the signature.

@param payload [String] The payload of the token to sign. @return [String] The signature.

# File lib/sandal/sig/es.rb, line 30
def sign(payload)
  hash = @digest.digest(payload)
  asn1_sig = @key.dsa_sign_asn1(hash)
  r, s = self.class.decode_asn1_signature(asn1_sig)
  self.class.encode_jws_signature(r, s, @prime_size)
end
valid?(signature, payload) click to toggle source

Validates a payload signature and returns whether the signature matches.

@param signature [String] The signature to validate. @param payload [String] The payload of the token. @return [Boolean] true if the signature is correct; otherwise false.

# File lib/sandal/sig/es.rb, line 42
def valid?(signature, payload)
  hash = @digest.digest(payload)
  r, s = self.class.decode_jws_signature(signature)
  asn1_sig = self.class.encode_asn1_signature(r, s)
  @key.dsa_verify_asn1(hash, asn1_sig)
end

Private Instance Methods

make_key(key, curve_name) click to toggle source

Makes an EC key and ensures that it has the right curve name.

@param key [OpenSSL::PKey::EC or String] The key. @param curve_name [String] The curve name. @return [OpenSSL::PKey::EC] The key. @raise [ArgumentError] The key has the wrong curve name.

# File lib/sandal/sig/es.rb, line 100
def make_key(key, curve_name)
  key = OpenSSL::PKey::EC.new(key) if key.is_a?(String)
  unless key.group.curve_name == curve_name
    raise ArgumentError, "The key must be in the #{curve_name} group."
  end
  key
end