Add support for AES-GCM-ESP as an IPSec algorithm
Allows native AES-GCM-ESP to be used as an IPSec transport/tunnel mode
algorithm with kernel support
Bug: 63589918
Test: IPsecService tests added, existing ones pass
Change-Id: I9f742027730f6affabb0667300f6d0d65983410c
diff --git a/server/XfrmController.cpp b/server/XfrmController.cpp
index ddfda5a..5db053a 100644
--- a/server/XfrmController.cpp
+++ b/server/XfrmController.cpp
@@ -72,6 +72,8 @@
// Exposed for testing
constexpr uint32_t ALGO_MASK_CRYPT_ALL = ~0;
// Exposed for testing
+constexpr uint32_t ALGO_MASK_AEAD_ALL = ~0;
+// Exposed for testing
constexpr uint8_t REPLAY_WINDOW_SIZE = 4;
namespace {
@@ -384,6 +386,7 @@
const std::string& remoteAddress, int64_t underlyingNetworkHandle, int32_t spi,
const std::string& authAlgo, const std::vector<uint8_t>& authKey, int32_t authTruncBits,
const std::string& cryptAlgo, const std::vector<uint8_t>& cryptKey, int32_t cryptTruncBits,
+ const std::string& aeadAlgo, const std::vector<uint8_t>& aeadKey, int32_t aeadIcvBits,
int32_t encapType, int32_t encapLocalPort, int32_t encapRemotePort) {
android::RWLock::AutoWLock lock(mLock);
@@ -399,6 +402,8 @@
ALOGD("authTruncBits=%d", authTruncBits);
ALOGD("cryptAlgo=%s", cryptAlgo.c_str());
ALOGD("cryptTruncBits=%d,", cryptTruncBits);
+ ALOGD("aeadAlgo=%s", aeadAlgo.c_str());
+ ALOGD("aeadIcvBits=%d,", aeadIcvBits);
ALOGD("encapType=%d", encapType);
ALOGD("encapLocalPort=%d", encapLocalPort);
ALOGD("encapRemotePort=%d", encapRemotePort);
@@ -418,6 +423,9 @@
saInfo.crypt = XfrmAlgo{
.name = cryptAlgo, .key = cryptKey, .truncLenBits = static_cast<uint16_t>(cryptTruncBits)};
+ saInfo.aead = XfrmAlgo{
+ .name = aeadAlgo, .key = aeadKey, .truncLenBits = static_cast<uint16_t>(aeadIcvBits)};
+
saInfo.direction = static_cast<XfrmDirection>(direction);
switch (static_cast<XfrmMode>(mode)) {
@@ -635,9 +643,22 @@
xfrm_usersa_info usersa{};
nlattr_algo_crypt crypt{};
nlattr_algo_auth auth{};
+ nlattr_algo_aead aead{};
nlattr_encap_tmpl encap{};
- enum { NLMSG_HDR, USERSA, USERSA_PAD, CRYPT, CRYPT_PAD, AUTH, AUTH_PAD, ENCAP, ENCAP_PAD };
+ enum {
+ NLMSG_HDR,
+ USERSA,
+ USERSA_PAD,
+ CRYPT,
+ CRYPT_PAD,
+ AUTH,
+ AUTH_PAD,
+ AEAD,
+ AEAD_PAD,
+ ENCAP,
+ ENCAP_PAD
+ };
std::vector<iovec> iov = {
{NULL, 0}, // reserved for the eventual addition of a NLMSG_HDR
@@ -647,10 +668,23 @@
{kPadBytes, 0}, // up to NLATTR_ALIGNTO pad bytes
{&auth, 0}, // adjust size if auth algo is present
{kPadBytes, 0}, // up to NLATTR_ALIGNTO pad bytes
- {&encap, 0}, // adjust size if auth algo is present
+ {&aead, 0}, // adjust size if aead algo is present
+ {kPadBytes, 0}, // up to NLATTR_ALIGNTO pad bytes
+ {&encap, 0}, // adjust size if encapsulating
{kPadBytes, 0}, // up to NLATTR_ALIGNTO pad bytes
};
+ if (!record.aead.name.empty() && (!record.auth.name.empty() || !record.crypt.name.empty())) {
+ return netdutils::statusFromErrno(EINVAL, "Invalid xfrm algo selection; AEAD is mutually "
+ "exclusive with both Authentication and "
+ "Encryption");
+ }
+
+ if (record.aead.key.size() > MAX_ALGO_LENGTH || record.auth.key.size() > MAX_ALGO_LENGTH ||
+ record.crypt.key.size() > MAX_ALGO_LENGTH) {
+ return netdutils::statusFromErrno(EINVAL, "Key length invalid; exceeds MAX_ALGO_LENGTH");
+ }
+
int len;
len = iov[USERSA].iov_len = fillUserSaInfo(record, &usersa);
iov[USERSA_PAD].iov_len = NLMSG_ALIGN(len) - len;
@@ -661,36 +695,70 @@
len = iov[AUTH].iov_len = fillNlAttrXfrmAlgoAuth(record.auth, &auth);
iov[AUTH_PAD].iov_len = NLA_ALIGN(len) - len;
+ len = iov[AEAD].iov_len = fillNlAttrXfrmAlgoAead(record.aead, &aead);
+ iov[AEAD_PAD].iov_len = NLA_ALIGN(len) - len;
+
len = iov[ENCAP].iov_len = fillNlAttrXfrmEncapTmpl(record, &encap);
iov[ENCAP_PAD].iov_len = NLA_ALIGN(len) - len;
return sock.sendMessage(XFRM_MSG_UPDSA, NETLINK_REQUEST_FLAGS, 0, &iov);
}
int XfrmController::fillNlAttrXfrmAlgoEnc(const XfrmAlgo& inAlgo, nlattr_algo_crypt* algo) {
+ if (inAlgo.name.empty()) { // Do not fill anything if algorithm not provided
+ return 0;
+ }
+
int len = NLA_HDRLEN + sizeof(xfrm_algo);
+ // Kernel always changes last char to null terminator; no safety checks needed.
strncpy(algo->crypt.alg_name, inAlgo.name.c_str(), sizeof(algo->crypt.alg_name));
algo->crypt.alg_key_len = inAlgo.key.size() * 8; // bits
- memcpy(algo->key, &inAlgo.key[0], inAlgo.key.size()); // FIXME :safety checks
+ memcpy(algo->key, &inAlgo.key[0], inAlgo.key.size());
len += inAlgo.key.size();
fillXfrmNlaHdr(&algo->hdr, XFRMA_ALG_CRYPT, len);
return len;
}
int XfrmController::fillNlAttrXfrmAlgoAuth(const XfrmAlgo& inAlgo, nlattr_algo_auth* algo) {
+ if (inAlgo.name.empty()) { // Do not fill anything if algorithm not provided
+ return 0;
+ }
+
int len = NLA_HDRLEN + sizeof(xfrm_algo_auth);
+ // Kernel always changes last char to null terminator; no safety checks needed.
strncpy(algo->auth.alg_name, inAlgo.name.c_str(), sizeof(algo->auth.alg_name));
algo->auth.alg_key_len = inAlgo.key.size() * 8; // bits
// This is the extra field for ALG_AUTH_TRUNC
algo->auth.alg_trunc_len = inAlgo.truncLenBits;
- memcpy(algo->key, &inAlgo.key[0], inAlgo.key.size()); // FIXME :safety checks
+ memcpy(algo->key, &inAlgo.key[0], inAlgo.key.size());
len += inAlgo.key.size();
fillXfrmNlaHdr(&algo->hdr, XFRMA_ALG_AUTH_TRUNC, len);
return len;
}
+int XfrmController::fillNlAttrXfrmAlgoAead(const XfrmAlgo& inAlgo, nlattr_algo_aead* algo) {
+ if (inAlgo.name.empty()) { // Do not fill anything if algorithm not provided
+ return 0;
+ }
+
+ int len = NLA_HDRLEN + sizeof(xfrm_algo_aead);
+ // Kernel always changes last char to null terminator; no safety checks needed.
+ strncpy(algo->aead.alg_name, inAlgo.name.c_str(), sizeof(algo->aead.alg_name));
+ algo->aead.alg_key_len = inAlgo.key.size() * 8; // bits
+
+ // This is the extra field for ALG_AEAD. ICV length is the same as truncation length
+ // for any AEAD algorithm.
+ algo->aead.alg_icv_len = inAlgo.truncLenBits;
+
+ memcpy(algo->key, &inAlgo.key[0], inAlgo.key.size());
+ len += inAlgo.key.size();
+
+ fillXfrmNlaHdr(&algo->hdr, XFRMA_ALG_AEAD, len);
+ return len;
+}
+
int XfrmController::fillNlAttrXfrmEncapTmpl(const XfrmSaInfo& record, nlattr_encap_tmpl* tmpl) {
if (record.encap.type == XfrmEncapType::NONE) {
return 0;