aboutsummaryrefslogtreecommitdiff
path: root/common/src/main/java/org/conscrypt/ScryptSecretKeyFactory.java
diff options
context:
space:
mode:
Diffstat (limited to 'common/src/main/java/org/conscrypt/ScryptSecretKeyFactory.java')
-rw-r--r--common/src/main/java/org/conscrypt/ScryptSecretKeyFactory.java129
1 files changed, 129 insertions, 0 deletions
diff --git a/common/src/main/java/org/conscrypt/ScryptSecretKeyFactory.java b/common/src/main/java/org/conscrypt/ScryptSecretKeyFactory.java
new file mode 100644
index 00000000..8b7afe34
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/ScryptSecretKeyFactory.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed 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.conscrypt;
+
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.InvalidKeyException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactorySpi;
+
+@Internal
+public class ScryptSecretKeyFactory extends SecretKeyFactorySpi {
+
+ @Override
+ protected SecretKey engineGenerateSecret(KeySpec inKeySpec) throws InvalidKeySpecException {
+
+ char[] password;
+ byte[] salt;
+ int n, r, p, keyOutputBits;
+
+ if (inKeySpec instanceof ScryptKeySpec) {
+ ScryptKeySpec spec = (ScryptKeySpec) inKeySpec;
+ password = spec.getPassword();
+ salt = spec.getSalt();
+ n = spec.getCostParameter();
+ r = spec.getBlockSize();
+ p = spec.getParallelizationParameter();
+ keyOutputBits = spec.getKeyLength();
+ } else {
+ // Extract parameters from any `KeySpec` that has getters with the correct name. This allows,
+ // for example, code to use BouncyCastle's KeySpec with the conscrypt provider.
+ try {
+ password = (char[]) getValue(inKeySpec, "getPassword");
+ salt = (byte[]) getValue(inKeySpec, "getSalt");
+ n = (int) getValue(inKeySpec, "getCostParameter");
+ r = (int) getValue(inKeySpec, "getBlockSize");
+ p = (int) getValue(inKeySpec, "getParallelizationParameter");
+ keyOutputBits = (int) getValue(inKeySpec, "getKeyLength");
+ } catch (Exception e) {
+ throw new InvalidKeySpecException("Not a valid scrypt KeySpec", e);
+ }
+ }
+
+ if (keyOutputBits % 8 != 0) {
+ throw new InvalidKeySpecException("Cannot produce fractional-byte outputs");
+ }
+
+ try {
+ return new ScryptKey(
+ NativeCrypto.Scrypt_generate_key(
+ new String(password).getBytes("UTF-8"), salt, n, r, p, keyOutputBits / 8));
+ } catch (UnsupportedEncodingException e) {
+ // Impossible according to the Java docs: UTF-8 is always supported.
+ throw new RuntimeException(e);
+ }
+ }
+
+ private Object getValue(KeySpec spec, String methodName)
+ throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+ Method method = spec.getClass().getMethod(methodName, (Class<?>[]) null);
+ return method.invoke(spec);
+ }
+
+ @Override
+ protected KeySpec engineGetKeySpec(
+ SecretKey secretKey, @SuppressWarnings("rawtypes") Class aClass)
+ throws InvalidKeySpecException {
+ if (secretKey == null) {
+ throw new InvalidKeySpecException("Null KeySpec");
+ }
+ throw new NotImplementedException();
+ }
+
+ @Override
+ protected SecretKey engineTranslateKey(SecretKey secretKey) throws InvalidKeyException {
+ if (secretKey == null) {
+ throw new InvalidKeyException("Null SecretKey");
+ }
+ throw new NotImplementedException();
+ }
+
+ private static class ScryptKey implements SecretKey {
+ private static final long serialVersionUID = 2024924811854189128L;
+ private final byte[] key;
+
+ public ScryptKey(byte[] key) {
+ this.key = key;
+ }
+
+ @Override
+ public String getAlgorithm() {
+ // capitalised because BouncyCastle does it.
+ return "SCRYPT";
+ }
+
+ @Override
+ public String getFormat() {
+ return "RAW";
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ return key;
+ }
+ }
+
+ private static class NotImplementedException extends RuntimeException {
+ NotImplementedException() {
+ super("Not implemented");
+ }
+ }
+}