Files
filestash/server/common/crypto.go

266 lines
5.2 KiB
Go

package common
import (
"bytes"
"compress/zlib"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"hash/fnv"
"io"
"math/big"
mathrand "math/rand"
"os"
"runtime"
"sort"
"sync"
)
var (
Letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
GCMNonce NonceGenerator = NewNonceGenerator(12)
)
func EncryptString(secret string, data string) (string, error) {
d, err := compress([]byte(data))
if err != nil {
return "", err
}
d, err = EncryptAESGCM([]byte(secret), d)
if err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(d), nil
}
func DecryptString(secret string, data string) (string, error) {
d, err := base64.URLEncoding.DecodeString(data)
if err != nil {
return "", err
}
d, err = decrypt([]byte(secret), d)
if err != nil {
return "", err
}
d, err = decompress(d)
if err != nil {
return "", err
}
return string(d), nil
}
func Hash(str string, n int) string {
hasher := sha256.New()
hasher.Write([]byte(str))
return hashSize(hasher.Sum(nil), n)
}
func QuickHash(str string, n int) string {
hasher := fnv.New64()
hasher.Write([]byte(str))
return hashSize(hasher.Sum(nil), n)
}
func HashStream(r io.Reader, n int) string {
hasher := sha256.New()
io.Copy(hasher, r)
h := hex.EncodeToString(hasher.Sum(nil))
if n == 0 {
return h
} else if n >= len(h) {
return h
}
return h[0:n]
}
func hashSize(b []byte, n int) string {
h := ""
for i := 0; i < len(b); i++ {
if n > 0 && len(h) >= n {
break
}
h += ReversedBaseChange(Letters, int(b[i]))
}
if len(h) > n {
return h[0 : len(h)-1]
}
return h
}
func ReversedBaseChange(alphabet []rune, i int) string {
str := ""
for {
str += string(alphabet[i%len(alphabet)])
i = i / len(alphabet)
if i == 0 {
break
}
}
return str
}
func RandomString(n int) string {
b := make([]rune, n)
for i := range b {
max := *big.NewInt(int64(len(Letters)))
r, err := rand.Int(rand.Reader, &max)
if err != nil {
b[i] = Letters[0]
} else {
b[i] = Letters[r.Int64()]
}
}
return string(b)
}
func QuickString(n int) string {
b := make([]rune, n)
for i := range b {
b[i] = Letters[mathrand.Intn(len(Letters))]
}
return string(b)
}
func EncryptAESGCM(key []byte, plaintext []byte) ([]byte, error) {
c, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return nil, err
}
nonce := GCMNonce.Next()
if gcm.NonceSize() != len(nonce) {
Log.Error("common::crypto nonce size isn't '12' but '%d'", gcm.NonceSize())
return nil, ErrNotValid
}
return gcm.Seal(nonce, nonce, plaintext, nil), nil
}
func decrypt(key []byte, ciphertext []byte) ([]byte, error) {
c, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return nil, err
}
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
return nil, NewError("ciphertext too short", 500)
}
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
return gcm.Open(nil, nonce, ciphertext, nil)
}
func compress(something []byte) ([]byte, error) {
var b bytes.Buffer
w := zlib.NewWriter(&b)
w.Write(something)
w.Close()
return b.Bytes(), nil
}
func decompress(something []byte) ([]byte, error) {
b := bytes.NewBuffer(something)
r, err := zlib.NewReader(b)
if err != nil {
return []byte(""), nil
}
r.Close()
return io.ReadAll(r)
}
func sign(something []byte) ([]byte, error) {
return something, nil
}
func verify(something []byte) ([]byte, error) {
return something, nil
}
// Create a unique ID that can be use to identify different session
func GenerateID(params map[string]string) string {
p := ""
orderedKeys := make([]string, len(params))
for key, _ := range params {
orderedKeys = append(orderedKeys, key)
}
sort.Strings(orderedKeys)
for _, key := range orderedKeys {
switch key {
case "timestamp":
case "password":
case "path":
default:
if val := params[key]; val != "" {
p += key + "=>" + params[key] + ", "
}
}
}
if p == "" {
return "na"
}
p += "salt=>" + SECRET_KEY
return Hash(p, 20)
}
// Create an ID that identify a machine
func GenerateMachineID() string {
if runtime.GOOS == "linux" {
if f, err := os.OpenFile("/etc/machine-id", os.O_RDONLY, os.ModePerm); err == nil {
defer f.Close()
b := make([]byte, 32)
if _, err = f.Read(b); err == nil {
return string(b)
}
} else if f, err := os.OpenFile("/var/lib/dbus/machine-id", os.O_RDONLY, os.ModePerm); err == nil {
defer f.Close()
b := make([]byte, 32)
if _, err = f.Read(b); err == nil {
return string(b)
}
}
}
return "na"
}
type NonceGenerator struct {
current []byte
count int
*sync.Mutex
}
func NewNonceGenerator(size int) NonceGenerator {
firstNonce := make([]byte, size)
io.ReadFull(rand.Reader, firstNonce)
var m sync.Mutex
return NonceGenerator{firstNonce, size, &m}
}
func (this *NonceGenerator) Next() []byte {
this.Lock()
newNonce := make([]byte, this.count)
for i := len(this.current) - 1; i >= 0; i-- {
if this.current[i] < 255 {
this.current[i] += 1
break
}
this.current[i] = 0
}
newNonce = this.current
this.Unlock()
return newNonce
}