]> Cypherpunks.ru repositories - nncp.git/blob - src/cypherpunks.ru/nncp/eblob.go
Encrypted configuration file
[nncp.git] / src / cypherpunks.ru / nncp / eblob.go
1 /*
2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2017 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 package nncp
20
21 import (
22         "bytes"
23         "crypto/cipher"
24         "crypto/rand"
25         "crypto/subtle"
26         "errors"
27         "io"
28
29         "cypherpunks.ru/balloon"
30         "github.com/davecgh/go-xdr/xdr2"
31         "golang.org/x/crypto/blake2b"
32         "golang.org/x/crypto/hkdf"
33         "golang.org/x/crypto/twofish"
34 )
35
36 const (
37         DefaultS = 1 << 20 / 32
38         DefaultT = 1 << 4
39         DefaultP = 2
40 )
41
42 var (
43         MagicNNCPBv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'B', 0, 0, 1}
44 )
45
46 type EBlob struct {
47         Magic [8]byte
48         SCost uint32
49         TCost uint32
50         PCost uint32
51         Salt  *[32]byte
52         Blob  []byte
53         MAC   *[blake2b.Size256]byte
54 }
55
56 // Create an encrypted blob. sCost -- memory space requirements, number
57 // of hash-output sized (32 bytes) blocks. tCost -- time requirements,
58 // number of rounds. pCost -- number of parallel jobs.
59 func NewEBlob(sCost, tCost, pCost int, password, data []byte) ([]byte, error) {
60         salt := new([32]byte)
61         var err error
62         if _, err = rand.Read(salt[:]); err != nil {
63                 return nil, err
64         }
65         key := balloon.H(blake256, password, salt[:], sCost, tCost, pCost)
66         kdf := hkdf.New(blake256, key, nil, MagicNNCPBv1[:])
67         keyEnc := make([]byte, 32)
68         if _, err = io.ReadFull(kdf, keyEnc); err != nil {
69                 return nil, err
70         }
71         keyAuth := make([]byte, 64)
72         if _, err = io.ReadFull(kdf, keyAuth); err != nil {
73                 return nil, err
74         }
75         ciph, err := twofish.NewCipher(keyEnc)
76         if err != nil {
77                 return nil, err
78         }
79         ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
80         mac, err := blake2b.New256(keyAuth)
81         if err != nil {
82                 return nil, err
83         }
84         var blob bytes.Buffer
85         mw := io.MultiWriter(&blob, mac)
86         ae := &cipher.StreamWriter{S: ctr, W: mw}
87         if _, err = ae.Write(data); err != nil {
88                 return nil, err
89         }
90         macTag := new([blake2b.Size256]byte)
91         mac.Sum(macTag[:0])
92         eblob := EBlob{
93                 Magic: MagicNNCPBv1,
94                 SCost: uint32(sCost),
95                 TCost: uint32(tCost),
96                 PCost: uint32(pCost),
97                 Salt:  salt,
98                 Blob:  blob.Bytes(),
99                 MAC:   macTag,
100         }
101         var eblobRaw bytes.Buffer
102         if _, err = xdr.Marshal(&eblobRaw, &eblob); err != nil {
103                 return nil, err
104         }
105         return eblobRaw.Bytes(), nil
106 }
107
108 func DeEBlob(eblobRaw, password []byte) ([]byte, error) {
109         var eblob EBlob
110         var err error
111         if _, err = xdr.Unmarshal(bytes.NewReader(eblobRaw), &eblob); err != nil {
112                 return nil, err
113         }
114         if eblob.Magic != MagicNNCPBv1 {
115                 return nil, BadMagic
116         }
117         key := balloon.H(
118                 blake256,
119                 password,
120                 eblob.Salt[:],
121                 int(eblob.SCost),
122                 int(eblob.TCost),
123                 int(eblob.PCost),
124         )
125         kdf := hkdf.New(blake256, key, nil, MagicNNCPBv1[:])
126         keyEnc := make([]byte, 32)
127         if _, err = io.ReadFull(kdf, keyEnc); err != nil {
128                 return nil, err
129         }
130         keyAuth := make([]byte, 64)
131         if _, err = io.ReadFull(kdf, keyAuth); err != nil {
132                 return nil, err
133         }
134         ciph, err := twofish.NewCipher(keyEnc)
135         if err != nil {
136                 return nil, err
137         }
138         ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize))
139         mac, err := blake2b.New256(keyAuth)
140         if err != nil {
141                 return nil, err
142         }
143         var blob bytes.Buffer
144         tr := io.TeeReader(bytes.NewReader(eblob.Blob), mac)
145         ae := &cipher.StreamReader{S: ctr, R: tr}
146         if _, err = io.Copy(&blob, ae); err != nil {
147                 return nil, err
148         }
149         if subtle.ConstantTimeCompare(mac.Sum(nil), eblob.MAC[:]) != 1 {
150                 return nil, errors.New("Unauthenticated blob")
151         }
152         return blob.Bytes(), nil
153 }