From cf37857942f8479421ade4a68de2406559dd00f9 Mon Sep 17 00:00:00 2001 From: Pontus Freyhult Date: Tue, 22 Aug 2023 15:49:59 +0200 Subject: [PATCH] Add some examples programs --- examples/keycreator/main.go | 86 ++++++++++++++++ examples/reader/main.go | 192 ++++++++++++++++++++++++++++++++++++ examples/writer/main.go | 108 ++++++++++++++++++++ 3 files changed, 386 insertions(+) create mode 100644 examples/keycreator/main.go create mode 100644 examples/reader/main.go create mode 100644 examples/writer/main.go diff --git a/examples/keycreator/main.go b/examples/keycreator/main.go new file mode 100644 index 0000000..e06e9fb --- /dev/null +++ b/examples/keycreator/main.go @@ -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) + } +} diff --git a/examples/reader/main.go b/examples/reader/main.go new file mode 100644 index 0000000..c1752d1 --- /dev/null +++ b/examples/reader/main.go @@ -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) + } + +} diff --git a/examples/writer/main.go b/examples/writer/main.go new file mode 100644 index 0000000..cafda8f --- /dev/null +++ b/examples/writer/main.go @@ -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) + } +}