diff options
Diffstat (limited to 'common/src/main/java/org/conscrypt/ScryptSecretKeyFactory.java')
-rw-r--r-- | common/src/main/java/org/conscrypt/ScryptSecretKeyFactory.java | 129 |
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"); + } + } +} |