diff options
author | Magnus Ahltorp <map@kth.se> | 2014-11-15 00:22:05 +0100 |
---|---|---|
committer | Magnus Ahltorp <map@kth.se> | 2014-11-19 05:03:19 +0100 |
commit | eabc9b4bdca8409601400276018eb9eec6a162d0 (patch) | |
tree | 4a14a9b1b5ac8dece1ead56861576b424ce59639 /src/sign.erl | |
parent | c224989af3216b92c668c2f979b83551d49760cc (diff) |
Move signing code to sign module
Diffstat (limited to 'src/sign.erl')
-rw-r--r-- | src/sign.erl | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/src/sign.erl b/src/sign.erl new file mode 100644 index 0000000..1239023 --- /dev/null +++ b/src/sign.erl @@ -0,0 +1,142 @@ +%%% Copyright (c) 2014, NORDUnet A/S. +%%% See LICENSE for licensing information. +%%% +%%% @doc Signing service + +-module(sign). +-behaviour(gen_server). + +%% API. +-export([start_link/2, stop/0]). +-export([sign/1, get_pubkey/0, get_logid/0]). +%% API for tests. +-export([read_keyfile_rsa/2, read_keyfiles_ec/2]). +%% gen_server callbacks. +-export([init/1, handle_call/3, terminate/2, + handle_cast/2, handle_info/2, code_change/3]). + +-import(stacktrace, [call/2]). + +-include_lib("public_key/include/public_key.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +-record(state, {pubkey :: public_key:rsa_public_key(), + privkey :: public_key:rsa_private_key(), + logid :: binary() + }). + +start_link(Keyfile, Passphrase) -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [Keyfile, Passphrase], []). + +stop() -> + call(?MODULE, stop). + + +init([PrivKeyfile, PubKeyfile]) -> + %% Read RSA keypair. + %% {Private_key, Public_key} = read_keyfile_rsa(Keyfile, Passphrase), + %% LogID = crypto:hash(sha256, + %% public_key:der_encode('RSAPublicKey', Public_key)), + %% Read EC keypair. + {Private_key, Public_key, LogID} = read_keyfiles_ec(PrivKeyfile, PubKeyfile), + _Tree = ht:reset_tree([db:size() - 1]), + {ok, #state{pubkey = Public_key, + privkey = Private_key, + logid = LogID}}. + +%% TODO: Merge the keyfile reading functions. +%% @doc Read one password protected PEM file with an RSA keypair. +read_keyfile_rsa(Filename, Passphrase) -> + {ok, PemBin} = file:read_file(Filename), + [KeyPem] = public_key:pem_decode(PemBin), % Use first entry. + Privatekey = decode_key(KeyPem, Passphrase), + {Privatekey, public_key(Privatekey)}. + +%% @doc Read two PEM files, one with a private EC key and one with the +%% corresponding public EC key. +read_keyfiles_ec(PrivkeyFile, Pubkeyfile) -> + {ok, PemBinPriv} = file:read_file(PrivkeyFile), + [OTPPubParamsPem, PrivkeyPem] = public_key:pem_decode(PemBinPriv), + Privatekey = decode_key(PrivkeyPem), + + {_, ParamsBin, ParamsEnc} = OTPPubParamsPem, + PubParamsPem = {'EcpkParameters', ParamsBin, ParamsEnc}, + Params = public_key:pem_entry_decode(PubParamsPem), + + {ok, PemBinPub} = file:read_file(Pubkeyfile), + [SPKIPem] = public_key:pem_decode(PemBinPub), + %% SPKI is missing #'AlgorithmIdentifier' so pem_entry_decode won't do. + %% Publickey = public_key:pem_entry_decode(SPKIPem), + #'SubjectPublicKeyInfo'{algorithm = AlgoDer} = SPKIPem, + SPKI = public_key:der_decode('SubjectPublicKeyInfo', AlgoDer), + #'SubjectPublicKeyInfo'{subjectPublicKey = {_, Octets}} = SPKI, + Point = #'ECPoint'{point = Octets}, + Publickey = {Point, Params}, + + KeyID = crypto:hash(sha256, AlgoDer), + + {Privatekey, Publickey, KeyID}. + +%% -spec signhash_rsa(iolist() | binary(), public_key:rsa_private_key()) -> binary(). +%% signhash_rsa(Data, PrivKey) -> +%% %% Was going to just crypto:sign/3 the hash but looking at +%% %% digitally_signed() in lib/ssl/src/ssl_handshake.erl it seems +%% %% like we should rather use (undocumented) encrypt_private/3. +%% %public_key:sign(hash(sha256, BinToSign), sha256, PrivKey) +%% public_key:encrypt_private(crypto:hash(sha256, Data), +%% PrivKey, +%% [{rsa_pad, rsa_pkcs1_padding}]). + +-spec signhash_ec(iolist() | binary(), public_key:ec_private_key()) -> binary(). +signhash_ec(Data, PrivKey) -> + public_key:sign(Data, sha256, PrivKey). + +decode_key(Entry) -> + public_key:pem_entry_decode(Entry). +decode_key(Entry, Passphrase) -> + public_key:pem_entry_decode(Entry, Passphrase). + +public_key(#'RSAPrivateKey'{modulus = Mod, publicExponent = Exp}) -> + #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}. + + +%%%%%%%%%%%%%%%%%%%% +%% Public API. + +sign(Data) -> + call(?MODULE, {sign, Data}). + +get_pubkey() -> + call(?MODULE, {get, pubkey}). + +get_logid() -> + call(?MODULE, {get, logid}). + +%%%%%%%%%%%%%%%%%%%% +%% gen_server callbacks. + +handle_cast(_Request, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +terminate(_Reason, _State) -> + io:format("~p terminating~n", [?MODULE]), + ok. + +handle_call(stop, _From, State) -> + {stop, normal, stopped, State}; + +handle_call({get, logid}, _From, State) -> + {reply, State#state.logid, State}; + +handle_call({get, pubkey}, _From, State) -> + {reply, State#state.pubkey, State}; + +handle_call({sign, Data}, _From, State) -> + %% FIXME: Merge RSA and DC. + {reply, signhash_ec(Data, State#state.privkey), State}. |