blob: f75528185398a4b07e1b52274e3a7a141ff88b7e [file] [log] [blame]
giolekva313ee2b2021-12-15 15:17:29 +04001package main
2
3import (
4 "bytes"
giolekva3f0dcda2021-12-22 23:32:49 +04005 "crypto/aes"
6 "crypto/cipher"
giolekvaf58a7692021-12-15 18:05:39 +04007 "crypto/rand"
giolekva3f0dcda2021-12-22 23:32:49 +04008 "crypto/rsa"
9 "crypto/sha256"
giolekva313ee2b2021-12-15 15:17:29 +040010 "crypto/tls"
giolekvacc3ebcb2021-12-17 10:52:17 +040011 "encoding/base64"
giolekva313ee2b2021-12-15 15:17:29 +040012 "encoding/json"
giolekva3f0dcda2021-12-22 23:32:49 +040013 "fmt"
giolekvaf58a7692021-12-15 18:05:39 +040014 "io"
giolekva313ee2b2021-12-15 15:17:29 +040015 "net/http"
giolekvaf58a7692021-12-15 18:05:39 +040016
giolekvacc3ebcb2021-12-17 10:52:17 +040017 "github.com/slackhq/nebula/cert"
giolekvaf58a7692021-12-15 18:05:39 +040018 "golang.org/x/crypto/curve25519"
giolekvacc3ebcb2021-12-17 10:52:17 +040019 "sigs.k8s.io/yaml"
giolekva313ee2b2021-12-15 15:17:29 +040020)
21
22type VPNClient interface {
giolekva8d6a0ca2021-12-19 17:42:25 +040023 Sign(apiAddr string, message []byte) ([]byte, error)
giolekva3f0dcda2021-12-22 23:32:49 +040024 Join(apiAddr, hostname string, publicKey, privateKey []byte, message, signature []byte) ([]byte, error)
25 Approve(apiAddr, hostname, ipCidr string, encPublicKey, netPublicKey []byte) error
26 Get(apiAddr, hostname string, encPrivateKey *rsa.PrivateKey, netPrivateKey []byte) ([]byte, error)
giolekva313ee2b2021-12-15 15:17:29 +040027}
28
29type directVPNClient struct {
30 addr string
31}
32
33func NewDirectVPNClient(addr string) VPNClient {
34 return &directVPNClient{addr}
35}
36
giolekva313ee2b2021-12-15 15:17:29 +040037type signReq struct {
38 Message []byte `json:"message"`
39}
40
41type signResp struct {
42 Signature []byte `json:"signature"`
43}
44
giolekva8d6a0ca2021-12-19 17:42:25 +040045func (c *directVPNClient) Sign(apiAddr string, message []byte) ([]byte, error) {
giolekva313ee2b2021-12-15 15:17:29 +040046 var data bytes.Buffer
47 if err := json.NewEncoder(&data).Encode(signReq{message}); err != nil {
48 return nil, err
49 }
50 client := &http.Client{
51 // TODO(giolekva): remove, for some reason valid certificates are not accepted on gioui android.
52 Transport: &http.Transport{
53 TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
54 },
55 }
giolekva8d6a0ca2021-12-19 17:42:25 +040056 r, err := client.Post(apiAddr+"/api/sign", "application/json", &data)
giolekva313ee2b2021-12-15 15:17:29 +040057 if err != nil {
giolekva313ee2b2021-12-15 15:17:29 +040058 return nil, err
59 }
60 resp := &signResp{}
61 if err := json.NewDecoder(r.Body).Decode(resp); err != nil {
62 return nil, err
63 }
64 return resp.Signature, nil
65}
giolekvaf58a7692021-12-15 18:05:39 +040066
67type joinReq struct {
68 Message []byte `json:"message"`
69 Signature []byte `json:"signature"`
70 Name string `json:"name"`
71 PublicKey []byte `json:"public_key"`
72 IPCidr string `json:"ip_cidr"`
73}
74
75type joinResp struct {
giolekvacc3ebcb2021-12-17 10:52:17 +040076 cfgYamlB64 string
giolekvaf58a7692021-12-15 18:05:39 +040077}
78
giolekva3f0dcda2021-12-22 23:32:49 +040079func (c *directVPNClient) Join(apiAddr, hostname string, publicKey, privateKey []byte, message, signature []byte) ([]byte, error) {
giolekvaf58a7692021-12-15 18:05:39 +040080 req := joinReq{
81 message,
82 signature,
giolekva1026d2d2021-12-19 19:09:15 +040083 hostname,
giolekva3f0dcda2021-12-22 23:32:49 +040084 cert.MarshalX25519PublicKey(publicKey),
giolekvaf58a7692021-12-15 18:05:39 +040085 "111.0.0.13/24",
86 }
87 var data bytes.Buffer
88 if err := json.NewEncoder(&data).Encode(req); err != nil {
89 return nil, err
90 }
91 client := &http.Client{
92 // TODO(giolekva): remove, for some reason valid certificates are not accepted on gioui android.
93 Transport: &http.Transport{
94 TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
95 },
96 }
giolekvacc3ebcb2021-12-17 10:52:17 +040097 r, err := client.Post(apiAddr+"/api/join", "application/json", &data)
giolekvaf58a7692021-12-15 18:05:39 +040098 if err != nil {
99 return nil, err
100 }
giolekvacc3ebcb2021-12-17 10:52:17 +0400101 var cfgYamlB bytes.Buffer
102 _, err = io.Copy(&cfgYamlB,
103 base64.NewDecoder(base64.StdEncoding, r.Body))
104 if err != nil {
giolekvaf58a7692021-12-15 18:05:39 +0400105 return nil, err
106 }
giolekvacc3ebcb2021-12-17 10:52:17 +0400107 cfgYaml := cfgYamlB.Bytes()
giolekvacc3ebcb2021-12-17 10:52:17 +0400108 var cfgMap map[string]interface{}
109 if err := yaml.Unmarshal(cfgYaml, &cfgMap); err != nil {
110 return nil, err
111 }
112 var pki map[string]interface{}
113 var ok bool
114 if pki, ok = cfgMap["pki"].(map[string]interface{}); !ok {
115 panic("Must not reach")
116 }
giolekva3f0dcda2021-12-22 23:32:49 +0400117 pki["key"] = string(cert.MarshalX25519PrivateKey(privateKey))
118 return yaml.Marshal(cfgMap)
119}
120
121type approveReq struct {
122 EncPublicKey []byte `json:"enc_public_key"`
123 Name string `json:"name"`
124 NetPublicKey []byte `json:"net_public_key"`
125 IPCidr string `json:"ip_cidr"`
126}
127
128func (c *directVPNClient) Approve(apiAddr, hostname, ipCidr string, encPublicKey, netPublicKey []byte) error {
129 req := approveReq{
130 encPublicKey,
131 hostname,
132 cert.MarshalX25519PublicKey(netPublicKey),
133 ipCidr,
134 }
135 var data bytes.Buffer
136 if err := json.NewEncoder(&data).Encode(req); err != nil {
137 return err
138 }
139 client := &http.Client{
140 // TODO(giolekva): remove, for some reason valid certificates are not accepted on gioui android.
141 Transport: &http.Transport{
142 TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
143 },
144 }
145 _, err := client.Post(apiAddr+"/api/approve", "application/json", &data)
146 return err
147}
148
149type getResp struct {
150 Key []byte `json:"key"`
151 Nonce []byte `json:"nonce"`
152 Data []byte `json:"data"`
153}
154
155func (c *directVPNClient) Get(apiAddr, hostname string, encPrivateKey *rsa.PrivateKey, netPrivateKey []byte) ([]byte, error) {
156 client := &http.Client{
157 // TODO(giolekva): remove, for some reason valid certificates are not accepted on gioui android.
158 Transport: &http.Transport{
159 TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
160 },
161 }
162 r, err := client.Get(apiAddr + "/api/get/" + hostname)
163 if err != nil {
164 fmt.Println(err.Error())
165 return nil, err
166 }
167 var resp getResp
168 if err := json.NewDecoder(r.Body).Decode(&resp); err != nil {
169 fmt.Println(err.Error())
170 return nil, err
171 }
172 // TODO(giolekva): encrypt key and nonce together
173 key, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, encPrivateKey, resp.Key, []byte(""))
174 if err != nil {
175 fmt.Println(err.Error())
176 return nil, err
177 }
178 nonce, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, encPrivateKey, resp.Nonce, []byte(""))
179 if err != nil {
180 fmt.Println(1123123)
181 fmt.Println(err.Error())
182 return nil, err
183 }
184 block, err := aes.NewCipher(key)
185 if err != nil {
186 fmt.Println(err.Error())
187 return nil, err
188 }
189 aesgcm, err := cipher.NewGCM(block)
190 if err != nil {
191 fmt.Println(err.Error())
192 return nil, err
193 }
194
195 cfgYaml, err := aesgcm.Open(nil, nonce, resp.Data, nil)
196 if err != nil {
197 fmt.Println(22222)
198 fmt.Println(err.Error())
199 return nil, err
200 }
201 var cfgMap map[string]interface{}
202 if err := yaml.Unmarshal(cfgYaml, &cfgMap); err != nil {
203 fmt.Println(err.Error())
204 return nil, err
205 }
206 var pki map[string]interface{}
207 var ok bool
208 if pki, ok = cfgMap["pki"].(map[string]interface{}); !ok {
209 panic("Must not reach")
210 }
211 pki["key"] = string(cert.MarshalX25519PrivateKey(netPrivateKey))
giolekvacc3ebcb2021-12-17 10:52:17 +0400212 return yaml.Marshal(cfgMap)
giolekvaf58a7692021-12-15 18:05:39 +0400213}
214
215func x25519Keypair() ([]byte, []byte, error) {
216 var pubkey, privkey [32]byte
217 if _, err := io.ReadFull(rand.Reader, privkey[:]); err != nil {
218 return nil, nil, err
219 }
220 curve25519.ScalarBaseMult(&pubkey, &privkey)
221 return pubkey[:], privkey[:], nil
222}