-
Notifications
You must be signed in to change notification settings - Fork 208
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
IPsec key rotation with algorithm change support.
Signed-off-by: viktor-kurchenko <[email protected]>
- Loading branch information
1 parent
60bd84d
commit 1007419
Showing
7 changed files
with
425 additions
and
208 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright Authors of Cilium | ||
|
||
package encrypt | ||
|
||
var rotators = map[string]func(key ipsecKey) (ipsecKey, error){ | ||
"": func(key ipsecKey) (ipsecKey, error) { return key.rotate() }, | ||
"gcm-aes": newGcmAesKey, | ||
"hmac-md5": newHmacMD5Key, | ||
"hmac-sha1": newHmacSHA1Key, | ||
"hmac-sha256": newHmacSHA256Key, | ||
"hmac-sha512": newHmacSHA512Key, | ||
} | ||
|
||
func IsIPsecAlgoSupported(algo string) bool { | ||
_, ok := rotators[algo] | ||
return ok | ||
} | ||
|
||
func rotateIPsecKey(key ipsecKey, algo string) (ipsecKey, error) { | ||
return rotators[algo](key) | ||
} | ||
|
||
func newGcmAesKey(key ipsecKey) (ipsecKey, error) { | ||
authKey, err := generateRandomHex(40) | ||
if err != nil { | ||
return ipsecKey{}, err | ||
} | ||
newKey := ipsecKey{ | ||
spi: key.nextSPI(), | ||
spiSuffix: key.spiSuffix, | ||
algo: "rfc4106(gcm(aes))", | ||
key: authKey, | ||
size: 128, | ||
} | ||
return newKey, nil | ||
} | ||
|
||
func newHmacMD5Key(key ipsecKey) (ipsecKey, error) { | ||
return newCbcAesKey(key, "hmac(md5)", 16, 32) | ||
} | ||
|
||
func newHmacSHA1Key(key ipsecKey) (ipsecKey, error) { | ||
return newCbcAesKey(key, "hmac(sha1)", 20, 32) | ||
} | ||
|
||
func newHmacSHA256Key(key ipsecKey) (ipsecKey, error) { | ||
return newCbcAesKey(key, "hmac(sha256)", 32, 32) | ||
} | ||
|
||
func newHmacSHA512Key(key ipsecKey) (ipsecKey, error) { | ||
return newCbcAesKey(key, "hmac(sha512)", 64, 32) | ||
} | ||
|
||
func newCbcAesKey(key ipsecKey, algo string, authKeylen int, cipherKeyLen int) (ipsecKey, error) { | ||
authKey, err := generateRandomHex(authKeylen) | ||
if err != nil { | ||
return ipsecKey{}, err | ||
} | ||
cipherKey, err := generateRandomHex(cipherKeyLen) | ||
if err != nil { | ||
return ipsecKey{}, err | ||
} | ||
newKey := ipsecKey{ | ||
spi: key.nextSPI(), | ||
spiSuffix: key.spiSuffix, | ||
algo: algo, | ||
key: authKey, | ||
cipherMode: "cbc(aes)", | ||
cipherKey: cipherKey, | ||
} | ||
return newKey, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,303 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright Authors of Cilium | ||
|
||
package encrypt | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func Test_IsIPsecAlgoSupported(t *testing.T) { | ||
testCases := []struct { | ||
have string | ||
expected bool | ||
}{ | ||
{ | ||
have: "", | ||
expected: true, | ||
}, | ||
{ | ||
have: "gcm-aes", | ||
expected: true, | ||
}, | ||
{ | ||
have: "hmac-md5", | ||
expected: true, | ||
}, | ||
{ | ||
have: "hmac-sha1", | ||
expected: true, | ||
}, | ||
{ | ||
have: "hmac-sha256", | ||
expected: true, | ||
}, | ||
{ | ||
have: "hmac-sha512", | ||
expected: true, | ||
}, | ||
{ | ||
have: "bla-bla", | ||
expected: false, | ||
}, | ||
} | ||
|
||
for _, tt := range testCases { | ||
// function to test | ||
actual := IsIPsecAlgoSupported(tt.have) | ||
|
||
require.Equal(t, tt.expected, actual) | ||
} | ||
} | ||
|
||
func Test_rotateIPsecKey(t *testing.T) { | ||
testCases := []struct { | ||
haveKey ipsecKey | ||
haveAlgo string | ||
expected ipsecKey | ||
}{ | ||
{ | ||
haveAlgo: "", | ||
haveKey: ipsecKey{ | ||
spi: 3, | ||
spiSuffix: false, | ||
algo: "rfc4106(gcm(aes))", | ||
key: "41049390e1e2b5d6543901daab6435f4042155fe", | ||
size: 128, | ||
}, | ||
expected: ipsecKey{ | ||
spi: 4, | ||
spiSuffix: false, | ||
algo: "rfc4106(gcm(aes))", | ||
key: "41049390e1e2b5d6543901daab6435f4042155fe", | ||
size: 128, | ||
}, | ||
}, | ||
{ | ||
haveAlgo: "", | ||
haveKey: ipsecKey{ | ||
spi: 16, | ||
spiSuffix: false, | ||
algo: "rfc4106(gcm(aes))", | ||
key: "41049390e1e2b5d6543901daab6435f4042155fe", | ||
size: 128, | ||
}, | ||
expected: ipsecKey{ | ||
spi: 1, | ||
spiSuffix: false, | ||
algo: "rfc4106(gcm(aes))", | ||
key: "41049390e1e2b5d6543901daab6435f4042155fe", | ||
size: 128, | ||
}, | ||
}, | ||
{ | ||
haveAlgo: "", | ||
haveKey: ipsecKey{ | ||
spi: 3, | ||
spiSuffix: true, | ||
algo: "rfc4106(gcm(aes))", | ||
key: "41049390e1e2b5d6543901daab6435f4042155fe", | ||
size: 128, | ||
}, | ||
expected: ipsecKey{ | ||
spi: 4, | ||
spiSuffix: true, | ||
algo: "rfc4106(gcm(aes))", | ||
key: "41049390e1e2b5d6543901daab6435f4042155fe", | ||
size: 128, | ||
}, | ||
}, | ||
{ | ||
haveAlgo: "", | ||
haveKey: ipsecKey{ | ||
spi: 16, | ||
spiSuffix: true, | ||
algo: "rfc4106(gcm(aes))", | ||
key: "41049390e1e2b5d6543901daab6435f4042155fe", | ||
size: 128, | ||
}, | ||
expected: ipsecKey{ | ||
spi: 1, | ||
spiSuffix: true, | ||
algo: "rfc4106(gcm(aes))", | ||
key: "41049390e1e2b5d6543901daab6435f4042155fe", | ||
size: 128, | ||
}, | ||
}, | ||
{ | ||
haveAlgo: "", | ||
haveKey: ipsecKey{ | ||
spi: 3, | ||
spiSuffix: false, | ||
algo: "hmac(sha256)", | ||
key: "e6b4bab427cd37bb64b39cd66a8476a62963174b78bc544fb525f4c2f548342b", | ||
cipherMode: "cbc(aes)", | ||
cipherKey: "0f12337d9ee75095ff21402dc98476f5f9107261073b70bb37747237d2691d3e", | ||
}, | ||
expected: ipsecKey{ | ||
spi: 4, | ||
spiSuffix: false, | ||
algo: "hmac(sha256)", | ||
key: "e6b4bab427cd37bb64b39cd66a8476a62963174b78bc544fb525f4c2f548342b", | ||
cipherMode: "cbc(aes)", | ||
cipherKey: "0f12337d9ee75095ff21402dc98476f5f9107261073b70bb37747237d2691d3e", | ||
}, | ||
}, | ||
{ | ||
haveAlgo: "", | ||
haveKey: ipsecKey{ | ||
spi: 16, | ||
spiSuffix: false, | ||
algo: "hmac(sha256)", | ||
key: "e6b4bab427cd37bb64b39cd66a8476a62963174b78bc544fb525f4c2f548342b", | ||
cipherMode: "cbc(aes)", | ||
cipherKey: "0f12337d9ee75095ff21402dc98476f5f9107261073b70bb37747237d2691d3e", | ||
}, | ||
expected: ipsecKey{ | ||
spi: 1, | ||
spiSuffix: false, | ||
algo: "hmac(sha256)", | ||
key: "e6b4bab427cd37bb64b39cd66a8476a62963174b78bc544fb525f4c2f548342b", | ||
cipherMode: "cbc(aes)", | ||
cipherKey: "0f12337d9ee75095ff21402dc98476f5f9107261073b70bb37747237d2691d3e", | ||
}, | ||
}, | ||
{ | ||
haveAlgo: "", | ||
haveKey: ipsecKey{ | ||
spi: 3, | ||
spiSuffix: true, | ||
algo: "hmac(sha256)", | ||
key: "e6b4bab427cd37bb64b39cd66a8476a62963174b78bc544fb525f4c2f548342b", | ||
cipherMode: "cbc(aes)", | ||
cipherKey: "0f12337d9ee75095ff21402dc98476f5f9107261073b70bb37747237d2691d3e", | ||
}, | ||
expected: ipsecKey{ | ||
spi: 4, | ||
spiSuffix: true, | ||
algo: "hmac(sha256)", | ||
key: "e6b4bab427cd37bb64b39cd66a8476a62963174b78bc544fb525f4c2f548342b", | ||
cipherMode: "cbc(aes)", | ||
cipherKey: "0f12337d9ee75095ff21402dc98476f5f9107261073b70bb37747237d2691d3e", | ||
}, | ||
}, | ||
{ | ||
haveAlgo: "", | ||
haveKey: ipsecKey{ | ||
spi: 16, | ||
spiSuffix: true, | ||
algo: "hmac(sha256)", | ||
key: "e6b4bab427cd37bb64b39cd66a8476a62963174b78bc544fb525f4c2f548342b", | ||
cipherMode: "cbc(aes)", | ||
cipherKey: "0f12337d9ee75095ff21402dc98476f5f9107261073b70bb37747237d2691d3e", | ||
}, | ||
expected: ipsecKey{ | ||
spi: 1, | ||
spiSuffix: true, | ||
algo: "hmac(sha256)", | ||
key: "e6b4bab427cd37bb64b39cd66a8476a62963174b78bc544fb525f4c2f548342b", | ||
cipherMode: "cbc(aes)", | ||
cipherKey: "0f12337d9ee75095ff21402dc98476f5f9107261073b70bb37747237d2691d3e", | ||
}, | ||
}, | ||
{ | ||
haveAlgo: "gcm-aes", | ||
haveKey: ipsecKey{ | ||
spi: 16, | ||
spiSuffix: true, | ||
}, | ||
expected: ipsecKey{ | ||
spi: 1, | ||
spiSuffix: true, | ||
algo: "rfc4106(gcm(aes))", | ||
key: "41049390e1e2b5d6543901daab6435f4042155fe", | ||
size: 128, | ||
}, | ||
}, | ||
{ | ||
haveAlgo: "hmac-md5", | ||
haveKey: ipsecKey{ | ||
spi: 1, | ||
spiSuffix: true, | ||
}, | ||
expected: ipsecKey{ | ||
spi: 2, | ||
spiSuffix: true, | ||
algo: "hmac(md5)", | ||
key: "1286b7f6f9f61a4f", | ||
cipherMode: "cbc(aes)", | ||
cipherKey: "efbeeb4230992f76a6e4cc2ff995b756", | ||
}, | ||
}, | ||
{ | ||
haveAlgo: "hmac-sha1", | ||
haveKey: ipsecKey{ | ||
spi: 2, | ||
spiSuffix: true, | ||
}, | ||
expected: ipsecKey{ | ||
spi: 3, | ||
spiSuffix: true, | ||
algo: "hmac(sha1)", | ||
key: "5448dd20e4528a9c2d5b", | ||
cipherMode: "cbc(aes)", | ||
cipherKey: "123d17f2bbbae8009d952b4d0d656f06", | ||
}, | ||
}, | ||
{ | ||
haveAlgo: "hmac-sha256", | ||
haveKey: ipsecKey{ | ||
spi: 3, | ||
spiSuffix: true, | ||
}, | ||
expected: ipsecKey{ | ||
spi: 4, | ||
spiSuffix: true, | ||
algo: "hmac(sha256)", | ||
key: "a9d204b6c2df6f0b707bbfdb71b4bd44", | ||
cipherMode: "cbc(aes)", | ||
cipherKey: "9bd24c14452783bb6f3c9335aff2ed2e", | ||
}, | ||
}, | ||
{ | ||
haveAlgo: "hmac-sha512", | ||
haveKey: ipsecKey{ | ||
spi: 4, | ||
spiSuffix: true, | ||
}, | ||
expected: ipsecKey{ | ||
spi: 5, | ||
spiSuffix: true, | ||
algo: "hmac(sha512)", | ||
key: "8b4d92bf9396e7febb4d51e87394bb158ebcc0d9d57e4da8e938b0e931223ec7", | ||
cipherMode: "cbc(aes)", | ||
cipherKey: "0151a41da39e3310d4f58b3930788dc4", | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range testCases { | ||
// function to test | ||
actual, err := rotateIPsecKey(tt.haveKey, tt.haveAlgo) | ||
|
||
require.NoError(t, err) | ||
require.Equal(t, tt.expected.spi, actual.spi) | ||
require.Equal(t, tt.expected.spiSuffix, actual.spiSuffix) | ||
require.Equal(t, tt.expected.algo, actual.algo) | ||
require.Equal(t, len(tt.expected.key), len(actual.key)) | ||
require.Equal(t, len(tt.expected.cipherKey), len(actual.cipherKey)) | ||
require.Equal(t, tt.expected.size, actual.size) | ||
require.Equal(t, tt.expected.cipherMode, actual.cipherMode) | ||
if tt.expected.cipherMode == "" { | ||
// this field will be randomly generated, `require.NotEqual` used for verification | ||
require.NotEqual(t, tt.expected.key, actual.key) | ||
require.Equal(t, tt.expected.cipherKey, actual.cipherKey) | ||
} else { | ||
// the following fields will be randomly generated, `require.NotEqual` used for verification | ||
require.NotEqual(t, tt.expected.key, actual.key) | ||
require.NotEqual(t, tt.expected.cipherKey, actual.cipherKey) | ||
} | ||
} | ||
} |
Oops, something went wrong.