/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2017 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+the Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
import (
"bytes"
- "crypto/cipher"
"crypto/rand"
- "crypto/subtle"
- "errors"
- "io"
+ "hash"
"cypherpunks.ru/balloon"
"github.com/davecgh/go-xdr/xdr2"
"golang.org/x/crypto/blake2b"
- "golang.org/x/crypto/hkdf"
- "golang.org/x/crypto/twofish"
+ "golang.org/x/crypto/chacha20poly1305"
)
const (
)
var (
- MagicNNCPBv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'B', 0, 0, 1}
+ MagicNNCPBv3 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'B', 0, 0, 3}
)
type EBlob struct {
PCost uint32
Salt *[32]byte
Blob []byte
- MAC *[blake2b.Size256]byte
+}
+
+func blake256() hash.Hash {
+ h, err := blake2b.New256(nil)
+ if err != nil {
+ panic(err)
+ }
+ return h
}
// Create an encrypted blob. sCost -- memory space requirements, number
if _, err = rand.Read(salt[:]); err != nil {
return nil, err
}
- key := balloon.H(blake256, password, salt[:], sCost, tCost, pCost)
- kdf := hkdf.New(blake256, key, nil, MagicNNCPBv1[:])
- keyEnc := make([]byte, 32)
- if _, err = io.ReadFull(kdf, keyEnc); err != nil {
- return nil, err
- }
- keyAuth := make([]byte, 64)
- if _, err = io.ReadFull(kdf, keyAuth); err != nil {
- return nil, err
- }
- ciph, err := twofish.NewCipher(keyEnc)
- if err != nil {
- return nil, err
- }
- ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
- mac, err := blake2b.New256(keyAuth)
- if err != nil {
- return nil, err
- }
- var blob bytes.Buffer
- mw := io.MultiWriter(&blob, mac)
- ae := &cipher.StreamWriter{S: ctr, W: mw}
- if _, err = ae.Write(data); err != nil {
- return nil, err
- }
- macTag := new([blake2b.Size256]byte)
- mac.Sum(macTag[:0])
eblob := EBlob{
- Magic: MagicNNCPBv1,
+ Magic: MagicNNCPBv3,
SCost: uint32(sCost),
TCost: uint32(tCost),
PCost: uint32(pCost),
Salt: salt,
- Blob: blob.Bytes(),
- MAC: macTag,
+ Blob: nil,
+ }
+ var eblobBuf bytes.Buffer
+ if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil {
+ return nil, err
+ }
+ key := balloon.H(blake256, password, salt[:], sCost, tCost, pCost)
+ aead, err := chacha20poly1305.New(key)
+ if err != nil {
+ return nil, err
}
- var eblobRaw bytes.Buffer
- if _, err = xdr.Marshal(&eblobRaw, &eblob); err != nil {
+ buf := make([]byte, 0, len(data)+aead.Overhead())
+ buf = aead.Seal(buf, make([]byte, aead.NonceSize()), data, eblobBuf.Bytes())
+ eblob.Blob = buf
+ eblobBuf.Reset()
+ if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil {
return nil, err
}
- return eblobRaw.Bytes(), nil
+ return eblobBuf.Bytes(), nil
}
func DeEBlob(eblobRaw, password []byte) ([]byte, error) {
if _, err = xdr.Unmarshal(bytes.NewReader(eblobRaw), &eblob); err != nil {
return nil, err
}
- if eblob.Magic != MagicNNCPBv1 {
+ if eblob.Magic != MagicNNCPBv3 {
return nil, BadMagic
}
key := balloon.H(
int(eblob.TCost),
int(eblob.PCost),
)
- kdf := hkdf.New(blake256, key, nil, MagicNNCPBv1[:])
- keyEnc := make([]byte, 32)
- if _, err = io.ReadFull(kdf, keyEnc); err != nil {
- return nil, err
- }
- keyAuth := make([]byte, 64)
- if _, err = io.ReadFull(kdf, keyAuth); err != nil {
- return nil, err
- }
- ciph, err := twofish.NewCipher(keyEnc)
+ aead, err := chacha20poly1305.New(key)
if err != nil {
return nil, err
}
- ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
- mac, err := blake2b.New256(keyAuth)
- if err != nil {
+
+ ciphertext := eblob.Blob
+ eblob.Blob = nil
+ var eblobBuf bytes.Buffer
+ if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil {
return nil, err
}
- var blob bytes.Buffer
- tr := io.TeeReader(bytes.NewReader(eblob.Blob), mac)
- ae := &cipher.StreamReader{S: ctr, R: tr}
- if _, err = io.Copy(&blob, ae); err != nil {
+ data, err := aead.Open(
+ ciphertext[:0],
+ make([]byte, aead.NonceSize()),
+ ciphertext,
+ eblobBuf.Bytes(),
+ )
+ if err != nil {
return nil, err
}
- if subtle.ConstantTimeCompare(mac.Sum(nil), eblob.MAC[:]) != 1 {
- return nil, errors.New("Unauthenticated blob")
- }
- return blob.Bytes(), nil
+ return data, nil
}