]> Cypherpunks.ru repositories - gohpenc.git/blob - main.go
43eb680f8f1e29193d12bc36a81adc60fa097fc2
[gohpenc.git] / main.go
1 /*
2 gohpenc -- Go high-performance encryption utility
3 Copyright (C) 2017-2022 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 // Go high-performance encryption utility
19 package main
20
21 import (
22         "bufio"
23         "crypto/cipher"
24         "crypto/rand"
25         "encoding/base32"
26         "encoding/binary"
27         "flag"
28         "fmt"
29         "io"
30         "os"
31         "runtime"
32         "sync"
33
34         "golang.org/x/crypto/chacha20poly1305"
35         "golang.org/x/crypto/poly1305"
36 )
37
38 const (
39         LenSize  = 4
40         SaltSize = 16
41 )
42
43 var (
44         doPSK     = flag.Bool("psk", false, "Generate PSK")
45         keyB32    = flag.String("k", "", "Encryption key")
46         decrypt   = flag.Bool("d", false, "Decrypt, instead of encrypt")
47         blockSize = flag.Int("b", 1<<10, "Blocksize, in KiB")
48         threads   = flag.Int("c", runtime.NumCPU(), "Number of threads")
49
50         Base32Codec *base32.Encoding = base32.StdEncoding.WithPadding(base32.NoPadding)
51
52         key []byte
53         bs  int
54         wg  sync.WaitGroup
55 )
56
57 type Task struct {
58         ctr  uint64
59         size int
60 }
61
62 type Worker struct {
63         aead    cipher.AEAD
64         nonce   []byte
65         input   []byte
66         ready   chan struct{}
67         task    chan Task
68         output  chan []byte
69         written chan struct{}
70 }
71
72 func NewWorker(key, salt []byte) *Worker {
73         aead, err := chacha20poly1305.NewX(key)
74         if err != nil {
75                 panic(err)
76         }
77         w := Worker{
78                 aead:    aead,
79                 nonce:   make([]byte, chacha20poly1305.NonceSizeX),
80                 input:   make([]byte, LenSize+bs+poly1305.TagSize),
81                 ready:   make(chan struct{}),
82                 task:    make(chan Task),
83                 output:  make(chan []byte),
84                 written: make(chan struct{}),
85         }
86         copy(w.nonce, salt)
87         go w.Run()
88         return &w
89 }
90
91 func (w *Worker) Run() {
92         var output []byte
93         var err error
94         for {
95                 w.ready <- struct{}{}
96                 task := <-w.task
97                 binary.BigEndian.PutUint64(w.nonce[SaltSize:], task.ctr)
98                 if *decrypt {
99                         output, err = w.aead.Open(
100                                 w.input[:LenSize],
101                                 w.nonce,
102                                 w.input[LenSize:LenSize+task.size+poly1305.TagSize],
103                                 w.input[:LenSize],
104                         )
105                         if err != nil {
106                                 panic(err)
107                         }
108                         output = output[LenSize:]
109                 } else {
110                         binary.BigEndian.PutUint32(w.input, uint32(task.size))
111                         output = w.aead.Seal(
112                                 w.input[:LenSize],
113                                 w.nonce,
114                                 w.input[LenSize:LenSize+task.size],
115                                 w.input[:LenSize],
116                         )
117                 }
118                 w.output <- output
119                 <-w.written
120                 wg.Done()
121         }
122 }
123
124 func main() {
125         flag.Parse()
126
127         if *doPSK {
128                 key := make([]byte, chacha20poly1305.KeySize)
129                 if _, err := io.ReadFull(rand.Reader, key); err != nil {
130                         panic(err)
131                 }
132                 fmt.Println(Base32Codec.EncodeToString(key))
133                 return
134         }
135
136         var err error
137         key, err = Base32Codec.DecodeString(*keyB32)
138         if err != nil {
139                 panic(err)
140         }
141         if len(key) != chacha20poly1305.KeySize {
142                 panic("Invalid key size")
143         }
144         salt := make([]byte, SaltSize)
145
146         if *decrypt {
147                 if _, err = io.ReadFull(os.Stdin, salt); err != nil {
148                         panic(err)
149                 }
150         } else {
151                 if _, err = io.ReadFull(rand.Reader, salt); err != nil {
152                         panic(err)
153                 }
154                 if _, err = os.Stdout.Write(salt); err != nil {
155                         panic(err)
156                 }
157         }
158
159         bs = *blockSize * (1 << 10)
160         if bs > 1<<32 {
161                 panic("blocksize exceeds 32-bits")
162         }
163         stdin := bufio.NewReaderSize(os.Stdin, LenSize+bs+poly1305.TagSize)
164
165         workers := make([]*Worker, *threads)
166         for i := 0; i < *threads; i++ {
167                 workers[i] = NewWorker(key, salt)
168         }
169         go func() {
170                 var ctr uint64
171                 for {
172                         w := workers[ctr%uint64(len(workers))]
173                         if _, err := os.Stdout.Write(<-w.output); err != nil {
174                                 panic(err)
175                         }
176                         w.written <- struct{}{}
177                         ctr++
178                 }
179         }()
180
181         var ctr uint64
182         var size int
183         for {
184                 w := workers[ctr%uint64(len(workers))]
185                 <-w.ready
186                 if *decrypt {
187                         _, err = io.ReadFull(stdin, w.input[:LenSize])
188                         if err != nil {
189                                 if err == io.EOF {
190                                         break
191                                 }
192                                 panic(err)
193                         }
194                         size = int(binary.BigEndian.Uint32(w.input[:LenSize]))
195                         if _, err = io.ReadFull(
196                                 stdin,
197                                 w.input[LenSize:LenSize+size+poly1305.TagSize],
198                         ); err != nil {
199                                 panic(err)
200                         }
201                 } else {
202                         size, err = stdin.Read(w.input[LenSize : LenSize+bs])
203                         if err != nil {
204                                 if err == io.EOF {
205                                         break
206                                 }
207                                 panic(err)
208                         }
209                 }
210                 wg.Add(1)
211                 w.task <- Task{ctr, size}
212                 ctr++
213         }
214         wg.Wait()
215 }