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