MaxGekk commented on code in PR #40704: URL: https://github.com/apache/spark/pull/40704#discussion_r1163834505
########## sql/catalyst/src/main/java/org/apache/spark/sql/catalyst/expressions/ExpressionImplUtils.java: ########## @@ -115,11 +128,74 @@ private static byte[] aesInternal( cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec); return cipher.doFinal(input, GCM_IV_LEN, input.length - GCM_IV_LEN); } + } else if (mode.equalsIgnoreCase("CBC") && + (padding.equalsIgnoreCase("PKCS") || padding.equalsIgnoreCase("DEFAULT"))) { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + if (opmode == Cipher.ENCRYPT_MODE) { + byte[] salt = new byte[CBC_SALT_LEN]; + secureRandom.nextBytes(salt); + final byte[] keyAndIv = getKeyAndIv(key, salt); + final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, key.length); + final byte[] iv = Arrays.copyOfRange(keyAndIv, key.length, key.length + CBC_IV_LEN); + cipher.init( + Cipher.ENCRYPT_MODE, + new SecretKeySpec(keyValue, "AES"), + new IvParameterSpec(iv)); + byte[] encrypted = cipher.doFinal(input, 0, input.length); + ByteBuffer byteBuffer = ByteBuffer.allocate( + SALTED_MAGIC.length + CBC_SALT_LEN + encrypted.length); + byteBuffer.put(SALTED_MAGIC); + byteBuffer.put(salt); + byteBuffer.put(encrypted); + return byteBuffer.array(); + } else { + assert(opmode == Cipher.DECRYPT_MODE); + final byte[] shouldBeMagic = Arrays.copyOfRange(input, 0, SALTED_MAGIC.length); + if (!Arrays.equals(shouldBeMagic, SALTED_MAGIC)) { + throw QueryExecutionErrors.aesInvalidSalt(shouldBeMagic); + } + final byte[] salt = Arrays.copyOfRange( + input, SALTED_MAGIC.length, SALTED_MAGIC.length + CBC_SALT_LEN); + final byte[] keyAndIv = getKeyAndIv(key, salt); + final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, key.length); + final byte[] iv = Arrays.copyOfRange(keyAndIv, key.length, key.length + CBC_IV_LEN); + cipher.init( + Cipher.DECRYPT_MODE, + new SecretKeySpec(keyValue, "AES"), + new IvParameterSpec(iv, 0, CBC_IV_LEN)); + return cipher.doFinal(input, CBC_IV_LEN, input.length - CBC_IV_LEN); + } } else { throw QueryExecutionErrors.aesModeUnsupportedError(mode, padding); } } catch (GeneralSecurityException e) { throw QueryExecutionErrors.aesCryptoError(e.getMessage()); } } + + // Derive the key and init vector in the same way as OpenSSL's EVP_BytesToKey + // since the version 1.1.0c which switched to SHA-256 as the hash. + private static byte[] getKeyAndIv(byte[] key, byte[] salt) { + final byte[] keyAndSalt = arrConcat(key, salt); + byte[] hash = new byte[0]; + byte[] keyAndIv = new byte[0]; + for (int i = 0; i < 3 && keyAndIv.length < key.length + CBC_IV_LEN; i++) { + final byte[] hashData = arrConcat(hash, keyAndSalt); + try { + final MessageDigest md = MessageDigest.getInstance("SHA-256"); + hash = md.digest(hashData); + keyAndIv = arrConcat(keyAndIv, hash); + } catch (NoSuchAlgorithmException e) { + // Double-check the used algorithm in the message digest. Review Comment: If you throw the internal error (actually, `SparkException`), you have to mark `getKeyAndIv()` by `throws SparkException` and: - catch it everywhere in `aesInternal()` or - mark `aesInternal()` with `throws SparkException` and this leads to marking `aesDecrypt()` and `aesEncrypt()`. All this happens because `SparkException.internalError` is not a `RuntimeException`. Don't think it worths to do so for the case which never happens. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org For additional commands, e-mail: reviews-h...@spark.apache.org