Handle malformed encrypted requests without panics
Signed-off-by: Sean Du <do4suki@gmail.com>
This commit is contained in:
@@ -33,7 +33,10 @@ func handshake(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
decryptedData := encrypt.RSADecrypt(data)
|
decryptedData, err := encrypt.RSADecrypt(data)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
params, err := url.ParseQuery(string(decryptedData))
|
params, err := url.ParseQuery(string(decryptedData))
|
||||||
if ss.CheckErr(err) {
|
if ss.CheckErr(err) {
|
||||||
|
|||||||
@@ -25,7 +25,10 @@ func authKey(ctx *gin.Context) {
|
|||||||
if ss.CheckErr(err) {
|
if ss.CheckErr(err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dummyTokenDecrypted := encrypt.RSADecrypt(dummyToken)
|
dummyTokenDecrypted, err := encrypt.RSADecrypt(dummyToken)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// aesKey := dummyTokenDecrypted[0:16]
|
// aesKey := dummyTokenDecrypted[0:16]
|
||||||
// authData, err := base64.StdEncoding.DecodeString(reqData.Get("auth_data").String())
|
// authData, err := base64.StdEncoding.DecodeString(reqData.Get("auth_data").String())
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ func login(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
xmcKey := utils.XOR(clientToken, serverToken)
|
xmcKey := utils.XOR(clientToken, serverToken)
|
||||||
|
if len(xmcKey) < 16 {
|
||||||
|
ss.Abort(errors.New("invalid login token data"))
|
||||||
|
return
|
||||||
|
}
|
||||||
aesKey := xmcKey[0:16]
|
aesKey := xmcKey[0:16]
|
||||||
|
|
||||||
reqData := gjson.Parse(ctx.MustGet("request_data").(string))
|
reqData := gjson.Parse(ctx.MustGet("request_data").(string))
|
||||||
@@ -48,7 +52,15 @@ func login(ctx *gin.Context) {
|
|||||||
if ss.CheckErr(err) {
|
if ss.CheckErr(err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
loginKey := encrypt.AESCBCDecrypt(key, aesKey)[16:]
|
decryptedLoginKey, err := encrypt.AESCBCDecrypt(key, aesKey)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(decryptedLoginKey) < 16 {
|
||||||
|
ss.Abort(errors.New("invalid login key"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loginKey := decryptedLoginKey[16:]
|
||||||
// fmt.Println("loginKey", string(loginKey))
|
// fmt.Println("loginKey", string(loginKey))
|
||||||
|
|
||||||
// password, err := base64.StdEncoding.DecodeString(reqData.Get("login_passwd").String())
|
// password, err := base64.StdEncoding.DecodeString(reqData.Get("login_passwd").String())
|
||||||
|
|||||||
@@ -186,13 +186,21 @@ func (ss *Session) CheckErr(err error) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ss *Session) Respond(resp any) {
|
func (ss *Session) Respond(resp any) {
|
||||||
|
if ss.done {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
data, err := json.Marshal(resp)
|
data, err := json.Marshal(resp)
|
||||||
if ss.CheckErr(err) {
|
if ss.CheckErr(err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ss.resp.setHeader("Content-Type", "application/json")
|
ss.resp.setHeader("Content-Type", "application/json")
|
||||||
ss.resp.setHeader("X-Message-Sign", base64.StdEncoding.EncodeToString(encrypt.RSASignSHA1(data)))
|
signature, err := encrypt.RSASignSHA1(data)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ss.resp.setHeader("X-Message-Sign", base64.StdEncoding.EncodeToString(signature))
|
||||||
ss.resp.writeBody(http.StatusOK, string(data))
|
ss.resp.writeBody(http.StatusOK, string(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+24
-10
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -17,32 +18,45 @@ func padding(plainText []byte, blockSize int) []byte {
|
|||||||
return plainText
|
return plainText
|
||||||
}
|
}
|
||||||
|
|
||||||
func unPadding(cipherText []byte) []byte {
|
func unPadding(cipherText []byte) ([]byte, error) {
|
||||||
|
if len(cipherText) == 0 {
|
||||||
|
return nil, fmt.Errorf("AES plaintext is empty")
|
||||||
|
}
|
||||||
|
|
||||||
end := cipherText[len(cipherText)-1]
|
end := cipherText[len(cipherText)-1]
|
||||||
cipherText = cipherText[:len(cipherText)-int(end)]
|
if end == 0 || int(end) > len(cipherText) {
|
||||||
return cipherText
|
return nil, fmt.Errorf("invalid AES padding")
|
||||||
|
}
|
||||||
|
for _, value := range cipherText[len(cipherText)-int(end):] {
|
||||||
|
if value != end {
|
||||||
|
return nil, fmt.Errorf("invalid AES padding")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cipherText[:len(cipherText)-int(end)], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AESCBCEncrypt(plainText []byte, key []byte) []byte {
|
func AESCBCEncrypt(plainText []byte, key []byte) ([]byte, error) {
|
||||||
block, err := aes.NewCipher(key)
|
block, err := aes.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
plainText = padding(plainText, block.BlockSize())
|
plainText = padding(plainText, block.BlockSize())
|
||||||
blockMode := cipher.NewCBCEncrypter(block, iv)
|
blockMode := cipher.NewCBCEncrypter(block, iv)
|
||||||
cipherText := make([]byte, len(plainText))
|
cipherText := make([]byte, len(plainText))
|
||||||
blockMode.CryptBlocks(cipherText, plainText)
|
blockMode.CryptBlocks(cipherText, plainText)
|
||||||
return cipherText
|
return cipherText, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AESCBCDecrypt(cipherText []byte, key []byte) []byte {
|
func AESCBCDecrypt(cipherText []byte, key []byte) ([]byte, error) {
|
||||||
block, err := aes.NewCipher(key)
|
block, err := aes.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(cipherText) == 0 || len(cipherText)%block.BlockSize() != 0 {
|
||||||
|
return nil, fmt.Errorf("AES ciphertext must be a non-empty multiple of the block size")
|
||||||
}
|
}
|
||||||
plainText := make([]byte, len(cipherText))
|
plainText := make([]byte, len(cipherText))
|
||||||
blockMode := cipher.NewCBCDecrypter(block, iv)
|
blockMode := cipher.NewCBCDecrypter(block, iv)
|
||||||
blockMode.CryptBlocks(plainText, cipherText)
|
blockMode.CryptBlocks(plainText, cipherText)
|
||||||
plainText = unPadding(plainText)
|
return unPadding(plainText)
|
||||||
return plainText
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAESCBCDecryptRoundTrip(t *testing.T) {
|
||||||
|
key := []byte("1234567890abcdef")
|
||||||
|
plainText := []byte("login payload")
|
||||||
|
|
||||||
|
cipherText, err := AESCBCEncrypt(plainText, key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("encrypt: %v", err)
|
||||||
|
}
|
||||||
|
decrypted, err := AESCBCDecrypt(cipherText, key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("decrypt: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(decrypted, plainText) {
|
||||||
|
t.Fatalf("decrypted plaintext = %q, want %q", decrypted, plainText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAESCBCDecryptRejectsMalformedCiphertext(t *testing.T) {
|
||||||
|
key := []byte("1234567890abcdef")
|
||||||
|
for _, cipherText := range [][]byte{nil, make([]byte, 15)} {
|
||||||
|
if _, err := AESCBCDecrypt(cipherText, key); err == nil {
|
||||||
|
t.Fatalf("AESCBCDecrypt(%d bytes) succeeded", len(cipherText))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := unPadding([]byte{1, 2, 3, 2}); err == nil {
|
||||||
|
t.Fatal("unPadding accepted invalid padding")
|
||||||
|
}
|
||||||
|
}
|
||||||
+45
-77
@@ -7,6 +7,7 @@ import (
|
|||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
"honoka-chan/config"
|
"honoka-chan/config"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -75,100 +76,67 @@ func RSAGen(bits int) {
|
|||||||
pem.Encode(publickeyfile, &publickeyblock)
|
pem.Encode(publickeyfile, &publickeyblock)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RSAEncrypt(plainText []byte, publickeypath string) []byte {
|
func RSAEncrypt(plainText []byte, publickeypath string) ([]byte, error) {
|
||||||
//open publickeyfile
|
data, err := os.ReadFile(publickeypath)
|
||||||
publickeyfile, err := os.Open(publickeypath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
|
||||||
defer publickeyfile.Close()
|
|
||||||
|
|
||||||
//get publickeyfile info
|
|
||||||
publickeyfileInfo, _ := publickeyfile.Stat()
|
|
||||||
|
|
||||||
//read publickeyfile content
|
|
||||||
//1. make size
|
|
||||||
buf := make([]byte, publickeyfileInfo.Size())
|
|
||||||
//2. read file to buf
|
|
||||||
publickeyfile.Read(buf)
|
|
||||||
//3. decode pem
|
|
||||||
publickeyDecodeBlock, _ := pem.Decode(buf)
|
|
||||||
//4. x509 decode
|
|
||||||
publicKeyInterface, err := x509.ParsePKIXPublicKey(publickeyDecodeBlock.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
//assert
|
|
||||||
publicKey := publicKeyInterface.(*rsa.PublicKey)
|
|
||||||
|
|
||||||
//encrypt plainText
|
|
||||||
cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plainText)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cipherText
|
block, _ := pem.Decode(data)
|
||||||
|
if block == nil {
|
||||||
|
return nil, fmt.Errorf("invalid RSA public key PEM")
|
||||||
|
}
|
||||||
|
parsedKey, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
publicKey, ok := parsedKey.(*rsa.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("PEM does not contain an RSA public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
return rsa.EncryptPKCS1v15(rand.Reader, publicKey, plainText)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RSADecrypt(cipherText []byte) []byte {
|
func RSADecrypt(cipherText []byte) ([]byte, error) {
|
||||||
//open privatekeyfile
|
privateKey, err := loadPrivateKey()
|
||||||
privatekeyfile, err := os.Open(config.PrivateKeyPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
|
||||||
defer privatekeyfile.Close()
|
|
||||||
//get privatekeyfile content
|
|
||||||
privatekeyinfo, _ := privatekeyfile.Stat()
|
|
||||||
buf := make([]byte, privatekeyinfo.Size())
|
|
||||||
privatekeyfile.Read(buf)
|
|
||||||
//pem decode
|
|
||||||
privatekeyblock, _ := pem.Decode(buf)
|
|
||||||
//X509 decode
|
|
||||||
parseKey, err := x509.ParsePKCS8PrivateKey(privatekeyblock.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
privateKey := parseKey.(*rsa.PrivateKey)
|
|
||||||
//decrypt the cipher
|
|
||||||
plainText, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, cipherText)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return plainText
|
return rsa.DecryptPKCS1v15(rand.Reader, privateKey, cipherText)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RSASignSHA1(cipherText []byte) []byte {
|
func RSASignSHA1(cipherText []byte) ([]byte, error) {
|
||||||
//open privatekeyfile
|
privateKey, err := loadPrivateKey()
|
||||||
privatekeyfile, err := os.Open(config.PrivateKeyPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
defer privatekeyfile.Close()
|
|
||||||
//get privatekeyfile content
|
|
||||||
privatekeyinfo, _ := privatekeyfile.Stat()
|
|
||||||
buf := make([]byte, privatekeyinfo.Size())
|
|
||||||
privatekeyfile.Read(buf)
|
|
||||||
//pem decode
|
|
||||||
privatekeyblock, _ := pem.Decode(buf)
|
|
||||||
//X509 decode
|
|
||||||
parseKey, err := x509.ParsePKCS8PrivateKey(privatekeyblock.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
privateKey := parseKey.(*rsa.PrivateKey)
|
|
||||||
|
|
||||||
msgHash := sha1.New()
|
msgHash := sha1.New()
|
||||||
_, err = msgHash.Write(cipherText)
|
_, _ = msgHash.Write(cipherText)
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
msgHashSum := msgHash.Sum(nil)
|
msgHashSum := msgHash.Sum(nil)
|
||||||
|
|
||||||
signature, err := rsa.SignPSS(rand.Reader, privateKey, crypto.SHA1, msgHashSum, nil)
|
return rsa.SignPSS(rand.Reader, privateKey, crypto.SHA1, msgHashSum, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadPrivateKey() (*rsa.PrivateKey, error) {
|
||||||
|
data, err := os.ReadFile(config.PrivateKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return signature
|
block, _ := pem.Decode(data)
|
||||||
|
if block == nil {
|
||||||
|
return nil, fmt.Errorf("invalid RSA private key PEM")
|
||||||
|
}
|
||||||
|
parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
privateKey, ok := parsedKey.(*rsa.PrivateKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("PEM does not contain an RSA private key")
|
||||||
|
}
|
||||||
|
return privateKey, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user