diff --git a/server/auth/internal/logic/useremailregisterlogic.go b/server/auth/internal/logic/useremailregisterlogic.go index b800ff0b..c650079e 100644 --- a/server/auth/internal/logic/useremailregisterlogic.go +++ b/server/auth/internal/logic/useremailregisterlogic.go @@ -60,6 +60,7 @@ func (l *UserEmailRegisterLogic) UserEmailRegister(req *types.RequestEmailRegist // 确认email 重新序列化 token.Email = req.Email token.WCId = req.WCId + token.GuestId = req.GuestId clurl, err := l.svcCtx.TokenManger.Generate(token) if err != nil { diff --git a/server/auth/internal/logic/usergoogleloginlogic.go b/server/auth/internal/logic/usergoogleloginlogic.go index 7fe4f336..21319226 100644 --- a/server/auth/internal/logic/usergoogleloginlogic.go +++ b/server/auth/internal/logic/usergoogleloginlogic.go @@ -91,6 +91,7 @@ func (l *UserGoogleLoginLogic) UserGoogleLogin(req *types.RequestGoogleLogin, us log.Println(r.Json()) googleId := r.Json().Get("id").Int() + user, err := l.svcCtx.AllModels.FsUser.FindUserByGoogleId(context.TODO(), googleId) if err != nil { if err != gorm.ErrRecordNotFound { diff --git a/utils/auth/confirmation_link.go b/utils/auth/confirmation_link.go index 29886165..c3392dcf 100644 --- a/utils/auth/confirmation_link.go +++ b/utils/auth/confirmation_link.go @@ -1,12 +1,7 @@ package auth import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "encoding/base64" - "encoding/gob" - "fmt" + "fusenapi/utils/encryption_decryption" "net/url" ) @@ -17,19 +12,21 @@ const ( ) type ConfirmationLink[T any] struct { - Secret []byte + // Secret []byte + SecretGCM *encryption_decryption.SecretGCM[T] + DefaultQueryKey string // 默认key 是 token link *url.URL } -func NewConfirmationLink[T any](key []byte, UrlStr string) *ConfirmationLink[T] { +func NewConfirmationLink[T any](key string, UrlStr string) *ConfirmationLink[T] { u, err := url.Parse(UrlStr) if err != nil { panic(err) } return &ConfirmationLink[T]{ - Secret: key, + SecretGCM: encryption_decryption.NewSecretGCM[T](key), DefaultQueryKey: "token", link: u, } @@ -62,91 +59,10 @@ func (cl *ConfirmationLink[T]) GenerateWithToken(token string) (string, error) { return cl.link.String(), nil } -func fusenMakeKey(keysting string) []byte { - - key := []byte(keysting) - - var result [32]byte - - // If key length is more than 32, truncate it - if len(key) > 32 { - key = key[:32] - } - - // If key length is less than 32, replicate it until it reaches 32 - for len(key) < 32 { - key = append(key, key...) - } - - // Only take the first 32 bytes - key = key[:32] - - // Swap the first 16 bytes with the last 16 bytes - copy(result[:], key[16:]) - copy(result[16:], key[:16]) - - return result[:] -} - func (cl *ConfirmationLink[T]) Encrypt(obj *T) (string, error) { - - var buf = bytes.NewBuffer(nil) - err := gob.NewEncoder(buf).Encode(obj) - if err != nil { - return "", err - } - - block, err := aes.NewCipher(cl.Secret) - if err != nil { - return "", err - } - - nonce := make([]byte, 12) - // if _, err := io.ReadFull(rand.Reader, nonce); err != nil { - // return "", err - // } - - aesgcm, err := cipher.NewGCM(block) - if err != nil { - return "", err - } - - ciphertext := aesgcm.Seal(nonce, nonce, buf.Bytes(), nil) - - return base64.URLEncoding.EncodeToString(ciphertext), nil + return cl.SecretGCM.Encrypt(obj) } func (cl *ConfirmationLink[T]) Decrypt(ciphertext string) (*T, error) { - block, err := aes.NewCipher(cl.Secret) - if err != nil { - return nil, err - } - - ct, err := base64.URLEncoding.DecodeString(ciphertext) - if err != nil { - return nil, err - } - - if len(ct) < 12 { - return nil, fmt.Errorf("ciphertext too short") - } - - aesgcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - - plaintext, err := aesgcm.Open(nil, ct[:12], ct[12:], nil) - if err != nil { - return nil, err - } - - // 解出golang的结构体 - var protected T - var buf = bytes.NewBuffer(plaintext) - err = gob.NewDecoder(buf).Decode(&protected) - if err != nil { - return nil, err - } - return &protected, nil + return cl.SecretGCM.Decrypt(ciphertext) } diff --git a/utils/auth/confirmation_link_test.go b/utils/auth/confirmation_link_test.go index 67245fab..12e31e5b 100644 --- a/utils/auth/confirmation_link_test.go +++ b/utils/auth/confirmation_link_test.go @@ -1,6 +1,9 @@ package auth import ( + "crypto/aes" + "crypto/cipher" + "encoding/hex" "log" "net/url" "testing" @@ -19,7 +22,7 @@ func BenchmarkConfirmationLink(b *testing.B) { } key := "21321321" - cl := NewConfirmationLink[Register](fusenMakeKey(key), "http://localhost:9900/api/auth/oauth2/register") + cl := NewConfirmationLink[Register](key, "http://localhost:9900/api/auth/oauth2/register") for i := 0; i < b.N; i++ { uri, _ := cl.Generate(&Register{Id: 39, Password: "21dsadsad", platform: "google", Expired: time.Now()}) @@ -29,6 +32,87 @@ func BenchmarkConfirmationLink(b *testing.B) { } } +func BenchmarkAesXor(b *testing.B) { + // 生成一个256位的密钥 + key := []byte("abcdefghijklmnopqrstuvwxyz123456") + + // // 初始向量IV必须是唯一的,但不需要保密 + iv := []byte("1234567890123456") + + // // 要加密的明文 + plaintext := []byte("hello world sadsadsadssadadsada ") + + // // 使用AES加密,返回一个Block接口 + // block, err := aes.NewCipher(key) + // if err != nil { + // panic(err) + // } + + // // 使用CTR模式 + // stream := cipher.NewCTR(block, iv) + + // // 加密明文 + // ciphertext := make([]byte, len(plaintext)) + // stream.XORKeyStream(ciphertext, plaintext) + + // // 转为hex编码打印出来 + // // fmt.Println(hex.EncodeToString(ciphertext)) + // ciphertextHex := hex.EncodeToString(ciphertext) + // // 将hex解码成[]byte + // ciphertext, _ = hex.DecodeString(ciphertextHex) + + // // 生成Block接口 + // block, err = aes.NewCipher(key) + // if err != nil { + // panic(err) + // } + + // // 生成CTR模式 + // stream = cipher.NewCTR(block, iv) + + // // 解密密文 + // plaintext = make([]byte, len(ciphertext)) + // stream.XORKeyStream(plaintext, ciphertext) + + // // 打印明文 + // fmt.Println(string(plaintext)) + + for i := 0; i < b.N; i++ { + // 使用AES加密,返回一个Block接口 + block, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + + // 使用CTR模式 + stream := cipher.NewCTR(block, iv) + + // 加密明文 + ciphertext := make([]byte, len(plaintext)) + stream.XORKeyStream(ciphertext, plaintext) + + // 转为hex编码打印出来 + // fmt.Println(hex.EncodeToString(ciphertext)) + ciphertextHex := hex.EncodeToString(ciphertext) + // 将hex解码成[]byte + ciphertext, _ = hex.DecodeString(ciphertextHex) + + // 生成Block接口 + block, err = aes.NewCipher(key) + if err != nil { + panic(err) + } + + // 生成CTR模式 + stream = cipher.NewCTR(block, iv) + + // 解密密文 + plaintext = make([]byte, len(ciphertext)) + stream.XORKeyStream(plaintext, ciphertext) + + } +} + func TestConfirmationLink(t *testing.T) { type Register struct { @@ -40,7 +124,7 @@ func TestConfirmationLink(t *testing.T) { key := "21321321" - cl := NewConfirmationLink[Register](fusenMakeKey(key), "http://localhost:9900/api/auth/oauth2/register") + cl := NewConfirmationLink[Register](key, "http://localhost:9900/api/auth/oauth2/register") uri, _ := cl.Generate(&Register{Id: 39, Password: "21dsadsad", platform: "google", Expired: time.Now()}) log.Println(uri) diff --git a/utils/encryption_decryption/aes_cbc.go b/utils/encryption_decryption/aes_cbc.go index 9135cfb0..c80e1fce 100644 --- a/utils/encryption_decryption/aes_cbc.go +++ b/utils/encryption_decryption/aes_cbc.go @@ -4,13 +4,13 @@ import ( "bytes" "crypto/aes" "crypto/cipher" + "crypto/rand" "encoding/base64" "fmt" - "math/rand" ) // 必须16字节 -var key = "fusen20230405145" +var cbckey = "fusen20230405145" // 加密(key必须16字节),前端加解密需要先把base64转字符串再取前16字节作为iv func CBCEncrypt(data string) (string, error) { @@ -19,11 +19,11 @@ func CBCEncrypt(data string) (string, error) { fmt.Println("cbc decrypt err:", err) } }() - block, err := aes.NewCipher([]byte(key)) + block, err := aes.NewCipher([]byte(cbckey)) if err != nil { return "", err } - blockSize := len(key) + blockSize := len(cbckey) padding := blockSize - len(data)%blockSize // 填充字节 if padding == 0 { padding = blockSize @@ -47,7 +47,7 @@ func CBCDecrypt(src string) (string, error) { fmt.Println("cbc decrypt err:", err) } }() - block, err := aes.NewCipher([]byte(key)) + block, err := aes.NewCipher([]byte(cbckey)) if err != nil { return "", err } diff --git a/utils/encryption_decryption/aes_crt.go b/utils/encryption_decryption/aes_crt.go new file mode 100644 index 00000000..0720ccd0 --- /dev/null +++ b/utils/encryption_decryption/aes_crt.go @@ -0,0 +1,102 @@ +package encryption_decryption + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "encoding/base64" + "encoding/gob" + "sync" +) + +type ISecretEncDec interface { + EncodeToString([]byte) string + DecodeString(string) ([]byte, error) +} + +type SecretCRT[T any] struct { + srcKey string + secretKey []byte + iv []byte + derivationKey func(keysting string) []byte + mu sync.Mutex + EncDec ISecretEncDec +} + +func NewSecretCRT[T any](key string, iv string) *SecretCRT[T] { + s := &SecretCRT[T]{ + derivationKey: DerivationKeyV1, + iv: []byte(iv), + EncDec: base64.URLEncoding, + } + s.secretKey = s.derivationKey(key) + return s +} + +func (s *SecretCRT[T]) UpdateDerivationKeyFunc(kfunc func(keysting string) []byte) { + s.mu.Lock() + defer s.mu.Unlock() + + s.derivationKey = kfunc + s.secretKey = s.derivationKey(s.srcKey) +} + +func (s *SecretCRT[T]) Encrypt(gobj *T) (string, error) { + s.mu.Lock() + defer s.mu.Unlock() + + var buf = bytes.NewBuffer(nil) + err := gob.NewEncoder(buf).Encode(gobj) + if err != nil { + return "", err + } + + // 使用AES加密,返回一个Block接口 + block, err := aes.NewCipher(s.secretKey) + if err != nil { + panic(err) + } + + // 使用CTR模式 + stream := cipher.NewCTR(block, s.iv) + + // 加密明文 + plaintext := buf.Bytes() + ciphertext := make([]byte, len(plaintext)) + stream.XORKeyStream(ciphertext, plaintext) + + // 转为hex编码打印出来 + + return s.EncDec.EncodeToString(ciphertext), nil +} + +func (s *SecretCRT[T]) Decrypt(ciphertext string) (*T, error) { + // 将hex解码成[]byte + + ciphertextbytes, err := s.EncDec.DecodeString(ciphertext) + if err != nil { + return nil, err + } + + // 生成Block接口 + block, err := aes.NewCipher(s.secretKey) + if err != nil { + panic(err) + } + + // 生成CTR模式 + stream := cipher.NewCTR(block, s.iv) + + // 解密密文 + plaintext := make([]byte, len(ciphertextbytes)) + stream.XORKeyStream(plaintext, ciphertextbytes) + + // 解出golang的结构体 + var protected T + var buf = bytes.NewBuffer(plaintext) + err = gob.NewDecoder(buf).Decode(&protected) + if err != nil { + return nil, err + } + return &protected, nil +} diff --git a/utils/encryption_decryption/aes_gcm.go b/utils/encryption_decryption/aes_gcm.go new file mode 100644 index 00000000..25bee8b7 --- /dev/null +++ b/utils/encryption_decryption/aes_gcm.go @@ -0,0 +1,129 @@ +package encryption_decryption + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/base64" + "encoding/gob" + "fmt" + "io" + "sync" +) + +func DerivationKeyV1(keysting string) []byte { + key := []byte(keysting) + + var result [16]byte + + // If key length is more than 32, truncate it + if len(key) > 16 { + key = key[:16] + } + + // If key length is less than 32, replicate it until it reaches 32 + for len(key) < 16 { + key = append(key, key...) + } + + // Only take the first 32 bytes + key = key[:16] + + // Swap the first 16 bytes with the last 16 bytes + copy(result[:], key[8:]) + copy(result[8:], key[:8]) + + return result[:] +} + +type SecretGCM[T any] struct { + srcKey string + secretKey []byte + derivationKey func(keysting string) []byte + mu sync.Mutex + EncDec ISecretEncDec +} + +func NewSecretGCM[T any](key string) *SecretGCM[T] { + s := &SecretGCM[T]{ + srcKey: key, + derivationKey: DerivationKeyV1, + EncDec: base64.URLEncoding, + } + s.secretKey = s.derivationKey(s.srcKey) + return s +} + +func (s *SecretGCM[T]) UpdateDerivationKeyFunc(kfunc func(keysting string) []byte) { + s.mu.Lock() + defer s.mu.Unlock() + + s.derivationKey = kfunc + s.secretKey = s.derivationKey(s.srcKey) +} + +func (s *SecretGCM[T]) Encrypt(gobj *T) (string, error) { + s.mu.Lock() + defer s.mu.Unlock() + + var buf = bytes.NewBuffer(nil) + err := gob.NewEncoder(buf).Encode(gobj) + if err != nil { + return "", err + } + + block, err := aes.NewCipher(s.secretKey) + if err != nil { + return "", err + } + + nonce := make([]byte, 12) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return "", err + } + + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + + ciphertext := aesgcm.Seal(nonce, nonce, buf.Bytes(), nil) + + return s.EncDec.EncodeToString(ciphertext), nil +} + +func (s *SecretGCM[T]) Decrypt(ciphertext string) (*T, error) { + block, err := aes.NewCipher(s.secretKey) + if err != nil { + return nil, err + } + + ct, err := s.EncDec.DecodeString(ciphertext) + if err != nil { + return nil, err + } + + if len(ct) < 12 { + return nil, fmt.Errorf("ciphertext too short") + } + + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + plaintext, err := aesgcm.Open(nil, ct[:12], ct[12:], nil) + if err != nil { + return nil, err + } + + // 解出golang的结构体 + var protected T + var buf = bytes.NewBuffer(plaintext) + err = gob.NewDecoder(buf).Decode(&protected) + if err != nil { + return nil, err + } + return &protected, nil +} diff --git a/utils/encryption_decryption/aes_obj_test.go b/utils/encryption_decryption/aes_obj_test.go new file mode 100644 index 00000000..b9056af6 --- /dev/null +++ b/utils/encryption_decryption/aes_obj_test.go @@ -0,0 +1,90 @@ +package encryption_decryption_test + +import ( + "fusenapi/utils/auth" + "fusenapi/utils/encryption_decryption" + "log" + "testing" +) + +func TestGCM(t *testing.T) { + token := &auth.RegisterToken{ + OperateType: auth.OpTypeRegister, + Password: "fusen_password", + WCId: 123, + } + + key := "fusen123321" + + gcm := encryption_decryption.NewSecretGCM[auth.RegisterToken](key) + tstr, err := gcm.Encrypt(token) + if err != nil { + t.Error(err) + } + + log.Println(tstr) + log.Println(gcm.Decrypt(tstr)) + +} + +func BenchmarkCRT(b *testing.B) { + token := &auth.RegisterToken{ + OperateType: auth.OpTypeRegister, + Password: "fusen_password", + WCId: 123, + } + + key := "fusen123321" + iv := "1234567890123456" + + for i := 0; i < b.N; i++ { + crt := encryption_decryption.NewSecretCRT[auth.RegisterToken](key, iv) + tstr, err := crt.Encrypt(token) + if err != nil { + b.Error(err) + } + + crt.Decrypt(tstr) + } +} + +func BenchmarkGCM(b *testing.B) { + token := &auth.RegisterToken{ + OperateType: auth.OpTypeRegister, + Password: "fusen_password", + WCId: 123, + } + + key := "fusen123321" + + for i := 0; i < b.N; i++ { + crt := encryption_decryption.NewSecretGCM[auth.RegisterToken](key) + tstr, err := crt.Encrypt(token) + if err != nil { + b.Error(err) + } + + crt.Decrypt(tstr) + } +} + +func TestCRT(t *testing.T) { + token := &auth.RegisterToken{ + OperateType: auth.OpTypeRegister, + Password: "fusen_password", + WCId: 123, + } + + key := "fusen123321" + iv := "1234567890123456" + + crt := encryption_decryption.NewSecretCRT[auth.RegisterToken](key, iv) + tstr, err := crt.Encrypt(token) + if err != nil { + t.Error(err) + } + + log.Println(tstr) + log.Println(crt.Decrypt(tstr)) + +}