177 lines
4.2 KiB
Go
177 lines
4.2 KiB
Go
package encryptedstring
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// IPotentiallyUnencrypted describes an interface for a struct which was potentially not encrypted
|
|
type IPotentiallyUnencrypted interface {
|
|
// WasUnencrypted determines wheter the value was set from data marked as unencrypted
|
|
WasUnencrypted() bool
|
|
}
|
|
|
|
// EncryptedString is a string wrapper which will en-/decrypt the actual value upon (un)marshalling
|
|
type EncryptedString struct {
|
|
data string
|
|
}
|
|
|
|
// New returns a new EncryptedString instance with a specific value
|
|
func New(value string) EncryptedString {
|
|
es := EncryptedString{}
|
|
es.encrypt(value)
|
|
return es
|
|
}
|
|
|
|
// Value returns the current value
|
|
func (es EncryptedString) Value() string {
|
|
if decrypted, err := es.decrypt(); err == nil {
|
|
return decrypted
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// String implements the fmt.Stringer interface
|
|
func (es EncryptedString) String() string {
|
|
if es.WasUnencrypted() {
|
|
if data, err := getEncryptionProvider().Encrypt([]byte(es.data)); err == nil {
|
|
return defaultIdentifier + hex.EncodeToString(data)
|
|
}
|
|
} else if strings.HasPrefix(es.data, defaultIdentifier) {
|
|
return es.data
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// SetValue sets the current value
|
|
func (es *EncryptedString) SetValue(newValue string) {
|
|
es.encrypt(newValue)
|
|
}
|
|
|
|
// WasUnencrypted determines wheter the value was set from data marked as unencrypted
|
|
func (es EncryptedString) WasUnencrypted() bool {
|
|
return !strings.HasPrefix(es.data, defaultIdentifier)
|
|
}
|
|
|
|
// MarshalJSON satisfies the json.Marshaler interface
|
|
func (es EncryptedString) MarshalJSON() ([]byte, error) {
|
|
return []byte(fmt.Sprintf("\"%s\"", es.String())), nil
|
|
}
|
|
|
|
// UnmarshalJSON satisfies the json.Unmarshaler interface
|
|
func (es *EncryptedString) UnmarshalJSON(data []byte) error {
|
|
cleanData, ok := unquoteBytes(data)
|
|
if ok {
|
|
es.data = string(cleanData)
|
|
if _, err := es.decrypt(); err == nil {
|
|
return nil
|
|
}
|
|
}
|
|
return fmt.Errorf("'%s' cannot be unmarshalled from JSON", string(data))
|
|
}
|
|
|
|
// MarshalXML satisfies the xml.Marshaler interface
|
|
func (es EncryptedString) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
|
err := e.EncodeToken(start)
|
|
if nil != err {
|
|
return err
|
|
}
|
|
stringValue := es.String()
|
|
err = e.EncodeToken(xml.CharData([]byte(stringValue)))
|
|
if nil != err {
|
|
return err
|
|
}
|
|
err = e.EncodeToken(xml.EndElement{
|
|
Name: start.Name,
|
|
})
|
|
if nil != err {
|
|
return err
|
|
}
|
|
return e.Flush()
|
|
}
|
|
|
|
// UnmarshalXML satisfies the xml.Unmarshaler interface
|
|
func (es *EncryptedString) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
|
var (
|
|
token xml.Token
|
|
err error
|
|
)
|
|
|
|
Loop:
|
|
for {
|
|
if token, err = d.Token(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if token == nil {
|
|
break
|
|
}
|
|
|
|
switch se := token.(type) {
|
|
case xml.StartElement:
|
|
return fmt.Errorf("bad XML syntax")
|
|
case xml.CharData:
|
|
stringData := strings.Trim(string(se), " \t\r\n")
|
|
if 0 < len(stringData) {
|
|
es.data = stringData
|
|
if _, err = es.decrypt(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
case xml.EndElement:
|
|
break Loop
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// MarshalText satisfies the TextMarshaler interface
|
|
func (es EncryptedString) MarshalText() (text []byte, err error) {
|
|
return []byte(es.String()), nil
|
|
}
|
|
|
|
// UnmarshalText satisfies the TextUnmarshaler interface
|
|
func (es *EncryptedString) UnmarshalText(text []byte) error {
|
|
es.data = string(text)
|
|
if _, err := es.decrypt(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (es *EncryptedString) encrypt(value string) {
|
|
payload := []byte(value)
|
|
additionalBytes := len(payload) % 16
|
|
for i := 0; i < additionalBytes; i++ {
|
|
payload = append(payload, 0)
|
|
}
|
|
if encrypted, err := getEncryptionProvider().Encrypt(payload); err == nil {
|
|
es.data = defaultIdentifier + hex.EncodeToString(encrypted)
|
|
}
|
|
}
|
|
|
|
func (es *EncryptedString) decrypt() (decrypted string, err error) {
|
|
if strings.HasPrefix(es.data, defaultIdentifier) {
|
|
var payload []byte
|
|
payload, err = hex.DecodeString(es.data[len(defaultIdentifier):])
|
|
if err == nil {
|
|
if unencrypted, err := getEncryptionProvider().Decrypt(payload); err == nil {
|
|
for len(unencrypted) > 0 {
|
|
if unencrypted[len(unencrypted)-1] == 0 {
|
|
unencrypted = unencrypted[:len(unencrypted)-1]
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
decrypted = string(unencrypted)
|
|
}
|
|
}
|
|
} else {
|
|
decrypted = es.data
|
|
}
|
|
return
|
|
}
|