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