go_encryptedstring/encryptedString.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
}