sync. refactoring for code re-use
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
//"context"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
//"sync"
|
||||
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
//"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
//"os"
|
||||
//"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// TODO: Dynamically get key? Need to research best way to store private keys between two devices.
|
||||
var key = []byte("passphrasewhichneedstobe32bytes!")
|
||||
|
||||
func createToken(username string) (string, error) {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256,
|
||||
jwt.MapClaims{
|
||||
"username": username,
|
||||
"exp": time.Now().Add(time.Hour * 24).Unix(),
|
||||
})
|
||||
|
||||
jwtToken, err := token.SignedString(key)
|
||||
if err != nil {
|
||||
return "Error creating JWT.", err
|
||||
}
|
||||
return jwtToken, nil
|
||||
}
|
||||
|
||||
func verifyToken(tokenString string) error {
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
return key, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !token.Valid {
|
||||
return fmt.Errorf("invalid token")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func hash(text string) string {
|
||||
hasher := sha1.New()
|
||||
hasher.Write([]byte(text))
|
||||
return base64.URLEncoding.EncodeToString(hasher.Sum(nil))
|
||||
}
|
||||
|
||||
func encrypt() {
|
||||
var newCommunication Communication
|
||||
|
||||
text := []byte(newCommunication.Communication)
|
||||
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return
|
||||
//gc.IndentedJSON(http.StatusBadRequest, gin.H{"message": err})
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(c)
|
||||
if err != nil {
|
||||
return
|
||||
//gc.IndentedJSON(http.StatusBadRequest, gin.H{"message": err})
|
||||
}
|
||||
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return
|
||||
//gc.IndentedJSON(http.StatusBadRequest, gin.H{"message": err})
|
||||
}
|
||||
|
||||
var b []byte = gcm.Seal(nonce, nonce, text, nil)
|
||||
hex, err := convertBytesToHex(b)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
var test string = "{data: " + hex + ", hash: " + hash(string(text)) + "}"
|
||||
log.Println("encrypted string:" + test)
|
||||
//gc.IndentedJSON(http.StatusCreated, gin.H{"message": test})
|
||||
}
|
||||
|
||||
func convertBytesToHex(b []byte) (string, error) {
|
||||
// Handle nil pointer case
|
||||
if b == nil {
|
||||
return "", errors.New("nil pointer provided for hex string")
|
||||
}
|
||||
|
||||
// Split the hex string by spaces
|
||||
var h string = hex.EncodeToString(b)
|
||||
var builder strings.Builder
|
||||
for i := 0; i < len(h); i += 2 {
|
||||
end := i + 2
|
||||
if end > len(h) {
|
||||
end = len(h)
|
||||
}
|
||||
chunk := h[i:end]
|
||||
builder.WriteString(chunk)
|
||||
if end < len(h) {
|
||||
builder.WriteString(" ")
|
||||
}
|
||||
}
|
||||
|
||||
// Return the slice of uint8 and any errors encountered
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
func convertHexToBytes(hexString *string) ([]uint8, error) {
|
||||
// Handle nil pointer case
|
||||
if hexString == nil {
|
||||
return nil, errors.New("nil pointer provided for hex string")
|
||||
}
|
||||
|
||||
// Split the hex string by spaces
|
||||
hexBytes := strings.Fields(*hexString)
|
||||
|
||||
// Initialize an empty slice for uint8
|
||||
data := make([]uint8, len(hexBytes))
|
||||
|
||||
// Iterate and convert each hex byte
|
||||
for i, hexByte := range hexBytes {
|
||||
// Convert each hex string to a uint8 value (handling errors)
|
||||
value, err := strconv.ParseUint(hexByte, 16, 8)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing hex byte '%s': %w", hexByte, err)
|
||||
}
|
||||
|
||||
// Assign the converted value to the slice
|
||||
data[i] = uint8(value)
|
||||
}
|
||||
|
||||
// Return the slice of uint8 and any errors encountered
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func decrypt(input *Communication, output *Chat) bool {
|
||||
// TODO: Add testing flag for easier manipulation.
|
||||
//ciphertext, err := ioutil.ReadFile("myfile")
|
||||
ciphertext, err := convertHexToBytes(&input.Communication)
|
||||
|
||||
// if our program was unable to read the file
|
||||
// print out the reason why it can't
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(c)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
nonceSize := gcm.NonceSize()
|
||||
if len(ciphertext) < nonceSize {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
|
||||
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
s := string(plaintext)
|
||||
if validateHash(s, input.Hash) {
|
||||
output.Content = s
|
||||
return true
|
||||
}
|
||||
input.Communication = "DATA CORRUPTED OR TAMPERED"
|
||||
return false
|
||||
}
|
||||
|
||||
// Validate there's no tampering with SHA-1 sum. The decrypted hash and the transmitted hash should be identical.
|
||||
func validateHash(decrypted string, hash string) bool {
|
||||
// Calculate the SHA256 sum of the decrypted request
|
||||
hasher := sha1.New()
|
||||
hasher.Write([]byte(decrypted))
|
||||
decryptedHashString := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
|
||||
|
||||
// TODO: Add error handling if hash doesn't match.
|
||||
return decryptedHashString == hash
|
||||
}
|
||||
|
||||
// Serve the authentication and encryption layer to a provided local port.
|
||||
// Authentication takes place solely on the backend.
|
||||
func serveAuthentication(
|
||||
router *mux.Router,
|
||||
port int,
|
||||
) {
|
||||
// TODO: Add error handling
|
||||
authR := router.Host("http://localhost").Subrouter()
|
||||
authSrv := &http.Server{
|
||||
Addr: "0.0.0.0:" + string(port),
|
||||
WriteTimeout: time.Second * 15,
|
||||
ReadTimeout: time.Second * 15,
|
||||
IdleTimeout: time.Second * 60,
|
||||
Handler: authR,
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := authSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Println("Auth server is running on port " + string(port))
|
||||
}
|
||||
@@ -2,29 +2,17 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"sync"
|
||||
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
@@ -85,187 +73,10 @@ type ResponseWHistory struct {
|
||||
|
||||
var chats []Chat
|
||||
|
||||
// TODO: Test if 256 bit key works.
|
||||
// TODO: Dynamically get key? Need to research best way to store private keys between two devices.
|
||||
var key = []byte("passphrasewhichneedstobe32bytes!")
|
||||
|
||||
func createToken(username string) (string, error) {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256,
|
||||
jwt.MapClaims {
|
||||
"username": username,
|
||||
"exp": time.Now().Add(time.Hour * 24).Unix(),
|
||||
})
|
||||
|
||||
jwtToken, err := token.SignedString(key)
|
||||
if err != nil {
|
||||
return "Error creating JWT.", err
|
||||
}
|
||||
return jwtToken, nil
|
||||
}
|
||||
|
||||
func verifyToken(tokenString string) error {
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
return key, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !token.Valid {
|
||||
return fmt.Errorf("invalid token")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func hash(text string) string {
|
||||
hasher := sha1.New()
|
||||
hasher.Write([]byte(text))
|
||||
return base64.URLEncoding.EncodeToString(hasher.Sum(nil))
|
||||
}
|
||||
|
||||
func encrypt() {
|
||||
var newCommunication Communication
|
||||
|
||||
text := []byte(newCommunication.Communication)
|
||||
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return
|
||||
//gc.IndentedJSON(http.StatusBadRequest, gin.H{"message": err})
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(c)
|
||||
if err != nil {
|
||||
return
|
||||
//gc.IndentedJSON(http.StatusBadRequest, gin.H{"message": err})
|
||||
}
|
||||
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return
|
||||
//gc.IndentedJSON(http.StatusBadRequest, gin.H{"message": err})
|
||||
}
|
||||
|
||||
var b []byte = gcm.Seal(nonce, nonce, text, nil)
|
||||
hex, err := convertBytesToHex(b)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
var test string = "{data: " + hex + ", hash: " + hash(string(text)) + "}"
|
||||
log.Println("encrypted string:" + test)
|
||||
//gc.IndentedJSON(http.StatusCreated, gin.H{"message": test})
|
||||
}
|
||||
|
||||
func convertBytesToHex(b []byte) (string, error) {
|
||||
// Handle nil pointer case
|
||||
if b == nil {
|
||||
return "", errors.New("nil pointer provided for hex string")
|
||||
}
|
||||
|
||||
// Split the hex string by spaces
|
||||
var h string = hex.EncodeToString(b)
|
||||
var builder strings.Builder
|
||||
for i := 0; i < len(h); i += 2 {
|
||||
end := i + 2
|
||||
if end > len(h) {
|
||||
end = len(h)
|
||||
}
|
||||
chunk := h[i:end]
|
||||
builder.WriteString(chunk)
|
||||
if end < len(h) {
|
||||
builder.WriteString(" ")
|
||||
}
|
||||
}
|
||||
|
||||
// Return the slice of uint8 and any errors encountered
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
func convertHexToBytes(hexString *string) ([]uint8, error) {
|
||||
// Handle nil pointer case
|
||||
if hexString == nil {
|
||||
return nil, errors.New("nil pointer provided for hex string")
|
||||
}
|
||||
|
||||
// Split the hex string by spaces
|
||||
hexBytes := strings.Fields(*hexString)
|
||||
|
||||
// Initialize an empty slice for uint8
|
||||
data := make([]uint8, len(hexBytes))
|
||||
|
||||
// Iterate and convert each hex byte
|
||||
for i, hexByte := range hexBytes {
|
||||
// Convert each hex string to a uint8 value (handling errors)
|
||||
value, err := strconv.ParseUint(hexByte, 16, 8)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing hex byte '%s': %w", hexByte, err)
|
||||
}
|
||||
|
||||
// Assign the converted value to the slice
|
||||
data[i] = uint8(value)
|
||||
}
|
||||
|
||||
// Return the slice of uint8 and any errors encountered
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func decrypt(input *Communication, output *Chat) bool {
|
||||
// TODO: Add testing flag for easier manipulation.
|
||||
//ciphertext, err := ioutil.ReadFile("myfile")
|
||||
ciphertext, err := convertHexToBytes(&input.Communication)
|
||||
|
||||
// if our program was unable to read the file
|
||||
// print out the reason why it can't
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(c)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
nonceSize := gcm.NonceSize()
|
||||
if len(ciphertext) < nonceSize {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
|
||||
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
s := string(plaintext)
|
||||
if validateHash(s, input.Hash) {
|
||||
output.Content = s
|
||||
return true
|
||||
}
|
||||
input.Communication = "DATA CORRUPTED OR TAMPERED"
|
||||
return false
|
||||
}
|
||||
|
||||
// Validate there's no tampering with SHA-1 sum. The decrypted hash and the transmitted hash should be identical.
|
||||
func validateHash(decrypted string, hash string) bool {
|
||||
// Calculate the SHA256 sum of the decrypted request
|
||||
hasher := sha1.New()
|
||||
hasher.Write([]byte(decrypted))
|
||||
decryptedHashString := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
|
||||
|
||||
// TODO: Add error handling if hash doesn't match.
|
||||
return decryptedHashString == hash
|
||||
}
|
||||
|
||||
// User HTTP GET request has the prompt decrypted and verified with a SHA-1 checksum.
|
||||
// If there's been no data corruption, re-construct user prompt for ollama
|
||||
// TODO: Eventually, add additional logic that will allow for re-direction and contextual awareness (pre-processing)
|
||||
func request(w http.ResponseWriter, r *http.Request) {
|
||||
func chat(w http.ResponseWriter, r *http.Request) {
|
||||
model := "qwen:0.5b" // qwen:0.5b used for testing while hosting from my laptop. llama3 seems to be the best to use normally.
|
||||
|
||||
input, err := io.ReadAll(r.Body)
|
||||
@@ -310,16 +121,14 @@ func request(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
response := ResponseWHistory{}
|
||||
json.Unmarshal(output, &response)
|
||||
|
||||
log.Println(response)
|
||||
|
||||
} else {
|
||||
http.Error(w, "Decryption/Hash failed.", 401)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func chat(w http.ResponseWriter, r *http.Request) {
|
||||
func request(w http.ResponseWriter, r *http.Request) {
|
||||
input, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -361,16 +170,19 @@ func chat(w http.ResponseWriter, r *http.Request) {
|
||||
//c.IndentedJSON(http.StatusCreated, resp)
|
||||
}
|
||||
|
||||
func main() {
|
||||
func serve() {
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
r := mux.NewRouter()
|
||||
portfolioDir := "/Users/ashton/Documents/Development/portfolio/public_html/" // MacBook dir
|
||||
//portfolioDir := "/home/violet/documents/development/portfolio/public_html/" // ThinkPad dir
|
||||
r.PathPrefix("/").Handler(http.FileServer(http.Dir(portfolioDir)))
|
||||
portfolioDir := "/home/violet/documents/development/portfolio/public_html/"
|
||||
servePage(r, portfolioDir, "/", 1112)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: "0.0.0.0:8080",
|
||||
Addr: "0.0.0.0:1111",
|
||||
// Good practice to set timeouts to avoid Slowloris attacks.
|
||||
WriteTimeout: time.Second * 15,
|
||||
ReadTimeout: time.Second * 15,
|
||||
@@ -387,32 +199,7 @@ func main() {
|
||||
}
|
||||
}()
|
||||
|
||||
log.Println("Portfolio server is running on port 8080")
|
||||
|
||||
// Routing for AI web app and API
|
||||
apiR := r.Host("ai.joshashton.dev").Subrouter()
|
||||
apiR.HandleFunc("/request", chat)
|
||||
apiR.PathPrefix("/").Handler(http.FileServer(http.Dir("/home/violet/templates/construction/")))
|
||||
|
||||
apiSrv := &http.Server{
|
||||
Addr: "0.0.0.0:8081",
|
||||
// Good practice to set timeouts to avoid Slowloris attacks.
|
||||
WriteTimeout: time.Second * 15,
|
||||
ReadTimeout: time.Second * 15,
|
||||
IdleTimeout: time.Second * 60,
|
||||
Handler: apiR, // Pass our instance of gorilla/mux in.
|
||||
}
|
||||
|
||||
// Run our server in a goroutine so that it doesn't block.
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := apiSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Println("API server is running on port 8081")
|
||||
log.Println("Router is running on port 1111")
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
// We'll accept graceful shutdowns when quit via SIGINT (Ctrl+Shift+C)
|
||||
@@ -427,7 +214,6 @@ func main() {
|
||||
// Doesn't block if no connections, but will otherwise wait
|
||||
// until the timeout deadline.
|
||||
srv.Shutdown(ctx)
|
||||
apiSrv.Shutdown(ctx)
|
||||
// Optionally, you could run srv.Shutdown in a goroutine and block on
|
||||
// <-ctx.Done() if your application should wait for other services
|
||||
// to finalize based on context cancellation.
|
||||
@@ -435,3 +221,8 @@ func main() {
|
||||
wg.Wait()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// TODO: Authenticate user credentials to verify they are allowed to even access
|
||||
serve()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Serve an API using mux.Router().Host({domain}).Subrouter().
|
||||
// Provide the router, domain, an array of endpoints and functions, and
|
||||
// the port you would like the API accessible to.
|
||||
func serveApi(
|
||||
router *mux.Router,
|
||||
domain string,
|
||||
endpoint []string,
|
||||
function []func(http.ResponseWriter, *http.Request),
|
||||
port int,
|
||||
) {
|
||||
// TODO: Add error handling
|
||||
apiR := router.Host(domain).Subrouter()
|
||||
|
||||
for i := 0; i < len(endpoint); i++ {
|
||||
apiR.HandleFunc(endpoint[i], function[i])
|
||||
}
|
||||
|
||||
apiSrv := &http.Server{
|
||||
Addr: "0.0.0.0:" + string(port),
|
||||
WriteTimeout: time.Second * 15,
|
||||
ReadTimeout: time.Second * 15,
|
||||
IdleTimeout: time.Second * 60,
|
||||
Handler: apiR,
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := apiSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Println("API server is running on port " + string(port))
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Serve a directory to a port.
|
||||
func servePage(router *mux.Router, dest string, path string, port int) {
|
||||
router.PathPrefix(path).Handler(http.FileServer(http.Dir(dest)))
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: "0.0.0.0: " + string(port),
|
||||
// Good practice to set timeouts to avoid Slowloris attacks.
|
||||
WriteTimeout: time.Second * 15,
|
||||
ReadTimeout: time.Second * 15,
|
||||
IdleTimeout: time.Second * 60,
|
||||
Handler: router, // Pass our instance of gorilla/mux in.
|
||||
}
|
||||
|
||||
// Run our server in a goroutine so that it doesn't block.
|
||||
go func() {
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Println("Web server is running on port " + string(port))
|
||||
}
|
||||
Reference in New Issue
Block a user