[
https://issues.apache.org/jira/browse/NIFI-1257?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15132928#comment-15132928
]
ASF GitHub Bot commented on NIFI-1257:
--------------------------------------
Github user alopresto commented on a diff in the pull request:
https://github.com/apache/nifi/pull/201#discussion_r51930434
--- Diff:
nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/groovy/org/apache/nifi/processors/standard/util/crypto/CipherUtilityGroovyTest.groovy
---
@@ -0,0 +1,251 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.processors.standard.util.crypto
+
+import org.apache.nifi.security.util.EncryptionMethod
+import org.bouncycastle.jce.provider.BouncyCastleProvider
+import org.junit.After
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+import java.security.Security
+
+@RunWith(JUnit4.class)
+class CipherUtilityGroovyTest extends GroovyTestCase {
+ private static final Logger logger =
LoggerFactory.getLogger(CipherUtilityGroovyTest.class)
+
+ // TripleDES must precede DES for automatic grouping precedence
+ private static final List<String> CIPHERS = ["AES", "TRIPLEDES",
"DES", "RC2", "RC4", "RC5", "TWOFISH"]
+ private static final List<String> SYMMETRIC_ALGORITHMS =
EncryptionMethod.values().findAll { it.algorithm.startsWith("PBE") ||
it.algorithm.startsWith("AES") }*.algorithm
+ private static final Map<String, List<String>>
ALGORITHMS_MAPPED_BY_CIPHER = SYMMETRIC_ALGORITHMS.groupBy { String algorithm
-> CIPHERS.find { algorithm.contains(it) } }
+
+ // Manually mapped as of 01/19/16 0.5.0
+ private static final Map<Integer, List<String>>
ALGORITHMS_MAPPED_BY_KEY_LENGTH = [
+ (40) : ["PBEWITHSHAAND40BITRC2-CBC",
+ "PBEWITHSHAAND40BITRC4"],
+ (64) : ["PBEWITHMD5ANDDES",
+ "PBEWITHSHA1ANDDES"],
+ (112): ["PBEWITHSHAAND2-KEYTRIPLEDES-CBC",
+ "PBEWITHSHAAND3-KEYTRIPLEDES-CBC"],
+ (128): ["PBEWITHMD5AND128BITAES-CBC-OPENSSL",
+ "PBEWITHMD5ANDRC2",
+ "PBEWITHSHA1ANDRC2",
+ "PBEWITHSHA256AND128BITAES-CBC-BC",
+ "PBEWITHSHAAND128BITAES-CBC-BC",
+ "PBEWITHSHAAND128BITRC2-CBC",
+ "PBEWITHSHAAND128BITRC4",
+ "PBEWITHSHAANDTWOFISH-CBC",
+ "AES/CBC/PKCS7Padding",
+ "AES/CTR/NoPadding",
+ "AES/GCM/NoPadding"],
+ (192): ["PBEWITHMD5AND192BITAES-CBC-OPENSSL",
+ "PBEWITHSHA256AND192BITAES-CBC-BC",
+ "PBEWITHSHAAND192BITAES-CBC-BC",
+ "AES/CBC/PKCS7Padding",
+ "AES/CTR/NoPadding",
+ "AES/GCM/NoPadding"],
+ (256): ["PBEWITHMD5AND256BITAES-CBC-OPENSSL",
+ "PBEWITHSHA256AND256BITAES-CBC-BC",
+ "PBEWITHSHAAND256BITAES-CBC-BC",
+ "AES/CBC/PKCS7Padding",
+ "AES/CTR/NoPadding",
+ "AES/GCM/NoPadding"]
+ ]
+
+ @BeforeClass
+ static void setUpOnce() {
+ Security.addProvider(new BouncyCastleProvider());
+
+ // Fix because TRIPLEDES -> DESede
+ def tripleDESAlgorithms =
ALGORITHMS_MAPPED_BY_CIPHER.remove("TRIPLEDES")
+ ALGORITHMS_MAPPED_BY_CIPHER.put("DESede", tripleDESAlgorithms)
+
+ logger.info("Mapped algorithms: ${ALGORITHMS_MAPPED_BY_CIPHER}")
+ }
+
+ @Before
+ void setUp() throws Exception {
+
+ }
+
+ @After
+ void tearDown() throws Exception {
+
+ }
+
+ @Test
+ void testShouldParseCipherFromAlgorithm() {
+ // Arrange
+ final def EXPECTED_ALGORITHMS = ALGORITHMS_MAPPED_BY_CIPHER
+
+ // Act
+ SYMMETRIC_ALGORITHMS.each { String algorithm ->
+ String cipher =
CipherUtility.parseCipherFromAlgorithm(algorithm)
+ logger.info("Extracted ${cipher} from ${algorithm}")
+
+ // Assert
+ assert EXPECTED_ALGORITHMS.get(cipher).contains(algorithm)
+ }
+ }
+
+ @Test
+ void testShouldParseKeyLengthFromAlgorithm() {
+ // Arrange
+ final def EXPECTED_ALGORITHMS = ALGORITHMS_MAPPED_BY_KEY_LENGTH
+
+ // Act
+ SYMMETRIC_ALGORITHMS.each { String algorithm ->
+ int keyLength =
CipherUtility.parseKeyLengthFromAlgorithm(algorithm)
+ logger.info("Extracted ${keyLength} from ${algorithm}")
+
+ // Assert
+ assert EXPECTED_ALGORITHMS.get(keyLength).contains(algorithm)
+ }
+ }
+
+ @Test
+ void testShouldDetermineValidKeyLength() {
+ // Arrange
+
+ // Act
+ ALGORITHMS_MAPPED_BY_KEY_LENGTH.each { int keyLength, List<String>
algorithms ->
+ algorithms.each { String algorithm ->
+ logger.info("Checking ${keyLength} for ${algorithm}")
+
+ // Assert
+ assert CipherUtility.isValidKeyLength(keyLength,
CipherUtility.parseCipherFromAlgorithm(algorithm))
+ }
+ }
+ }
+
+ @Test
+ void testShouldDetermineInvalidKeyLength() {
+ // Arrange
+
+ // Act
+ ALGORITHMS_MAPPED_BY_KEY_LENGTH.each { int keyLength, List<String>
algorithms ->
+ algorithms.each { String algorithm ->
+ def invalidKeyLengths = [-1, 0, 1]
+ if (algorithm =~ "RC\\d") {
+ invalidKeyLengths += [39, 2049]
+ } else {
+ invalidKeyLengths += keyLength + 1
+ }
+ logger.info("Checking ${invalidKeyLengths.join(", ")} for
${algorithm}")
+
+ // Assert
+ invalidKeyLengths.each { int invalidKeyLength ->
+ assert
!CipherUtility.isValidKeyLength(invalidKeyLength,
CipherUtility.parseCipherFromAlgorithm(algorithm))
+ }
+ }
+ }
+ }
+
+ @Test
+ void testShouldDetermineValidKeyLengthForAlgorithm() {
+ // Arrange
+
+ // Act
+ ALGORITHMS_MAPPED_BY_KEY_LENGTH.each { int keyLength, List<String>
algorithms ->
+ algorithms.each { String algorithm ->
+ logger.info("Checking ${keyLength} for ${algorithm}")
+
+ // Assert
+ assert
CipherUtility.isValidKeyLengthForAlgorithm(keyLength, algorithm)
+ }
+ }
+ }
+
+ @Test
+ void testShouldDetermineInvalidKeyLengthForAlgorithm() {
+ // Arrange
+
+ // Act
+ ALGORITHMS_MAPPED_BY_KEY_LENGTH.each { int keyLength, List<String>
algorithms ->
+ algorithms.each { String algorithm ->
+ def invalidKeyLengths = [-1, 0, 1]
+ if (algorithm =~ "RC\\d") {
+ invalidKeyLengths += [39, 2049]
+ } else {
+ invalidKeyLengths += keyLength + 1
+ }
+ logger.info("Checking ${invalidKeyLengths.join(", ")} for
${algorithm}")
+
+ // Assert
+ invalidKeyLengths.each { int invalidKeyLength ->
+ assert
!CipherUtility.isValidKeyLengthForAlgorithm(invalidKeyLength, algorithm)
+ }
+ }
+ }
+
+ // Extra hard-coded checks
+ ["PBEWITHSHA256AND256BITAES-CBC-BC": 192].each { String algorithm,
int invalidKeyLength ->
--- End diff --
Done.
> Provide additional KDFs for EncryptContent
> ------------------------------------------
>
> Key: NIFI-1257
> URL: https://issues.apache.org/jira/browse/NIFI-1257
> Project: Apache NiFi
> Issue Type: Improvement
> Components: Core Framework
> Affects Versions: 0.4.0
> Reporter: Andy LoPresto
> Assignee: Andy LoPresto
> Priority: Critical
> Labels: encryption, security
> Fix For: 0.5.0
>
>
> Currently, the two key derivation functions (KDF) supported are NiFi Legacy
> (1000 iterations of MD5 digest over a password and optional salt) and OpenSSL
> PKCS#5 v1.5 (a single iteration of MD5 digest over a password and optional
> salt).
> Both of these are very weak -- they use a deprecated cryptographic hash
> function (CHF) with known weakness and susceptibility to collisions (with
> demonstrated attacks) and a non-configurable and tightly coupled iteration
> count to derive the key and IV.
> Current best practice KDFs (with work factor recommendations) are as follows:
> * PBKDF2 with variable hash function (SHA1, SHA256, SHA384, SHA512, or
> ideally HMAC variants of these functions) and variable iteration count (in
> the 10k - 1M range).
> * bcrypt with work factor of 12 - 16
> * scrypt with work factor of (2^14 - 2^20, 8, 1)
> The salt and iteration count should be stored alongside the hashed record
> (bcrypt handles this natively).
> Notes:
> * http://wildlyinaccurate.com/bcrypt-choosing-a-work-factor/
> * http://blog.ircmaxell.com/2012/12/seven-ways-to-screw-up-bcrypt.html
> *
> http://security.stackexchange.com/questions/17207/recommended-of-rounds-for-bcrypt
> *
> http://security.stackexchange.com/questions/3959/recommended-of-iterations-when-using-pkbdf2-sha256/3993#3993
> *
> http://security.stackexchange.com/questions/4781/do-any-security-experts-recommend-bcrypt-for-password-storage/6415
>
> *
> http://web.archive.org/web/20130407190430/http://chargen.matasano.com/chargen/2007/9/7/enough-with-the-rainbow-tables-what-you-need-to-know-about-s.html
> *
> https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2015/march/enough-with-the-salts-updates-on-secure-password-schemes/
> * http://www.tarsnap.com/scrypt.html
> * http://www.tarsnap.com/scrypt/scrypt.pdf
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)