You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
296 lines
7.3 KiB
296 lines
7.3 KiB
// Copyright (c) 2022 Proton Technologies AG |
|
// |
|
// This file is part of ProtonMail Bridge. |
|
// |
|
// ProtonMail 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. |
|
// |
|
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>. |
|
|
|
package credentials |
|
|
|
import ( |
|
"bytes" |
|
"encoding/base64" |
|
"encoding/gob" |
|
"encoding/json" |
|
"fmt" |
|
"strings" |
|
"testing" |
|
|
|
r "github.com/stretchr/testify/require" |
|
) |
|
|
|
const testSep = "\n" |
|
const secretFormat = "%v" + testSep + // UserID, |
|
"%v" + testSep + // Name, |
|
"%v" + testSep + // Emails, |
|
"%v" + testSep + // APIToken, |
|
"%v" + testSep + // Mailbox, |
|
"%v" + testSep + // BridgePassword, |
|
"%v" + testSep + // Version string |
|
"%v" + testSep + // Timestamp, |
|
"%v" + testSep + // IsHidden, |
|
"%v" // IsCombinedAddressMode |
|
|
|
// the best would be to run this test on mac, win, and linux separately |
|
|
|
type testCredentials struct { |
|
UserID, |
|
Name, |
|
Emails, |
|
APIToken, |
|
Mailbox, |
|
BridgePassword, |
|
Version string |
|
Timestamp int64 |
|
IsHidden, |
|
IsCombinedAddressMode bool |
|
} |
|
|
|
func init() { //nolint[gochecknoinits] |
|
gob.Register(testCredentials{}) |
|
} |
|
|
|
func (s *testCredentials) MarshalGob() string { |
|
buf := bytes.Buffer{} |
|
enc := gob.NewEncoder(&buf) |
|
if err := enc.Encode(s); err != nil { |
|
return "" |
|
} |
|
log.Infof("MarshalGob: %#v\n", buf.String()) |
|
return base64.StdEncoding.EncodeToString(buf.Bytes()) |
|
} |
|
|
|
func (s *testCredentials) Clear() { |
|
s.UserID = "" |
|
s.Name = "" |
|
s.Emails = "" |
|
s.APIToken = "" |
|
s.Mailbox = "" |
|
s.BridgePassword = "" |
|
s.Version = "" |
|
s.Timestamp = 0 |
|
s.IsHidden = false |
|
s.IsCombinedAddressMode = false |
|
} |
|
|
|
func (s *testCredentials) UnmarshalGob(secret string) error { |
|
s.Clear() |
|
b, err := base64.StdEncoding.DecodeString(secret) |
|
if err != nil { |
|
log.Infoln("decode base64", b) |
|
return err |
|
} |
|
buf := bytes.NewBuffer(b) |
|
dec := gob.NewDecoder(buf) |
|
if err = dec.Decode(s); err != nil { |
|
log.Info("decode gob", b, buf.Bytes()) |
|
return err |
|
} |
|
return nil |
|
} |
|
|
|
func (s *testCredentials) ToJSON() string { |
|
if b, err := json.Marshal(s); err == nil { |
|
log.Infof("MarshalJSON: %#v\n", string(b)) |
|
return base64.StdEncoding.EncodeToString(b) |
|
} |
|
return "" |
|
} |
|
|
|
func (s *testCredentials) FromJSON(secret string) error { |
|
b, err := base64.StdEncoding.DecodeString(secret) |
|
if err != nil { |
|
return err |
|
} |
|
if err = json.Unmarshal(b, s); err == nil { |
|
return nil |
|
} |
|
return err |
|
} |
|
|
|
func (s *testCredentials) MarshalFmt() string { |
|
buf := bytes.Buffer{} |
|
fmt.Fprintf( |
|
&buf, secretFormat, |
|
s.UserID, |
|
s.Name, |
|
s.Emails, |
|
s.APIToken, |
|
s.Mailbox, |
|
s.BridgePassword, |
|
s.Version, |
|
s.Timestamp, |
|
s.IsHidden, |
|
s.IsCombinedAddressMode, |
|
) |
|
log.Infof("MarshalFmt: %#v\n", buf.String()) |
|
return base64.StdEncoding.EncodeToString(buf.Bytes()) |
|
} |
|
|
|
func (s *testCredentials) UnmarshalFmt(secret string) error { |
|
b, err := base64.StdEncoding.DecodeString(secret) |
|
if err != nil { |
|
return err |
|
} |
|
buf := bytes.NewBuffer(b) |
|
log.Infoln("decode fmt", b, buf.Bytes()) |
|
_, err = fmt.Fscanf( |
|
buf, secretFormat, |
|
&s.UserID, |
|
&s.Name, |
|
&s.Emails, |
|
&s.APIToken, |
|
&s.Mailbox, |
|
&s.BridgePassword, |
|
&s.Version, |
|
&s.Timestamp, |
|
&s.IsHidden, |
|
&s.IsCombinedAddressMode, |
|
) |
|
if err != nil { |
|
return err |
|
} |
|
return nil |
|
} |
|
|
|
func (s *testCredentials) MarshalStrings() string { // this is the most space efficient |
|
items := []string{ |
|
s.UserID, // 0 |
|
s.Name, // 1 |
|
s.Emails, // 2 |
|
s.APIToken, // 3 |
|
s.Mailbox, // 4 |
|
s.BridgePassword, // 5 |
|
s.Version, // 6 |
|
} |
|
items = append(items, fmt.Sprint(s.Timestamp)) // 7 |
|
|
|
if s.IsHidden { // 8 |
|
items = append(items, "1") |
|
} else { |
|
items = append(items, "") |
|
} |
|
|
|
if s.IsCombinedAddressMode { // 9 |
|
items = append(items, "1") |
|
} else { |
|
items = append(items, "") |
|
} |
|
|
|
str := strings.Join(items, sep) |
|
|
|
log.Infof("MarshalJoin: %#v\n", str) |
|
return base64.StdEncoding.EncodeToString([]byte(str)) |
|
} |
|
|
|
func (s *testCredentials) UnmarshalStrings(secret string) error { |
|
b, err := base64.StdEncoding.DecodeString(secret) |
|
if err != nil { |
|
return err |
|
} |
|
items := strings.Split(string(b), sep) |
|
if len(items) != 10 { |
|
return ErrWrongFormat |
|
} |
|
|
|
s.UserID = items[0] |
|
s.Name = items[1] |
|
s.Emails = items[2] |
|
s.APIToken = items[3] |
|
s.Mailbox = items[4] |
|
s.BridgePassword = items[5] |
|
s.Version = items[6] |
|
if _, err = fmt.Sscanf(items[7], "%d", &s.Timestamp); err != nil { |
|
s.Timestamp = 0 |
|
} |
|
if s.IsHidden = false; items[8] == "1" { |
|
s.IsHidden = true |
|
} |
|
if s.IsCombinedAddressMode = false; items[9] == "1" { |
|
s.IsCombinedAddressMode = true |
|
} |
|
return nil |
|
} |
|
|
|
func (s *testCredentials) IsSame(rhs *testCredentials) bool { |
|
return s.Name == rhs.Name && |
|
s.Emails == rhs.Emails && |
|
s.APIToken == rhs.APIToken && |
|
s.Mailbox == rhs.Mailbox && |
|
s.BridgePassword == rhs.BridgePassword && |
|
s.Version == rhs.Version && |
|
s.Timestamp == rhs.Timestamp && |
|
s.IsHidden == rhs.IsHidden && |
|
s.IsCombinedAddressMode == rhs.IsCombinedAddressMode |
|
} |
|
|
|
func TestMarshalFormats(t *testing.T) { |
|
input := testCredentials{UserID: "007", Emails: "ja@pm.me;jakub@cu.th", Timestamp: 152469263742, IsHidden: true} |
|
log.Infof("input %#v\n", input) |
|
|
|
secretStrings := input.MarshalStrings() |
|
log.Infof("secretStrings %#v %d\n", secretStrings, len(secretStrings)) |
|
secretGob := input.MarshalGob() |
|
log.Infof("secretGob %#v %d\n", secretGob, len(secretGob)) |
|
secretJSON := input.ToJSON() |
|
log.Infof("secretJSON %#v %d\n", secretJSON, len(secretJSON)) |
|
secretFmt := input.MarshalFmt() |
|
log.Infof("secretFmt %#v %d\n", secretFmt, len(secretFmt)) |
|
|
|
output := testCredentials{APIToken: "refresh"} |
|
r.NoError(t, output.UnmarshalStrings(secretStrings)) |
|
log.Infof("strings out %#v \n", output) |
|
r.True(t, input.IsSame(&output), "strings out not same") |
|
|
|
output = testCredentials{APIToken: "refresh"} |
|
r.NoError(t, output.UnmarshalGob(secretGob)) |
|
log.Infof("gob out %#v\n \n", output) |
|
r.Equal(t, input, output) |
|
|
|
output = testCredentials{APIToken: "refresh"} |
|
r.NoError(t, output.FromJSON(secretJSON)) |
|
log.Infof("json out %#v \n", output) |
|
r.True(t, input.IsSame(&output), "json out not same") |
|
|
|
/* |
|
// Simple Fscanf not working! |
|
output = testCredentials{APIToken: "refresh"} |
|
r.NoError(t, output.UnmarshalFmt(secretFmt)) |
|
log.Infof("fmt out %#v \n", output) |
|
r.True(t, input.IsSame(&output), "fmt out not same") |
|
*/ |
|
} |
|
|
|
func TestMarshal(t *testing.T) { |
|
input := Credentials{ |
|
UserID: "", |
|
Name: "007", |
|
Emails: "ja@pm.me;aj@cus.tom", |
|
APIToken: "sdfdsfsdfsdfsdf", |
|
MailboxPassword: []byte("cdcdcdcd"), |
|
BridgePassword: "wew123", |
|
Version: "k11", |
|
Timestamp: 152469263742, |
|
IsHidden: true, |
|
IsCombinedAddressMode: false, |
|
} |
|
log.Infof("input %#v\n", input) |
|
|
|
secret := input.Marshal() |
|
log.Infof("secret %#v %d\n", secret, len(secret)) |
|
|
|
output := Credentials{APIToken: "refresh"} |
|
r.NoError(t, output.Unmarshal(secret)) |
|
log.Infof("output %#v\n", output) |
|
r.Equal(t, input, output) |
|
}
|
|
|