Skip to content

Commit 8c16134

Browse files
authored
Merge pull request #42 from ellemouton/passKeysAsParamsInsteadOfCMDOptions
wasm: pass keys in as parameters to ConnectServer
2 parents ebf947d + 71cddee commit 8c16134

File tree

3 files changed

+126
-109
lines changed

3 files changed

+126
-109
lines changed

cmd/wasm-client/config.go

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,12 @@
33

44
package main
55

6-
import (
7-
"errors"
8-
"fmt"
9-
)
10-
116
type config struct {
127
DebugLevel string `long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify <subsystem>=<level>,<subsystem2>=<level>,... to set the log level for individual subsystems -- Use show to list available subsystems"`
138
NameSpace string `long:"namespace" description:"The name of the JS namespace in which all the call back functions should be registered"`
149

15-
LocalPrivate string `long:"localprivate" description:"The static local private key to be used for creating the noise connection"`
16-
RemotePublic string `long:"remotepublic" description:"The static remote public key to be used for creating the noise connection"`
17-
1810
OnLocalPrivCreate string `long:"onlocalprivcreate" description:"The name of the js callback function that should be called once the local private key has been generated. This function is expected to persist the key so that it can be provided via a command line flag on a future connection"`
1911
OnRemoteKeyReceive string `long:"onremotekeyreceive" description:"The name of the js callback function to be called once the client receives the remote static key. This function is expected to persist the key so that it can be provided via a command line flag on a future connection"`
2012

2113
OnAuthData string `long:"onauthdata" description:"The name of the js callback function to be called once the client receives auth data from the remote peer."`
2214
}
23-
24-
func (c *config) validate() error {
25-
if c.NameSpace == "" {
26-
return fmt.Errorf("a non-empty namespace is required")
27-
}
28-
29-
if c.RemotePublic != "" && c.LocalPrivate == "" {
30-
return errors.New("cannot set remote pub key if local priv " +
31-
"key is not also set")
32-
}
33-
34-
if c.LocalPrivate == "" && c.OnLocalPrivCreate == "" {
35-
return errors.New("OnLocalPrivCreate must be defined if a " +
36-
"local key is not provided")
37-
}
38-
39-
if c.RemotePublic == "" && c.OnRemoteKeyReceive == "" {
40-
return errors.New("OnRemoteKeyReceive must be defined if a " +
41-
"remote key is not provided")
42-
}
43-
44-
return nil
45-
}

cmd/wasm-client/main.go

Lines changed: 113 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"context"
88
"crypto/tls"
99
"encoding/hex"
10+
"errors"
1011
"fmt"
1112
"net/http"
1213
"os"
@@ -126,72 +127,18 @@ type wasmClient struct {
126127

127128
statusChecker func() mailbox.ConnStatus
128129

129-
localStaticKey keychain.SingleKeyECDH
130-
remoteStaticKey *btcec.PublicKey
131-
132130
registry map[string]func(context.Context, *grpc.ClientConn,
133131
string, func(string, error))
134132
}
135133

136134
func newWasmClient(cfg *config) (*wasmClient, error) {
137-
if err := cfg.validate(); err != nil {
138-
return nil, err
139-
}
140-
141-
var (
142-
localStaticKey keychain.SingleKeyECDH
143-
remoteStaticKey *btcec.PublicKey
144-
)
145-
switch {
146-
case cfg.LocalPrivate == "" && cfg.RemotePublic == "":
147-
privKey, err := btcec.NewPrivateKey()
148-
if err != nil {
149-
return nil, err
150-
}
151-
localStaticKey = &keychain.PrivKeyECDH{PrivKey: privKey}
152-
153-
err = callJsCallback(
154-
cfg.OnLocalPrivCreate,
155-
hex.EncodeToString(privKey.Serialize()),
156-
)
157-
if err != nil {
158-
return nil, err
159-
}
160-
161-
case cfg.RemotePublic == "":
162-
privKeyByte, err := hex.DecodeString(cfg.LocalPrivate)
163-
if err != nil {
164-
return nil, err
165-
}
166-
167-
privKey, _ := btcec.PrivKeyFromBytes(privKeyByte)
168-
localStaticKey = &keychain.PrivKeyECDH{PrivKey: privKey}
169-
170-
default:
171-
// Both local and remote are set.
172-
localPrivKeyBytes, err := hex.DecodeString(cfg.LocalPrivate)
173-
if err != nil {
174-
return nil, err
175-
}
176-
privKey, _ := btcec.PrivKeyFromBytes(localPrivKeyBytes)
177-
localStaticKey = &keychain.PrivKeyECDH{PrivKey: privKey}
178-
179-
remoteKeyBytes, err := hex.DecodeString(cfg.RemotePublic)
180-
if err != nil {
181-
return nil, err
182-
}
183-
184-
remoteStaticKey, err = btcec.ParsePubKey(remoteKeyBytes)
185-
if err != nil {
186-
return nil, err
187-
}
135+
if cfg.NameSpace == "" {
136+
return nil, fmt.Errorf("a non-empty namespace is required")
188137
}
189138

190139
return &wasmClient{
191-
cfg: cfg,
192-
lndConn: nil,
193-
localStaticKey: localStaticKey,
194-
remoteStaticKey: remoteStaticKey,
140+
cfg: cfg,
141+
lndConn: nil,
195142
registry: make(map[string]func(context.Context,
196143
*grpc.ClientConn, string, func(string, error))),
197144
}, nil
@@ -205,14 +152,32 @@ func (w *wasmClient) IsReady(_ js.Value, _ []js.Value) interface{} {
205152
}
206153

207154
func (w *wasmClient) ConnectServer(_ js.Value, args []js.Value) interface{} {
208-
if len(args) != 3 {
155+
if len(args) != 5 {
209156
return js.ValueOf("invalid use of wasmClientConnectServer, " +
210-
"need 3 parameters: server, isDevServer, pairingPhrase")
157+
"need 5 parameters: server, isDevServer, " +
158+
"pairingPhrase, localStaticPrivKey, remoteStaticPubKey")
211159
}
212160

213161
mailboxServer := args[0].String()
214162
isDevServer := args[1].Bool()
215163
pairingPhrase := args[2].String()
164+
localStatic := args[3].String()
165+
remoteStatic := args[4].String()
166+
167+
// Check that the correct arguments and config combinations have been
168+
// provided.
169+
err := validateArgs(w.cfg, localStatic, remoteStatic)
170+
if err != nil {
171+
exit(err)
172+
}
173+
174+
// Parse the key arguments.
175+
localPriv, remotePub, err := parseKeys(
176+
w.cfg.OnLocalPrivCreate, localStatic, remoteStatic,
177+
)
178+
if err != nil {
179+
exit(err)
180+
}
216181

217182
// Disable TLS verification for the REST connections if this is a dev
218183
// server.
@@ -223,10 +188,9 @@ func (w *wasmClient) ConnectServer(_ js.Value, args []js.Value) interface{} {
223188
}
224189
}
225190

226-
var err error
227191
statusChecker, lndConnect, err := mailboxRPCConnection(
228-
mailboxServer, pairingPhrase, w.localStaticKey,
229-
w.remoteStaticKey, func(key *btcec.PublicKey) error {
192+
mailboxServer, pairingPhrase, localPriv, remotePub,
193+
func(key *btcec.PublicKey) error {
230194
return callJsCallback(
231195
w.cfg.OnRemoteKeyReceive,
232196
hex.EncodeToString(key.SerializeCompressed()),
@@ -308,6 +272,92 @@ func (w *wasmClient) InvokeRPC(_ js.Value, args []js.Value) interface{} {
308272

309273
}
310274

275+
// validateArgs checks that the correct keys and callback functions have been
276+
// provided.
277+
func validateArgs(cfg *config, localPrivKey, remotePubKey string) error {
278+
if remotePubKey != "" && localPrivKey == "" {
279+
return errors.New("cannot set remote pub key if local priv " +
280+
"key is not also set")
281+
}
282+
283+
if localPrivKey == "" && cfg.OnLocalPrivCreate == "" {
284+
return errors.New("OnLocalPrivCreate must be defined if a " +
285+
"local key is not provided")
286+
}
287+
288+
if remotePubKey == "" && cfg.OnRemoteKeyReceive == "" {
289+
return errors.New("OnRemoteKeyReceive must be defined if a " +
290+
"remote key is not provided")
291+
}
292+
293+
return nil
294+
}
295+
296+
// parseKeys parses the given keys from their string format and calls callback
297+
// functions where appropriate. NOTE: This function assumes that the parameter
298+
// combinations have been checked by validateArgs.
299+
func parseKeys(onLocalPrivCreate, localPrivKey, remotePubKey string) (
300+
keychain.SingleKeyECDH, *btcec.PublicKey, error) {
301+
302+
var (
303+
localStaticKey keychain.SingleKeyECDH
304+
remoteStaticKey *btcec.PublicKey
305+
)
306+
switch {
307+
308+
// This is a new session for which a local key has not yet been derived,
309+
// so we generate a new key and call the onLocalPrivCreate callback so
310+
// that this key can be persisted.
311+
case localPrivKey == "" && remotePubKey == "":
312+
privKey, err := btcec.NewPrivateKey()
313+
if err != nil {
314+
return nil, nil, err
315+
}
316+
localStaticKey = &keychain.PrivKeyECDH{PrivKey: privKey}
317+
318+
err = callJsCallback(
319+
onLocalPrivCreate,
320+
hex.EncodeToString(privKey.Serialize()),
321+
)
322+
if err != nil {
323+
return nil, nil, err
324+
}
325+
326+
// A local private key has been provided, so parse it.
327+
case remotePubKey == "":
328+
privKeyByte, err := hex.DecodeString(localPrivKey)
329+
if err != nil {
330+
return nil, nil, err
331+
}
332+
333+
privKey, _ := btcec.PrivKeyFromBytes(privKeyByte)
334+
localStaticKey = &keychain.PrivKeyECDH{PrivKey: privKey}
335+
336+
// Both local private key and remote public key have been provided,
337+
// so parse them both into the appropriate types.
338+
default:
339+
// Both local and remote are set.
340+
localPrivKeyBytes, err := hex.DecodeString(localPrivKey)
341+
if err != nil {
342+
return nil, nil, err
343+
}
344+
privKey, _ := btcec.PrivKeyFromBytes(localPrivKeyBytes)
345+
localStaticKey = &keychain.PrivKeyECDH{PrivKey: privKey}
346+
347+
remoteKeyBytes, err := hex.DecodeString(remotePubKey)
348+
if err != nil {
349+
return nil, nil, err
350+
}
351+
352+
remoteStaticKey, err = btcec.ParsePubKey(remoteKeyBytes)
353+
if err != nil {
354+
return nil, nil, err
355+
}
356+
}
357+
358+
return localStaticKey, remoteStaticKey, nil
359+
}
360+
311361
func callJsCallback(callbackName string, value string) error {
312362
retValue := js.Global().Call(callbackName, value)
313363

example/index.html

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,6 @@
2929
async function startInstance(module, instance) {
3030
namespace = $('#namespace').val();
3131

32-
let localKey = ""
33-
let remoteKey = ""
34-
35-
if (localStorage.getItem(namespace+":localKey")) {
36-
localKey = localStorage.getItem(namespace+":localKey")
37-
}
38-
39-
if (localStorage.getItem(namespace+":remoteKey")) {
40-
remoteKey = localStorage.getItem(namespace+":remoteKey")
41-
}
42-
4332
if (localStorage.getItem(namespace+":mailboxHost")) {
4433
server = localStorage.getItem(namespace+":mailboxHost")
4534
document.getElementById('server').value = server
@@ -50,8 +39,6 @@
5039
'wasm-client',
5140
'--debuglevel=trace',
5241
'--namespace='+namespace,
53-
'--localprivate='+localKey,
54-
'--remotepublic='+remoteKey,
5542
'--onlocalprivcreate=onLocalKeyCreate',
5643
'--onremotekeyreceive=onRemoteKeyReceive',
5744
'--onauthdata=onAuthData',
@@ -70,7 +57,7 @@
7057

7158
// If we have a remote key stored, it means that the handshake we will do to create a connection will not
7259
// involve the passphrase, so we can immediately connect to the server.
73-
if (remoteKey != "") {
60+
if (localStorage.getItem(namespace+":remoteKey")) {
7461
connectServer()
7562
} else {
7663
$('#passphrase').show();
@@ -100,6 +87,17 @@
10087
let server = $('#server').val();
10188
localStorage.setItem(namespace+":mailboxHost", server)
10289

90+
let localKey = ""
91+
let remoteKey = ""
92+
93+
if (localStorage.getItem(namespace+":localKey")) {
94+
localKey = localStorage.getItem(namespace+":localKey")
95+
}
96+
97+
if (localStorage.getItem(namespace+":remoteKey")) {
98+
remoteKey = localStorage.getItem(namespace+":remoteKey")
99+
}
100+
103101
let passphrase = $('#phrase').val();
104102
let connectedTicker = null;
105103
let isConnected = function () {
@@ -113,7 +111,7 @@
113111
}
114112
}
115113
connectedTicker = setInterval(isConnected, 200);
116-
window[namespace].wasmClientConnectServer(server, true, passphrase);
114+
window[namespace].wasmClientConnectServer(server, true, passphrase, localKey, remoteKey);
117115
}
118116

119117
async function clearStorage() {

0 commit comments

Comments
 (0)