1/*
2NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3Copyright (C) 2016-2021 Sergey Matveev <stargrave@stargrave.org>
4
5This program is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, version 3 of the License.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program.  If not, see <http://www.gnu.org/licenses/>.
16*/
17
18package nncp
19
20import (
21	"bytes"
22	"crypto/rand"
23	"hash"
24
25	xdr "github.com/davecgh/go-xdr/xdr2"
26	"go.cypherpunks.ru/balloon"
27	"golang.org/x/crypto/blake2b"
28	"golang.org/x/crypto/chacha20poly1305"
29)
30
31const (
32	DefaultS = 1 << 20 / 32
33	DefaultT = 1 << 4
34	DefaultP = 2
35)
36
37type EBlob struct {
38	Magic [8]byte
39	SCost uint32
40	TCost uint32
41	PCost uint32
42	Salt  *[32]byte
43	Blob  []byte
44}
45
46func blake256() hash.Hash {
47	h, err := blake2b.New256(nil)
48	if err != nil {
49		panic(err)
50	}
51	return h
52}
53
54// Create an encrypted blob. sCost -- memory space requirements, number
55// of hash-output sized (32 bytes) blocks. tCost -- time requirements,
56// number of rounds. pCost -- number of parallel jobs.
57func NewEBlob(sCost, tCost, pCost int, password, data []byte) ([]byte, error) {
58	salt := new([32]byte)
59	var err error
60	if _, err = rand.Read(salt[:]); err != nil {
61		return nil, err
62	}
63	eblob := EBlob{
64		Magic: MagicNNCPBv3.B,
65		SCost: uint32(sCost),
66		TCost: uint32(tCost),
67		PCost: uint32(pCost),
68		Salt:  salt,
69		Blob:  nil,
70	}
71	var eblobBuf bytes.Buffer
72	if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil {
73		return nil, err
74	}
75	key := balloon.H(blake256, password, salt[:], sCost, tCost, pCost)
76	aead, err := chacha20poly1305.New(key)
77	if err != nil {
78		return nil, err
79	}
80	buf := make([]byte, 0, len(data)+aead.Overhead())
81	buf = aead.Seal(buf, make([]byte, aead.NonceSize()), data, eblobBuf.Bytes())
82	eblob.Blob = buf
83	eblobBuf.Reset()
84	if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil {
85		return nil, err
86	}
87	return eblobBuf.Bytes(), nil
88}
89
90func DeEBlob(eblobRaw, password []byte) ([]byte, error) {
91	var eblob EBlob
92	var err error
93	if _, err = xdr.Unmarshal(bytes.NewReader(eblobRaw), &eblob); err != nil {
94		return nil, err
95	}
96	switch eblob.Magic {
97	case MagicNNCPBv1.B:
98		err = MagicNNCPBv1.TooOld()
99	case MagicNNCPBv2.B:
100		err = MagicNNCPBv1.TooOld()
101	case MagicNNCPBv3.B:
102	default:
103		err = BadMagic
104	}
105	if err != nil {
106		return nil, err
107	}
108	key := balloon.H(
109		blake256,
110		password,
111		eblob.Salt[:],
112		int(eblob.SCost),
113		int(eblob.TCost),
114		int(eblob.PCost),
115	)
116	aead, err := chacha20poly1305.New(key)
117	if err != nil {
118		return nil, err
119	}
120
121	ciphertext := eblob.Blob
122	eblob.Blob = nil
123	var eblobBuf bytes.Buffer
124	if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil {
125		return nil, err
126	}
127	data, err := aead.Open(
128		ciphertext[:0],
129		make([]byte, aead.NonceSize()),
130		ciphertext,
131		eblobBuf.Bytes(),
132	)
133	if err != nil {
134		return nil, err
135	}
136	return data, nil
137}
138