New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Hide kty in %JOSE.JWK{} when it is inspected #139
Conversation
Co-authored-by: Eric Meadows-Jönsson <eric.meadows.jonsson@gmail.com>
I think that k = JOSE.JWK.generate_key {:rsa, 1024}
JOSE.JWK.to_map k
{%{kty: :jose_jwk_kty_rsa},
%{
"d" => "hmNQPURAxO3rO7jLPHDIcbsGGvG1RiobZgqXV049BUdBcnBGUuNuEMF-QZXsrXA-4vcFR44rxehQQhnU_ZYWcTFTlOWRjN4H-6ufoAmZ38BePf3n8hKRT-RlBrlF7F1QOs5cV__AGp7gd9Sbd8ny_5Pw2HQZxPZAuZFOmXuNpzk",
"dp" => "aR9tQn0EiXCZitSKoqgPfYcIPlREeO4MiQX8zGikVzWNNIp6f1kOrtrxafjJfkoxLlm98G71GxRo6ifcb5HgrQ",
"dq" => "JtCP6D1eK7FnwogktcNhUchyG-AoQhNJ2_8_wBVV-VPuMlrCFDf-MkMy29kTUFSUBK6lmVQfVoDxWTbIdadRzQ",
"e" => "AQAB",
"kty" => "RSA",
"n" => "mb8rEbiorcezpJ7RyKqXFhikBE6irPzbPn0Ey24-jaNWIujgfr7REGvSIJQ3Vv2unjvbeVLBmfClSbk8UqcAmOI7Jrlevl9b7Cmb1dbRQ3jRMnfwmbrfvF3JL1zXCaxSnHxouiRJPG-FMfbsH2wJ_waHZAA50dDKmXA0gx7jm58",
"p" => "y7JcFZLoW5gN5Hbn0YQoET8IgLUxlhU0U0dMioY4SEDTCmXAONMxCBS1_zL_AlBRkWxgLGwBtfx86JZXOKYoSw",
"q" => "wTlvxNUrZ05B60TrZ8hKTe7Y0tVR2cLmpgwXFpjrp0mrdN3Xulj9nPjExRkqZiogL_ctXanekN_ajQ9p945tfQ",
"qi" => "dKtJWggGCiNMoIekigbVwrT3ovOqsizJ-ju2sgAtXoKjEvZcOGCrWM0YOB15gEknW_McbWZfpw8GQ0A6L2iTAg"
}}
JOSE.JWK.to_public_map k
{%{kty: :jose_jwk_kty_rsa},
%{
"e" => "AQAB",
"kty" => "RSA",
"n" => "mb8rEbiorcezpJ7RyKqXFhikBE6irPzbPn0Ey24-jaNWIujgfr7REGvSIJQ3Vv2unjvbeVLBmfClSbk8UqcAmOI7Jrlevl9b7Cmb1dbRQ3jRMnfwmbrfvF3JL1zXCaxSnHxouiRJPG-FMfbsH2wJ_waHZAA50dDKmXA0gx7jm58"
}} For this key type, the claims |
@victorolinasc you bring up a good point of only redacting the parts that should truly be private. I see a few options to go forward:
My vote would be for the third option given that is the easiest to implement and maintain. To be clear, this wouldn't have any impact on the internals of jwk = %JOSE.JWK{kty: kty} = JOSE.JWK.generate_key({:rsa, 1024})
jwk
|> JOSE.JWK.to_map()
|> IO.inspect()
jwk
|> Map.from_struct()
|> IO.inspect()
IO.inspect(kty) Overall hiding the internals seems like a high value effort since it is common to pass around a defimpl Inspect, for: JOSE.JWK do
@excluded [:__struct__, :__exception__]
def inspect(%JOSE.JWK{} = struct, opts) do
struct = reject(struct)
if gte_elixir_14_safe?() do
# This might be overkill since we know what the fields in the struct are
# [
# %{field: :keys, required: false},
# %{field: :kty, required: false},
# %{field: :fields, required: false}
# ]
# doing it this way does make it dynamic so that if the struct ever changes
# then we don't need to modify multiple places.
infos =
for %{field: field} = info <- JOSE.JWK.__info__(:struct),
field not in @excluded,
do: info
struct
|> Inspect.Map.inspect("JOSE.JWK", infos, opts)
else
struct
|> Map.drop(@excluded)
|> Inspect.Map.inspect("JOSE.JWK", opts)
end
end
def gte_elixir_14_safe?, do: Code.ensure_loaded?(Inspect.Map) and function_exported?(Inspect.Map, :inspect, 4)
# Handle every type of key by redacting the private parts but keeping the public parts
defp reject(%JOSE.JWK{kty: {:jose_jwk_kty_rsa, {:RSAPrivateKey, version, n, e, _, _, _, _, _, _, _}}} = struct) do
# redacting private parts by replacing them with descriptive atoms
%JOSE.JWK{
struct
| kty:
{:jose_jwk_kty_rsa,
{:RSAPrivateKey, version, n, e, :private_exponent, :prime_1, :prime_2, :exponent_1, :exponent_2, :coefficient,
:other_prime_infos}}
}
end
# Fallback to just redact the entire kty value
defp reject(%JOSE.JWK{kty: kty} = struct), do: %JOSE.JWK{struct | kty: :redacted}
end Looking forward to any thoughts and guidance. |
I'm not an expert, but just taking a look at the RSA Wikipedia Operation section
Which would lead me to consider redacting |
I like the approach of writing a custom inspect (and probably a custom to_string too). Although a higher effort, we have all the infrastructure in the library. It knows the types of keys we have and want here. So, I think your approach with option 2 is sound and the best way forward here. We can support all the keys we can generate with To my knowledge, Wdyt? |
This idea spawned from this discussion
Here is what the output would look like for an elixir client using Joken
I am not sure if the
keys
orfields
defined in the jose_jwk record would ever hold private information, but I am hoping that this can start a discussion.