From b1ecd04a28dd9ac4d9f28a9bb928c7b21c5b9dca Mon Sep 17 00:00:00 2001 From: ycc Date: Wed, 4 Feb 2026 18:28:14 +0100 Subject: [PATCH] better error management + shortcode overflow control --- asymcrypt.go | 87 ++++++++++++++++++---------------------- asymcrypt_test.go | 15 +++++-- client/config.go | 16 ++++++++ client/messagestorage.go | 32 +++++++++++---- server/invitation.go | 13 ++++-- symcrypt.go | 7 ++-- 6 files changed, 105 insertions(+), 65 deletions(-) diff --git a/asymcrypt.go b/asymcrypt.go index 39e856b..03da23d 100644 --- a/asymcrypt.go +++ b/asymcrypt.go @@ -7,8 +7,6 @@ import ( "github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/gopenpgp/v2/helper" - "github.com/pkg/errors" - "github.com/rs/zerolog/log" ) type KeyPair struct { @@ -27,7 +25,7 @@ func NewKeyPair() (*KeyPair, error) { // Return error! } pub, err := keys.GetArmoredPublicKey() if err != nil { - return nil, fmt.Errorf("failed to get public key: %w", err) + return nil, fmt.Errorf("gopenpgp: unable to get public key: %w", err) } priv, err := keys.Armor() if err != nil { @@ -39,27 +37,26 @@ func NewKeyPair() (*KeyPair, error) { // Return error! return &kp, nil } -func (Kp *KeyPair) GetCryptoKeyObject() *crypto.Key { +func (Kp *KeyPair) GetCryptoKeyObject() (*crypto.Key, error) { priv, err := base64.StdEncoding.DecodeString(Kp.Private) if err != nil { - log.Error().Msg("Create key from armoured b64 failed") + return nil, fmt.Errorf("failed to decode private key: %w", err) } key, err := crypto.NewKeyFromArmored(string(priv)) if err != nil { - log.Error().Msg("Create key from armoured failed") + return nil, fmt.Errorf("Ccreate key from armoured failed: %w", err) } - return key + return key, nil } func AsymEncrypt(publicKey string, data []byte) ([]byte, error) { pub, err := base64.StdEncoding.DecodeString(publicKey) if err != nil { - log.Error().Msg("Message encryption b64 failed") + return nil, fmt.Errorf("Message encryption b64 failed: %w", err) } ciphertext, err := encryptMessage(string(pub), crypto.NewPlainMessage(data)) if err != nil { - log.Error().Msg("Message encryption failed") - return nil, err + return nil, fmt.Errorf("Message encryption failed: %w", err) } return ciphertext.GetBinary(), err @@ -68,11 +65,11 @@ func AsymEncrypt(publicKey string, data []byte) ([]byte, error) { func AsymDecrypt(PrivateKey string, data []byte) ([]byte, error) { priv, err := base64.StdEncoding.DecodeString(PrivateKey) if err != nil { - log.Error().Msg("Message decryption b64 failed") + return nil, fmt.Errorf("Message decryption b64 failed: %w", err) } decrypted, err := decryptMessage(string(priv), nil, crypto.NewPGPMessage(data)) if err != nil { - log.Error().Msg("Message decryption failed") + return nil, fmt.Errorf("Message decryption failed: %w", err) } return decrypted.GetBinary(), err } @@ -80,11 +77,11 @@ func AsymDecrypt(PrivateKey string, data []byte) ([]byte, error) { func AsymEncryptArmored(PublicKey string, data []byte) ([]byte, error) { pub, err := base64.StdEncoding.DecodeString(PublicKey) if err != nil { - log.Error().Msg("Message encryption b64 failed") + return nil, fmt.Errorf("Message encryption b64 failed: %w", err) } armor, err := helper.EncryptBinaryMessageArmored(string(pub), data) if err != nil { - log.Error().Msg("Message encryption failed") + return nil, fmt.Errorf("Message encryption failed: %w", err) } return []byte(armor), err } @@ -92,11 +89,11 @@ func AsymEncryptArmored(PublicKey string, data []byte) ([]byte, error) { func AsymDecryptArmored(PrivateKey string, data []byte) ([]byte, error) { priv, err := base64.StdEncoding.DecodeString(PrivateKey) if err != nil { - log.Error().Msg("Message decryption b64 failed") + return nil, fmt.Errorf("Message decryption b64 failed: %w", err) } decrypted, err := helper.DecryptBinaryMessageArmored(string(priv), nil, string(data)) if err != nil { - log.Error().Msg("Message decryption failed") + return nil, fmt.Errorf("Message decryption failed: %w", err) } return []byte(decrypted), err } @@ -145,7 +142,7 @@ func encryptMessage(key string, message *crypto.PlainMessage) (*crypto.PGPMessag ciphertext, err := publicKeyRing.Encrypt(message, nil) if err != nil { - return nil, errors.Wrap(err, "gopenpgp: unable to encrypt message") + return nil, fmt.Errorf("gopenpgp: unable to encrypt message: %w", err) } return ciphertext, nil @@ -154,24 +151,24 @@ func encryptMessage(key string, message *crypto.PlainMessage) (*crypto.PGPMessag func decryptMessage(privateKey string, passphrase []byte, ciphertext *crypto.PGPMessage) (*crypto.PlainMessage, error) { privateKeyObj, err := crypto.NewKeyFromArmored(privateKey) if err != nil { - return nil, errors.Wrap(err, "gopenpgp: unable to parse the private key") + return nil, fmt.Errorf("gopenpgp: unable to parse the private key: %w", err) } privateKeyUnlocked, err := privateKeyObj.Unlock(passphrase) if err != nil { - return nil, errors.Wrap(err, "gopenpgp: unable to unlock key") + return nil, fmt.Errorf("gopenpgp: unable to unlock key: %w", err) } defer privateKeyUnlocked.ClearPrivateParams() privateKeyRing, err := crypto.NewKeyRing(privateKeyUnlocked) if err != nil { - return nil, errors.Wrap(err, "gopenpgp: unable to create the private key ring") + return nil, fmt.Errorf("gopenpgp: unable to create the private key ring: %w", err) } message, err := privateKeyRing.Decrypt(ciphertext, nil, 0) if err != nil { - return nil, errors.Wrap(err, "gopenpgp: unable to decrypt message") + return nil, fmt.Errorf("gopenpgp: unable to decrypt message: %w", err) } return message, nil @@ -180,19 +177,19 @@ func decryptMessage(privateKey string, passphrase []byte, ciphertext *crypto.PGP func createPublicKeyRing(publicKey string) (*crypto.KeyRing, error) { publicKeyObj, err := crypto.NewKeyFromArmored(publicKey) if err != nil { - return nil, errors.Wrap(err, "gopenpgp: unable to parse public key") + return nil, fmt.Errorf("gopenpgp: unable to parse public key: %w", err) } if publicKeyObj.IsPrivate() { publicKeyObj, err = publicKeyObj.ToPublic() if err != nil { - return nil, errors.Wrap(err, "gopenpgp: unable to extract public key from private key") + return nil, fmt.Errorf("gopenpgp: unable to extract public key from private key: %w", err) } } publicKeyRing, err := crypto.NewKeyRing(publicKeyObj) if err != nil { - return nil, errors.Wrap(err, "gopenpgp: unable to create new keyring") + return nil, fmt.Errorf("gopenpgp: unable to create new keyring: %w", err) } return publicKeyRing, nil @@ -202,18 +199,15 @@ func AsymEncryptAndSign(PublicEncryptionKey string, PrivateSignatureKey string, var enc EncryptedMessage pub, err := base64.StdEncoding.DecodeString(PublicEncryptionKey) if err != nil { - log.Error().Msg("Message encryption and sign b64 failed") - return nil, err + return nil, fmt.Errorf("Message encryption and sign b64 failed: %w", err) } priv, err := base64.StdEncoding.DecodeString(PrivateSignatureKey) if err != nil { - log.Error().Msg("Message encryption and sign b64 failed") - return nil, err + return nil, fmt.Errorf("Message encryption and sign b64 failed: %w", err) } ciphertext, signature, err := encryptAndSignMessage(string(pub), string(priv), crypto.NewPlainMessage(data)) if err != nil { - log.Error().Msg("Message encryption failed") - return nil, err + return nil, fmt.Errorf("Message encryption failed: %w", err) } enc.Data = ciphertext.GetBinary() enc.Signature = []byte(signature) @@ -223,18 +217,15 @@ func AsymEncryptAndSign(PublicEncryptionKey string, PrivateSignatureKey string, func AsymDecryptAndCheck(MyPrivateEncryptionKey string, MyContactPublicKey string, data []byte, Signature []byte) (DecryptedMessage []byte, err error) { priv, err := base64.StdEncoding.DecodeString(MyPrivateEncryptionKey) if err != nil { - log.Error().Msg("Message decryption and sign b64 failed") - return nil, err + return nil, fmt.Errorf("Message decryption and sign b64 failed: %w", err) } pub, err := base64.StdEncoding.DecodeString(MyContactPublicKey) if err != nil { - log.Error().Msg("Message decryption and sign b64 failed") - return nil, err + return nil, fmt.Errorf("Message decryption and sign b64 failed: %w", err) } DecryptedMessage, err = decryptAndCheckMessage(string(pub), string(priv), crypto.NewPGPMessage(data), crypto.NewPGPSignature(Signature)) if err != nil { - log.Error().Msg("Message decryption and sign failed") - return nil, err + return nil, fmt.Errorf("Message decryption and sign failed: %w", err) } return DecryptedMessage, err } @@ -248,30 +239,30 @@ func encryptAndSignMessage(pub string, priv string, message *crypto.PlainMessage } if err != nil { - return nil, nil, errors.Wrap(err, "gopenpgp: unable to encrypt message") + return nil, nil, fmt.Errorf("gopenpgp: unable to encrypt message") } if privateKeyObj, err = crypto.NewKeyFromArmored(priv); err != nil { - return nil, nil, errors.Wrap(err, "gopenpgp: unable to parse private key") + return nil, nil, fmt.Errorf("gopenpgp: unable to parse private key") } if unlockedKeyObj, err = privateKeyObj.Unlock(nil); err != nil { - return nil, nil, errors.Wrap(err, "gopenpgp: unable to unlock key") + return nil, nil, fmt.Errorf("gopenpgp: unable to unlock key") } defer unlockedKeyObj.ClearPrivateParams() if privateKeyRing, err = crypto.NewKeyRing(unlockedKeyObj); err != nil { - return nil, nil, errors.Wrap(err, "gopenpgp: unable to create private keyring") + return nil, nil, fmt.Errorf("gopenpgp: unable to create private keyring") } ciphertext, err := publicKeyRing.Encrypt(message, nil) if err != nil { - return nil, nil, errors.Wrap(err, "gopenpgp: unable to encrypt message") + return nil, nil, fmt.Errorf("gopenpgp: unable to encrypt message") } signature, err := privateKeyRing.SignDetached(message) if err != nil { - return nil, nil, errors.Wrap(err, "gopenpgp: unable to encrypt message") + return nil, nil, fmt.Errorf("gopenpgp: unable to encrypt message") } return ciphertext, signature.GetBinary(), nil } @@ -285,30 +276,30 @@ func decryptAndCheckMessage(pub string, priv string, message *crypto.PGPMessage, } if err != nil { - return nil, errors.Wrap(err, "gopenpgp: unable to encrypt message") + return nil, fmt.Errorf("gopenpgp: unable to encrypt message") } if privateKeyObj, err = crypto.NewKeyFromArmored(priv); err != nil { - return nil, errors.Wrap(err, "gopenpgp: unable to parse private key") + return nil, fmt.Errorf("gopenpgp: unable to parse private key") } if unlockedKeyObj, err = privateKeyObj.Unlock(nil); err != nil { - return nil, errors.Wrap(err, "gopenpgp: unable to unlock key") + return nil, fmt.Errorf("gopenpgp: unable to unlock key") } defer unlockedKeyObj.ClearPrivateParams() if privateKeyRing, err = crypto.NewKeyRing(unlockedKeyObj); err != nil { - return nil, errors.Wrap(err, "gopenpgp: unable to create private keyring") + return nil, fmt.Errorf("gopenpgp: unable to create private keyring") } plainmessage, err := privateKeyRing.Decrypt(message, nil, 0) if err != nil { - return nil, errors.Wrap(err, "gopenpgp: unable to decrypt message") + return nil, fmt.Errorf("gopenpgp: unable to decrypt message") } err = publicKeyRing.VerifyDetached(plainmessage, signature, crypto.GetUnixTime()) if err != nil { - return nil, errors.Wrap(err, "gopenpgp: unable to check message signature") + return nil, fmt.Errorf("gopenpgp: unable to check message signature") } return plainmessage.GetBinary(), nil } diff --git a/asymcrypt_test.go b/asymcrypt_test.go index bd89552..f7ad571 100644 --- a/asymcrypt_test.go +++ b/asymcrypt_test.go @@ -57,12 +57,21 @@ func TestGetKey(t *testing.T) { } // fmt.Println(kp.Public) // fmt.Println(kp.Private) - key := kp.GetCryptoKeyObject() + key, err := kp.GetCryptoKeyObject() + if err != nil { + t.Fatal(err) + } // fmt.Println(key.Armor()) - Armpubkey, _ := key.GetArmoredPublicKey() + Armpubkey, err := key.GetArmoredPublicKey() + if err != nil { + t.Fatal(err) + } pubkey := base64.StdEncoding.EncodeToString([]byte(Armpubkey)) println(len([]byte(pubkey))) - binpubkey, _ := key.GetPublicKey() + binpubkey, err := key.GetPublicKey() + if err != nil { + t.Fatal(err) + } println(len(binpubkey)) assert.Equal(t, kp.Public, pubkey, "The two public keys should be the same.") //if kp.Public != pubkey { diff --git a/client/config.go b/client/config.go index 4486c40..891714e 100644 --- a/client/config.go +++ b/client/config.go @@ -3,6 +3,7 @@ package client import ( "encoding/json" "errors" + "fmt" "os" "sync" @@ -65,6 +66,18 @@ func GetConfig() *Config { return instance } +func (c *Config) Validate() error { + if c.StoragePath == "" { + return errors.New("storage_path is required") + } + + if c.Chunksize < 1024 || c.Chunksize > 10*1024*1024 { + return fmt.Errorf("chunksize must be between 1KB and 10MB, got %d", c.Chunksize) + } + + return nil +} + func (c *Config) Load(filename string) error { data, err := os.ReadFile(filename) if err != nil { @@ -74,6 +87,9 @@ func (c *Config) Load(filename string) error { if err != nil { return err } + if err := c.Validate(); err != nil { + return fmt.Errorf("invalid config: %w", err) + } // override values if not set or wrong if c.HttpTimeOut <= 0 { c.HttpTimeOut = 10 diff --git a/client/messagestorage.go b/client/messagestorage.go index c3a492f..1b12a75 100644 --- a/client/messagestorage.go +++ b/client/messagestorage.go @@ -44,7 +44,10 @@ func StoreMessage(peer *Peer, usermessage *meowlib.UserMessage, filenames []stri dbid = peer.DbIds[len(peer.DbIds)-1] } // Open Db - db, _ := sql.Open("sqlite3", filepath.Join(cfg.StoragePath, identity.Uuid, dbid+GetConfig().DbSuffix)) // Open the created SQLite File + db, err := sql.Open("sqlite3", filepath.Join(cfg.StoragePath, identity.Uuid, dbid+GetConfig().DbSuffix)) // Open the created SQLite File + if err != nil { + return err + } defer db.Close() // Detach Files hiddenFilenames := []string{} @@ -114,7 +117,10 @@ func GetNewMessages(peer *Peer, lastDbId int, password string) ([]*InternalUserM } fileidx := len(peer.DbIds) - 1 // There fileidx should provide the db that we need (unless wantMore overlaps the next DB) - db, _ := sql.Open("sqlite3", filepath.Join(cfg.StoragePath, identity.Uuid, peer.DbIds[fileidx]+GetConfig().DbSuffix)) // Open the created SQLite File + db, err := sql.Open("sqlite3", filepath.Join(cfg.StoragePath, identity.Uuid, peer.DbIds[fileidx]+GetConfig().DbSuffix)) // Open the created SQLite File + if err != nil { + return nil, err + } defer db.Close() // if it's first app query, it won't hold a lastIndex, so let's start from end if lastDbId == 0 { @@ -184,7 +190,10 @@ func GetMessagesHistory(peer *Peer, inAppMsgCount int, lastDbId int, wantMore in countStack += newCount } // There fileidx should provide the db that we need (unless wantMore overlaps the next DB) - db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, peer.DbIds[fileidx]+GetConfig().DbSuffix)) // Open the created SQLite File + db, err := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, peer.DbIds[fileidx]+GetConfig().DbSuffix)) // Open the created SQLite File + if err != nil { + return nil, err + } defer db.Close() // if it's first app query, it won't hold a lastIndex, so let's start from end if lastDbId == 0 { @@ -231,7 +240,10 @@ func GetMessagesHistory(peer *Peer, inAppMsgCount int, lastDbId int, wantMore in func GetDbMessage(dbFile string, dbId int64, password string) (*meowlib.DbMessage, error) { // There fileidx should provide the db that we need (unless wantMore overlaps the next DB) - db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, dbFile+GetConfig().DbSuffix)) // Open the created SQLite File + db, err := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, dbFile+GetConfig().DbSuffix)) // Open the created SQLite dbFile + if err != nil { + return nil, err + } defer db.Close() stm, err := db.Prepare("SELECT id, m FROM message WHERE id=?") @@ -267,7 +279,10 @@ func GetDbMessage(dbFile string, dbId int64, password string) (*meowlib.DbMessag } func UpdateDbMessage(dbm *meowlib.DbMessage, dbFile string, dbId int64, password string) error { - db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, dbFile+GetConfig().DbSuffix)) // Open the created SQLite File + db, err := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, dbFile+GetConfig().DbSuffix)) // Open the created SQLite dbFile + if err != nil { + return err + } defer db.Close() // Encrypt message out, err := proto.Marshal(dbm) @@ -325,11 +340,14 @@ func InternalUserMessagePreview(msg *InternalUserMessage, password string) ([]by } func getMessageCount(dbid string) (int, error) { - db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, dbid+GetConfig().DbSuffix)) // Open the created SQLite File + db, err := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, dbid+GetConfig().DbSuffix)) // Open the created SQLite File + if err != nil { + return 0, err + } defer db.Close() var count int query := "SELECT COUNT(*) FROM message" - err := db.QueryRow(query).Scan(&count) + err = db.QueryRow(query).Scan(&count) if err != nil { return 0, err } diff --git a/server/invitation.go b/server/invitation.go index 9440e61..f50b7e8 100644 --- a/server/invitation.go +++ b/server/invitation.go @@ -12,6 +12,8 @@ import ( "github.com/go-redis/redis" ) +const MaxShortcodeLength = 64 + func (r *RedisRouter) StoreInvitation(invitation []byte, timeout int, password string, serverTimeout int, urlLen int) (string, time.Time, error) { id, err := r.createShortId(urlLen) if err != nil { @@ -84,10 +86,10 @@ func (r *RedisRouter) GetInvitation(id string, password string) ([]byte, error) // deleteInvitation removes all invitation-related keys from Redis func (r *RedisRouter) deleteInvitation(id string) { - r.Client.Del("mwiv:" + id) // invitation data - r.Client.Del("mwpw:" + id) // password - r.Client.Del("mwfa:" + id) // failed attempts - r.Client.Del("mwan:" + id) // answer to invitation + r.Client.Del("mwiv:" + id) // invitation data + r.Client.Del("mwpw:" + id) // password + r.Client.Del("mwfa:" + id) // failed attempts + r.Client.Del("mwan:" + id) // answer to invitation } func (r *RedisRouter) StoreAnswerToInvitation(id string, timeout int, invitation []byte, serverTimeout int) time.Time { @@ -110,6 +112,9 @@ func (r *RedisRouter) createShortId(length int) (string, error) { alphabet := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" alphabetLen := big.NewInt(int64(len(alphabet))) + if length < 1 || length > MaxShortcodeLength { + return "", fmt.Errorf("invalid shortcode length: %d (must be 1-%d)", length, MaxShortcodeLength) + } // for not in redis for { var id strings.Builder diff --git a/symcrypt.go b/symcrypt.go index ab7d2ed..b5d40ee 100644 --- a/symcrypt.go +++ b/symcrypt.go @@ -1,8 +1,9 @@ package meowlib import ( + "fmt" + "github.com/ProtonMail/gopenpgp/v2/crypto" - "github.com/pkg/errors" ) func SymEncrypt(password string, data []byte) ([]byte, error) { @@ -12,7 +13,7 @@ func SymEncrypt(password string, data []byte) ([]byte, error) { pgpMessage, err = crypto.EncryptMessageWithPassword(message, []byte(password)) if err != nil { - return nil, errors.Wrap(err, "gopenpgp: unable to encrypt message with password") + return nil, fmt.Errorf("gopenpgp: unable to encrypt message with password: %w", err) } return pgpMessage.GetBinary(), nil } @@ -25,7 +26,7 @@ func SymDecrypt(password string, data []byte) ([]byte, error) { pgpMessage = crypto.NewPGPMessage(data) message, err = crypto.DecryptMessageWithPassword(pgpMessage, []byte(password)) if err != nil { - return nil, errors.Wrap(err, "gopenpgp: unable to decrypt message with password") + return nil, fmt.Errorf("gopenpgp: unable to decrypt message with password: %w", err) } return message.GetBinary(), nil }