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