]> Cypherpunks.ru repositories - udpobfs.git/blob - crypto.go
Unify copyright comment format
[udpobfs.git] / crypto.go
1 // udpobfs -- simple point-to-point UDP obfuscation proxy
2 // Copyright (C) 2023-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 udpobfs
17
18 import (
19         "crypto/subtle"
20         "io"
21         "log"
22
23         "golang.org/x/crypto/blowfish"
24         "lukechampine.com/blake3"
25 )
26
27 const (
28         App     = "go.cypherpunks.ru/udpobfs/v2"
29         MACLen  = 6
30         SeqLen  = 8
31         SeedLen = 64
32 )
33
34 func MustNewBlowfish(key []byte) *blowfish.Cipher {
35         ciph, err := blowfish.NewCipher(key)
36         if err != nil {
37                 log.Fatal(err)
38         }
39         return ciph
40 }
41
42 type CryptoState struct {
43         rxEnc, rxMAC   *blake3.Hasher
44         txEnc, txMAC   *blake3.Hasher
45         rxObfs, txObfs *blowfish.Cipher
46         rxSeq, txSeq   [SeqLen]byte
47 }
48
49 func NewCryptoState(seed []byte, isInit bool) *CryptoState {
50         var hInitEncKey, hInitMACKey [32]byte
51         var hRespEncKey, hRespMACKey [32]byte
52         var obfsInitKey, obfsRespKey [32]byte
53         blake3.DeriveKey(hInitEncKey[:], App+" init enc", seed)
54         blake3.DeriveKey(hInitMACKey[:], App+" init mac", seed)
55         blake3.DeriveKey(hRespEncKey[:], App+" resp enc", seed)
56         blake3.DeriveKey(hRespMACKey[:], App+" resp mac", seed)
57         blake3.DeriveKey(obfsInitKey[:], App+" init obfs", seed)
58         blake3.DeriveKey(obfsRespKey[:], App+" resp obfs", seed)
59         hInitEnc := blake3.New(32, hInitEncKey[:])
60         hInitMAC := blake3.New(MACLen, hInitMACKey[:])
61         hRespEnc := blake3.New(32, hRespEncKey[:])
62         hRespMAC := blake3.New(MACLen, hRespMACKey[:])
63         obfsInit := MustNewBlowfish(obfsInitKey[:])
64         obfsResp := MustNewBlowfish(obfsRespKey[:])
65         state := CryptoState{}
66         if isInit {
67                 state.rxEnc = hRespEnc
68                 state.rxMAC = hRespMAC
69                 state.rxObfs = obfsResp
70                 state.txEnc = hInitEnc
71                 state.txMAC = hInitMAC
72                 state.txObfs = obfsInit
73         } else {
74                 state.rxEnc = hInitEnc
75                 state.rxMAC = hInitMAC
76                 state.rxObfs = obfsInit
77                 state.txEnc = hRespEnc
78                 state.txMAC = hRespMAC
79                 state.txObfs = obfsResp
80         }
81         return &state
82 }
83
84 func (state *CryptoState) Tx(dst, src []byte) []byte {
85         if len(dst) != SeqLen+len(src) {
86                 log.Fatal("unexpected dst len")
87         }
88         if Incr(state.txSeq[MACLen:]) {
89                 Incr(state.txSeq[:MACLen])
90         }
91         copy(dst, state.txSeq[:])
92         state.txEnc.Reset()
93         MustWrite(state.txEnc, state.txSeq[:])
94         if _, err := io.ReadFull(state.txEnc.XOF(), dst[SeqLen:]); err != nil {
95                 log.Fatal(err)
96         }
97         subtle.XORBytes(dst[SeqLen:], dst[SeqLen:], src)
98         state.txMAC.Reset()
99         MustWrite(state.txMAC, dst)
100         state.txMAC.Sum(dst[:0])
101         state.txObfs.Encrypt(dst[:SeqLen], dst[:SeqLen])
102         return dst[:len(src)+SeqLen]
103 }
104
105 func (state *CryptoState) Rx(dst, src []byte) []byte {
106         if len(dst) != len(src) {
107                 log.Fatal("unexpected dst len")
108         }
109         state.rxObfs.Decrypt(dst[:SeqLen], src[:SeqLen])
110         var theirMAC [MACLen]byte
111         copy(theirMAC[:], dst)
112         copy(dst, state.rxSeq[:MACLen])
113         state.rxMAC.Reset()
114         MustWrite(state.rxMAC, dst[:SeqLen])
115         MustWrite(state.rxMAC, src[SeqLen:])
116         var ourMAC [MACLen]byte
117         state.rxMAC.Sum(ourMAC[:0])
118         if subtle.ConstantTimeCompare(ourMAC[:], theirMAC[:]) != 1 {
119                 Incr(dst[:MACLen])
120                 state.rxMAC.Reset()
121                 MustWrite(state.rxMAC, dst[:SeqLen])
122                 MustWrite(state.rxMAC, src[SeqLen:])
123                 state.rxMAC.Sum(ourMAC[:0])
124                 if subtle.ConstantTimeCompare(ourMAC[:], theirMAC[:]) != 1 {
125                         return nil
126                 }
127         }
128         copy(state.rxSeq[:], dst)
129         state.rxEnc.Reset()
130         MustWrite(state.rxEnc, dst[:SeqLen])
131         if _, err := io.ReadFull(state.rxEnc.XOF(), dst); err != nil {
132                 log.Fatal(err)
133         }
134         subtle.XORBytes(dst, dst, src[SeqLen:])
135         return dst[:len(src)-SeqLen]
136 }