More EC Support in Kong's JWT Parser?


#1

I have been dabbling with Ping Access using JWKS endpoints, and see Kong has some EC support(ES256) but not all flavors(ES384, ES512) just yet:

I will be frank and say that I am just not the best with crypto and signing, but I was wondering if Kong would be okay with expanding the options here and if what I am doing looks right?

Signing around EC

--- Supported algorithms for signing tokens.
  ["ES256"] = function(data, key)
    local pkeyPrivate = openssl_pkey.new(key)
    local signature = pkeyPrivate:sign(openssl_digest.new("sha256"):update(data))

    local derSequence = asn_sequence.parse_simple_sequence(signature)
    local r = asn_sequence.unsign_integer(derSequence[1], 32)
    local s = asn_sequence.unsign_integer(derSequence[2], 32)
    assert(#r == 32)
    assert(#s == 32)
    return r .. s 
    end,
  ["ES384"] = function(data, key)
    local pkeyPrivate = openssl_pkey.new(key)
    local signature = pkeyPrivate:sign(openssl_digest.new("sha384"):update(data))

    local derSequence = asn_sequence.parse_simple_sequence(signature)
    local r = asn_sequence.unsign_integer(derSequence[1], 48)
    local s = asn_sequence.unsign_integer(derSequence[2], 48)
    assert(#r == 48)
    assert(#s == 48)
    return r .. s 
   end,
  ["ES512"] = function(data, key)
    local pkeyPrivate = openssl_pkey.new(key)
    local signature = pkeyPrivate:sign(openssl_digest.new("sha512"):update(data))

    local derSequence = asn_sequence.parse_simple_sequence(signature)
    local r = asn_sequence.unsign_integer(derSequence[1], 64)
    local s = asn_sequence.unsign_integer(derSequence[2], 64)
    assert(#r == 64)
    assert(#s == 64)
    return r .. s 
    end
}

And the Verify additions around EC

--- Supported algorithms for verifying tokens.
local alg_verify = {
  ["ES256"] = function(data, signature, key)
    local pkey_ok, pkey = pcall(openssl_pkey.new, key)
    assert(pkey_ok, "Consumer Public Key is Invalid")
    assert(#signature == 64, "Signature must be 64 bytes.")
    local asn = {}
    asn[1] = asn_sequence.resign_integer(string_sub(signature, 1, 32))
    asn[2] = asn_sequence.resign_integer(string_sub(signature, 33, 64))
    local signatureAsn = asn_sequence.create_simple_sequence(asn)
    local digest = openssl_digest.new('sha256'):update(data)
    return pkey:verify(signatureAsn, digest)
  end,
  ["ES384"] = function(data, signature, key)
    local pkey_ok, pkey = pcall(openssl_pkey.new, key)
    assert(pkey_ok, "Consumer Public Key is Invalid")
    assert(#signature == 96, "Signature must be 96 bytes.")
    local asn = {}
    asn[1] = asn_sequence.resign_integer(string_sub(signature, 1, 48))
    asn[2] = asn_sequence.resign_integer(string_sub(signature, 49, 96))
    local signatureAsn = asn_sequence.create_simple_sequence(asn)
    local digest = openssl_digest.new('sha384'):update(data)
    return pkey:verify(signatureAsn, digest)
  end,
  ["ES512"] = function(data, signature, key)
    local pkey_ok, pkey = pcall(openssl_pkey.new, key)
    assert(pkey_ok, "Consumer Public Key is Invalid")
    assert(#signature == 128, "Signature must be 128 bytes.")
    local asn = {}
    asn[1] = asn_sequence.resign_integer(string_sub(signature, 1, 64))
    asn[2] = asn_sequence.resign_integer(string_sub(signature, 65, 128))
    local signatureAsn = asn_sequence.create_simple_sequence(asn)
    local digest = openssl_digest.new('sha512'):update(data)
    return pkey:verify(signatureAsn, digest)
  end
}

Also anyone know much about jwks in general? Yes I get the kty, and kid is to help pick which record to pick from in the jwks list, and ES256 tells me the alg. I am curious if we need to account for crv at all in signature validation as I don’t see Kong doing that for the existing ES256. But essentially how can I take x and y to produce the public key is what I am getting at in the Kongs signature verify calls -> [“ES256”] = function(data, signature, key) or am I totally off base and x and y are not even used as the public key :laughing: .

  {
      "kty": "EC",
      "kid": "7s",
      "use": "sig",
      "alg": "ES256",
      "x": "XXXXXXXXXXXXXXXXXXXXXX",
      "y": "XXXXXXXXXXXXXXXXXXXXXX",
      "crv": "P-256"
    }

#2

Figured it all out myself I think. Will PR it soon.