github.com/mholt/caddy-l4@v0.0.0-20241104153248-ec8fae209322/modules/l4openvpn/crypto.go (about) 1 // Copyright 2024 VNXME 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package l4openvpn 16 17 import ( 18 "crypto/aes" 19 "crypto/cipher" 20 "crypto/hmac" 21 "crypto/md5" 22 "crypto/sha1" 23 "crypto/sha256" 24 "crypto/sha512" 25 "encoding/base64" 26 "encoding/hex" 27 "errors" 28 "golang.org/x/crypto/blake2b" 29 "golang.org/x/crypto/blake2s" 30 "golang.org/x/crypto/ripemd160" 31 "golang.org/x/crypto/sha3" 32 "hash" 33 "io" 34 "os" 35 "regexp" 36 "slices" 37 "strings" 38 ) 39 40 // AuthDigest represents a digest used for computing HMACs of control messages. 41 type AuthDigest struct { 42 // Creator is a function returning something that implements hash.Hash interface. 43 // Creator is required whenever Generator is nil. 44 Creator func() hash.Hash 45 // Generator is a function returning an HMAC for a given set of key and plain bytes. 46 // Generator is optional, but it takes precedence over Creator. 47 Generator func(key, plain []byte) []byte 48 // Names contains a list of digest names in various notations. 49 Names []string 50 // Size is a number of bytes all computed HMACs will have. 51 Size int 52 } 53 54 // HMACGenerateOnClient computes an HMAC of a given plain text with a part of a given StaticKey 55 // (to be sent by the client to the server). 56 func (ad *AuthDigest) HMACGenerateOnClient(sk *StaticKey, plain []byte) []byte { 57 key := sk.GetClientAuthKey(ad.Size) 58 if ad.Generator != nil { 59 return ad.Generator(key, plain) 60 } 61 return HMACCreateAndGenerate(ad.Creator, key, plain) 62 } 63 64 // HMACGenerateOnServer computes an HMAC of a given plain text with a part of a given StaticKey 65 // (to be sent by the server to the client). 66 func (ad *AuthDigest) HMACGenerateOnServer(sk *StaticKey, plain []byte) []byte { 67 key := sk.GetServerAuthKey(ad.Size) 68 if ad.Generator != nil { 69 return ad.Generator(key, plain) 70 } 71 return HMACCreateAndGenerate(ad.Creator, key, plain) 72 } 73 74 // HMACValidateOnClient compares an expected HMAC (as received by the client from the server) 75 // with an actual HMAC of a given plain text it computes with a part of a given StaticKey. 76 func (ad *AuthDigest) HMACValidateOnClient(sk *StaticKey, plain, expected []byte) bool { 77 actual := ad.HMACGenerateOnServer(sk, plain) 78 return hmac.Equal(actual, expected) 79 } 80 81 // HMACValidateOnServer compares an expected HMAC (as received by the server from the client) 82 // with an actual HMAC of a given plain text it computes with a part of a given StaticKey. 83 func (ad *AuthDigest) HMACValidateOnServer(sk *StaticKey, plain, expected []byte) bool { 84 actual := ad.HMACGenerateOnClient(sk, plain) 85 return hmac.Equal(actual, expected) 86 } 87 88 // CryptCipher represents a cipher used for en/decrypting control messages. 89 type CryptCipher struct { 90 // Decryptor is a function returning plain bytes from encrypted ones. 91 Decryptor func(key, iv, encrypted []byte) []byte 92 // Encryptor is a function returning encrypted bytes from plain ones. 93 Encryptor func(key, iv, plain []byte) []byte 94 // Names contains a list of cipher names in various notations. 95 Names []string 96 // SizeBlock is a number of bytes an initialization vector (IV) must have. 97 SizeBlock int 98 // SizeKey is a number of bytes an en/decryption key must have. 99 SizeKey int 100 } 101 102 // DecryptOnClient decrypts given encrypted bytes with a part of a given StaticKey 103 // (as received by the client from the server). 104 func (cc *CryptCipher) DecryptOnClient(sk *StaticKey, iv, encrypted []byte) []byte { 105 key := sk.GetClientDecryptKey(cc.SizeKey) 106 return cc.Decryptor(key, iv, encrypted) 107 } 108 109 // DecryptOnServer decrypts given encrypted bytes with a part of a given StaticKey 110 // (as received by the server from the client). 111 func (cc *CryptCipher) DecryptOnServer(sk *StaticKey, iv, encrypted []byte) []byte { 112 key := sk.GetServerDecryptKey(cc.SizeKey) 113 return cc.Decryptor(key, iv, encrypted) 114 } 115 116 // EncryptOnClient encrypts given plain bytes with a part of a given StaticKey 117 // (to be sent by the client to the server). 118 func (cc *CryptCipher) EncryptOnClient(sk *StaticKey, iv, plain []byte) []byte { 119 key := sk.GetClientEncryptKey(cc.SizeKey) 120 return cc.Encryptor(key, iv, plain) 121 } 122 123 // EncryptOnServer encrypts given plain bytes with a part of a given StaticKey 124 // (to be sent by the server to the client). 125 func (cc *CryptCipher) EncryptOnServer(sk *StaticKey, iv, plain []byte) []byte { 126 key := sk.GetServerEncryptKey(cc.SizeKey) 127 return cc.Encryptor(key, iv, plain) 128 } 129 130 // StaticKey is an OpenVPN static key used for authentication and encryption of control messages. 131 // 132 // Notes: 133 // 134 // Authentication. If no key direction is set (i.e. a bidirectional key), OpenVPN uses key[64:64+size] for 135 // computing and validating HMACs on both the client and the server. If the client has `key-direction 1` 136 // and the server has `key-direction 0`, OpenVPN uses key[192:192+size] for computing HMACs on the client and 137 // validating them on the server. If the client has `key-direction 0` and the server has `key-direction 1` 138 // (i.e. an inverse direction in violation of the recommendations), OpenVPN uses key[64:64+size] for computing 139 // HMACs on the client and validating them on the server. Inverse and Bidi are mutually exclusive. If both are 140 // set, Bidi takes precedence. 141 // 142 // En/decryption. OpenVPN always takes 2 different keys for encryption and decryption, so Bidi is completely 143 // ignored. Unless Inverse is set, key[128:128+size] is used for encryption on the client and key[0:0+size] is 144 // used for decryption on the client with the server applying these keys in the other way. 145 type StaticKey struct { 146 // Bidi mimics `key-direction` omitted for both the server and the client. 147 Bidi bool 148 // Inverse mimics `key-direction 1` set for the server and `key-direction 0` set for the client. 149 Inverse bool 150 // KeyBytes must contain 128 or 256 bytes of the static key. 151 KeyBytes []byte 152 } 153 154 // FromBase64 fills sk's KeyBytes from a given base64 string. 155 func (sk *StaticKey) FromBase64(s string) (err error) { 156 sk.KeyBytes, err = base64.StdEncoding.DecodeString(s) 157 return 158 } 159 160 // FromGroupKeyFile fills sk's KeyBytes from a given group key file. 161 func (sk *StaticKey) FromGroupKeyFile(path string) error { 162 return sk.FromFile(path, StaticKeyFromFileHex, StaticKeyBytesTotal*2, sk.FromHex) 163 } 164 165 // FromServerKeyFile fills sk's KeyBytes from a given server key file. 166 func (sk *StaticKey) FromServerKeyFile(path string) error { 167 return sk.FromFile(path, StaticKeyFromFileBase64, base64.StdEncoding.EncodedLen(StaticKeyBytesHalf), sk.FromBase64) 168 } 169 170 // FromFile fills sk's KeyBytes from a given file. 171 func (sk *StaticKey) FromFile(path string, re *regexp.Regexp, size int, from func(string) error) error { 172 file, err := os.Open(path) 173 if err != nil { 174 return err 175 } 176 defer func() { _ = file.Close() }() 177 178 n := 1024 179 buf := make([]byte, n) 180 n, err = file.Read(buf) 181 if err != nil && !errors.Is(err, io.EOF) { 182 return err 183 } 184 185 if n > 0 { 186 var s string 187 if r := re.FindStringSubmatch(string(buf[:n])); r != nil && len(r) == 2 { 188 s = strings.ReplaceAll(r[1], "\r", "") 189 s = strings.ReplaceAll(s, "\n", "") 190 if size == 0 || len(s) == size { 191 return from(s) 192 } 193 } 194 } 195 196 return ErrInvalidStaticKeyFileContents 197 } 198 199 // FromHex fills sk's KeyBytes from a given hex string. 200 func (sk *StaticKey) FromHex(s string) (err error) { 201 sk.KeyBytes, err = hex.DecodeString(s) 202 return 203 } 204 205 // GetClientAuthBytes returns a quarter of KeyBytes to be used for authentication of control messages 206 // composed by the client. 207 func (sk *StaticKey) GetClientAuthBytes() []byte { 208 if sk.Inverse || sk.Bidi { 209 return sk.GetQuarterBytes(1) 210 } 211 return sk.GetQuarterBytes(3) 212 } 213 214 // GetClientAuthKey returns a key of a given size from GetClientAuthBytes. 215 func (sk *StaticKey) GetClientAuthKey(size int) []byte { 216 return sk.GetClientAuthBytes()[:min(size, StaticKeyBytesQuarter)] 217 } 218 219 // GetClientEncryptBytes returns a quarter of KeyBytes to be used for encryption of control messages 220 // composed by the client. 221 func (sk *StaticKey) GetClientEncryptBytes() []byte { 222 if sk.Inverse { 223 return sk.GetQuarterBytes(0) 224 } 225 return sk.GetQuarterBytes(2) 226 } 227 228 // GetClientEncryptKey returns a key of a given size from GetClientEncryptBytes. 229 func (sk *StaticKey) GetClientEncryptKey(size int) []byte { 230 return sk.GetClientEncryptBytes()[:min(size, StaticKeyBytesQuarter)] 231 } 232 233 // GetClientDecryptBytes returns a quarter of KeyBytes to be used for decryption of control messages 234 // received by the client. 235 func (sk *StaticKey) GetClientDecryptBytes() []byte { 236 if sk.Inverse { 237 return sk.GetQuarterBytes(2) 238 } 239 return sk.GetQuarterBytes(0) 240 } 241 242 // GetClientDecryptKey returns a key of a given size from GetClientDecryptBytes. 243 func (sk *StaticKey) GetClientDecryptKey(size int) []byte { 244 return sk.GetClientDecryptBytes()[:min(size, StaticKeyBytesQuarter)] 245 } 246 247 // GetServerAuthBytes returns a quarter of KeyBytes to be used for authentication of control messages 248 // composed by the server. 249 func (sk *StaticKey) GetServerAuthBytes() []byte { 250 if sk.Inverse && !sk.Bidi { 251 return sk.GetQuarterBytes(3) 252 } 253 return sk.GetQuarterBytes(1) 254 } 255 256 // GetServerAuthKey returns a key of a given size from GetServerAuthKey. 257 func (sk *StaticKey) GetServerAuthKey(size int) []byte { 258 return sk.GetServerAuthBytes()[:min(size, StaticKeyBytesQuarter)] 259 } 260 261 // GetServerEncryptBytes returns a quarter of KeyBytes to be used for encryption of control messages 262 // composed by the server. 263 func (sk *StaticKey) GetServerEncryptBytes() []byte { 264 return sk.GetClientDecryptBytes() 265 } 266 267 // GetServerEncryptKey returns a key of a given size from GetServerEncryptBytes. 268 func (sk *StaticKey) GetServerEncryptKey(size int) []byte { 269 return sk.GetClientDecryptKey(size) 270 } 271 272 // GetServerDecryptBytes returns a quarter of KeyBytes to be used for decryption of control messages 273 // received by the server. 274 func (sk *StaticKey) GetServerDecryptBytes() []byte { 275 return sk.GetClientEncryptBytes() 276 } 277 278 // GetServerDecryptKey returns a key of a given size from GetServerDecryptBytes. 279 func (sk *StaticKey) GetServerDecryptKey(size int) []byte { 280 return sk.GetClientEncryptKey(size) 281 } 282 283 // GetQuarterBytes returns a nth (0-based) quarter of KeyBytes 284 func (sk *StaticKey) GetQuarterBytes(q uint) []byte { 285 q = q % 4 286 if len(sk.KeyBytes) < StaticKeyBytesTotal { 287 q = q % 2 288 } 289 if len(sk.KeyBytes) < StaticKeyBytesHalf { 290 q = 0 291 } 292 if len(sk.KeyBytes) < StaticKeyBytesQuarter { 293 return sk.KeyBytes 294 } 295 return sk.KeyBytes[q*StaticKeyBytesQuarter : (q+1)*StaticKeyBytesQuarter] 296 } 297 298 // ToBase64 returns a base64 string representing KeyBytes. 299 func (sk *StaticKey) ToBase64() string { 300 return base64.StdEncoding.EncodeToString(sk.KeyBytes) 301 } 302 303 // ToHex returns a hex string representing KeyBytes. 304 func (sk *StaticKey) ToHex() string { 305 return hex.EncodeToString(sk.KeyBytes) 306 } 307 308 // AuthDigests contains all the supported items of AuthDigest type. 309 var AuthDigests = []*AuthDigest{ 310 // Legacy digests 311 {Creator: md5.New, Names: []string{"MD5", "SSL3-MD5", "md5", "ssl3-md5"}, Size: md5.Size}, 312 {Creator: sha1.New, Names: []string{"SHA-1", "SHA1", "SSL3-SHA1", "sha-1", "sha1", "ssl3-sha1"}, Size: sha1.Size}, 313 {Creator: ripemd160.New, Names: []string{"RIPEMD-160", "RMD-160", "RIPEMD160", "RIPEMD", "RMD160", "ripemd-160", "rmd-160", "ripemd160", "ripemd", "rmd160"}, Size: ripemd160.Size}, 314 // SHA2 digests 315 {Creator: sha256.New224, Names: []string{"SHA-224", "SHA2-224", "SHA224", "sha-224", "sha2-224", "sha224"}, Size: sha256.Size224}, 316 {Creator: sha256.New, Names: []string{"SHA-256", "SHA2-256", "SHA256", "sha-256", "sha2-256", "sha256"}, Size: sha256.Size}, 317 {Creator: sha512.New384, Names: []string{"SHA-384", "SHA2-384", "SHA384", "sha-384", "sha2-384", "sha384"}, Size: sha512.Size384}, 318 {Creator: sha512.New, Names: []string{"SHA-512", "SHA2-512", "SHA512", "sha-512", "sha2-512", "sha512"}, Size: sha512.Size}, 319 {Creator: sha512.New512_224, Names: []string{"SHA-512/224", "SHA2-512/224", "SHA512-224", "sha-512/224", "sha2-512/224", "sha512-224"}, Size: sha512.Size224}, 320 {Creator: sha512.New512_256, Names: []string{"SHA-512/256", "SHA2-512/256", "SHA512-256", "sha-512/256", "sha2-512/256", "sha512-256"}, Size: sha512.Size256}, 321 // SHA3 digests 322 {Creator: sha3.New224, Names: []string{"SHA3-224", "sha3-224"}, Size: 28}, 323 {Creator: sha3.New256, Names: []string{"SHA3-256", "sha3-256"}, Size: 32}, 324 {Creator: sha3.New384, Names: []string{"SHA3-384", "sha3-384"}, Size: 48}, 325 {Creator: sha3.New512, Names: []string{"SHA3-512", "sha3-512"}, Size: 64}, 326 // BLAKE digests 327 { 328 Creator: func() hash.Hash { 329 h, _ := blake2s.New256(nil) 330 return h 331 }, 332 Names: []string{"BLAKE2s-256", "BLAKE2S-256", "blake2s-256", "blake2S-256"}, 333 Size: blake2s.Size, 334 }, 335 { 336 Creator: func() hash.Hash { 337 h, _ := blake2b.New512(nil) 338 return h 339 }, 340 Names: []string{"BLAKE2b-512", "BLAKE2B-512", "blake2b-512", "blake2B-512"}, 341 Size: blake2b.Size, 342 }, 343 // SHAKE digests 344 { 345 Creator: func() hash.Hash { 346 return sha3.NewShake128() 347 }, 348 Names: []string{"SHAKE-128", "SHAKE128", "shake-128", "shake128"}, 349 Size: 32, 350 }, 351 { 352 Creator: func() hash.Hash { 353 return sha3.NewShake256() 354 }, 355 Names: []string{"SHAKE-256", "SHAKE256", "shake-256", "shake256"}, 356 Size: 64, 357 }, 358 // Custom digests 359 { 360 // This MD5-SHA1 implementation outputs bytes that match many online generators. However, 361 // its output never matches the HMACs of the sample packets generated on Windows and macOS. 362 // Since OpenVPN uses the OpenSSL library under the hood, the reason for this hash mismatch 363 // should most likely be traced there. Assuming the MD5-SHA1 digest has a very limited use, 364 // there is little probability anyone will ever face this issue. 365 Generator: func(key, plain []byte) []byte { 366 hmacMD5 := hmac.New(md5.New, key[:md5.Size]) 367 hmacMD5.Write(plain) 368 hmacSHA1 := hmac.New(sha1.New, key[:sha1.Size]) 369 hmacSHA1.Write(plain) 370 result := make([]byte, 0, md5.Size+sha1.Size) 371 return hmacSHA1.Sum(hmacMD5.Sum(result)) 372 }, 373 Names: []string{"MD5+SHA1", "MD5-SHA1", "MD5SHA1", "md5+sha1", "md5-sha1", "md5sha1"}, 374 Size: md5.Size + sha1.Size, 375 }, 376 } 377 378 // AuthDigestSizes contains sizes of all the supported items of AuthDigest type. 379 var AuthDigestSizes = func() []int { 380 presence := make([]bool, AuthHMACBytesMax+1) 381 n := 0 382 for _, ad := range AuthDigests { 383 if !presence[ad.Size] { 384 n++ 385 presence[ad.Size] = true 386 } 387 } 388 sizes := make([]int, 0, n) 389 for i, present := range presence { 390 if present { 391 sizes = append(sizes, i) 392 } 393 } 394 return sizes 395 }() 396 397 // CryptCiphers contains all the supported items of CryptCipher type. 398 var CryptCiphers = []*CryptCipher{ 399 { 400 Decryptor: func(key, iv, encrypted []byte) []byte { 401 plain := make([]byte, len(encrypted)) 402 block, _ := aes.NewCipher(key) 403 ctr := cipher.NewCTR(block, iv) 404 ctr.XORKeyStream(plain, encrypted) 405 return plain 406 }, 407 Encryptor: func(key, iv, plain []byte) []byte { 408 encrypted := make([]byte, len(plain)) 409 block, _ := aes.NewCipher(key) 410 ctr := cipher.NewCTR(block, iv) 411 ctr.XORKeyStream(encrypted, plain) 412 return encrypted 413 }, 414 Names: []string{"AES-256-CTR", "aes-256-ctr"}, 415 SizeBlock: 16, 416 SizeKey: 32, 417 }, 418 } 419 420 // AuthDigestDefault is the default AuthDigest used in the crypt and crypt2 modes (SHA-256). 421 var AuthDigestDefault = AuthDigestFindByName("SHA-256") 422 423 // CryptCipherDefault is the default CryptCipher used in the crypt and crypt2 modes (AES-256-CTR). 424 var CryptCipherDefault = CryptCipherFindByName("AES-256-CTR") 425 426 var StaticKeyFromFileBase64 = regexp.MustCompile("^(?:#.*?\\r?\\n)*" + 427 "-----BEGIN OpenVPN tls-crypt-v2 (?:client|server) key-----\\r?\\n" + 428 "([0-9a-zA-Z+=\\/\\r\\n]+)" + 429 "-----END OpenVPN tls-crypt-v2 (?:client|server) key-----(?:\\r?\\n)?$") 430 var StaticKeyFromFileHex = regexp.MustCompile("^(?:#.*?\\r?\\n)*" + 431 "-----BEGIN OpenVPN Static key V1-----\\r?\\n" + 432 "([0-9a-fA-F\\r\\n]+)" + 433 "-----END OpenVPN Static key V1-----(?:\\r?\\n)?$") 434 435 var ( 436 ErrInvalidStaticKeyFileContents = errors.New("invalid static key file contents") 437 ) 438 439 const ( 440 AuthHMACBytesMax = sha512.Size 441 AuthHMACBytesMin = md5.Size 442 443 CryptHMACBytesTotal = sha256.Size 444 445 StaticKeyBytesTotal = 256 446 StaticKeyBytesHalf = StaticKeyBytesTotal / 2 447 StaticKeyBytesQuarter = StaticKeyBytesTotal / 4 448 ) 449 450 // AuthDigestFindByName returns a pointer to AuthDigest having a given name or nil. 451 func AuthDigestFindByName(name string) *AuthDigest { 452 for _, ad := range AuthDigests { 453 if slices.Contains(ad.Names, name) { 454 return ad 455 } 456 } 457 return nil 458 } 459 460 // CryptCipherFindByName returns a pointer to CryptCipher having a given name or nil. 461 func CryptCipherFindByName(name string) *CryptCipher { 462 for _, cc := range CryptCiphers { 463 if slices.Contains(cc.Names, name) { 464 return cc 465 } 466 } 467 return nil 468 } 469 470 // HMACCreateAndGenerate uses a given digest creator to compute an HMAC of a given plain text with a given key. 471 func HMACCreateAndGenerate(creator func() hash.Hash, key, plain []byte) []byte { 472 hmacDigest := hmac.New(creator, key) 473 hmacDigest.Write(plain) 474 return hmacDigest.Sum(nil) 475 } 476 477 // StaticKeyNewFromBase64 returns a pointer to StaticKey filled with bytes from a given base64 string. 478 func StaticKeyNewFromBase64(s string, inverse bool, bidi bool) *StaticKey { 479 sk := &StaticKey{Inverse: inverse, Bidi: bidi} 480 _ = sk.FromBase64(s) 481 return sk 482 } 483 484 // StaticKeyNewFromHex returns a pointer to StaticKey filled with bytes from a given hex string. 485 func StaticKeyNewFromHex(s string, inverse bool, bidi bool) *StaticKey { 486 sk := &StaticKey{Inverse: inverse, Bidi: bidi} 487 _ = sk.FromHex(s) 488 return sk 489 }