* Fixed workability
* Base32 code is simplified with base32.NoPadding
* No XOF, key derivation, just simple 24-byte nonce with 16-byte salt
and a counter
* Updated dependencies
* Raised copyright years
gohpenc is incompatible with hpenc and much simpler:
gohpenc is incompatible with hpenc and much simpler:
-* it uses only ChaCha20-Poly1305 algorithm
+* it uses only XChaCha20-Poly1305 algorithm
* no random data generation mode
* no metadata in output stream and no structure validation. Only blocks
authentication
* no random data generation mode
* no metadata in output stream and no structure validation. Only blocks
authentication
-* simpler key derivation -- new key for each block
+* no key derivation -- new key for each block
But it still satisfies most of hpenc aims:
But it still satisfies most of hpenc aims:
How encryption/authentication is performed:
How encryption/authentication is performed:
-* First 32 bytes of the stream contain random data -- salt
-* BLAKE2X is initialized: unknown length, PSK key as a MAC key. It
- creates XOF that will be used as a KDF
-* Salt is fed into that XOF
-* All data is processed block by block
-* New key is derived for each block by reading it from the XOF
-* ChaCha20-Poly1305 algorithm is initialized with that key
+* First 16 bytes of the stream contain random data -- nonce salt
+* XChaCha20-Poly1305 algorithm is initialized with the key and 24-byte
+ nonce, where 16 bytes is the salt, and 8 bytes is 64-bit unsigned
+ big-endian block number
* 32-bit big-endian value with the length of the block is outputted,
then an encrypted and authenticated block goes further, with
authenticated data containing that 32-bit length value
* 32-bit big-endian value with the length of the block is outputted,
then an encrypted and authenticated block goes further, with
authenticated data containing that 32-bit length value
+++ /dev/null
-package main
-
-import (
- "encoding/base32"
- "strings"
-)
-
-func ToBase32(data []byte) string {
- return strings.TrimRight(base32.StdEncoding.EncodeToString(data), "=")
-}
-
-func FromBase32(data string) ([]byte, error) {
- padSize := len(data) % 8
- if padSize != 0 {
- padSize = 8 - padSize
- pad := make([]byte, 0, padSize)
- for i := 0; i < padSize; i++ {
- pad = append(pad, '=')
- }
- data += string(pad)
- }
- return base32.StdEncoding.DecodeString(data)
-}
-require golang.org/x/crypto v0.0.0-20191001170739-f9e2070545dc
+require golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191001170739-f9e2070545dc h1:KyTYo8xkh/2WdbFLUyQwBS0Jfn3qfZ9QmuPbok2oENE=
-golang.org/x/crypto v0.0.0-20191001170739-f9e2070545dc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc=
+golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
/*
gohpenc -- Go high-performance encryption utility
/*
gohpenc -- Go high-performance encryption utility
-Copyright (C) 2017-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2017-2020 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
"encoding/binary"
"flag"
"fmt"
"encoding/binary"
"flag"
"fmt"
- "golang.org/x/crypto/blake2b"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/chacha20poly1305"
+ "golang.org/x/crypto/poly1305"
-type WorkerTask struct {
+var (
+ doPSK = flag.Bool("psk", false, "Generate PSK")
+ keyB32 = flag.String("k", "", "Encryption key")
+ decrypt = flag.Bool("d", false, "Decrypt, instead of encrypt")
+ blockSize = flag.Int("b", 1<<10, "Blocksize, in KiB")
+ threads = flag.Int("c", runtime.NumCPU(), "Number of threads")
+
+ Base32Codec *base32.Encoding = base32.StdEncoding.WithPadding(base32.NoPadding)
+
+ bs int
+ wg sync.WaitGroup
+)
+
+type Task struct {
+ ctr uint64
+ size int
- wg *sync.WaitGroup
- buf []byte
+ aead cipher.AEAD
+ nonce []byte
+ input []byte
- task chan WorkerTask
- done chan []byte
+ task chan Task
+ output chan []byte
-func (w *Worker) Thread(doDecrypt bool) {
- var task WorkerTask
- nonce := make([]byte, chacha20poly1305.NonceSize)
- var done []byte
+func NewWorker(key, salt []byte) *Worker {
+ aead, err := chacha20poly1305.NewX(key)
+ if err != nil {
+ panic(err)
+ }
+ w := Worker{
+ aead: aead,
+ nonce: make([]byte, chacha20poly1305.NonceSizeX),
+ input: make([]byte, LenSize+bs+poly1305.TagSize),
+ ready: make(chan struct{}),
+ task: make(chan Task),
+ output: make(chan []byte),
+ written: make(chan struct{}),
+ }
+ copy(w.nonce, salt)
+ go w.Run()
+ return &w
+}
+
+func (w *Worker) Run() {
+ var output []byte
+ var err error
for {
w.ready <- struct{}{}
for {
w.ready <- struct{}{}
- task = <-w.task
- aead, err := chacha20poly1305.New(task.key)
- if err != nil {
- panic(err)
- }
- if doDecrypt {
- done, err = aead.Open(w.buf[:0], nonce, w.buf[LenSize:LenSize+task.n], w.buf[:LenSize])
+ task := <-w.task
+ binary.BigEndian.PutUint64(w.nonce[SaltSize:], task.ctr)
+ if *decrypt {
+ output, err = w.aead.Open(
+ w.input[:LenSize],
+ w.nonce,
+ w.input[LenSize:LenSize+task.size+poly1305.TagSize],
+ w.input[:LenSize],
+ )
if err != nil {
panic(err)
}
if err != nil {
panic(err)
}
+ output = output[LenSize:]
- binary.BigEndian.PutUint32(w.buf, uint32(task.n))
- done = aead.Seal(w.buf[:LenSize], nonce, w.buf[LenSize:LenSize+task.n], w.buf[:LenSize])
+ binary.BigEndian.PutUint32(w.input, uint32(task.size))
+ output = w.aead.Seal(
+ w.input[:LenSize],
+ w.nonce,
+ w.input[LenSize:LenSize+task.size],
+ w.input[:LenSize],
+ )
- var (
- doPSK = flag.Bool("psk", false, "Generate PSK")
- key = flag.String("k", "", "Encryption key")
- doDecrypt = flag.Bool("d", false, "Decrypt, instead of encrypt")
- blockSize = flag.Int("b", 1<<10, "Blocksize, in KiB")
- threads = flag.Int("c", runtime.NumCPU(), "Number of threads")
- )
- bs := *blockSize * 1 << 10
- key := make([]byte, 32)
- if _, err := rand.Read(key); err != nil {
+ key := make([]byte, chacha20poly1305.KeySize)
+ if _, err := io.ReadFull(rand.Reader, key); err != nil {
- fmt.Println(ToBase32(key))
+ fmt.Println(Base32Codec.EncodeToString(key))
- if len(*key) != 52 {
- panic("Invalid key size")
- }
- keyDecoded, err := FromBase32(*key)
+ var err error
+ key, err = Base32Codec.DecodeString(*keyB32)
if err != nil {
panic(err)
}
if err != nil {
panic(err)
}
- tmpAEAD, err := chacha20poly1305.New(make([]byte, chacha20poly1305.KeySize))
- if err != nil {
- panic(err)
+ if len(key) != chacha20poly1305.KeySize {
+ panic("Invalid key size")
- keys, err := blake2b.NewXOF(blake2b.OutputLengthUnknown, keyDecoded)
- if err != nil {
- panic(err)
+ salt := make([]byte, SaltSize)
+
+ if *decrypt {
+ if _, err = io.ReadFull(os.Stdin, salt); err != nil {
+ panic(err)
+ }
+ } else {
+ if _, err = io.ReadFull(rand.Reader, salt); err != nil {
+ panic(err)
+ }
+ if _, err = os.Stdout.Write(salt); err != nil {
+ panic(err)
+ }
- var wg sync.WaitGroup
- workers := make([]*Worker, 0, *threads)
+ bs = *blockSize * (1 << 10)
+ stdin := bufio.NewReaderSize(os.Stdin, LenSize+bs+poly1305.TagSize)
+
+ workers := make([]*Worker, *threads)
for i := 0; i < *threads; i++ {
for i := 0; i < *threads; i++ {
- w := Worker{
- wg: &wg,
- buf: make([]byte, LenSize+bs+tmpAEAD.Overhead()),
- ready: make(chan struct{}),
- task: make(chan WorkerTask),
- done: make(chan []byte),
- written: make(chan struct{}),
- }
- go w.Thread(*doDecrypt)
- workers = append(workers, &w)
+ workers[i] = NewWorker(key, salt)
- w = workers[i%len(workers)]
- if _, err = os.Stdout.Write(<-w.done); err != nil {
+ w := workers[ctr%uint64(len(workers))]
+ if _, err := os.Stdout.Write(<-w.output); err != nil {
panic(err)
}
w.written <- struct{}{}
panic(err)
}
w.written <- struct{}{}
- stdin := bufio.NewReaderSize(os.Stdin, LenSize+bs)
- if *doDecrypt {
- if _, err = io.CopyN(keys, stdin, SaltSize); err != nil {
- panic(err)
- }
- } else {
- salt := make([]byte, SaltSize)
- if _, err = rand.Read(salt); err != nil {
- panic(err)
- }
- if _, err = keys.Write(salt); err != nil {
- panic(err)
- }
- if _, err = os.Stdout.Write(salt); err != nil {
- panic(err)
- }
- }
-
- i := 0
- var n int
- var w *Worker
+ var ctr uint64
+ var size int
- key := make([]byte, chacha20poly1305.KeySize)
- if _, err = io.ReadFull(keys, key); err != nil {
- panic(err)
- }
- w = workers[i%len(workers)]
+ w := workers[ctr%uint64(len(workers))]
- if *doDecrypt {
- _, err = io.ReadFull(stdin, w.buf[:LenSize])
+ if *decrypt {
+ _, err = io.ReadFull(stdin, w.input[:LenSize])
if err != nil {
if err == io.EOF {
break
}
panic(err)
}
if err != nil {
if err == io.EOF {
break
}
panic(err)
}
- n = int(binary.BigEndian.Uint32(w.buf[:LenSize]))
- if n, err = io.ReadFull(stdin, w.buf[LenSize:LenSize+n+tmpAEAD.Overhead()]); err != nil {
+ size = int(binary.BigEndian.Uint32(w.input[:LenSize]))
+ if _, err = io.ReadFull(
+ stdin,
+ w.input[LenSize:LenSize+size+poly1305.TagSize],
+ ); err != nil {
- n, err = stdin.Read(w.buf[LenSize : LenSize+bs])
+ size, err = stdin.Read(w.input[LenSize : LenSize+bs])
if err != nil {
if err == io.EOF {
break
if err != nil {
if err == io.EOF {
break
- w.task <- WorkerTask{key, n}
- i++
+ w.task <- Task{ctr, size}
+ ctr++