185 lines
5.6 KiB
Go
185 lines
5.6 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"crypto/aes"
|
||
|
"crypto/cipher"
|
||
|
"crypto/ecdh"
|
||
|
"crypto/rand"
|
||
|
"encoding/base64"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
"net/url"
|
||
|
"strings"
|
||
|
"syscall/js"
|
||
|
)
|
||
|
|
||
|
func main() {
|
||
|
// Redirect to log-in if not signed in
|
||
|
localStorage := js.Global().Get("localStorage")
|
||
|
if localStorage.Call("getItem", "DONOTSHARE-secretKey").IsNull() {
|
||
|
js.Global().Get("window").Get("location").Call("replace", "/login"+js.Global().Get("window").Get("location").Get("search").String())
|
||
|
}
|
||
|
|
||
|
statusBox := js.Global().Get("document").Call("getElementById", "statusBox")
|
||
|
|
||
|
// Check if the token is valid
|
||
|
requestUri, err := url.JoinPath(js.Global().Get("window").Get("location").Get("origin").String(), "/api/loggedIn")
|
||
|
if err != nil {
|
||
|
statusBox.Set("innerText", "Error joining URL: "+err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
loggedInBody := map[string]interface{}{
|
||
|
"token": localStorage.Call("getItem", "DONOTSHARE-secretKey").String(),
|
||
|
}
|
||
|
|
||
|
// Marshal the body
|
||
|
body, err := json.Marshal(loggedInBody)
|
||
|
if err != nil {
|
||
|
statusBox.Set("innerText", "Error marshaling signup body: "+err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
response, err := http.Post(requestUri, "application/json", bytes.NewReader(body))
|
||
|
if err != nil {
|
||
|
statusBox.Set("innerText", "Error contacting server: "+err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Check if the response is 200
|
||
|
if response.StatusCode == 401 {
|
||
|
// Close the response body
|
||
|
err = response.Body.Close()
|
||
|
if err != nil {
|
||
|
fmt.Println("Could not close response body: " + err.Error() + ", memory leaks may occur")
|
||
|
}
|
||
|
|
||
|
// Redirect to log-out if not signed in
|
||
|
js.Global().Get("window").Get("location").Call("replace", "/logout"+js.Global().Get("window").Get("location").Get("search").String())
|
||
|
return
|
||
|
} else if response.StatusCode == 500 {
|
||
|
// Read the response
|
||
|
var responseMap map[string]interface{}
|
||
|
decoder := json.NewDecoder(response.Body)
|
||
|
err = decoder.Decode(&responseMap)
|
||
|
if err != nil {
|
||
|
js.Global().Call("alert", "Error decoding server response: "+err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Close the response body
|
||
|
err = response.Body.Close()
|
||
|
if err != nil {
|
||
|
fmt.Println("Could not close response body: " + err.Error() + ", memory leaks may occur")
|
||
|
}
|
||
|
|
||
|
// Alert the user if the server is down
|
||
|
js.Global().Call("alert", "Something went wrong! (error code: "+responseMap["code"].(string)+")")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Close the response body
|
||
|
err = response.Body.Close()
|
||
|
if err != nil {
|
||
|
fmt.Println("Could not close response body: " + err.Error() + ", memory leaks may occur")
|
||
|
}
|
||
|
|
||
|
query, err := url.ParseQuery(strings.TrimPrefix(js.Global().Get("window").Get("location").Get("search").String(), "?"))
|
||
|
if err != nil {
|
||
|
statusBox.Set("innerText", "Error parsing query: "+err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Check if the access token we were given is valid and that the scope is correct
|
||
|
requestUri, err = url.JoinPath(js.Global().Get("window").Get("location").Get("origin").String(), "/api/oauth/clientKeyShare")
|
||
|
if err != nil {
|
||
|
statusBox.Set("innerText", "Error joining URL: "+err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
request, err := http.NewRequest("GET", requestUri, nil)
|
||
|
if err != nil {
|
||
|
statusBox.Set("innerText", "Error creating request: "+err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
request.Header.Set("Authorization", "Bearer "+query.Get("accessToken"))
|
||
|
|
||
|
response, err = http.DefaultClient.Do(request)
|
||
|
if err != nil {
|
||
|
statusBox.Set("innerText", "Error contacting server: "+err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Close the response body
|
||
|
err = response.Body.Close()
|
||
|
if err != nil {
|
||
|
fmt.Println("Could not close response body: " + err.Error() + ", memory leaks may occur")
|
||
|
}
|
||
|
|
||
|
if response.StatusCode == 200 {
|
||
|
// Get the ECDH public key from the query string
|
||
|
clientKeyBytes, err := base64.URLEncoding.DecodeString(query.Get("ecdhPublicKey"))
|
||
|
if err != nil {
|
||
|
statusBox.Set("innerText", "Error decoding ECDH public key: "+err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Encode the ECDH public key
|
||
|
key, err := ecdh.X25519().NewPublicKey(clientKeyBytes)
|
||
|
if err != nil {
|
||
|
statusBox.Set("innerText", "Error encoding ECDH public key: "+err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Generate a new ECDH key pair
|
||
|
privateKey, err := ecdh.X25519().GenerateKey(rand.Reader)
|
||
|
if err != nil {
|
||
|
statusBox.Set("innerText", "Error generating ECDH key pair: "+err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Generate the shared secret
|
||
|
sharedSecret, err := privateKey.ECDH(key)
|
||
|
if err != nil {
|
||
|
statusBox.Set("innerText", "Error generating shared secret: "+err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// AES-GCM encrypt the DONOTSHARE-clientKey
|
||
|
block, err := aes.NewCipher(sharedSecret)
|
||
|
if err != nil {
|
||
|
statusBox.Set("innerText", "Error creating AES cipher: "+err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
gcm, err := cipher.NewGCM(block)
|
||
|
if err != nil {
|
||
|
statusBox.Set("innerText", "Error creating GCM cipher: "+err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
nonce := make([]byte, gcm.NonceSize())
|
||
|
_, err = rand.Read(nonce)
|
||
|
if err != nil {
|
||
|
statusBox.Set("innerText", "Error generating nonce: "+err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Un-base64 the client key
|
||
|
decodedClientKey, err := base64.StdEncoding.DecodeString(localStorage.Call("getItem", "DONOTSHARE-clientKey").String())
|
||
|
if err != nil {
|
||
|
statusBox.Set("innerText", "Error decoding client key: "+err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
encryptedClientKey := gcm.Seal(nil, nonce, decodedClientKey, nil)
|
||
|
|
||
|
// Redirect back to the referrer with the encrypted client key
|
||
|
redirectUri := strings.Split(js.Global().Get("document").Get("referrer").String(), "?")[0]
|
||
|
js.Global().Get("window").Get("location").Call("replace", redirectUri+"?ecdhPublicKey="+base64.URLEncoding.EncodeToString(privateKey.PublicKey().Bytes())+"&nonce="+base64.URLEncoding.EncodeToString(nonce)+"&cipherText="+base64.URLEncoding.EncodeToString(encryptedClientKey))
|
||
|
}
|
||
|
}
|