Refactor salty-keygen as a library function (#8)
Co-authored-by: James Mills <prologic@shortcircuit.net.au> Reviewed-on: #8pull/12/head
parent
1af9e6828d
commit
fb3d6fc9e8
@ -0,0 +1,27 @@ |
||||
--- |
||||
kind: pipeline |
||||
name: default |
||||
|
||||
steps: |
||||
- name: build-and-test |
||||
image: r.mills.io/prologic/golang-alpine:latest |
||||
commands: |
||||
- go test |
||||
|
||||
- name: notify-irc |
||||
image: plugins/webhook |
||||
settings: |
||||
urls: |
||||
- https://msgbus.mills.io/ci.mills.io |
||||
when: |
||||
status: |
||||
- success |
||||
- failure |
||||
|
||||
trigger: |
||||
branch: |
||||
- main |
||||
event: |
||||
- tag |
||||
- push |
||||
- pull_request |
@ -0,0 +1,24 @@ |
||||
package salty |
||||
|
||||
import ( |
||||
"github.com/keys-pub/keys" |
||||
"github.com/keys-pub/keys/saltpack" |
||||
) |
||||
|
||||
// Encrypt encrypts the `input` using the Private Key `key` to the Public Keys of the
|
||||
// `recipients`. Armour serializing is used by default. The armour bytes are returned on
|
||||
// success or nil bytes and an error on failure.
|
||||
func Encrypt(key *keys.EdX25519Key, input []byte, recipients []string) ([]byte, error) { |
||||
ids := keys.NewIDSet() |
||||
for _, recipient := range recipients { |
||||
ids.Add(keys.ID(recipient)) |
||||
} |
||||
|
||||
return saltpack.Signcrypt(input, true, key, ids.IDs()...) |
||||
} |
||||
|
||||
// Decrypt decrypts the `input` using the Private Key `key` and returns the unencrypted
|
||||
// bytes and the sender's public key on success, or nill bytes and a nil sender on failure.
|
||||
func Decrypt(key *keys.EdX25519Key, input []byte) ([]byte, *keys.EdX25519PublicKey, error) { |
||||
return saltpack.SigncryptOpen(input, true, saltpack.NewKeyring(key)) |
||||
} |
@ -0,0 +1,27 @@ |
||||
package salty |
||||
|
||||
import ( |
||||
"io" |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/assert" |
||||
) |
||||
|
||||
func TestCrypto(t *testing.T) { |
||||
assert := assert.New(t) |
||||
|
||||
key, pub := GenerateKeys(io.Discard) |
||||
assert.NotEmpty(key) |
||||
assert.NotEmpty(pub) |
||||
assert.Equal(key.PublicKey().String(), pub) |
||||
|
||||
input := []byte("Hello World!") |
||||
|
||||
encrypted, err := Encrypt(key, input, []string{pub}) |
||||
assert.NoError(err) |
||||
|
||||
decrypted, sender, err := Decrypt(key, encrypted) |
||||
assert.NoError(err) |
||||
assert.Equal(pub, sender.String()) |
||||
assert.Equal(decrypted, input) |
||||
} |
@ -0,0 +1,54 @@ |
||||
package salty |
||||
|
||||
import ( |
||||
"bufio" |
||||
"encoding/base64" |
||||
"fmt" |
||||
"io" |
||||
"strings" |
||||
"time" |
||||
|
||||
"github.com/keys-pub/keys" |
||||
) |
||||
|
||||
const privateKeySizeLimit = 1 << 8 // 256 bytes
|
||||
|
||||
// GenerateKeys creates a new pair of Ed25519 keys and writes the Private Key
|
||||
// to the `out io.Writer` and returns the Private and Public Keys.
|
||||
// The Private Key written to `out` is Base64 encoded.
|
||||
func GenerateKeys(out io.Writer) (*keys.EdX25519Key, string) { |
||||
k := keys.GenerateEdX25519Key() |
||||
|
||||
fmt.Fprintf(out, "# created: %s\n", time.Now().Format(time.RFC3339)) |
||||
fmt.Fprintf(out, "# public key: %s\n", k.PublicKey().ID().String()) |
||||
fmt.Fprintf(out, "%s\n", base64.StdEncoding.EncodeToString(k.Private())) |
||||
|
||||
return k, k.PublicKey().ID().String() |
||||
} |
||||
|
||||
// ParseIdentity parses the Salty Identity file given by `r io.Reader` which has a
|
||||
// line-oriented format where comments (lines beginning with a #) and the and blank
|
||||
// lines are ignored and the private key is the first non-comment / non-blank line.
|
||||
// The Private Key is a Base64 decoded.
|
||||
// This returns the parsed Ed25519 key on success or nil key and error if it fails.
|
||||
func ParseIdentity(r io.Reader) (*keys.EdX25519Key, error) { |
||||
scanner := bufio.NewScanner(io.LimitReader(r, privateKeySizeLimit)) |
||||
var n int |
||||
for scanner.Scan() { |
||||
line := scanner.Text() |
||||
n++ |
||||
if strings.HasPrefix(line, "#") || line == "" { |
||||
continue |
||||
} |
||||
bs, err := base64.StdEncoding.DecodeString(line) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("error at line %d: %v", n, err) |
||||
} |
||||
return keys.NewEdX25519KeyFromPrivateKey(keys.Bytes64(bs)), nil |
||||
} |
||||
if err := scanner.Err(); err != nil { |
||||
return nil, fmt.Errorf("failed to read identity file: %v", err) |
||||
} |
||||
|
||||
return nil, fmt.Errorf("no key found") |
||||
} |
@ -0,0 +1,21 @@ |
||||
package salty |
||||
|
||||
import ( |
||||
"bytes" |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/assert" |
||||
) |
||||
|
||||
func TestKeys(t *testing.T) { |
||||
assert := assert.New(t) |
||||
|
||||
buf := &bytes.Buffer{} |
||||
key, pub := GenerateKeys(buf) |
||||
assert.NotEmpty(pub) |
||||
|
||||
parsedKey, err := ParseIdentity(buf) |
||||
assert.NoError(err) |
||||
assert.Equal(key, parsedKey) |
||||
assert.Equal(pub, key.PublicKey().String()) |
||||
} |
Loading…
Reference in new issue