diff --git a/.golangci.yml b/.golangci.yml
index 5b406a47..9ac51197 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -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
diff --git a/internal/vault/migrate.go b/internal/vault/migrate.go
new file mode 100644
index 00000000..f7639d0e
--- /dev/null
+++ b/internal/vault/migrate.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 .
+
+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)
+ }
+}
diff --git a/internal/vault/migrate_test.go b/internal/vault/migrate_test.go
new file mode 100644
index 00000000..0934faa0
--- /dev/null
+++ b/internal/vault/migrate_test.go
@@ -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 .
+
+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)
+}
diff --git a/internal/vault/migrate_v2.3.x.go b/internal/vault/migrate_v2.3.x.go
new file mode 100644
index 00000000..c88eaa9f
--- /dev/null
+++ b/internal/vault/migrate_v2.3.x.go
@@ -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 .
+
+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())
+}
diff --git a/internal/vault/migrate_v2.4.x.go b/internal/vault/migrate_v2.4.x.go
new file mode 100644
index 00000000..4f50a973
--- /dev/null
+++ b/internal/vault/migrate_v2.4.x.go
@@ -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 .
+
+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())
+}
diff --git a/internal/vault/types_certs.go b/internal/vault/types_certs.go
new file mode 100644
index 00000000..db7031b8
--- /dev/null
+++ b/internal/vault/types_certs.go
@@ -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 .
+
+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,
+ }
+}
diff --git a/internal/vault/types_data.go b/internal/vault/types_data.go
new file mode 100644
index 00000000..a285a9f0
--- /dev/null
+++ b/internal/vault/types_data.go
@@ -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 .
+
+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(),
+ }
+}
diff --git a/internal/vault/types_file.go b/internal/vault/types_file.go
new file mode 100644
index 00000000..b1c9fbda
--- /dev/null
+++ b/internal/vault/types_file.go
@@ -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 .
+
+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
+}
diff --git a/internal/vault/types_settings.go b/internal/vault/types_settings.go
new file mode 100644
index 00000000..48bb3319
--- /dev/null
+++ b/internal/vault/types_settings.go
@@ -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 .
+
+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(),
+ }
+}
diff --git a/internal/vault/types.go b/internal/vault/types_user.go
similarity index 62%
rename from internal/vault/types.go
rename to internal/vault/types_user.go
index 3971ac82..9673d554 100644
--- a/internal/vault/types.go
+++ b/internal/vault/types_user.go
@@ -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.
diff --git a/internal/vault/vault.go b/internal/vault/vault.go
index 0379a49e..657cb0bc 100644
--- a/internal/vault/vault.go
+++ b/internal/vault/vault.go
@@ -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
-}