Skip to content

Commit

Permalink
Add some examples programs
Browse files Browse the repository at this point in the history
  • Loading branch information
pontus committed Aug 23, 2023
1 parent 81777f2 commit cf37857
Show file tree
Hide file tree
Showing 3 changed files with 386 additions and 0 deletions.
86 changes: 86 additions & 0 deletions examples/keycreator/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// package main for keycreator, an example of what a key creator
// can look like
package main

import (
"fmt"
"os"

"github.com/neicnordic/crypt4gh/keys"
"golang.org/x/crypto/chacha20poly1305"
)

// usage prints a nice message to instruct the user
func usage(path string) {
fmt.Printf(`Usage: %s PUBLICKEY PRIVATEKEY PASSWORD
Generate a pair of public and privatekey and write the public key to PUBLICKEY
and the private to PRIVATEKEY. PRIVATEKEY will be encrypted with PASSWORD.
`, path)
os.Exit(0)

}

// generateAndWriteKeyFiles does the heavy lifting here, generating
// a keypair and writing them to the specified filenames, the
// private key encrypted with password
func generateAndWriteKeyFiles(publicKeyFileName, privateKeyFileName, password string) (err error) {
publicKey := [chacha20poly1305.KeySize]byte{}
var privateKey [chacha20poly1305.KeySize]byte

// Get a keypair
publicKey, privateKey, err = keys.GenerateKeyPair()
if err != nil {
return fmt.Errorf("Error while generating key pair: %v", err)
}

w, err := os.Create(privateKeyFileName)
if err != nil {
return fmt.Errorf("Error when opening private key output %s: %v", privateKeyFileName, err)
}

// Write the private key as a crypt4gh x25519 key
if err := keys.WriteCrypt4GHX25519PrivateKey(w, privateKey, []byte(password)); err != nil {
return fmt.Errorf("Error when writing private key: %v", err)
}

if err = w.Close(); err != nil {
return fmt.Errorf("Error when closing private key file: %v", err)
}

w, err = os.Create(publicKeyFileName)
if err != nil {
return fmt.Errorf("Error when opening public key output %s: %v", publicKeyFileName, err)
}

if err := keys.WriteCrypt4GHX25519PublicKey(w, publicKey); err != nil {
return fmt.Errorf("Error when closing public key file: %v", err)
}

if err = w.Close(); err != nil {
return fmt.Errorf("Error when closing private key file: %v", err)
}

fmt.Printf("Wrote public key to %s and private key to %s\n\n", publicKeyFileName, privateKeyFileName)

return nil
}

// main is the function we start in
func main() {
args := os.Args

if len(args) != 4 {
usage(args[0]) // Won't return
}

publicKeyFileName := args[1]
privateKeyFileName := args[2]
password := args[3]

// Use our utility function
err := generateAndWriteKeyFiles(publicKeyFileName, privateKeyFileName, password)
if err != nil {
fmt.Printf("Error during key creation: %v", err)
}
}
192 changes: 192 additions & 0 deletions examples/reader/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// package main for reader, an example of what a crypt4gh file reader
// can look like
package main

import (
"fmt"
"io"
"log"
"os"

"strconv"

"github.com/neicnordic/crypt4gh/keys"
"github.com/neicnordic/crypt4gh/streaming"
"golang.org/x/crypto/chacha20poly1305"
)

// readPublicKey reads the public key from filename and returns the key
// and/or any error encountered.
func readPublicKey(filename string) ([chacha20poly1305.KeySize]byte, error) {
reader, err := os.Open(filename)

if err != nil {
var nilKey [chacha20poly1305.KeySize]byte
return nilKey, err
}

key, err := keys.ReadPublicKey(reader)
reader.Close()
return key, err
}

// readPrivateKey reads the private key file designated by filename
// encrypted with password, if any.
func readPrivateKey(filename string, password []byte) ([chacha20poly1305.KeySize]byte, error) {
reader, err := os.Open(filename)

if err != nil {
var nilKey [chacha20poly1305.KeySize]byte
return nilKey, err
}

key, err := keys.ReadPrivateKey(reader, password)
reader.Close()
return key, err
}

// readFile reads and decrypts
func readFile(filename string, writer io.Writer, readerKey [chacha20poly1305.KeySize]byte, start, end int64) error {

underReader, err := os.Open(filename)
if err != nil {
return err
}

defer underReader.Close()

reader, err := streaming.NewCrypt4GHReader(underReader, readerKey, nil)
if err != nil {
return err
}
defer reader.Close()

if start != 0 {
// We don't want to read from start, skip ahead to where we should be
if _, err := reader.Seek(start, 0); err != nil {
return err
}
}

// Calculate how much we should read (if given)
togo := end - start

buf := make([]byte, 4096)

// Loop until we've read what we should (if no/faulty end given, that's EOF)
for end == 0 || togo > 0 {
rbuf := buf[:]

if end != 0 && togo < 4096 {
// If we don't want to read as much as 4096 bytes
rbuf = buf[:togo]
}
r, err := reader.Read(rbuf)
togo -= int64(r)

// Nothing more to read?
if err == io.EOF && r == 0 {
// Fall out without error if we had EOF (if we got any data, do one
// more lap in the loop)
return nil
}

if err != nil && err != io.EOF {
// An error we want to signal?
return err
}

wbuf := rbuf[:r]
for len(wbuf) > 0 {
// Loop until we've written all that we could read,
// fall out on error
w, err := writer.Write(wbuf)

if err != nil {
return err
}
wbuf = wbuf[w:]
}
}

return nil
}

// usage prints a friendly message
func usage(path string) {
fmt.Printf(`Usage: %s INPUT READERPRIVATEKEY PASSWORD [START [END]]
Read and decrypt INPUT (encrypted for the public key corresponding to
READERPRIVATEKEY) and write to stdout.
If start is given, start reading at that offset in the file rather than from
the start. If end is given, stop there (byte at offsent end is not included).
`, path)
os.Exit(0)
}

// getStartEnd returns the start and end values to use or 0
// if not provided
func getStartEnd(args []string) (start, end int64, err error) {
if len(args) >= 5 {
start, err = strconv.ParseInt(args[4], 10, 0)

if err != nil {
return 0, 0, fmt.Errorf("Couldn't parse start offset %s as a decimal number: %v", args[4], err)
}
}
if len(args) == 6 {
end, err = strconv.ParseInt(args[5], 10, 0)
if err != nil {
return 0, 0, fmt.Errorf("Couldn't parse end offset %s as a decimal number: %v", args[5], err)
}
}

if end != 0 && end <= start {
log.Printf("End specified (%d) but before start (%d), ignoring", end, start)
end = 0
}

if end != 0 {
fmt.Printf("Will start at %d and read to the end.\n", start)
return start, end, nil
}

fmt.Printf("Will start at %d and read to %d.\n", start, end)
return start, end, nil

}

// main is where we start
func main() {
args := os.Args

if len(args) < 4 || len(args) > 6 {
usage(args[0]) // Won't return
}

inputFilename := args[1]
privateKeyFileName := args[2]
password := args[3]

// We need the private key to decrypt the file
privateKey, err := readPrivateKey(privateKeyFileName, []byte(password))
if err != nil {
log.Fatalf("Unexpected error while reading reader key: %v", err)
}

fmt.Printf("Will read from %s, decrypt and output to stderr.\n", inputFilename)

// Pick up start and end if passed (will get 0 otherwise)
start, end, err := getStartEnd(args)
if err != nil {
log.Fatalf("Error while parsing start and end: %v", err)
}

err = readFile(inputFilename, os.Stdout, privateKey, start, end)
if err != nil {
log.Fatalf("Error while decrypting file %s: %v", inputFilename, err)
}

}
108 changes: 108 additions & 0 deletions examples/writer/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// package main for writer, an example of what a crypt4gh file writer
// can look like
package main

import (
"fmt"
"io"
"log"
"os"

"github.com/neicnordic/crypt4gh/keys"
"github.com/neicnordic/crypt4gh/streaming"
"golang.org/x/crypto/chacha20poly1305"
)

// readPublicKey reads the public key from filename and returns the key
// and/or any error encountered.
func readPublicKey(filename string) ([chacha20poly1305.KeySize]byte, error) {
reader, err := os.Open(filename)

if err != nil {
var nilKey [chacha20poly1305.KeySize]byte
return nilKey, err
}

key, err := keys.ReadPublicKey(reader)
reader.Close()
return key, err
}

// readPrivateKey reads the private key from filename, possibly decrypts it
// (if password is supplied) and returns the key and/or any error encountered.
func readPrivateKey(filename string, password []byte) ([chacha20poly1305.KeySize]byte, error) {
reader, err := os.Open(filename)

if err != nil {
var nilKey [chacha20poly1305.KeySize]byte
return nilKey, err
}

key, err := keys.ReadPrivateKey(reader, password)
reader.Close()
return key, err
}

// writeFile reads from the supplied reader and writes to filename
// as a stream encrypted for readerKey, using writerKey
func writeFile(reader io.Reader, writerKey, readerKey [chacha20poly1305.KeySize]byte, filename string) error {

underWriter, err := os.Create(filename)
if err != nil {
return err
}

defer underWriter.Close()

readerPublicKeyList := [][chacha20poly1305.KeySize]byte{readerKey}
readerPublicKeyList = append(readerPublicKeyList, readerKey)
writer, err := streaming.NewCrypt4GHWriter(underWriter, writerKey, readerPublicKeyList, nil)
if err != nil {
return err
}

if _, err = io.Copy(writer, reader); err != nil {
return err
}
return nil
}

func usage(path string) {
fmt.Printf(`Usage: %s OUTPUT READERPUBLICKEY WRITERPRIVATEKEY PASSWORD
Create OUTPUT and wite stdin as a crypt4gh encrypted file, encrypted for
READERPUBLICKEY with WRITERPRIVATEKEY.
`, path)
os.Exit(0)
}

func main() {
args := os.Args

if len(args) == 1 {
usage(args[0]) // Won't return
}

outputFilename := args[1]
publicKeyFileName := args[2]
privateKeyFileName := args[3]
password := args[4]

var err error
publicKey, err := readPublicKey(publicKeyFileName)
if err != nil {
log.Fatalf("Unexpected error while reading receiver key: %v", err)
}

privateKey, err := readPrivateKey(privateKeyFileName, []byte(password))
if err != nil {
log.Fatalf("Unexpected error while reading sender key: %v", err)
}

fmt.Printf("Will write stdin to %s close when done (e.g. Ctrl-D)\n\n", outputFilename)
err = writeFile(os.Stdin, privateKey, publicKey, outputFilename)
if err != nil {
log.Fatalf("Error from writing encrypted file %s: %v", outputFilename, err)
}
}

0 comments on commit cf37857

Please sign in to comment.