make funcs public
This commit is contained in:
303
lib.go
303
lib.go
@@ -1,303 +0,0 @@
|
|||||||
package noteslib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/pbkdf2"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha256"
|
|
||||||
"database/sql"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
_ "modernc.org/sqlite"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Note struct {
|
|
||||||
Body string `json:"body"`
|
|
||||||
Color int64 `json:"color"`
|
|
||||||
Date string `json:"date"`
|
|
||||||
ID string `json:"id"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeColorInt(color int64) (r int64, g int64, b int64, a int64) {
|
|
||||||
// https://developer.android.com/reference/android/graphics/Color#color-ints
|
|
||||||
if color == 0 {
|
|
||||||
return 255, 255, 255, 255
|
|
||||||
}
|
|
||||||
a = (color >> 24) & 0xff // or color >>> 24
|
|
||||||
r = (color >> 16) & 0xff
|
|
||||||
g = (color >> 8) & 0xff
|
|
||||||
b = (color) & 0xff
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeToColorInt(r int64, g int64, b int64, a int64) int64 {
|
|
||||||
// https://developer.android.com/reference/android/graphics/Color#color-ints
|
|
||||||
return (a&0xff)<<24 | (r&0xff)<<16 | (g&0xff)<<8 | (b & 0xff)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetch(serverAddress string, username string) (jsonBody [][2]string, err error) {
|
|
||||||
response, err := http.Get(
|
|
||||||
fmt.Sprintf("https://%s/get?user=%s",
|
|
||||||
serverAddress,
|
|
||||||
url.QueryEscape(username)),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer response.Body.Close()
|
|
||||||
|
|
||||||
responseBody, err := io.ReadAll(response.Body)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(responseBody, &jsonBody)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func load(db *sql.DB) (notes [][2]string, err error) {
|
|
||||||
var rowCount int
|
|
||||||
err = db.QueryRow("SELECT COUNT(*) FROM notes").Scan(&rowCount)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const limit = 100
|
|
||||||
if rowCount > limit { // change this if there's ever an option to increase limit
|
|
||||||
rowCount = limit // change this if there's ever an option to increase limit
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := db.Query("SELECT * FROM notes LIMIT ?", rowCount)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
notes = make([][2]string, rowCount)
|
|
||||||
|
|
||||||
for i := 0; rows.Next(); i++ {
|
|
||||||
var currentRow [2]string
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
notes[i] = currentRow
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func list(password string, jsonBody [2]string, notes *[]Note, wg *sync.WaitGroup) (err error) {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
decryptedData, err := decrypt(jsonBody[1], password)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var decryptedBody Note
|
|
||||||
err = json.Unmarshal(decryptedData, &decryptedBody)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
decryptedBody.ID = jsonBody[0]
|
|
||||||
if len(decryptedBody.Title) > 0 && len(decryptedBody.Body) > 0 {
|
|
||||||
*notes = append(*notes, decryptedBody) // ew
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func del(serverAddress string, username string, uuid string, db *sql.DB) (err error) {
|
|
||||||
response, err := http.Get(fmt.Sprintf("https://%s/remove?user=%s&id=%s", serverAddress, url.QueryEscape(username), uuid))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer response.Body.Close()
|
|
||||||
|
|
||||||
_, err = io.ReadAll(response.Body)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt, err := db.Prepare("DELETE FROM notes WHERE id=?")
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
_, err = stmt.Exec(uuid)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func create(title string, body string, newColor string, serverAddress string, username string, password string, templateNote Note) (err error) {
|
|
||||||
aInt, err := strconv.ParseInt(newColor[0:2], 16, 64)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rInt, err := strconv.ParseInt(newColor[2:4], 16, 64)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
gInt, err := strconv.ParseInt(newColor[4:6], 16, 64)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
bInt, err := strconv.ParseInt(newColor[6:8], 16, 64)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
note := Note{
|
|
||||||
Title: title,
|
|
||||||
Body: strings.ReplaceAll(body, "/", "\n"),
|
|
||||||
Color: encodeToColorInt(rInt, gInt, bInt, aInt),
|
|
||||||
Date: strconv.FormatInt(time.Now().UnixMilli(), 10),
|
|
||||||
}
|
|
||||||
jsonNote, err := json.Marshal(note)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := encrypt(jsonNote, password)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := http.Get(
|
|
||||||
fmt.Sprintf(
|
|
||||||
"https://%s/new?user=%s&data=%s",
|
|
||||||
serverAddress, url.QueryEscape(username),
|
|
||||||
url.QueryEscape(data),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer response.Body.Close()
|
|
||||||
_, err = io.ReadAll(response.Body)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func deriveKey(password string, salt []byte) (block cipher.Block, err error) {
|
|
||||||
key, err := pbkdf2.Key(sha256.New, password, salt, 65536, 32)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
block, err = aes.NewCipher(key)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func decrypt(data string, password string) (strippedPlaintext []byte, err error) {
|
|
||||||
dataBytes, err := base64.StdEncoding.DecodeString(data)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
dataBytesLength := len(dataBytes)
|
|
||||||
if dataBytesLength < 32 {
|
|
||||||
err = fmt.Errorf("Data is of length %d, 32 required.", dataBytesLength)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
iv := dataBytes[0:16]
|
|
||||||
salt := dataBytes[16:32]
|
|
||||||
ciphertext := dataBytes[32:]
|
|
||||||
|
|
||||||
ciphertextLength := len(ciphertext)
|
|
||||||
if ciphertextLength%aes.BlockSize != 0 {
|
|
||||||
err = fmt.Errorf("Ciphertext length %d is not a multiple of block size %d\n", ciphertextLength, aes.BlockSize)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
plaintext := make([]byte, ciphertextLength)
|
|
||||||
block, err := deriveKey(password, salt)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mode := cipher.NewCBCDecrypter(block, iv)
|
|
||||||
mode.CryptBlocks(plaintext, ciphertext)
|
|
||||||
|
|
||||||
strippedPlaintext, err = unpad(plaintext, 16)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func encrypt(data []byte, password string) (encrypted string, err error) {
|
|
||||||
salt := make([]byte, 16)
|
|
||||||
iv := make([]byte, 16)
|
|
||||||
rand.Read(salt)
|
|
||||||
rand.Read(iv)
|
|
||||||
|
|
||||||
plaintext, err := pad(data, 16)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
block, err := deriveKey(password, salt)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mode := cipher.NewCBCEncrypter(block, iv)
|
|
||||||
ciphertext := make([]byte, len(plaintext))
|
|
||||||
mode.CryptBlocks(ciphertext, plaintext)
|
|
||||||
|
|
||||||
encrypted = base64.StdEncoding.EncodeToString(
|
|
||||||
bytes.Join(
|
|
||||||
[][]byte{
|
|
||||||
iv,
|
|
||||||
salt,
|
|
||||||
ciphertext,
|
|
||||||
},
|
|
||||||
[]byte(""),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func pad(buf []byte, size int) ([]byte, error) {
|
|
||||||
// https://github.com/mergermarket/go-pkcs7/blob/master/pkcs7.go
|
|
||||||
bufLen := len(buf)
|
|
||||||
padLen := size - bufLen%size
|
|
||||||
padded := make([]byte, bufLen+padLen)
|
|
||||||
copy(padded, buf)
|
|
||||||
for i := range padLen {
|
|
||||||
padded[bufLen+i] = byte(padLen)
|
|
||||||
}
|
|
||||||
return padded, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpad(padded []byte, size int) (buf []byte, err error) {
|
|
||||||
if len(padded)%size != 0 {
|
|
||||||
err = fmt.Errorf("Padded value wasn't in correct size for pkcs7")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
bufLen := len(padded) - int(padded[len(padded)-1])
|
|
||||||
buf = make([]byte, bufLen)
|
|
||||||
copy(buf, padded[:bufLen])
|
|
||||||
return
|
|
||||||
}
|
|
Reference in New Issue
Block a user