Keith Mok | 690919a | 2023-04-12 15:44:32 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2023, The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include "MacsecPskPlugin.h" |
| 18 | #include <openssl/cipher.h> |
| 19 | #include <openssl/mem.h> |
| 20 | |
| 21 | #include <android-base/format.h> |
| 22 | #include <android-base/logging.h> |
| 23 | |
| 24 | namespace aidl::android::hardware::macsec { |
| 25 | |
| 26 | constexpr auto ok = &ndk::ScopedAStatus::ok; |
| 27 | |
| 28 | // vendor should hide the key in TEE/TA |
| 29 | // CAK key can be either 16 / 32 bytes |
| 30 | const std::vector<uint8_t> CAK_ID_1 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 31 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; |
| 32 | const std::vector<uint8_t> CAK_KEY_1 = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, |
| 33 | 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}; |
| 34 | std::vector<uint8_t> CKN_1 = {0x31, 0x32, 0x33, 0x34}; // maximum 16 bytes |
| 35 | |
| 36 | const std::vector<uint8_t> CAK_ID_2 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 37 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 38 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 39 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}; |
| 40 | const std::vector<uint8_t> CAK_KEY_2 = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, |
| 41 | 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, |
| 42 | 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, |
| 43 | 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}; |
| 44 | std::vector<uint8_t> CKN_2 = {0x35, 0x36, 0x37, 0x38}; // maximum 16 bytes |
| 45 | |
| 46 | static ndk::ScopedAStatus resultToStatus(binder_exception_t res, const std::string& msg = "") { |
| 47 | if (msg.empty()) { |
| 48 | return ndk::ScopedAStatus::fromExceptionCode(res); |
| 49 | } |
| 50 | return ndk::ScopedAStatus::fromExceptionCodeWithMessage(res, msg.c_str()); |
| 51 | } |
| 52 | |
| 53 | static int omac1_aes(CMAC_CTX* ctx, const uint8_t* data, size_t data_len, |
| 54 | uint8_t* mac /* 16 bytes */) { |
| 55 | size_t outlen; |
| 56 | |
| 57 | // Just reuse same key in ctx |
| 58 | if (!CMAC_Reset(ctx)) { |
| 59 | return -1; |
| 60 | } |
| 61 | |
| 62 | if (!CMAC_Update(ctx, data, data_len)) { |
| 63 | return -1; |
| 64 | } |
| 65 | |
| 66 | if (!CMAC_Final(ctx, mac, &outlen) || outlen != 16) { |
| 67 | return -1; |
| 68 | } |
| 69 | return 0; |
| 70 | } |
| 71 | |
| 72 | static void put_be16(uint8_t* addr, uint16_t value) { |
| 73 | *addr++ = value >> 8; |
| 74 | *addr = value & 0xff; |
| 75 | } |
| 76 | |
| 77 | /* IEEE Std 802.1X-2010, 6.2.1 KDF */ |
| 78 | static int aes_kdf(CMAC_CTX* ctx, const char* label, const uint8_t* context, int ctx_bits, |
| 79 | int ret_bits, uint8_t* ret) { |
| 80 | const int h = 128; |
| 81 | const int r = 8; |
| 82 | int i, n; |
| 83 | int lab_len, ctx_len, ret_len, buf_len; |
| 84 | uint8_t* buf; |
| 85 | |
| 86 | lab_len = strlen(label); |
| 87 | ctx_len = (ctx_bits + 7) / 8; |
| 88 | ret_len = ((ret_bits & 0xffff) + 7) / 8; |
| 89 | buf_len = lab_len + ctx_len + 4; |
| 90 | |
| 91 | memset(ret, 0, ret_len); |
| 92 | |
| 93 | n = (ret_bits + h - 1) / h; |
| 94 | if (n > ((0x1 << r) - 1)) return -1; |
| 95 | |
| 96 | buf = (uint8_t*)calloc(1, buf_len); |
| 97 | if (buf == NULL) return -1; |
| 98 | |
| 99 | memcpy(buf + 1, label, lab_len); |
| 100 | memcpy(buf + lab_len + 2, context, ctx_len); |
| 101 | put_be16(&buf[buf_len - 2], ret_bits); |
| 102 | |
| 103 | for (i = 0; i < n; i++) { |
| 104 | int res; |
| 105 | |
| 106 | buf[0] = (uint8_t)(i + 1); |
| 107 | res = omac1_aes(ctx, buf, buf_len, ret); |
| 108 | if (res) { |
| 109 | free(buf); |
| 110 | return -1; |
| 111 | } |
| 112 | ret = ret + h / 8; |
| 113 | } |
| 114 | free(buf); |
| 115 | return 0; |
| 116 | } |
| 117 | |
| 118 | MacsecPskPlugin::MacsecPskPlugin() { |
| 119 | // always make sure ckn is 16 bytes, zero padded |
| 120 | CKN_1.resize(16); |
| 121 | CKN_2.resize(16); |
| 122 | |
| 123 | addTestKey(CAK_ID_1, CAK_KEY_1, CKN_1); |
| 124 | addTestKey(CAK_ID_2, CAK_KEY_2, CKN_2); |
| 125 | } |
| 126 | |
| 127 | MacsecPskPlugin::~MacsecPskPlugin() { |
| 128 | for (auto s : mKeys) { |
| 129 | OPENSSL_cleanse(&s.kekEncCtx, sizeof(AES_KEY)); |
| 130 | OPENSSL_cleanse(&s.kekDecCtx, sizeof(AES_KEY)); |
| 131 | CMAC_CTX_free(s.ickCtx); |
| 132 | CMAC_CTX_free(s.cakCtx); |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | ndk::ScopedAStatus MacsecPskPlugin::addTestKey(const std::vector<uint8_t>& keyId, |
| 137 | const std::vector<uint8_t>& CAK, |
| 138 | const std::vector<uint8_t>& CKN) { |
| 139 | if (CAK.size() != 16 && CAK.size() != 32) { |
| 140 | return resultToStatus(EX_ILLEGAL_ARGUMENT, "CAK length must be 16 or 32 bytes"); |
| 141 | } |
| 142 | |
| 143 | if (keyId.size() != CAK.size()) { |
| 144 | return resultToStatus(EX_ILLEGAL_ARGUMENT, "Key ID must be same as CAK length"); |
| 145 | } |
| 146 | |
| 147 | std::vector<uint8_t> ckn; |
| 148 | ckn = CKN; |
| 149 | ckn.resize(16); // make sure it is always zero padded with maximum length of |
| 150 | // 16 bytes |
| 151 | |
| 152 | AES_KEY kekEncCtx; |
| 153 | AES_KEY kekDecCtx; |
| 154 | CMAC_CTX* ickCtx; |
| 155 | CMAC_CTX* cakCtx; |
| 156 | |
| 157 | // Create the CAK openssl context |
| 158 | cakCtx = CMAC_CTX_new(); |
| 159 | |
| 160 | CMAC_Init(cakCtx, CAK.data(), CAK.size(), |
| 161 | CAK.size() == 16 ? EVP_aes_128_cbc() : EVP_aes_256_cbc(), NULL); |
| 162 | |
| 163 | // derive KEK from CAK (ieee802_1x_kek_aes_cmac) |
| 164 | std::vector<uint8_t> kek; |
| 165 | kek.resize(CAK.size()); |
| 166 | |
| 167 | aes_kdf(cakCtx, "IEEE8021 KEK", (const uint8_t*)ckn.data(), ckn.size() * 8, 8 * kek.size(), |
| 168 | kek.data()); |
| 169 | |
| 170 | AES_set_encrypt_key(kek.data(), kek.size() << 3, &kekEncCtx); |
| 171 | AES_set_decrypt_key(kek.data(), kek.size() << 3, &kekDecCtx); |
| 172 | |
| 173 | // derive ICK from CAK (ieee802_1x_ick_aes_cmac) |
| 174 | std::vector<uint8_t> ick; |
| 175 | ick.resize(CAK.size()); |
| 176 | |
| 177 | aes_kdf(cakCtx, "IEEE8021 ICK", (const uint8_t*)CKN.data(), CKN.size() * 8, 8 * ick.size(), |
| 178 | ick.data()); |
| 179 | |
| 180 | ickCtx = CMAC_CTX_new(); |
| 181 | |
| 182 | CMAC_Init(ickCtx, ick.data(), ick.size(), |
| 183 | ick.size() == 16 ? EVP_aes_128_cbc() : EVP_aes_256_cbc(), NULL); |
| 184 | |
| 185 | mKeys.push_back({keyId, kekEncCtx, kekDecCtx, ickCtx, cakCtx}); |
| 186 | |
| 187 | return ok(); |
| 188 | } |
| 189 | |
| 190 | ndk::ScopedAStatus MacsecPskPlugin::calcIcv(const std::vector<uint8_t>& keyId, |
| 191 | const std::vector<uint8_t>& data, |
| 192 | std::vector<uint8_t>* out) { |
| 193 | CMAC_CTX* ctx = NULL; |
| 194 | |
| 195 | for (auto s : mKeys) { |
| 196 | if (s.keyId == keyId) { |
| 197 | ctx = s.ickCtx; |
| 198 | break; |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | if (ctx == NULL) { |
| 203 | return resultToStatus(EX_ILLEGAL_ARGUMENT, "Key not exist"); |
| 204 | } |
| 205 | |
| 206 | out->resize(16); |
| 207 | if (omac1_aes(ctx, data.data(), data.size(), out->data()) != 0) { |
| 208 | return resultToStatus(EX_SERVICE_SPECIFIC, "Internal error"); |
| 209 | } |
| 210 | |
| 211 | return ok(); |
| 212 | } |
| 213 | |
| 214 | ndk::ScopedAStatus MacsecPskPlugin::generateSak(const std::vector<uint8_t>& keyId, |
| 215 | const std::vector<uint8_t>& data, |
| 216 | const int sakLength, std::vector<uint8_t>* out) { |
| 217 | CMAC_CTX* ctx = NULL; |
| 218 | |
| 219 | if ((sakLength != 16) && (sakLength != 32)) { |
| 220 | return resultToStatus(EX_ILLEGAL_ARGUMENT, "Invalid SAK length"); |
| 221 | } |
| 222 | |
| 223 | if (data.size() < sakLength) { |
| 224 | return resultToStatus(EX_ILLEGAL_ARGUMENT, "Invalid data length"); |
| 225 | } |
| 226 | |
| 227 | for (auto s : mKeys) { |
| 228 | if (s.keyId == keyId) { |
| 229 | ctx = s.cakCtx; |
| 230 | break; |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | if (ctx == NULL) { |
| 235 | return resultToStatus(EX_ILLEGAL_ARGUMENT, "Key not exist"); |
| 236 | } |
| 237 | |
| 238 | out->resize(sakLength); |
| 239 | |
| 240 | if (aes_kdf(ctx, "IEEE8021 SAK", data.data(), data.size() * 8, out->size() * 8, out->data()) != |
| 241 | 0) { |
| 242 | return resultToStatus(EX_SERVICE_SPECIFIC, "Internal error"); |
| 243 | } |
| 244 | |
| 245 | return ok(); |
| 246 | } |
| 247 | |
| 248 | ndk::ScopedAStatus MacsecPskPlugin::wrapSak(const std::vector<uint8_t>& keyId, |
| 249 | const std::vector<uint8_t>& sak, |
| 250 | std::vector<uint8_t>* out) { |
| 251 | if (sak.size() == 0 || sak.size() % 8 != 0) { |
| 252 | return resultToStatus(EX_ILLEGAL_ARGUMENT, |
| 253 | "SAK length not multiple of 8 or greater than 0"); |
| 254 | } |
| 255 | |
| 256 | AES_KEY* ctx = NULL; |
| 257 | |
| 258 | for (auto s : mKeys) { |
| 259 | if (s.keyId == keyId) { |
| 260 | ctx = &s.kekEncCtx; |
| 261 | break; |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | if (ctx == NULL) { |
| 266 | return resultToStatus(EX_ILLEGAL_ARGUMENT, "Key not exist"); |
| 267 | } |
| 268 | |
| 269 | out->resize(sak.size() + 8); |
| 270 | |
| 271 | if (AES_wrap_key(ctx, NULL, out->data(), sak.data(), sak.size()) > 0) { |
| 272 | return ok(); |
| 273 | } |
| 274 | |
| 275 | return resultToStatus(EX_SERVICE_SPECIFIC, "Internal error"); |
| 276 | } |
| 277 | |
| 278 | ndk::ScopedAStatus MacsecPskPlugin::unwrapSak(const std::vector<uint8_t>& keyId, |
| 279 | const std::vector<uint8_t>& sak, |
| 280 | std::vector<uint8_t>* out) { |
| 281 | if (sak.size() <= 8 || sak.size() % 8 != 0) { |
| 282 | return resultToStatus(EX_ILLEGAL_ARGUMENT, |
| 283 | "SAK length not multiple of 8 or greater than 0"); |
| 284 | } |
| 285 | |
| 286 | AES_KEY* ctx = NULL; |
| 287 | |
| 288 | for (auto s : mKeys) { |
| 289 | if (s.keyId == keyId) { |
| 290 | ctx = &s.kekDecCtx; |
| 291 | break; |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | if (ctx == NULL) { |
| 296 | return resultToStatus(EX_ILLEGAL_ARGUMENT, "Key not exist"); |
| 297 | } |
| 298 | |
| 299 | out->resize(sak.size() - 8); |
| 300 | |
| 301 | if (AES_unwrap_key(ctx, NULL, out->data(), sak.data(), sak.size()) > 0) { |
| 302 | return ok(); |
| 303 | } |
| 304 | |
| 305 | return resultToStatus(EX_SERVICE_SPECIFIC, "Internal error"); |
| 306 | } |
| 307 | |
| 308 | } // namespace aidl::android::hardware::macsec |