GODT-2034: Basic vault migration ability (proof of concept)
This commit is contained in:
parent
4c4c592f31
commit
6bf67917fb
|
@ -15,6 +15,9 @@ issues:
|
|||
- at least one file in a package should have a package comment
|
||||
# Package comments.
|
||||
- "package-comments: should have a package comment"
|
||||
# Migration uses underscores to make versions clearer.
|
||||
- "var-naming: don't use underscores in Go names"
|
||||
- "ST1003: should not use underscores in Go names"
|
||||
|
||||
exclude-rules:
|
||||
- path: _test\.go
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) 2022 Proton AG
|
||||
//
|
||||
// This file is part of Proton Mail Bridge.
|
||||
//
|
||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package vault
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Version int
|
||||
|
||||
const (
|
||||
v2_3_x Version = iota
|
||||
v2_4_x
|
||||
v2_5_x
|
||||
|
||||
Current = v2_5_x
|
||||
)
|
||||
|
||||
// upgrade migrates the vault from the given version to the next version.
|
||||
func upgrade(v Version, b []byte) ([]byte, error) {
|
||||
switch v {
|
||||
case v2_3_x:
|
||||
return upgrade_2_3_x(b)
|
||||
|
||||
case v2_4_x:
|
||||
return upgrade_2_4_x(b)
|
||||
|
||||
case Current:
|
||||
return nil, fmt.Errorf("already at current version %d", Current)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown version %d", v)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright (c) 2022 Proton AG
|
||||
//
|
||||
// This file is part of Proton Mail Bridge.
|
||||
//
|
||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package vault
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/sha256"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/vmihailenco/msgpack/v5"
|
||||
)
|
||||
|
||||
func TestMigrate(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
// Create a v2.3.x vault.
|
||||
b := newLegacyVault(t, []byte("my secret key"), v2_3_x, Data_2_3_x{
|
||||
Settings: Settings_2_3_x{
|
||||
GluonDir: "v2.3.x-gluon-dir",
|
||||
IMAPPort: "1234", // string in v2.3.x, current version uses int
|
||||
SMTPPort: "5678", // string in v2.3.x, current version uses int
|
||||
},
|
||||
Users: []UserData_2_3_x{{
|
||||
ID: "user-id", // called "ID" in v2.3.x, current version has "UserID"
|
||||
Name: "user-name", // called "Name" in v2.3.x, current version has "Username"
|
||||
GluonKey: []byte("gluon-key"), // []byte in v2.3.x and current version, string in v2.4.x (intermediate)
|
||||
SplitMode: true, // bool in v2.3.x and v2.4.x, enum in current version
|
||||
}},
|
||||
})
|
||||
|
||||
// Write the vault to disk.
|
||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "vault.enc"), b, 0600))
|
||||
|
||||
// Migrate the vault.
|
||||
s, corrupt, err := New(dir, "default-gluon-dir", []byte("my secret key"))
|
||||
require.NoError(t, err)
|
||||
require.False(t, corrupt)
|
||||
|
||||
// Check the migrated vault.
|
||||
require.Equal(t, "v2.3.x-gluon-dir", s.GetGluonDir())
|
||||
require.Equal(t, 1234, s.GetIMAPPort())
|
||||
require.Equal(t, 5678, s.GetSMTPPort())
|
||||
|
||||
// The user should be migrated.
|
||||
userIDs := s.GetUserIDs()
|
||||
require.Len(t, userIDs, 1)
|
||||
|
||||
// The migrated user should be correct.
|
||||
require.NoError(t, s.GetUser("user-id", func(user *User) {
|
||||
require.Equal(t, "user-id", user.UserID())
|
||||
require.Equal(t, "user-name", user.Username())
|
||||
require.Equal(t, []byte("gluon-key"), user.GluonKey())
|
||||
require.Equal(t, SplitMode, user.AddressMode())
|
||||
}))
|
||||
}
|
||||
|
||||
func newLegacyVault[T any](t *testing.T, key []byte, version Version, data T) []byte {
|
||||
hash256 := sha256.Sum256(key)
|
||||
|
||||
aes, err := aes.NewCipher(hash256[:])
|
||||
require.NoError(t, err)
|
||||
|
||||
gcm, err := cipher.NewGCM(aes)
|
||||
require.NoError(t, err)
|
||||
|
||||
b, err := msgpack.Marshal(data)
|
||||
require.NoError(t, err)
|
||||
|
||||
dec, err := msgpack.Marshal(File{Version: version, Data: b})
|
||||
require.NoError(t, err)
|
||||
|
||||
nonce, err := crypto.RandomToken(gcm.NonceSize())
|
||||
require.NoError(t, err)
|
||||
|
||||
return gcm.Seal(nonce, nonce, dec, nil)
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright (c) 2022 Proton AG
|
||||
//
|
||||
// This file is part of Proton Mail Bridge.
|
||||
//
|
||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package vault
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/bradenaw/juniper/xslices"
|
||||
"github.com/vmihailenco/msgpack/v5"
|
||||
)
|
||||
|
||||
type Data_2_3_x struct {
|
||||
Settings Settings_2_3_x
|
||||
Users []UserData_2_3_x
|
||||
}
|
||||
|
||||
func (data Data_2_3_x) migrate() Data_2_4_x {
|
||||
return Data_2_4_x{
|
||||
Settings: data.Settings.migrate(),
|
||||
Users: xslices.Map(data.Users, func(user UserData_2_3_x) UserData_2_4_x { return user.migrate() }),
|
||||
}
|
||||
}
|
||||
|
||||
type Settings_2_3_x struct {
|
||||
GluonDir string
|
||||
|
||||
IMAPPort string
|
||||
SMTPPort string
|
||||
}
|
||||
|
||||
func (settings Settings_2_3_x) migrate() Settings_2_4_x {
|
||||
imapPort, err := strconv.Atoi(settings.IMAPPort)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
smtpPort, err := strconv.Atoi(settings.SMTPPort)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return Settings_2_4_x{
|
||||
GluonDir: settings.GluonDir,
|
||||
|
||||
IMAPPort: imapPort,
|
||||
SMTPPort: smtpPort,
|
||||
}
|
||||
}
|
||||
|
||||
type UserData_2_3_x struct {
|
||||
ID string
|
||||
Name string
|
||||
|
||||
GluonKey []byte
|
||||
SplitMode bool
|
||||
}
|
||||
|
||||
func (user UserData_2_3_x) migrate() UserData_2_4_x {
|
||||
return UserData_2_4_x{
|
||||
UserID: user.ID,
|
||||
Username: user.Name,
|
||||
GluonKey: string(user.GluonKey),
|
||||
SplitMode: user.SplitMode,
|
||||
}
|
||||
}
|
||||
|
||||
func upgrade_2_3_x(b []byte) ([]byte, error) {
|
||||
var old Data_2_3_x
|
||||
|
||||
if err := msgpack.Unmarshal(b, &old); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return msgpack.Marshal(old.migrate())
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
// Copyright (c) 2022 Proton AG
|
||||
//
|
||||
// This file is part of Proton Mail Bridge.
|
||||
//
|
||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package vault
|
||||
|
||||
import (
|
||||
"github.com/bradenaw/juniper/xslices"
|
||||
"github.com/vmihailenco/msgpack/v5"
|
||||
)
|
||||
|
||||
type Data_2_4_x struct {
|
||||
Settings Settings_2_4_x
|
||||
Users []UserData_2_4_x
|
||||
}
|
||||
|
||||
func (data Data_2_4_x) migrate() Data {
|
||||
return Data{
|
||||
Settings: data.Settings.migrate(),
|
||||
Users: xslices.Map(data.Users, func(user UserData_2_4_x) UserData { return user.migrate() }),
|
||||
}
|
||||
}
|
||||
|
||||
type Settings_2_4_x struct {
|
||||
GluonDir string
|
||||
|
||||
IMAPPort int
|
||||
SMTPPort int
|
||||
}
|
||||
|
||||
func (settings Settings_2_4_x) migrate() Settings {
|
||||
newSettings := newDefaultSettings(settings.GluonDir)
|
||||
|
||||
newSettings.IMAPPort = settings.IMAPPort
|
||||
newSettings.SMTPPort = settings.SMTPPort
|
||||
|
||||
return newSettings
|
||||
}
|
||||
|
||||
type UserData_2_4_x struct {
|
||||
UserID string
|
||||
Username string
|
||||
|
||||
GluonKey string
|
||||
SplitMode bool
|
||||
}
|
||||
|
||||
func (user UserData_2_4_x) migrate() UserData {
|
||||
var mode AddressMode
|
||||
|
||||
if user.SplitMode {
|
||||
mode = SplitMode
|
||||
} else {
|
||||
mode = CombinedMode
|
||||
}
|
||||
|
||||
return UserData{
|
||||
UserID: user.UserID,
|
||||
Username: user.Username,
|
||||
GluonKey: []byte(user.GluonKey),
|
||||
AddressMode: mode,
|
||||
}
|
||||
}
|
||||
|
||||
func upgrade_2_4_x(b []byte) ([]byte, error) {
|
||||
var old Data_2_4_x
|
||||
|
||||
if err := msgpack.Unmarshal(b, &old); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return msgpack.Marshal(old.migrate())
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2022 Proton AG
|
||||
//
|
||||
// This file is part of Proton Mail Bridge.
|
||||
//
|
||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package vault
|
||||
|
||||
import "github.com/ProtonMail/proton-bridge/v2/internal/certs"
|
||||
|
||||
type Certs struct {
|
||||
Bridge Cert
|
||||
Installed bool
|
||||
}
|
||||
|
||||
type Cert struct {
|
||||
Cert, Key []byte
|
||||
}
|
||||
|
||||
func newDefaultCerts() Certs {
|
||||
return Certs{
|
||||
Bridge: newTLSCert(),
|
||||
}
|
||||
}
|
||||
|
||||
func newTLSCert() Cert {
|
||||
template, err := certs.NewTLSTemplate()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
certPEM, keyPEM, err := certs.GenerateCert(template)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return Cert{
|
||||
Cert: certPEM,
|
||||
Key: keyPEM,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2022 Proton AG
|
||||
//
|
||||
// This file is part of Proton Mail Bridge.
|
||||
//
|
||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package vault
|
||||
|
||||
type Data struct {
|
||||
Settings Settings
|
||||
Users []UserData
|
||||
Cookies []byte
|
||||
Certs Certs
|
||||
}
|
||||
|
||||
func newDefaultData(gluonDir string) Data {
|
||||
return Data{
|
||||
Settings: newDefaultSettings(gluonDir),
|
||||
Certs: newDefaultCerts(),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright (c) 2022 Proton AG
|
||||
//
|
||||
// This file is part of Proton Mail Bridge.
|
||||
//
|
||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package vault
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
|
||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||
"github.com/vmihailenco/msgpack/v5"
|
||||
)
|
||||
|
||||
// File holds a versioned, serialized data.
|
||||
type File struct {
|
||||
Version Version
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func unmarshalFile[T any](gcm cipher.AEAD, enc []byte, data *T) error {
|
||||
dec, err := gcm.Open(nil, enc[:gcm.NonceSize()], enc[gcm.NonceSize():], nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var f File
|
||||
|
||||
if err := msgpack.Unmarshal(dec, &f); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for v := f.Version; v < Current; v++ {
|
||||
b, err := upgrade(v, f.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.Data = b
|
||||
}
|
||||
|
||||
if err := msgpack.Unmarshal(f.Data, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func marshalFile[T any](gcm cipher.AEAD, t T) ([]byte, error) {
|
||||
b, err := msgpack.Marshal(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dec, err := msgpack.Marshal(File{Version: Current, Data: b})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonce, err := crypto.RandomToken(gcm.NonceSize())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return gcm.Seal(nonce, nonce, dec, nil), nil
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) 2022 Proton AG
|
||||
//
|
||||
// This file is part of Proton Mail Bridge.
|
||||
//
|
||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package vault
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"runtime"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||
)
|
||||
|
||||
type Settings struct {
|
||||
GluonDir string
|
||||
|
||||
IMAPPort int
|
||||
SMTPPort int
|
||||
IMAPSSL bool
|
||||
SMTPSSL bool
|
||||
|
||||
UpdateChannel updater.Channel
|
||||
UpdateRollout float64
|
||||
|
||||
ColorScheme string
|
||||
ProxyAllowed bool
|
||||
ShowAllMail bool
|
||||
Autostart bool
|
||||
AutoUpdate bool
|
||||
|
||||
LastVersion string
|
||||
FirstStart bool
|
||||
FirstStartGUI bool
|
||||
|
||||
SyncWorkers int
|
||||
SyncBuffer int
|
||||
}
|
||||
|
||||
func newDefaultSettings(gluonDir string) Settings {
|
||||
return Settings{
|
||||
GluonDir: gluonDir,
|
||||
|
||||
IMAPPort: 1143,
|
||||
SMTPPort: 1025,
|
||||
IMAPSSL: false,
|
||||
SMTPSSL: false,
|
||||
|
||||
UpdateChannel: updater.DefaultUpdateChannel,
|
||||
UpdateRollout: rand.Float64(), //nolint:gosec
|
||||
|
||||
ColorScheme: "",
|
||||
ProxyAllowed: true,
|
||||
ShowAllMail: true,
|
||||
Autostart: false,
|
||||
AutoUpdate: true,
|
||||
|
||||
LastVersion: "0.0.0",
|
||||
FirstStart: true,
|
||||
FirstStartGUI: true,
|
||||
|
||||
SyncWorkers: runtime.NumCPU(),
|
||||
SyncBuffer: runtime.NumCPU(),
|
||||
}
|
||||
}
|
|
@ -17,81 +17,7 @@
|
|||
|
||||
package vault
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"runtime"
|
||||
|
||||
"github.com/ProtonMail/gluon/imap"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||
)
|
||||
|
||||
type Data struct {
|
||||
Settings Settings
|
||||
Users []UserData
|
||||
Cookies []byte
|
||||
Certs Certs
|
||||
}
|
||||
|
||||
type Certs struct {
|
||||
Bridge Cert
|
||||
Installed bool
|
||||
}
|
||||
|
||||
type Cert struct {
|
||||
Cert, Key []byte
|
||||
}
|
||||
|
||||
type Settings struct {
|
||||
GluonDir string
|
||||
|
||||
IMAPPort int
|
||||
SMTPPort int
|
||||
IMAPSSL bool
|
||||
SMTPSSL bool
|
||||
|
||||
UpdateChannel updater.Channel
|
||||
UpdateRollout float64
|
||||
|
||||
ColorScheme string
|
||||
ProxyAllowed bool
|
||||
ShowAllMail bool
|
||||
Autostart bool
|
||||
AutoUpdate bool
|
||||
|
||||
LastVersion string
|
||||
FirstStart bool
|
||||
FirstStartGUI bool
|
||||
|
||||
SyncWorkers int
|
||||
SyncBuffer int
|
||||
}
|
||||
|
||||
func newDefaultSettings(gluonDir string) Settings {
|
||||
return Settings{
|
||||
GluonDir: gluonDir,
|
||||
|
||||
IMAPPort: 1143,
|
||||
SMTPPort: 1025,
|
||||
IMAPSSL: false,
|
||||
SMTPSSL: false,
|
||||
|
||||
UpdateChannel: updater.DefaultUpdateChannel,
|
||||
UpdateRollout: rand.Float64(), //nolint:gosec
|
||||
|
||||
ColorScheme: "",
|
||||
ProxyAllowed: true,
|
||||
ShowAllMail: true,
|
||||
Autostart: false,
|
||||
AutoUpdate: true,
|
||||
|
||||
LastVersion: "0.0.0",
|
||||
FirstStart: true,
|
||||
FirstStartGUI: true,
|
||||
|
||||
SyncWorkers: runtime.NumCPU(),
|
||||
SyncBuffer: runtime.NumCPU(),
|
||||
}
|
||||
}
|
||||
import "github.com/ProtonMail/gluon/imap"
|
||||
|
||||
// UserData holds information about a single bridge user.
|
||||
// The user may or may not be logged in.
|
|
@ -20,7 +20,6 @@ package vault
|
|||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -29,10 +28,8 @@ import (
|
|||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/certs"
|
||||
"github.com/bradenaw/juniper/xslices"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/vmihailenco/msgpack/v5"
|
||||
)
|
||||
|
||||
// Vault is an encrypted data vault that stores bridge and user data.
|
||||
|
@ -228,9 +225,7 @@ func newVault(path, gluonDir string, gcm cipher.AEAD) (*Vault, bool, error) {
|
|||
|
||||
var corrupt bool
|
||||
|
||||
if dec, err := decrypt(gcm, enc); err != nil {
|
||||
corrupt = true
|
||||
} else if err := msgpack.Unmarshal(dec, new(Data)); err != nil {
|
||||
if err := unmarshalFile(gcm, enc, new(Data)); err != nil {
|
||||
corrupt = true
|
||||
}
|
||||
|
||||
|
@ -255,14 +250,9 @@ func (vault *Vault) get() Data {
|
|||
vault.encLock.RLock()
|
||||
defer vault.encLock.RUnlock()
|
||||
|
||||
dec, err := decrypt(vault.gcm, vault.enc)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var data Data
|
||||
|
||||
if err := msgpack.Unmarshal(dec, &data); err != nil {
|
||||
if err := unmarshalFile(vault.gcm, vault.enc, &data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
|
@ -273,25 +263,15 @@ func (vault *Vault) mod(fn func(data *Data)) error {
|
|||
vault.encLock.Lock()
|
||||
defer vault.encLock.Unlock()
|
||||
|
||||
dec, err := decrypt(vault.gcm, vault.enc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var data Data
|
||||
|
||||
if err := msgpack.Unmarshal(dec, &data); err != nil {
|
||||
if err := unmarshalFile(vault.gcm, vault.enc, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fn(&data)
|
||||
|
||||
mod, err := msgpack.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
enc, err := encrypt(vault.gcm, mod)
|
||||
enc, err := marshalFile(vault.gcm, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -318,23 +298,7 @@ func (vault *Vault) modUser(userID string, fn func(userData *UserData)) error {
|
|||
}
|
||||
|
||||
func initVault(path, gluonDir string, gcm cipher.AEAD) ([]byte, error) {
|
||||
bridgeCert, err := newTLSCert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dec, err := msgpack.Marshal(Data{
|
||||
Settings: newDefaultSettings(gluonDir),
|
||||
|
||||
Certs: Certs{
|
||||
Bridge: bridgeCert,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
enc, err := encrypt(gcm, dec)
|
||||
enc, err := marshalFile(gcm, newDefaultData(gluonDir))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -345,34 +309,3 @@ func initVault(path, gluonDir string, gcm cipher.AEAD) ([]byte, error) {
|
|||
|
||||
return enc, nil
|
||||
}
|
||||
|
||||
func decrypt(gcm cipher.AEAD, enc []byte) ([]byte, error) {
|
||||
return gcm.Open(nil, enc[:gcm.NonceSize()], enc[gcm.NonceSize():], nil)
|
||||
}
|
||||
|
||||
func encrypt(gcm cipher.AEAD, data []byte) ([]byte, error) {
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
|
||||
if _, err := rand.Read(nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return gcm.Seal(nonce, nonce, data, nil), nil
|
||||
}
|
||||
|
||||
func newTLSCert() (Cert, error) {
|
||||
template, err := certs.NewTLSTemplate()
|
||||
if err != nil {
|
||||
return Cert{}, err
|
||||
}
|
||||
|
||||
certPEM, keyPEM, err := certs.GenerateCert(template)
|
||||
if err != nil {
|
||||
return Cert{}, err
|
||||
}
|
||||
|
||||
return Cert{
|
||||
Cert: certPEM,
|
||||
Key: keyPEM,
|
||||
}, nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue