+ var wg sync.WaitGroup
+ var lastMet bool
+ workers := make([]*Worker, 0, *jobs)
+ for i := 0; i < *jobs; i++ {
+ w := Worker{
+ buf: make([]byte, *bs+chacha20poly1305.Overhead),
+ readyIn: make(chan struct{}),
+ readyOut: make(chan struct{}),
+ }
+ go func() {
+ ciph, err := chacha20poly1305.New(key)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ nonce := make([]byte, chacha20poly1305.NonceSize)
+ var ciphertext, tag []byte
+ var s *chacha20.Cipher
+ var p *poly1305.MAC
+ for {
+ w.readyIn <- struct{}{}
+ <-w.readyIn
+ binary.BigEndian.PutUint64(nonce, w.ctr)
+ if *doDec {
+ tag = w.buf[len(w.buf)-poly1305.TagSize:]
+ ciphertext = w.buf[:len(w.buf)-poly1305.TagSize]
+ s, err = chacha20.NewUnauthenticatedCipher(key, nonce)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ var polyKey [32]byte
+ s.XORKeyStream(polyKey[:], polyKey[:])
+ s.SetCounter(1)
+ p = poly1305.New(&polyKey)
+ writeWithPadding(p, nil)
+ writeWithPadding(p, ciphertext)
+ writeUint64(p, 0)
+ writeUint64(p, len(ciphertext))
+ if p.Verify(tag) {
+ w.buf = ciphertext
+ s.XORKeyStream(ciphertext, ciphertext)
+ } else {
+ lastMet = true
+ if _, err = io.ReadFull(kdf, key); err != nil {
+ log.Fatalln(err)
+ }
+ ciph, err = chacha20poly1305.New(key)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ w.buf, err = ciph.Open(w.buf[:0], nonce, w.buf, nil)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ lastMet = true
+ }
+ } else {
+ if w.last {
+ if _, err = io.ReadFull(kdf, key); err != nil {
+ log.Fatalln(err)
+ }
+ ciph, err = chacha20poly1305.New(key)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ }
+ w.buf = ciph.Seal(w.buf[:0], nonce, w.buf, nil)
+ }
+ w.readyOut <- struct{}{}
+ <-w.readyOut
+ wg.Done()
+ }
+ }()
+ workers = append(workers, &w)