22-export ([sign /2 , sign /3 , hmac /1 , hmac /2 , verify /3 , verify /4 ]).
33-export ([to_pubkey /1 , to_pubkey /2 , to_address /1 , to_address /2 , new /0 , new /1 ]).
44-export ([new_keyfile /2 , load_keyfile /1 , load_keyfile /2 , load_key /1 , load_key /2 ]).
5+ -export ([to_json /1 , from_json /1 , from_json /2 ]).
56-include (" include/ar.hrl" ).
67-include_lib (" public_key/include/public_key.hrl" ).
78
@@ -18,6 +19,7 @@ new(KeyType = {KeyAlg, PublicExpnt}) when KeyType =:= {rsa, 65537} ->
1819 = crypto :generate_key (KeyAlg , {4096 , PublicExpnt }),
1920 {{KeyType , Priv , Pub }, {KeyType , Pub }}.
2021
22+
2123% % @doc Sign some data with a private key.
2224sign (Key , Data ) ->
2325 sign (Key , Data , sha256 ).
@@ -91,50 +93,19 @@ new_keyfile(KeyType, WalletName) ->
9193 {? RSA_SIGN_ALG , PublicExpnt } ->
9294 {[Expnt , Pb ], [Expnt , Pb , Prv , P1 , P2 , E1 , E2 , C ]} =
9395 crypto :generate_key (rsa , {? RSA_PRIV_KEY_SZ , PublicExpnt }),
94- Ky =
95- hb_json :encode (
96- #{
97- kty => <<" RSA" >>,
98- ext => true ,
99- e => hb_util :encode (Expnt ),
100- n => hb_util :encode (Pb ),
101- d => hb_util :encode (Prv ),
102- p => hb_util :encode (P1 ),
103- q => hb_util :encode (P2 ),
104- dp => hb_util :encode (E1 ),
105- dq => hb_util :encode (E2 ),
106- qi => hb_util :encode (C )
107- }
108- ),
96+ PrivKey = {KeyType , Prv , Pb },
97+ Ky = to_json (PrivKey ),
10998 {Pb , Prv , Ky };
11099 {? ECDSA_SIGN_ALG , secp256k1 } ->
111100 {OrigPub , Prv } = crypto :generate_key (ecdh , secp256k1 ),
112- <<4 :8 , PubPoint /binary >> = OrigPub ,
113- PubPointMid = byte_size (PubPoint ) div 2 ,
114- <<X :PubPointMid /binary , Y :PubPointMid /binary >> = PubPoint ,
115- Ky =
116- hb_json :encode (
117- #{
118- kty => <<" EC" >>,
119- crv => <<" secp256k1" >>,
120- x => hb_util :encode (X ),
121- y => hb_util :encode (Y ),
122- d => hb_util :encode (Prv )
123- }
124- ),
125- {compress_ecdsa_pubkey (OrigPub ), Prv , Ky };
101+ CompressedPub = compress_ecdsa_pubkey (OrigPub ),
102+ PrivKey = {KeyType , Prv , CompressedPub },
103+ Ky = to_json (PrivKey ),
104+ {CompressedPub , Prv , Ky };
126105 {? EDDSA_SIGN_ALG , ed25519 } ->
127106 {{_ , Prv , Pb }, _ } = new (KeyType ),
128- Ky =
129- hb_json :encode (
130- #{
131- kty => <<" OKP" >>,
132- alg => <<" EdDSA" >>,
133- crv => <<" Ed25519" >>,
134- x => hb_util :encode (Pb ),
135- d => hb_util :encode (Prv )
136- }
137- ),
107+ PrivKey = {KeyType , Prv , Pb },
108+ Ky = to_json (PrivKey ),
138109 {Pb , Prv , Ky }
139110 end ,
140111 Filename = wallet_filepath (WalletName , Pub , KeyType ),
@@ -179,7 +150,44 @@ load_keyfile(File) ->
179150% % @doc Extract the public and private key from a keyfile.
180151load_keyfile (File , Opts ) ->
181152 {ok , Body } = file :read_file (File ),
182- Key = hb_json :decode (Body ),
153+ from_json (Body , Opts ).
154+
155+ % % @doc Convert a wallet private key to JSON (JWK) format
156+ to_json ({PrivKey , _PubKey }) ->
157+ to_json (PrivKey );
158+ to_json ({{? RSA_SIGN_ALG , PublicExpnt }, Priv , Pub }) when PublicExpnt =:= 65537 ->
159+ hb_json :encode (#{
160+ kty => <<" RSA" >>,
161+ ext => true ,
162+ e => hb_util :encode (<<PublicExpnt :32 >>),
163+ n => hb_util :encode (Pub ),
164+ d => hb_util :encode (Priv )
165+ });
166+ to_json ({{? ECDSA_SIGN_ALG , secp256k1 }, Priv , CompressedPub }) ->
167+ % For ECDSA, we need to expand the compressed pubkey to get X,Y coordinates
168+ % This is a simplified version - ideally we'd implement pubkey expansion
169+ hb_json :encode (#{
170+ kty => <<" EC" >>,
171+ crv => <<" secp256k1" >>,
172+ d => hb_util :encode (Priv )
173+ % TODO: Add x and y coordinates from expanded pubkey
174+ });
175+ to_json ({{? EDDSA_SIGN_ALG , ed25519 }, Priv , Pub }) ->
176+ hb_json :encode (#{
177+ kty => <<" OKP" >>,
178+ alg => <<" EdDSA" >>,
179+ crv => <<" Ed25519" >>,
180+ x => hb_util :encode (Pub ),
181+ d => hb_util :encode (Priv )
182+ }).
183+
184+ % % @doc Parse a wallet from JSON (JWK) format
185+ from_json (JsonBinary ) ->
186+ from_json (JsonBinary , #{}).
187+
188+ % % @doc Parse a wallet from JSON (JWK) format with options
189+ from_json (JsonBinary , Opts ) ->
190+ Key = hb_json :decode (JsonBinary ),
183191 {Pub , Priv , KeyType } =
184192 case hb_maps :get (<<" kty" >>, Key , undefined , Opts ) of
185193 <<" EC" >> ->
0 commit comments