]> Cypherpunks.ru repositories - gohpenc.git/blob - main.go
Simplifying because of modules
[gohpenc.git] / main.go
1 /*
2 gohpenc -- Go high-performance encryption utility
3 Copyright (C) 2017-2019 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/rand"
24         "encoding/binary"
25         "flag"
26         "fmt"
27         "io"
28         "os"
29         "runtime"
30         "sync"
31
32         "golang.org/x/crypto/blake2b"
33         "golang.org/x/crypto/chacha20poly1305"
34 )
35
36 const (
37         LenSize  = 4
38         SaltSize = 32
39 )
40
41 type WorkerTask struct {
42         key []byte
43         n   int
44 }
45
46 type Worker struct {
47         wg      *sync.WaitGroup
48         buf     []byte
49         ready   chan struct{}
50         task    chan WorkerTask
51         done    chan []byte
52         written chan struct{}
53 }
54
55 func (w *Worker) Thread(doDecrypt bool) {
56         var task WorkerTask
57         nonce := make([]byte, chacha20poly1305.NonceSize)
58         var done []byte
59         for {
60                 w.ready <- struct{}{}
61                 task = <-w.task
62                 aead, err := chacha20poly1305.New(task.key)
63                 if err != nil {
64                         panic(err)
65                 }
66                 if doDecrypt {
67                         done, err = aead.Open(w.buf[:0], nonce, w.buf[LenSize:LenSize+task.n], w.buf[:LenSize])
68                         if err != nil {
69                                 panic(err)
70                         }
71                 } else {
72                         binary.BigEndian.PutUint32(w.buf, uint32(task.n))
73                         done = aead.Seal(w.buf[:LenSize], nonce, w.buf[LenSize:LenSize+task.n], w.buf[:LenSize])
74                 }
75                 w.done <- done
76                 <-w.written
77                 w.wg.Done()
78         }
79 }
80
81 func main() {
82         var (
83                 doPSK     = flag.Bool("psk", false, "Generate PSK")
84                 key       = flag.String("k", "", "Encryption key")
85                 doDecrypt = flag.Bool("d", false, "Decrypt, instead of encrypt")
86                 blockSize = flag.Int("b", 1<<10, "Blocksize, in KiB")
87                 threads   = flag.Int("c", runtime.NumCPU(), "Number of threads")
88         )
89         flag.Parse()
90         bs := *blockSize * 1 << 10
91
92         if *doPSK {
93                 key := make([]byte, 32)
94                 if _, err := rand.Read(key); err != nil {
95                         panic(err)
96                 }
97                 fmt.Println(ToBase32(key))
98                 return
99         }
100
101         if len(*key) != 52 {
102                 panic("Invalid key size")
103         }
104         keyDecoded, err := FromBase32(*key)
105         if err != nil {
106                 panic(err)
107         }
108         tmpAEAD, err := chacha20poly1305.New(make([]byte, chacha20poly1305.KeySize))
109         if err != nil {
110                 panic(err)
111         }
112         keys, err := blake2b.NewXOF(blake2b.OutputLengthUnknown, keyDecoded)
113         if err != nil {
114                 panic(err)
115         }
116
117         var wg sync.WaitGroup
118         workers := make([]*Worker, 0, *threads)
119         for i := 0; i < *threads; i++ {
120                 w := Worker{
121                         wg:      &wg,
122                         buf:     make([]byte, LenSize+bs+tmpAEAD.Overhead()),
123                         ready:   make(chan struct{}),
124                         task:    make(chan WorkerTask),
125                         done:    make(chan []byte),
126                         written: make(chan struct{}),
127                 }
128                 go w.Thread(*doDecrypt)
129                 workers = append(workers, &w)
130         }
131         go func() {
132                 i := 0
133                 var w *Worker
134                 for {
135                         w = workers[i%len(workers)]
136                         if _, err = os.Stdout.Write(<-w.done); err != nil {
137                                 panic(err)
138                         }
139                         w.written <- struct{}{}
140                         i++
141                 }
142         }()
143
144         stdin := bufio.NewReaderSize(os.Stdin, LenSize+bs)
145         if *doDecrypt {
146                 if _, err = io.CopyN(keys, stdin, SaltSize); err != nil {
147                         panic(err)
148                 }
149         } else {
150                 salt := make([]byte, SaltSize)
151                 if _, err = rand.Read(salt); err != nil {
152                         panic(err)
153                 }
154                 if _, err = keys.Write(salt); err != nil {
155                         panic(err)
156                 }
157                 if _, err = os.Stdout.Write(salt); err != nil {
158                         panic(err)
159                 }
160         }
161
162         i := 0
163         var n int
164         var w *Worker
165         for {
166                 key := make([]byte, chacha20poly1305.KeySize)
167                 if _, err = io.ReadFull(keys, key); err != nil {
168                         panic(err)
169                 }
170                 w = workers[i%len(workers)]
171                 <-w.ready
172                 if *doDecrypt {
173                         _, err = io.ReadFull(stdin, w.buf[:LenSize])
174                         if err != nil {
175                                 if err == io.EOF {
176                                         break
177                                 }
178                                 panic(err)
179                         }
180                         n = int(binary.BigEndian.Uint32(w.buf[:LenSize]))
181                         if n, err = io.ReadFull(stdin, w.buf[LenSize:LenSize+n+tmpAEAD.Overhead()]); err != nil {
182                                 panic(err)
183                         }
184                 } else {
185                         n, err = stdin.Read(w.buf[LenSize : LenSize+bs])
186                         if err != nil {
187                                 if err == io.EOF {
188                                         break
189                                 }
190                                 panic(err)
191                         }
192                 }
193                 wg.Add(1)
194                 w.task <- WorkerTask{key, n}
195                 i++
196         }
197         wg.Wait()
198 }