Class: Sandal::Sig::ES

Inherits:
Object
  • Object
show all
Defined in:
lib/sandal/sig/es.rb

Overview

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

Direct Known Subclasses

ES256, ES384, ES512

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, sha_size, prime_size, key) ⇒ ES

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

Parameters:

  • sha_size (Integer)

    The size of the SHA algorithm.

  • prime_size (Integer)

    The size of the ECDSA primes.

  • key (OpenSSL::PKey::EC)

    The key to use for signing (private) or validation (public).



19
20
21
22
23
24
# 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

Instance Attribute Details

#nameObject (readonly)

The JWA name of the algorithm.



10
11
12
# File 'lib/sandal/sig/es.rb', line 10

def name
  @name
end

Class Method Details

.decode_asn1_signature(signature) ⇒ OpenSSL::BN

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

Parameters:

  • signature (String)

    The ASN.1 signature.

Returns:

  • (OpenSSL::BN, OpenSSL::BN)

    A pair of BNs.



53
54
55
56
# 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) ⇒ OpenSSL::BN

Decodes a JWS signature into a pair of BNs.

Parameters:

  • signature (String)

    The ASN.1 signature.

Returns:

  • (OpenSSL::BN, OpenSSL::BN)

    A pair of BNs.



72
73
74
75
76
77
78
# 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) ⇒ String

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

Parameters:

  • r (OpenSSL::BN)

    The “r” value.

  • s (OpenSSL::BN)

    The “s” value.

Returns:

  • (String)

    The ASN.1 signature.



63
64
65
66
# 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) ⇒ String

Encodes a pair of BNs into a JWS signature.

Parameters:

  • r (OpenSSL::BN)

    The “r” value.

  • s (OpenSSL::BN)

    The “s” value.

  • prime_size (Integer)

    The size of the ECDSA primes.

Returns:

  • (String)

    The ASN.1 signature.



86
87
88
89
90
# 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

Instance Method Details

#sign(payload) ⇒ String

Signs a payload and returns the signature.

Parameters:

  • payload (String)

    The payload of the token to sign.

Returns:

  • (String)

    The signature.



30
31
32
33
34
35
# 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) ⇒ Boolean

Validates a payload signature and returns whether the signature matches.

Parameters:

  • signature (String)

    The signature to validate.

  • payload (String)

    The payload of the token.

Returns:

  • (Boolean)

    true if the signature is correct; otherwise false.



42
43
44
45
46
47
# 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