# README
ghoststring
A Go string wrapper type that is encrypted when stringified or JSONified. The following standard library interfaces are fulfilled:
json.Marshaler
json.Unmarshaler
fmt.Stringer
fmt.GoStringer
encoding.TextMarshaler
encoding.TextUnmarshaler
encoding.BinaryMarshaler
encoding.BinaryUnmarshaler
usage
Typical usage assumes at least two systems that share a key. For example, two or more web services may not directly communicate, but are passed data via state transfer through a browser session to which they both need access without leaking to the browser.
Each instance of GhostString
requires a non-empty namespace which must match a
registered Ghostifyer
, e.g.:
Declare a type that uses a GhostString
as a field:
type Message struct {
Recipient string `json:"recipient"`
Content ghoststring.GhostString `json:"content"`
Mood ghoststring.GhostString `json:"mood"`
}
Load a secret key from somewhere that meets your security requirements, such as from a mounted kubernetes secret:
secretKeyBytes, err := os.ReadFile("/path/to/secret-key")
if err != nil {
return err
}
Create a Ghostifyer
with the secret key and namespace in use:
gh, err := ghoststring.NewAES256GCMSingleKeyGhostifyer("heck.example.org", string(secretKeyBytes))
if err != nil {
return err
}
Register the Ghostifyer
:
if err := ghoststring.SetGhostifyer(gh); err != nil {
return err
}
Use the declared type as with any other struct type:
msg := &Message{
Recipient: "[email protected]",
Content: ghoststring.GhostString{
Namespace: "heck.example.org",
Str: "We meet me at the fjord at dawn. Bring donuts, please.",
},
Mood: ghoststring.GhostString{
Namespace: "heck.example.org",
Str: "giddy",
},
}
When the type instance is encoded to JSON, the GhostString
fields will automatically
encode to a JSON string type:
{
"recipient": "[email protected]",
"content": "👻:NTNjZjdmMWZjNzU2NTY1ODFkN2E4ZDI4aGVjay5leGFtcGxlLm9yZzo60XHqdSEbswpPbKLrDUuBZCrtUQLjmJ1NxHsqHMgBJjrB10O7JC4rwiMZw+wf/BOJGmu9ZCpMjgMpu18/VgDBpLn4n1nBNw==",
"mood": "👻:YmYzZDVjNDVhNTUyMTA1Mzg0ZWU0NjI0aGVjay5leGFtcGxlLm9yZzo6SML6iqmwSoDhX3b2SQXsMLJPMHHS"
}
In the event of an unrecognized namespace, the GhostString
fields will be encoded as
empty strings, e.g.:
msg := &Message{
Recipient: "[email protected]",
Content: ghoststring.GhostString{
Namespace: "heck.example.org",
Str: "Next time I'm bringing the coffee.",
},
Mood: ghoststring.GhostString{
Namespace: "wat.example.org",
Str: "zzz",
},
}
Because the "wat.example.org"
namespace isn't registered, the output will look like
this:
{
"recipient": "[email protected]",
"content": "👻:NTNjZjdmMWZjNzU2NTY1ODFkN2E4ZDI4aGVjay5leGFtcGxlLm9yZzo60XHqdSEbswpPbKLrDUuBZCrtUQLjmJ1NxHsqHMgBJjrB10O7JC4rwiMZw+wf/BOJGmu9ZCpMjgMpu18/VgDBpLn4n1nBNw==",
"mood": ""
}
Any system that needs to read the encrypted contents must decode the JSON into a type that
uses GhostString
for the matching fields in a process where a matching Ghostifyer
has
been registered. Other systems may treat the values as opaque strings.