initial commit

Signed-off-by: jolheiser <john.olheiser@gmail.com>
main v0.0.1
jolheiser 2022-09-25 23:27:53 -05:00
commit fcfe79244f
Signed by: jolheiser
GPG Key ID: B853ADA5DA7BBF7A
6 changed files with 188 additions and 0 deletions

19
LICENSE 100644
View File

@ -0,0 +1,19 @@
Copyright (c) 2022 John Olheiser
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

7
README.md 100644
View File

@ -0,0 +1,7 @@
# confage
Config values using [age](https://age-encryption.org).
## License
[MIT](LICENSE)

104
confage.go 100644
View File

@ -0,0 +1,104 @@
package confage
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"strings"
"filippo.io/age"
)
// Type is a config type
type Type[T any] struct {
i age.Identity
r age.Recipient
Value T
}
// MustNew returns a new Type, panicking on error
func MustNew[T any](key string, val T) Type[T] {
t, err := New(key, val)
if err != nil {
panic(err)
}
return t
}
// New returns a new Type
func New[T any](key string, val T) (v Type[T], err error) {
v.Value = val
var i age.Identity
var r age.Recipient
switch {
case strings.HasPrefix(key, "AGE-SECRET-KEY-1"):
ii, err := age.ParseX25519Identity(key)
if err != nil {
return v, err
}
i = ii
r = ii.Recipient()
default:
i, err = age.NewScryptIdentity(key)
if err != nil {
return v, err
}
r, err = age.NewScryptRecipient(key)
if err != nil {
return v, err
}
}
v.i, v.r = i, r
return v, nil
}
// String implements fmt.Stringer
// It simply returns MarshalText
func (t Type[T]) String() string {
b, err := t.MarshalText()
if err != nil {
return fmt.Sprintf("string error: %v", err)
}
return string(b)
}
// MarshalText implements encoding.MarshalText
func (v Type[T]) MarshalText() ([]byte, error) {
var b bytes.Buffer
w, err := age.Encrypt(&b, v.r)
if err != nil {
return nil, err
}
if err := json.NewEncoder(w).Encode(v.Value); err != nil {
return nil, err
}
if err := w.Close(); err != nil {
return nil, err
}
out := base64.StdEncoding.EncodeToString(b.Bytes())
return []byte(out), nil
}
// UnmarshalText implements encoding.UnmarshalText
func (v *Type[T]) UnmarshalText(text []byte) error {
b, err := base64.StdEncoding.DecodeString(string(text))
if err != nil {
return err
}
r, err := age.Decrypt(bytes.NewBuffer(b), v.i)
if err != nil {
return err
}
var out bytes.Buffer
if _, err := io.Copy(&out, r); err != nil {
return err
}
var val T
if err := json.Unmarshal(out.Bytes(), &val); err != nil {
return err
}
(*v).Value = val
return nil
}

37
confage_test.go 100644
View File

@ -0,0 +1,37 @@
package confage
import (
"encoding/json"
"testing"
"github.com/matryer/is"
)
func TestTypeScrypt(t *testing.T) {
assert := is.New(t)
enc := MustNew("passphrase", 0)
enc.Value = 100
payload, err := json.Marshal(enc)
assert.NoErr(err) // Should be able to marshal JSON
dec := MustNew("passphrase", 0)
err = json.Unmarshal(payload, &dec)
assert.NoErr(err) // Should be able to unmarshal JSON
assert.Equal(enc.Value, dec.Value) // Values should match
}
func TestTypeX25519(t *testing.T) {
assert := is.New(t)
enc := MustNew("AGE-SECRET-KEY-1F7SU7MXLVHU0SWQ3L2ZMW7G2NN2YTH88NLU6LVDHENTZLMCT3M5S799RDK", 100)
payload, err := json.Marshal(enc)
assert.NoErr(err) // Should be able to marshal JSON
dec := MustNew("AGE-SECRET-KEY-1F7SU7MXLVHU0SWQ3L2ZMW7G2NN2YTH88NLU6LVDHENTZLMCT3M5S799RDK", 0)
err = json.Unmarshal(payload, &dec)
assert.NoErr(err) // Should be able to unmarshal JSON
assert.Equal(enc.Value, dec.Value) // Values should match
}

13
go.mod 100644
View File

@ -0,0 +1,13 @@
module go.jolheiser.com/confage
go 1.19
require (
filippo.io/age v1.0.0
github.com/matryer/is v1.4.0
)
require (
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
golang.org/x/sys v0.0.0-20210903071746-97244b99971b // indirect
)

8
go.sum 100644
View File

@ -0,0 +1,8 @@
filippo.io/age v1.0.0 h1:V6q14n0mqYU3qKFkZ6oOaF9oXneOviS3ubXsSVBRSzc=
filippo.io/age v1.0.0/go.mod h1:PaX+Si/Sd5G8LgfCwldsSba3H1DDQZhIhFGkhbHaBq8=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/sys v0.0.0-20210903071746-97244b99971b h1:3Dq0eVHn0uaQJmPO+/aYPI/fRMqdrVDbu7MQcku54gg=
golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=