Skip to content

Commit

Permalink
Add support for reading/writing PKIX for Curve25519 and Curve448.
Browse files Browse the repository at this point in the history
  • Loading branch information
potatosalad committed May 19, 2017
1 parent 7abadb2 commit feeba51
Show file tree
Hide file tree
Showing 13 changed files with 876 additions and 47 deletions.
59 changes: 59 additions & 0 deletions include/jose_public_key.hrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*-
%% vim: ts=4 sw=4 ft=erlang noet
%%%-------------------------------------------------------------------
%%% @author Andrew Bennett <[email protected]>
%%% @copyright 2014-2017, Andrew Bennett
%%% @doc
%%%
%%% @end
%%% Created : 12 May 2017 by Andrew Bennett <[email protected]>
%%%-------------------------------------------------------------------

-ifndef(JOSE_PUBLIC_KEY_HRL).

-include_lib("public_key/include/public_key.hrl").

-define('jose_id-X25519', {1,3,101,110}).
-define('jose_id-X448', {1,3,101,111}).
-define('jose_id-EdDSA25519', {1,3,101,112}).
-define('jose_id-EdDSA448', {1,3,101,113}).

-record(jose_EdDSA25519PublicKey, {
publicKey = undefined :: undefined | << _:256 >>
}).

-record(jose_EdDSA25519PrivateKey, {
publicKey = undefined :: undefined | #jose_EdDSA25519PublicKey{},
privateKey = undefined :: undefined | << _:256 >>
}).

-record(jose_EdDSA448PublicKey, {
publicKey = undefined :: undefined | << _:456 >>
}).

-record(jose_EdDSA448PrivateKey, {
publicKey = undefined :: undefined | #jose_EdDSA448PublicKey{},
privateKey = undefined :: undefined | << _:456 >>
}).

-record(jose_X25519PublicKey, {
publicKey = undefined :: undefined | << _:256 >>
}).

-record(jose_X25519PrivateKey, {
publicKey = undefined :: undefined | #jose_X25519PublicKey{},
privateKey = undefined :: undefined | << _:256 >>
}).

-record(jose_X448PublicKey, {
publicKey = undefined :: undefined | << _:448 >>
}).

-record(jose_X448PrivateKey, {
publicKey = undefined :: undefined | #jose_X448PublicKey{},
privateKey = undefined :: undefined | << _:448 >>
}).

-define(JOSE_PUBLIC_KEY_HRL, 1).

-endif.
18 changes: 17 additions & 1 deletion src/jose_jwk_kty.erl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
%%%-------------------------------------------------------------------
-module(jose_jwk_kty).

-include_lib("public_key/include/public_key.hrl").
-include_lib("jose_public_key.hrl").

-callback generate_key(Parameters) -> KTY
when
Expand Down Expand Up @@ -53,6 +53,22 @@ from_key(ECPrivateKey=#'ECPrivateKey'{}) ->
{?KTY_EC_MODULE, ?KTY_EC_MODULE:from_key(ECPrivateKey)};
from_key(ECPublicKey={#'ECPoint'{}, _}) ->
{?KTY_EC_MODULE, ?KTY_EC_MODULE:from_key(ECPublicKey)};
from_key(EdDSA25519PrivateKey=#'jose_EdDSA25519PrivateKey'{}) ->
{?KTY_OKP_Ed25519_MODULE, ?KTY_OKP_Ed25519_MODULE:from_key(EdDSA25519PrivateKey)};
from_key(EdDSA25519PublicKey=#'jose_EdDSA25519PublicKey'{}) ->
{?KTY_OKP_Ed25519_MODULE, ?KTY_OKP_Ed25519_MODULE:from_key(EdDSA25519PublicKey)};
from_key(EdDSA448PrivateKey=#'jose_EdDSA448PrivateKey'{}) ->
{?KTY_OKP_Ed448_MODULE, ?KTY_OKP_Ed448_MODULE:from_key(EdDSA448PrivateKey)};
from_key(EdDSA448PublicKey=#'jose_EdDSA448PublicKey'{}) ->
{?KTY_OKP_Ed448_MODULE, ?KTY_OKP_Ed448_MODULE:from_key(EdDSA448PublicKey)};
from_key(X25519PrivateKey=#'jose_X25519PrivateKey'{}) ->
{?KTY_OKP_X25519_MODULE, ?KTY_OKP_X25519_MODULE:from_key(X25519PrivateKey)};
from_key(X25519PublicKey=#'jose_X25519PublicKey'{}) ->
{?KTY_OKP_X25519_MODULE, ?KTY_OKP_X25519_MODULE:from_key(X25519PublicKey)};
from_key(X448PrivateKey=#'jose_X448PrivateKey'{}) ->
{?KTY_OKP_X448_MODULE, ?KTY_OKP_X448_MODULE:from_key(X448PrivateKey)};
from_key(X448PublicKey=#'jose_X448PublicKey'{}) ->
{?KTY_OKP_X448_MODULE, ?KTY_OKP_X448_MODULE:from_key(X448PublicKey)};
from_key(RSAPrivateKey=#'RSAPrivateKey'{}) ->
{?KTY_RSA_MODULE, ?KTY_RSA_MODULE:from_key(RSAPrivateKey)};
from_key(RSAPublicKey=#'RSAPublicKey'{}) ->
Expand Down
55 changes: 51 additions & 4 deletions src/jose_jwk_kty_okp_ed25519.erl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
-behaviour(jose_jwk_kty).
-behaviour(jose_jwk_use_sig).

-include_lib("jose_public_key.hrl").

%% jose_jwk callbacks
-export([from_map/1]).
-export([to_key/1]).
Expand All @@ -29,10 +31,15 @@
-export([verifier/2]).
-export([verify/4]).
%% API
-export([from_key/1]).
-export([from_okp/1]).
-export([from_openssh_key/1]).
-export([from_pem/1]).
-export([from_pem/2]).
-export([to_okp/1]).
-export([to_openssh_key/2]).
-export([to_pem/1]).
-export([to_pem/2]).

%% Macros
-define(crv, <<"Ed25519">>).
Expand Down Expand Up @@ -60,10 +67,13 @@ from_map(F = #{ <<"kty">> := <<"OKP">>, <<"crv">> := ?crv, <<"x">> := X }) ->
<< PK:?publickeybytes/binary >> = base64url:decode(X),
{PK, maps:without([<<"crv">>, <<"kty">>, <<"x">>], F)}.

to_key(PK = << _:?publickeybytes/binary >>) ->
PK;
to_key(SK = << _:?secretkeybytes/binary >>) ->
SK.
to_key(<< PublicKey:?publickeybytes/binary >>) ->
#'jose_EdDSA25519PublicKey'{ publicKey = PublicKey };
to_key(<< PrivateKey:?secretbytes/binary, PublicKey:?publickeybytes/binary >>) ->
#'jose_EdDSA25519PrivateKey'{
publicKey = #'jose_EdDSA25519PublicKey'{ publicKey = PublicKey },
privateKey = PrivateKey
}.

to_map(PK = << _:?publickeybytes/binary >>, F) ->
F#{
Expand Down Expand Up @@ -145,6 +155,11 @@ verify(Message, ALG, Signature, PK = << _:?publickeybytes/binary >>)
%% API functions
%%====================================================================

from_key(#'jose_EdDSA25519PrivateKey'{publicKey=#'jose_EdDSA25519PublicKey'{publicKey=Public}, privateKey=Secret}) ->
{<< Secret/binary, Public/binary >>, #{}};
from_key(#'jose_EdDSA25519PublicKey'{publicKey=Public}) ->
{Public, #{}}.

from_okp({'Ed25519', SK = << Secret:?secretbytes/binary, PK:?publickeybytes/binary >>}) ->
case jose_curve25519:eddsa_secret_to_public(Secret) of
PK ->
Expand All @@ -164,6 +179,22 @@ from_openssh_key({<<"ssh-ed25519">>, _PK, SK, Comment}) ->
{KTY, maps:merge(#{ <<"kid">> => Comment }, OtherFields)}
end.

from_pem(PEMBinary) when is_binary(PEMBinary) ->
case jose_jwk_pem:from_binary(PEMBinary) of
{?MODULE, {Key, Fields}} ->
{Key, Fields};
PEMError ->
PEMError
end.

from_pem(Password, PEMBinary) when is_binary(PEMBinary) ->
case jose_jwk_pem:from_binary(Password, PEMBinary) of
{?MODULE, {Key, Fields}} ->
{Key, Fields};
PEMError ->
PEMError
end.

to_okp(SK = << _:?secretkeybytes/binary >>) ->
{'Ed25519', SK};
to_okp(PK = << _:?publickeybytes/binary >>) ->
Expand All @@ -173,6 +204,22 @@ to_openssh_key(SK = << _:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -
Comment = maps:get(<<"kid">>, F, <<>>),
jose_jwk_openssh_key:to_binary([[{{<<"ssh-ed25519">>, PK}, {<<"ssh-ed25519">>, PK, SK, Comment}}]]).

to_pem(SK = << _:?secretkeybytes/binary >>) ->
EdDSA25519PrivateKey = to_key(SK),
PEMEntry = jose_public_key:pem_entry_encode('EdDSA25519PrivateKey', EdDSA25519PrivateKey),
jose_public_key:pem_encode([PEMEntry]);
to_pem(PK = << _:?publickeybytes/binary >>) ->
EdDSA25519PublicKey = to_key(PK),
PEMEntry = jose_public_key:pem_entry_encode('EdDSA25519PublicKey', EdDSA25519PublicKey),
jose_public_key:pem_encode([PEMEntry]).

to_pem(Password, SK = << _:?secretkeybytes/binary >>) ->
EdDSA25519PrivateKey = to_key(SK),
jose_jwk_pem:to_binary(Password, 'EdDSA25519PrivateKey', EdDSA25519PrivateKey);
to_pem(Password, PK = << _:?publickeybytes/binary >>) ->
EdDSA25519PublicKey = to_key(PK),
jose_jwk_pem:to_binary(Password, 'EdDSA25519PublicKey', EdDSA25519PublicKey).

%%%-------------------------------------------------------------------
%%% Internal functions
%%%-------------------------------------------------------------------
55 changes: 51 additions & 4 deletions src/jose_jwk_kty_okp_ed448.erl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
-behaviour(jose_jwk_kty).
-behaviour(jose_jwk_use_sig).

-include_lib("jose_public_key.hrl").

%% jose_jwk callbacks
-export([from_map/1]).
-export([to_key/1]).
Expand All @@ -29,10 +31,15 @@
-export([verifier/2]).
-export([verify/4]).
%% API
-export([from_key/1]).
-export([from_okp/1]).
-export([from_openssh_key/1]).
-export([from_pem/1]).
-export([from_pem/2]).
-export([to_okp/1]).
-export([to_openssh_key/2]).
-export([to_pem/1]).
-export([to_pem/2]).

%% Macros
-define(crv, <<"Ed448">>).
Expand Down Expand Up @@ -60,10 +67,13 @@ from_map(F = #{ <<"kty">> := <<"OKP">>, <<"crv">> := ?crv, <<"x">> := X }) ->
<< PK:?publickeybytes/binary >> = base64url:decode(X),
{PK, maps:without([<<"crv">>, <<"kty">>, <<"x">>], F)}.

to_key(PK = << _:?publickeybytes/binary >>) ->
PK;
to_key(SK = << _:?secretkeybytes/binary >>) ->
SK.
to_key(<< PublicKey:?publickeybytes/binary >>) ->
#'jose_EdDSA448PublicKey'{ publicKey = PublicKey };
to_key(<< PrivateKey:?secretbytes/binary, PublicKey:?publickeybytes/binary >>) ->
#'jose_EdDSA448PrivateKey'{
publicKey = #'jose_EdDSA448PublicKey'{ publicKey = PublicKey },
privateKey = PrivateKey
}.

to_map(PK = << _:?publickeybytes/binary >>, F) ->
F#{
Expand Down Expand Up @@ -145,6 +155,11 @@ verify(Message, ALG, Signature, PK = << _:?publickeybytes/binary >>)
%% API functions
%%====================================================================

from_key(#'jose_EdDSA448PrivateKey'{publicKey=#'jose_EdDSA448PublicKey'{publicKey=Public}, privateKey=Secret}) ->
{<< Secret/binary, Public/binary >>, #{}};
from_key(#'jose_EdDSA448PublicKey'{publicKey=Public}) ->
{Public, #{}}.

from_okp({'Ed448', SK = << Secret:?secretbytes/binary, PK:?publickeybytes/binary >>}) ->
case jose_curve448:eddsa_secret_to_public(Secret) of
PK ->
Expand All @@ -164,6 +179,22 @@ from_openssh_key({<<"ssh-ed448">>, _PK, SK, Comment}) ->
{KTY, maps:merge(#{ <<"kid">> => Comment }, OtherFields)}
end.

from_pem(PEMBinary) when is_binary(PEMBinary) ->
case jose_jwk_pem:from_binary(PEMBinary) of
{?MODULE, {Key, Fields}} ->
{Key, Fields};
PEMError ->
PEMError
end.

from_pem(Password, PEMBinary) when is_binary(PEMBinary) ->
case jose_jwk_pem:from_binary(Password, PEMBinary) of
{?MODULE, {Key, Fields}} ->
{Key, Fields};
PEMError ->
PEMError
end.

to_okp(SK = << _:?secretkeybytes/binary >>) ->
{'Ed448', SK};
to_okp(PK = << _:?publickeybytes/binary >>) ->
Expand All @@ -173,6 +204,22 @@ to_openssh_key(SK = << _:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -
Comment = maps:get(<<"kid">>, F, <<>>),
jose_jwk_openssh_key:to_binary([[{{<<"ssh-ed448">>, PK}, {<<"ssh-ed448">>, PK, SK, Comment}}]]).

to_pem(SK = << _:?secretkeybytes/binary >>) ->
EdDSA448PrivateKey = to_key(SK),
PEMEntry = jose_public_key:pem_entry_encode('EdDSA448PrivateKey', EdDSA448PrivateKey),
jose_public_key:pem_encode([PEMEntry]);
to_pem(PK = << _:?publickeybytes/binary >>) ->
EdDSA448PublicKey = to_key(PK),
PEMEntry = jose_public_key:pem_entry_encode('EdDSA448PublicKey', EdDSA448PublicKey),
jose_public_key:pem_encode([PEMEntry]).

to_pem(Password, SK = << _:?secretkeybytes/binary >>) ->
EdDSA448PrivateKey = to_key(SK),
jose_jwk_pem:to_binary(Password, 'EdDSA448PrivateKey', EdDSA448PrivateKey);
to_pem(Password, PK = << _:?publickeybytes/binary >>) ->
EdDSA448PublicKey = to_key(PK),
jose_jwk_pem:to_binary(Password, 'EdDSA448PublicKey', EdDSA448PublicKey).

%%%-------------------------------------------------------------------
%%% Internal functions
%%%-------------------------------------------------------------------
55 changes: 51 additions & 4 deletions src/jose_jwk_kty_okp_x25519.erl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
-behaviour(jose_jwk_kty).
-behaviour(jose_jwk_use_enc).

-include_lib("jose_public_key.hrl").

%% jose_jwk callbacks
-export([from_map/1]).
-export([to_key/1]).
Expand All @@ -27,10 +29,15 @@
-export([block_encryptor/2]).
-export([derive_key/2]).
%% API
-export([from_key/1]).
-export([from_okp/1]).
-export([from_openssh_key/1]).
-export([from_pem/1]).
-export([from_pem/2]).
-export([to_okp/1]).
-export([to_openssh_key/2]).
-export([to_pem/1]).
-export([to_pem/2]).

%% Macros
-define(crv, <<"X25519">>).
Expand Down Expand Up @@ -58,10 +65,13 @@ from_map(F = #{ <<"kty">> := <<"OKP">>, <<"crv">> := ?crv, <<"x">> := X }) ->
<< PK:?publickeybytes/binary >> = base64url:decode(X),
{PK, maps:without([<<"crv">>, <<"kty">>, <<"x">>], F)}.

to_key(PK = << _:?publickeybytes/binary >>) ->
PK;
to_key(SK = << _:?secretkeybytes/binary >>) ->
SK.
to_key(<< PublicKey:?publickeybytes/binary >>) ->
#'jose_X25519PublicKey'{ publicKey = PublicKey };
to_key(<< PrivateKey:?secretbytes/binary, PublicKey:?publickeybytes/binary >>) ->
#'jose_X25519PrivateKey'{
publicKey = #'jose_X25519PublicKey'{ publicKey = PublicKey },
privateKey = PrivateKey
}.

to_map(PK = << _:?publickeybytes/binary >>, F) ->
F#{
Expand Down Expand Up @@ -145,6 +155,11 @@ derive_key(PK = << _:?publickeybytes/binary >>, << Secret:?secretbytes/binary, _
%% API functions
%%====================================================================

from_key(#'jose_X25519PrivateKey'{publicKey=#'jose_X25519PublicKey'{publicKey=Public}, privateKey=Secret}) ->
{<< Secret/binary, Public/binary >>, #{}};
from_key(#'jose_X25519PublicKey'{publicKey=Public}) ->
{Public, #{}}.

from_okp({'X25519', SK = << Secret:?secretbytes/binary, PK:?publickeybytes/binary >>}) ->
case jose_curve25519:x25519_secret_to_public(Secret) of
PK ->
Expand All @@ -164,6 +179,22 @@ from_openssh_key({<<"ssh-x25519">>, _PK, SK, Comment}) ->
{KTY, maps:merge(#{ <<"kid">> => Comment }, OtherFields)}
end.

from_pem(PEMBinary) when is_binary(PEMBinary) ->
case jose_jwk_pem:from_binary(PEMBinary) of
{?MODULE, {Key, Fields}} ->
{Key, Fields};
PEMError ->
PEMError
end.

from_pem(Password, PEMBinary) when is_binary(PEMBinary) ->
case jose_jwk_pem:from_binary(Password, PEMBinary) of
{?MODULE, {Key, Fields}} ->
{Key, Fields};
PEMError ->
PEMError
end.

to_okp(SK = << _:?secretkeybytes/binary >>) ->
{'X25519', SK};
to_okp(PK = << _:?publickeybytes/binary >>) ->
Expand All @@ -173,6 +204,22 @@ to_openssh_key(SK = << _:?secretbytes/binary, PK:?publickeybytes/binary >>, F) -
Comment = maps:get(<<"kid">>, F, <<>>),
jose_jwk_openssh_key:to_binary([[{{<<"ssh-x25519">>, PK}, {<<"ssh-x25519">>, PK, SK, Comment}}]]).

to_pem(SK = << _:?secretkeybytes/binary >>) ->
X25519PrivateKey = to_key(SK),
PEMEntry = jose_public_key:pem_entry_encode('X25519PrivateKey', X25519PrivateKey),
jose_public_key:pem_encode([PEMEntry]);
to_pem(PK = << _:?publickeybytes/binary >>) ->
X25519PublicKey = to_key(PK),
PEMEntry = jose_public_key:pem_entry_encode('X25519PublicKey', X25519PublicKey),
jose_public_key:pem_encode([PEMEntry]).

to_pem(Password, SK = << _:?secretkeybytes/binary >>) ->
X25519PrivateKey = to_key(SK),
jose_jwk_pem:to_binary(Password, 'X25519PrivateKey', X25519PrivateKey);
to_pem(Password, PK = << _:?publickeybytes/binary >>) ->
X25519PublicKey = to_key(PK),
jose_jwk_pem:to_binary(Password, 'X25519PublicKey', X25519PublicKey).

%%%-------------------------------------------------------------------
%%% Internal functions
%%%-------------------------------------------------------------------
Loading

0 comments on commit feeba51

Please sign in to comment.