]> Cypherpunks.ru repositories - udpobfs.git/blob - crypto.go
RWMutex is not that bad
[udpobfs.git] / crypto.go
1 /*
2 udpobfs -- simple point-to-point UDP obfuscation proxy
3 Copyright (C) 2023 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, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 package udpobfs
19
20 import (
21         "crypto/subtle"
22         "io"
23         "log"
24
25         "golang.org/x/crypto/blowfish"
26         "lukechampine.com/blake3"
27 )
28
29 const (
30         App     = "go.cypherpunks.ru/udpobfs/v2"
31         MACLen  = 6
32         SeqLen  = 8
33         SeedLen = 64
34 )
35
36 func MustNewBlowfish(key []byte) *blowfish.Cipher {
37         ciph, err := blowfish.NewCipher(key)
38         if err != nil {
39                 log.Fatal(err)
40         }
41         return ciph
42 }
43
44 type CryptoState struct {
45         rxEnc, rxMAC   *blake3.Hasher
46         txEnc, txMAC   *blake3.Hasher
47         rxObfs, txObfs *blowfish.Cipher
48         rxSeq, txSeq   [SeqLen]byte
49 }
50
51 func NewCryptoState(seed []byte, isInit bool) *CryptoState {
52         var hInitEncKey, hInitMACKey [32]byte
53         var hRespEncKey, hRespMACKey [32]byte
54         var obfsInitKey, obfsRespKey [32]byte
55         blake3.DeriveKey(hInitEncKey[:], App+" init enc", seed)
56         blake3.DeriveKey(hInitMACKey[:], App+" init mac", seed)
57         blake3.DeriveKey(hRespEncKey[:], App+" resp enc", seed)
58         blake3.DeriveKey(hRespMACKey[:], App+" resp mac", seed)
59         blake3.DeriveKey(obfsInitKey[:], App+" init obfs", seed)
60         blake3.DeriveKey(obfsRespKey[:], App+" resp obfs", seed)
61         hInitEnc := blake3.New(32, hInitEncKey[:])
62         hInitMAC := blake3.New(MACLen, hInitMACKey[:])
63         hRespEnc := blake3.New(32, hRespEncKey[:])
64         hRespMAC := blake3.New(MACLen, hRespMACKey[:])
65         obfsInit := MustNewBlowfish(obfsInitKey[:])
66         obfsResp := MustNewBlowfish(obfsRespKey[:])
67         state := CryptoState{}
68         if isInit {
69                 state.rxEnc = hRespEnc
70                 state.rxMAC = hRespMAC
71                 state.rxObfs = obfsResp
72                 state.txEnc = hInitEnc
73                 state.txMAC = hInitMAC
74                 state.txObfs = obfsInit
75         } else {
76                 state.rxEnc = hInitEnc
77                 state.rxMAC = hInitMAC
78                 state.rxObfs = obfsInit
79                 state.txEnc = hRespEnc
80                 state.txMAC = hRespMAC
81                 state.txObfs = obfsResp
82         }
83         return &state
84 }
85
86 func (state *CryptoState) Tx(dst, src []byte) []byte {
87         if len(dst) != SeqLen+len(src) {
88                 log.Fatal("unexpected dst len")
89         }
90         if Incr(state.txSeq[MACLen:]) {
91                 Incr(state.txSeq[:MACLen])
92         }
93         copy(dst, state.txSeq[:])
94         state.txEnc.Reset()
95         MustWrite(state.txEnc, state.txSeq[:])
96         if _, err := io.ReadFull(state.txEnc.XOF(), dst[SeqLen:]); err != nil {
97                 log.Fatal(err)
98         }
99         subtle.XORBytes(dst[SeqLen:], dst[SeqLen:], src)
100         state.txMAC.Reset()
101         MustWrite(state.txMAC, dst)
102         state.txMAC.Sum(dst[:0])
103         state.txObfs.Encrypt(dst[:SeqLen], dst[:SeqLen])
104         return dst[:len(src)+SeqLen]
105 }
106
107 func (state *CryptoState) Rx(dst, src []byte) []byte {
108         if len(dst) != len(src) {
109                 log.Fatal("unexpected dst len")
110         }
111         state.rxObfs.Decrypt(dst[:SeqLen], src[:SeqLen])
112         var theirMAC [MACLen]byte
113         copy(theirMAC[:], dst)
114         copy(dst, state.rxSeq[:MACLen])
115         state.rxMAC.Reset()
116         MustWrite(state.rxMAC, dst[:SeqLen])
117         MustWrite(state.rxMAC, src[SeqLen:])
118         var ourMAC [MACLen]byte
119         state.rxMAC.Sum(ourMAC[:0])
120         if subtle.ConstantTimeCompare(ourMAC[:], theirMAC[:]) != 1 {
121                 Incr(dst[:MACLen])
122                 state.rxMAC.Reset()
123                 MustWrite(state.rxMAC, dst[:SeqLen])
124                 MustWrite(state.rxMAC, src[SeqLen:])
125                 state.rxMAC.Sum(ourMAC[:0])
126                 if subtle.ConstantTimeCompare(ourMAC[:], theirMAC[:]) != 1 {
127                         return nil
128                 }
129         }
130         copy(state.rxSeq[:], dst)
131         state.rxEnc.Reset()
132         MustWrite(state.rxEnc, dst[:SeqLen])
133         if _, err := io.ReadFull(state.rxEnc.XOF(), dst); err != nil {
134                 log.Fatal(err)
135         }
136         subtle.XORBytes(dst, dst, src[SeqLen:])
137         return dst[:len(src)-SeqLen]
138 }