aboutsummaryrefslogtreecommitdiff
path: root/org.ifaa.android.manager
diff options
context:
space:
mode:
Diffstat (limited to 'org.ifaa.android.manager')
-rw-r--r--org.ifaa.android.manager/Android.mk27
-rw-r--r--org.ifaa.android.manager/src/android/security/keystore/SoterKeyStoreKeyPairRSAGeneratorSpi.java524
-rw-r--r--org.ifaa.android.manager/src/android/security/keystore/SoterKeyStoreProvider.java160
-rw-r--r--org.ifaa.android.manager/src/android/security/keystore/SoterKeyStoreSpi.java60
-rw-r--r--org.ifaa.android.manager/src/android/security/keystore/SoterRSAKeyGenParameterSpec.java96
-rw-r--r--org.ifaa.android.manager/src/android/security/keystore/SoterUtil.java166
-rw-r--r--org.ifaa.android.manager/src/org/ifaa/android/manager/IFAAManager.java23
-rw-r--r--org.ifaa.android.manager/src/org/ifaa/android/manager/IFAAManagerFactory.java64
8 files changed, 1120 insertions, 0 deletions
diff --git a/org.ifaa.android.manager/Android.mk b/org.ifaa.android.manager/Android.mk
new file mode 100644
index 0000000..4847383
--- /dev/null
+++ b/org.ifaa.android.manager/Android.mk
@@ -0,0 +1,27 @@
+#
+# Copyright (C) 2016 The CyanogenMod 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := org.ifaa.android.manager
+LOCAL_MODULE_TAGS := optional
+LOCAL_JAVA_LIBRARIES := bouncycastle
+
+include $(BUILD_JAVA_LIBRARY)
diff --git a/org.ifaa.android.manager/src/android/security/keystore/SoterKeyStoreKeyPairRSAGeneratorSpi.java b/org.ifaa.android.manager/src/android/security/keystore/SoterKeyStoreKeyPairRSAGeneratorSpi.java
new file mode 100644
index 0000000..eaa89d9
--- /dev/null
+++ b/org.ifaa.android.manager/src/android/security/keystore/SoterKeyStoreKeyPairRSAGeneratorSpi.java
@@ -0,0 +1,524 @@
+package android.security.keystore;
+
+import android.app.ActivityThread;
+import android.app.Application;
+import android.content.Context;
+import android.os.Process;
+import android.security.Credentials;
+import android.security.KeyPairGeneratorSpec;
+import android.security.KeyStore;
+import android.security.KeyStore.State;
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keystore.KeyProperties.BlockMode;
+import android.security.keystore.KeyProperties.Digest;
+import android.security.keystore.KeyProperties.EncryptionPadding;
+import android.security.keystore.KeyProperties.KeyAlgorithm;
+import android.security.keystore.KeyProperties.Purpose;
+import android.security.keystore.KeyProperties.SignaturePadding;
+import android.util.Log;
+import com.android.internal.util.ArrayUtils;
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.org.bouncycastle.asn1.ASN1Integer;
+import com.android.org.bouncycastle.asn1.DERBitString;
+import com.android.org.bouncycastle.asn1.DERInteger;
+import com.android.org.bouncycastle.asn1.DERNull;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.org.bouncycastle.asn1.x509.Certificate;
+import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.org.bouncycastle.asn1.x509.TBSCertificate;
+import com.android.org.bouncycastle.asn1.x509.Time;
+import com.android.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
+import com.android.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import com.android.org.bouncycastle.jce.X509Principal;
+import com.android.org.bouncycastle.jce.provider.X509CertificateObject;
+import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPair;
+import java.security.KeyPairGeneratorSpi;
+import java.security.PrivateKey;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
+import java.util.HashSet;
+import java.util.Set;
+import libcore.util.EmptyArray;
+
+public class SoterKeyStoreKeyPairRSAGeneratorSpi extends KeyPairGeneratorSpi {
+ private static final int RSA_DEFAULT_KEY_SIZE = 2048;
+ private static final int RSA_MAX_KEY_SIZE = 8192;
+ private static final int RSA_MIN_KEY_SIZE = 512;
+ public static final long UINT32_MAX_VALUE = 4294967295L;
+ private static final long UINT32_RANGE = 4294967296L;
+ private static final BigInteger UINT64_RANGE = BigInteger.ONE.shiftLeft(64);
+ public static final BigInteger UINT64_MAX_VALUE = UINT64_RANGE.subtract(BigInteger.ONE);
+ private static volatile SecureRandom sRng;
+ private boolean isAutoAddCounterWhenGetPublicKey = false;
+ private boolean isAutoSignedWithAttkWhenGetPublicKey = false;
+ private boolean isAutoSignedWithCommonkWhenGetPublicKey = false;
+ private boolean isForSoter = false;
+ private boolean isNeedNextAttk = false;
+ private boolean isSecmsgFidCounterSignedWhenSign = false;
+ private String mAutoSignedKeyNameWhenGetPublicKey = "";
+ private boolean mEncryptionAtRestRequired;
+ private String mEntryAlias;
+ private String mJcaKeyAlgorithm;
+ private int mKeySizeBits;
+ private KeyStore mKeyStore;
+ private int mKeymasterAlgorithm = -1;
+ private int[] mKeymasterBlockModes;
+ private int[] mKeymasterDigests;
+ private int[] mKeymasterEncryptionPaddings;
+ private int[] mKeymasterPurposes;
+ private int[] mKeymasterSignaturePaddings;
+ private final int mOriginalKeymasterAlgorithm = 1;
+ private BigInteger mRSAPublicExponent;
+ private SecureRandom mRng;
+ private KeyGenParameterSpec mSpec;
+
+ public void initialize(int keysize, SecureRandom random) {
+ throw new IllegalArgumentException(KeyGenParameterSpec.class.getName() + " required to initialize this KeyPairGenerator");
+ }
+
+ public void initialize(AlgorithmParameterSpec params, SecureRandom random) throws InvalidAlgorithmParameterException {
+ resetAll();
+ if (params == null) {
+ try {
+ throw new InvalidAlgorithmParameterException("Must supply params of type " + KeyGenParameterSpec.class.getName() + " or " + KeyPairGeneratorSpec.class.getName());
+ } catch (Throwable th) {
+ if (!false) {
+ resetAll();
+ }
+ }
+ } else {
+ int keymasterAlgorithm = this.mOriginalKeymasterAlgorithm;
+ if (params instanceof KeyGenParameterSpec) {
+ KeyGenParameterSpec spec = (KeyGenParameterSpec) params;
+ this.mEntryAlias = SoterUtil.getPureKeyAliasFromKeyName(spec.getKeystoreAlias());
+ this.mSpec = spec;
+ this.mKeymasterAlgorithm = keymasterAlgorithm;
+ this.mEncryptionAtRestRequired = false;
+ this.mKeySizeBits = spec.getKeySize();
+ initAlgorithmSpecificParameters();
+ if (this.mKeySizeBits == -1) {
+ this.mKeySizeBits = getDefaultKeySize(keymasterAlgorithm);
+ }
+ checkValidKeySize(keymasterAlgorithm, this.mKeySizeBits);
+ if (spec.getKeystoreAlias() == null) {
+ throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
+ }
+ try {
+ String jcaKeyAlgorithm = KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm(keymasterAlgorithm);
+ this.mKeymasterPurposes = Purpose.allToKeymaster(spec.getPurposes());
+ this.mKeymasterBlockModes = BlockMode.allToKeymaster(spec.getBlockModes());
+ this.mKeymasterEncryptionPaddings = EncryptionPadding.allToKeymaster(spec.getEncryptionPaddings());
+ if ((spec.getPurposes() & 1) != 0 && spec.isRandomizedEncryptionRequired()) {
+ int[] arr$ = this.mKeymasterEncryptionPaddings;
+ int len$ = arr$.length;
+ int i$ = 0;
+ while (i$ < len$) {
+ int keymasterPadding = arr$[i$];
+ if (isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(keymasterPadding)) {
+ i$++;
+ } else {
+ throw new InvalidAlgorithmParameterException("Randomized encryption (IND-CPA) required but may be violated by padding scheme: " + EncryptionPadding.fromKeymaster(keymasterPadding) + ". See " + KeyGenParameterSpec.class.getName() + " documentation.");
+ }
+ }
+ }
+ this.mKeymasterSignaturePaddings = SignaturePadding.allToKeymaster(spec.getSignaturePaddings());
+ if (spec.isDigestsSpecified()) {
+ this.mKeymasterDigests = Digest.allToKeymaster(spec.getDigests());
+ } else {
+ this.mKeymasterDigests = EmptyArray.INT;
+ }
+ KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), this.mSpec.isUserAuthenticationRequired(), this.mSpec.getUserAuthenticationValidityDurationSeconds());
+ this.mJcaKeyAlgorithm = jcaKeyAlgorithm;
+ this.mRng = random;
+ this.mKeyStore = KeyStore.getInstance();
+ if (!true) {
+ resetAll();
+ return;
+ }
+ return;
+ } catch (RuntimeException e) {
+ throw new InvalidAlgorithmParameterException(e);
+ }
+ }
+ throw new InvalidAlgorithmParameterException("Unsupported params class: " + params.getClass().getName() + ". Supported: " + KeyGenParameterSpec.class.getName() + ", " + KeyPairGeneratorSpec.class.getName());
+ }
+ }
+
+ private void resetAll() {
+ this.mEntryAlias = null;
+ this.mJcaKeyAlgorithm = null;
+ this.mKeymasterAlgorithm = -1;
+ this.mKeymasterPurposes = null;
+ this.mKeymasterBlockModes = null;
+ this.mKeymasterEncryptionPaddings = null;
+ this.mKeymasterSignaturePaddings = null;
+ this.mKeymasterDigests = null;
+ this.mKeySizeBits = 0;
+ this.mSpec = null;
+ this.mRSAPublicExponent = null;
+ this.mEncryptionAtRestRequired = false;
+ this.mRng = null;
+ this.mKeyStore = null;
+ this.isForSoter = false;
+ this.isAutoSignedWithAttkWhenGetPublicKey = false;
+ this.isAutoSignedWithCommonkWhenGetPublicKey = false;
+ this.mAutoSignedKeyNameWhenGetPublicKey = "";
+ this.isSecmsgFidCounterSignedWhenSign = false;
+ this.isAutoAddCounterWhenGetPublicKey = false;
+ this.isNeedNextAttk = false;
+ }
+
+ private void initAlgorithmSpecificParameters() throws InvalidAlgorithmParameterException {
+ AlgorithmParameterSpec algSpecificSpec = this.mSpec.getAlgorithmParameterSpec();
+ BigInteger publicExponent = RSAKeyGenParameterSpec.F4;
+ if (algSpecificSpec instanceof RSAKeyGenParameterSpec) {
+ RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) algSpecificSpec;
+ if (this.mKeySizeBits == -1) {
+ this.mKeySizeBits = rsaSpec.getKeysize();
+ } else if (this.mKeySizeBits != rsaSpec.getKeysize()) {
+ throw new InvalidAlgorithmParameterException("RSA key size must match between " + this.mSpec + " and " + algSpecificSpec + ": " + this.mKeySizeBits + " vs " + rsaSpec.getKeysize());
+ }
+ publicExponent = rsaSpec.getPublicExponent();
+ if (publicExponent.compareTo(BigInteger.ZERO) < 1) {
+ throw new InvalidAlgorithmParameterException("RSA public exponent must be positive: " + publicExponent);
+ } else if (publicExponent.compareTo(UINT64_MAX_VALUE) > 0) {
+ throw new InvalidAlgorithmParameterException("Unsupported RSA public exponent: " + publicExponent + ". Maximum supported value: " + UINT64_MAX_VALUE);
+ }
+ }
+ this.mRSAPublicExponent = publicExponent;
+ SoterRSAKeyGenParameterSpec soterSpec = SoterUtil.convertKeyNameToParameterSpec(this.mSpec.getKeystoreAlias());
+ if (soterSpec != null) {
+ this.isForSoter = soterSpec.isForSoter();
+ this.isAutoSignedWithAttkWhenGetPublicKey = soterSpec.isAutoSignedWithAttkWhenGetPublicKey();
+ this.isAutoSignedWithCommonkWhenGetPublicKey = soterSpec.isAutoSignedWithCommonkWhenGetPublicKey();
+ this.mAutoSignedKeyNameWhenGetPublicKey = soterSpec.getAutoSignedKeyNameWhenGetPublicKey();
+ this.isSecmsgFidCounterSignedWhenSign = soterSpec.isSecmsgFidCounterSignedWhenSign();
+ this.isAutoAddCounterWhenGetPublicKey = soterSpec.isAutoAddCounterWhenGetPublicKey();
+ this.isNeedNextAttk = soterSpec.isNeedUseNextAttk();
+ }
+ }
+
+ public KeyPair generateKeyPair() {
+ if (this.mKeyStore == null || this.mSpec == null) {
+ throw new IllegalStateException("Not initialized");
+ }
+ int flags = this.mEncryptionAtRestRequired ? 1 : 0;
+ if ((flags & 1) == 0 || this.mKeyStore.state() == State.UNLOCKED) {
+ KeymasterArguments args = new KeymasterArguments();
+ args.addUnsignedInt(805306371, (long) this.mKeySizeBits);
+ args.addEnum(268435458, this.mKeymasterAlgorithm);
+ args.addEnums(536870913, this.mKeymasterPurposes);
+ args.addEnums(536870916, this.mKeymasterBlockModes);
+ args.addEnums(536870918, this.mKeymasterEncryptionPaddings);
+ args.addEnums(536870918, this.mKeymasterSignaturePaddings);
+ args.addEnums(536870917, this.mKeymasterDigests);
+ KeymasterUtils.addUserAuthArgs(args, this.mSpec.isUserAuthenticationRequired(), this.mSpec.getUserAuthenticationValidityDurationSeconds());
+ if (this.mSpec.getKeyValidityStart() != null) {
+ args.addDate(1610613136, this.mSpec.getKeyValidityStart());
+ }
+ if (this.mSpec.getKeyValidityForOriginationEnd() != null) {
+ args.addDate(1610613137, this.mSpec.getKeyValidityForOriginationEnd());
+ }
+ if (this.mSpec.getKeyValidityForConsumptionEnd() != null) {
+ args.addDate(1610613138, this.mSpec.getKeyValidityForConsumptionEnd());
+ }
+ addAlgorithmSpecificParameters(args);
+ byte[] additionalEntropy = getRandomBytesToMixIntoKeystoreRng(this.mRng, (this.mKeySizeBits + 7) / 8);
+ String privateKeyAlias = "USRPKEY_" + this.mEntryAlias;
+ try {
+ Credentials.deleteAllTypesForAlias(this.mKeyStore, this.mEntryAlias);
+ int errorCode = this.mKeyStore.generateKey(privateKeyAlias, args, additionalEntropy, flags, new KeyCharacteristics());
+ if (errorCode != 1) {
+ throw new ProviderException("Failed to generate key pair", KeyStore.getKeyStoreException(errorCode));
+ }
+ KeyPair result = SoterKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(this.mKeyStore, privateKeyAlias);
+ if (this.mJcaKeyAlgorithm.equalsIgnoreCase(result.getPrivate().getAlgorithm())) {
+ if (this.mKeyStore.put("USRCERT_" + this.mEntryAlias, generateSelfSignedCertificate(result.getPrivate(), result.getPublic()).getEncoded(), -1, flags)) {
+ if (!true) {
+ Credentials.deleteAllTypesForAlias(this.mKeyStore, this.mEntryAlias);
+ }
+ return result;
+ }
+ throw new ProviderException("Failed to store self-signed certificate");
+ }
+ throw new ProviderException("Generated key pair algorithm does not match requested algorithm: " + result.getPrivate().getAlgorithm() + " vs " + this.mJcaKeyAlgorithm);
+ } catch (CertificateEncodingException e) {
+ throw new ProviderException("Failed to obtain encoded form of self-signed certificate", e);
+ } catch (UnrecoverableKeyException e) {
+ throw new ProviderException("Failed to load generated key pair from keystore", e);
+ } catch (Exception e) {
+ Credentials.deleteAllTypesForAlias(this.mKeyStore, this.mEntryAlias);
+ throw new ProviderException("Failed to generate self-signed certificate", e);
+ }
+ } else {
+ throw new IllegalStateException("Encryption at rest using secure lock screen credential requested for key pair, but the user has not yet entered the credential");
+ }
+ }
+
+ static byte[] getRandomBytesToMixIntoKeystoreRng(SecureRandom rng, int sizeBytes) {
+ if (sizeBytes <= 0) {
+ return EmptyArray.BYTE;
+ }
+ if (rng == null) {
+ rng = getRng();
+ }
+ byte[] result = new byte[sizeBytes];
+ rng.nextBytes(result);
+ return result;
+ }
+
+ private static SecureRandom getRng() {
+ if (sRng == null) {
+ sRng = new SecureRandom();
+ }
+ return sRng;
+ }
+
+ private void addAlgorithmSpecificParameters(KeymasterArguments keymasterArgs) {
+ if (this.mRSAPublicExponent != null) {
+ keymasterArgs.addUnsignedLong(1342177480, this.mRSAPublicExponent);
+ }
+ if (this.isForSoter) {
+ keymasterArgs.addBoolean(1879059192);
+ keymasterArgs.addUnsignedInt(805317375, (long) Process.myUid());
+ }
+ if (this.isAutoSignedWithAttkWhenGetPublicKey) {
+ keymasterArgs.addBoolean(1879059193);
+ }
+ if (this.isAutoSignedWithCommonkWhenGetPublicKey) {
+ keymasterArgs.addBoolean(1879059194);
+ if (!SoterUtil.isNullOrNil(this.mAutoSignedKeyNameWhenGetPublicKey)) {
+ keymasterArgs.addBytes(-1879037189, ("USRPKEY_" + this.mAutoSignedKeyNameWhenGetPublicKey).getBytes());
+ }
+ }
+ if (this.isAutoAddCounterWhenGetPublicKey) {
+ keymasterArgs.addBoolean(1879059196);
+ }
+ if (this.isSecmsgFidCounterSignedWhenSign) {
+ keymasterArgs.addBoolean(1879059197);
+ }
+ if (this.isNeedNextAttk) {
+ keymasterArgs.addBoolean(1879059198);
+ }
+ }
+
+ private X509Certificate generateSelfSignedCertificate(PrivateKey privateKey, PublicKey publicKey) throws Exception {
+ Log.d("Soter", "generateSelfSignedCertificate");
+ String signatureAlgorithm = getCertificateSignatureAlgorithm(this.mKeymasterAlgorithm, this.mKeySizeBits, this.mSpec);
+ if (signatureAlgorithm == null) {
+ Log.d("Soter", "generateSelfSignedCertificateWithFakeSignature1");
+ return generateSelfSignedCertificateWithFakeSignature(publicKey);
+ }
+ try {
+ Log.d("Soter", "generateSelfSignedCertificateWithValidSignature");
+ return generateSelfSignedCertificateWithValidSignature(privateKey, publicKey, signatureAlgorithm);
+ } catch (Exception e) {
+ Log.d("Soter", "generateSelfSignedCertificateWithFakeSignature2");
+ return generateSelfSignedCertificateWithFakeSignature(publicKey);
+ }
+ }
+
+ private byte[] getRealKeyBlobByKeyName(String keyName) {
+ Log.d("Soter", "start retrieve key blob by key name: " + keyName);
+ return this.mKeyStore.get("USRPKEY_" + keyName);
+ }
+
+ private X509Certificate generateSelfSignedCertificateWithValidSignature(PrivateKey privateKey, PublicKey publicKey, String signatureAlgorithm) throws Exception {
+ X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
+ certGen.setPublicKey(publicKey);
+ certGen.setSerialNumber(this.mSpec.getCertificateSerialNumber());
+ certGen.setSubjectDN(this.mSpec.getCertificateSubject());
+ certGen.setIssuerDN(this.mSpec.getCertificateSubject());
+ certGen.setNotBefore(this.mSpec.getCertificateNotBefore());
+ certGen.setNotAfter(this.mSpec.getCertificateNotAfter());
+ certGen.setSignatureAlgorithm(signatureAlgorithm);
+ return certGen.generate(privateKey);
+ }
+
+ private X509Certificate generateSelfSignedCertificateWithFakeSignature(PublicKey publicKey) throws Exception {
+ AlgorithmIdentifier sigAlgId;
+ byte[] signature;
+ V3TBSCertificateGenerator tbsGenerator = new V3TBSCertificateGenerator();
+ switch (this.mKeymasterAlgorithm) {
+ case 1:
+ sigAlgId = new AlgorithmIdentifier(PKCSObjectIdentifiers.sha256WithRSAEncryption, DERNull.INSTANCE);
+ signature = new byte[1];
+ break;
+ case 3:
+ sigAlgId = new AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA256);
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(new DERInteger(0));
+ v.add(new DERInteger(0));
+ signature = new DERSequence().getEncoded();
+ break;
+ default:
+ throw new ProviderException("Unsupported key algorithm: " + this.mKeymasterAlgorithm);
+ }
+ ASN1InputStream publicKeyInfoIn = new ASN1InputStream(publicKey.getEncoded());
+ Throwable th2 = null;
+ try {
+ tbsGenerator.setSubjectPublicKeyInfo(SubjectPublicKeyInfo.getInstance(publicKeyInfoIn.readObject()));
+ if (publicKeyInfoIn != null) {
+ if (th2 != null) {
+ try {
+ publicKeyInfoIn.close();
+ } catch (Throwable x2) {
+ th2.addSuppressed(x2);
+ }
+ } else {
+ publicKeyInfoIn.close();
+ }
+ }
+ tbsGenerator.setSerialNumber(new ASN1Integer(this.mSpec.getCertificateSerialNumber()));
+ X509Principal subject = new X509Principal(this.mSpec.getCertificateSubject().getEncoded());
+ tbsGenerator.setSubject(subject);
+ tbsGenerator.setIssuer(subject);
+ tbsGenerator.setStartDate(new Time(this.mSpec.getCertificateNotBefore()));
+ tbsGenerator.setEndDate(new Time(this.mSpec.getCertificateNotAfter()));
+ tbsGenerator.setSignature(sigAlgId);
+ TBSCertificate tbsCertificate = tbsGenerator.generateTBSCertificate();
+ ASN1EncodableVector result = new ASN1EncodableVector();
+ result.add(tbsCertificate);
+ result.add(sigAlgId);
+ result.add(new DERBitString(signature));
+ return new X509CertificateObject(Certificate.getInstance(new DERSequence(result)));
+ } finally {
+ if (publicKeyInfoIn != null) {
+ publicKeyInfoIn.close();
+ }
+ }
+ }
+
+ private static int getDefaultKeySize(int keymasterAlgorithm) {
+ return RSA_DEFAULT_KEY_SIZE;
+ }
+
+ private static void checkValidKeySize(int keymasterAlgorithm, int keySize) throws InvalidAlgorithmParameterException {
+ if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
+ throw new InvalidAlgorithmParameterException("RSA key size must be >= 512 and <= 8192");
+ }
+ }
+
+ private static String getCertificateSignatureAlgorithm(int keymasterAlgorithm, int keySizeBits, KeyGenParameterSpec spec) {
+ if ((spec.getPurposes() & 4) == 0) {
+ return null;
+ }
+ if (spec.isUserAuthenticationRequired()) {
+ return null;
+ }
+ if (!spec.isDigestsSpecified()) {
+ return null;
+ }
+ if (!ArrayUtils.contains(SignaturePadding.allToKeymaster(spec.getSignaturePaddings()), 5)) {
+ return null;
+ }
+ int maxDigestOutputSizeBits = keySizeBits - 240;
+ int bestKeymasterDigest = -1;
+ int bestDigestOutputSizeBits = -1;
+ for (Integer intValue : getAvailableKeymasterSignatureDigests(spec.getDigests(), getSupportedEcdsaSignatureDigests())) {
+ int keymasterDigest = intValue.intValue();
+ int outputSizeBits = getDigestOutputSizeBits(keymasterDigest);
+ if (outputSizeBits <= maxDigestOutputSizeBits) {
+ if (bestKeymasterDigest == -1) {
+ bestKeymasterDigest = keymasterDigest;
+ bestDigestOutputSizeBits = outputSizeBits;
+ } else if (outputSizeBits > bestDigestOutputSizeBits) {
+ bestKeymasterDigest = keymasterDigest;
+ bestDigestOutputSizeBits = outputSizeBits;
+ }
+ }
+ }
+ if (bestKeymasterDigest == -1) {
+ return null;
+ }
+ return Digest.fromKeymasterToSignatureAlgorithmDigest(bestKeymasterDigest) + "WithRSA";
+ }
+
+ private static String[] getSupportedEcdsaSignatureDigests() {
+ return new String[]{"NONE", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"};
+ }
+
+ private static Set<Integer> getAvailableKeymasterSignatureDigests(String[] authorizedKeyDigests, String[] supportedSignatureDigests) {
+ Set<Integer> authorizedKeymasterKeyDigests = new HashSet();
+ for (int keymasterDigest : Digest.allToKeymaster(authorizedKeyDigests)) {
+ authorizedKeymasterKeyDigests.add(Integer.valueOf(keymasterDigest));
+ }
+ Set<Integer> supportedKeymasterSignatureDigests = new HashSet();
+ for (int keymasterDigest2 : Digest.allToKeymaster(supportedSignatureDigests)) {
+ supportedKeymasterSignatureDigests.add(Integer.valueOf(keymasterDigest2));
+ }
+ Set<Integer> result = new HashSet(supportedKeymasterSignatureDigests);
+ result.retainAll(authorizedKeymasterKeyDigests);
+ return result;
+ }
+
+ public static int getDigestOutputSizeBits(int keymasterDigest) {
+ switch (keymasterDigest) {
+ case 0:
+ return -1;
+ case 1:
+ return 128;
+ case 2:
+ return 160;
+ case 3:
+ return 224;
+ case 4:
+ return 256;
+ case 5:
+ return 384;
+ case 6:
+ return RSA_MIN_KEY_SIZE;
+ default:
+ throw new IllegalArgumentException("Unknown digest: " + keymasterDigest);
+ }
+ }
+
+ public static BigInteger toUint64(long value) {
+ if (value >= 0) {
+ return BigInteger.valueOf(value);
+ }
+ return BigInteger.valueOf(value).add(UINT64_RANGE);
+ }
+
+ public static boolean isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(int keymasterPadding) {
+ switch (keymasterPadding) {
+ case 1:
+ return false;
+ case 2:
+ case 4:
+ return true;
+ default:
+ throw new IllegalArgumentException("Unsupported asymmetric encryption padding scheme: " + keymasterPadding);
+ }
+ }
+
+ public static Context getApplicationContext() {
+ Application application = ActivityThread.currentApplication();
+ if (application != null) {
+ return application;
+ }
+ throw new IllegalStateException("Failed to obtain application Context from ActivityThread");
+ }
+
+ public static byte[] intToByteArray(int value) {
+ ByteBuffer converter = ByteBuffer.allocate(4);
+ converter.order(ByteOrder.nativeOrder());
+ converter.putInt(value);
+ return converter.array();
+ }
+}
diff --git a/org.ifaa.android.manager/src/android/security/keystore/SoterKeyStoreProvider.java b/org.ifaa.android.manager/src/android/security/keystore/SoterKeyStoreProvider.java
new file mode 100644
index 0000000..7b3cbdc
--- /dev/null
+++ b/org.ifaa.android.manager/src/android/security/keystore/SoterKeyStoreProvider.java
@@ -0,0 +1,160 @@
+package android.security.keystore;
+
+import android.security.KeyStore;
+import android.security.keymaster.ExportResult;
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keystore.KeyProperties.KeyAlgorithm;
+import android.util.Log;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.UnrecoverableKeyException;
+import java.security.interfaces.ECKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import org.json.JSONException;
+
+public class SoterKeyStoreProvider extends Provider {
+ private static final String ANDROID_PACKAGE_NAME = "android.security.keystore";
+ public static final String PROVIDER_NAME = "SoterKeyStore";
+ private static final String SOTER_PACKAGE_NAME = "android.security.keystore";
+
+ public SoterKeyStoreProvider() {
+ super(PROVIDER_NAME, 1.0d, "provider for soter");
+ put("KeyPairGenerator.RSA", "android.security.keystore.SoterKeyStoreKeyPairRSAGeneratorSpi");
+ put("KeyStore.SoterKeyStore", "android.security.keystore.SoterKeyStoreSpi");
+ putKeyFactoryImpl("RSA");
+ }
+
+ private void putKeyFactoryImpl(String algorithm) {
+ put("KeyFactory." + algorithm, "android.security.keystore.AndroidKeyStoreKeyFactorySpi");
+ }
+
+ public static void install() {
+ Security.addProvider(new SoterKeyStoreProvider());
+ }
+
+ public static AndroidKeyStorePrivateKey getAndroidKeyStorePrivateKey(AndroidKeyStorePublicKey publicKey) {
+ String keyAlgorithm = publicKey.getAlgorithm();
+ if ("EC".equalsIgnoreCase(keyAlgorithm)) {
+ return new AndroidKeyStoreECPrivateKey(publicKey.getAlias(), ((ECKey) publicKey).getParams());
+ }
+ if ("RSA".equalsIgnoreCase(keyAlgorithm)) {
+ return new AndroidKeyStoreRSAPrivateKey(publicKey.getAlias(), ((RSAKey) publicKey).getModulus());
+ }
+ throw new ProviderException("Unsupported Android Keystore public key algorithm: " + keyAlgorithm);
+ }
+
+ public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(KeyStore keyStore, String privateKeyAlias) throws UnrecoverableKeyException {
+ KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
+ int errorCode = keyStore.getKeyCharacteristics(privateKeyAlias, null, null, keyCharacteristics);
+ if (errorCode != 1) {
+ throw ((UnrecoverableKeyException) new UnrecoverableKeyException("Failed to obtain information about private key").initCause(KeyStore.getKeyStoreException(errorCode)));
+ }
+ ExportResult exportResult = keyStore.exportKey(privateKeyAlias, 0, null, null);
+ if (exportResult.resultCode != 1) {
+ throw ((UnrecoverableKeyException) new UnrecoverableKeyException("Failed to obtain X.509 form of public key").initCause(KeyStore.getKeyStoreException(errorCode)));
+ }
+ byte[] x509EncodedPublicKey = exportResult.exportData;
+ Integer keymasterAlgorithm = keyCharacteristics.getEnum(268435458);
+ if (keymasterAlgorithm == null) {
+ throw new UnrecoverableKeyException("Key algorithm unknown");
+ }
+ try {
+ return getAndroidKeyStorePublicKey(privateKeyAlias, KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm(keymasterAlgorithm.intValue()), x509EncodedPublicKey);
+ } catch (IllegalArgumentException e) {
+ throw ((UnrecoverableKeyException) new UnrecoverableKeyException("Failed to load private key").initCause(e));
+ }
+ }
+
+ public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(KeyStore keyStore, String privateKeyAlias) throws UnrecoverableKeyException {
+ AndroidKeyStorePublicKey publicKey = loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias);
+ return new KeyPair(publicKey, getAndroidKeyStorePrivateKey(publicKey));
+ }
+
+ public static AndroidKeyStorePublicKey getAndroidKeyStorePublicKey(String alias, String keyAlgorithm, byte[] x509EncodedForm) {
+ try {
+ KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
+ byte[] realPublicKey = SoterUtil.getDataFromRaw(x509EncodedForm, SoterUtil.JSON_KEY_PUBLIC);
+ if (realPublicKey != null) {
+ PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(realPublicKey));
+ if ("EC".equalsIgnoreCase(keyAlgorithm)) {
+ Log.d("Soter", "AndroidKeyStoreECPublicKey");
+ return new AndroidKeyStoreECPublicKey(alias, (ECPublicKey) publicKey);
+ } else if ("RSA".equalsIgnoreCase(keyAlgorithm)) {
+ Log.d("Soter", "AndroidKeyStoreRSAPublicKey");
+ return new AndroidKeyStoreRSAPublicKey(alias, (RSAPublicKey) publicKey);
+ } else {
+ throw new ProviderException("Unsupported Android Keystore public key algorithm: " + keyAlgorithm);
+ }
+ }
+ throw new NullPointerException("invalid soter public key");
+ } catch (NoSuchAlgorithmException e) {
+ throw new ProviderException("Failed to obtain " + keyAlgorithm + " KeyFactory", e);
+ } catch (InvalidKeySpecException e2) {
+ throw new ProviderException("Invalid X.509 encoding of public key", e2);
+ } catch (JSONException e3) {
+ throw new ProviderException("Not in json format");
+ }
+ }
+
+ public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(KeyStore keyStore, String privateKeyAlias) throws UnrecoverableKeyException {
+ return (AndroidKeyStorePrivateKey) loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias).getPrivate();
+ }
+
+ public static AndroidKeyStorePublicKey loadJsonPublicKeyFromKeystore(KeyStore keyStore, String privateKeyAlias) throws UnrecoverableKeyException {
+ KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
+ int errorCode = keyStore.getKeyCharacteristics(privateKeyAlias, null, null, keyCharacteristics);
+ if (errorCode != 1) {
+ throw ((UnrecoverableKeyException) new UnrecoverableKeyException("Failed to obtain information about private key").initCause(KeyStore.getKeyStoreException(errorCode)));
+ }
+ ExportResult exportResult = keyStore.exportKey(privateKeyAlias, 0, null, null);
+ if (exportResult.resultCode != 1) {
+ throw ((UnrecoverableKeyException) new UnrecoverableKeyException("Failed to obtain X.509 form of public key").initCause(KeyStore.getKeyStoreException(errorCode)));
+ }
+ byte[] x509EncodedPublicKey = exportResult.exportData;
+ Integer keymasterAlgorithm = keyCharacteristics.getEnum(268435458);
+ if (keymasterAlgorithm == null) {
+ throw new UnrecoverableKeyException("Key algorithm unknown");
+ }
+ try {
+ return getJsonPublicKey(privateKeyAlias, KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm(keymasterAlgorithm.intValue()), x509EncodedPublicKey);
+ } catch (IllegalArgumentException e) {
+ throw ((UnrecoverableKeyException) new UnrecoverableKeyException("Failed to load private key").initCause(e));
+ }
+ }
+
+ public static AndroidKeyStorePublicKey getJsonPublicKey(String alias, String keyAlgorithm, byte[] x509EncodedForm) {
+ try {
+ KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
+ byte[] realPublicKey = SoterUtil.getDataFromRaw(x509EncodedForm, SoterUtil.JSON_KEY_PUBLIC);
+ if (realPublicKey != null) {
+ PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(realPublicKey));
+ if ("EC".equalsIgnoreCase(keyAlgorithm)) {
+ Log.d("Soter", "AndroidKeyStoreECPublicKey");
+ return new AndroidKeyStoreECPublicKey(alias, (ECPublicKey) publicKey);
+ } else if ("RSA".equalsIgnoreCase(keyAlgorithm)) {
+ Log.d("Soter", "getJsonPublicKey");
+ RSAPublicKey rsaPubKey = (RSAPublicKey) publicKey;
+ return new AndroidKeyStoreRSAPublicKey(alias, x509EncodedForm, rsaPubKey.getModulus(), rsaPubKey.getPublicExponent());
+ } else {
+ throw new ProviderException("Unsupported Android Keystore public key algorithm: " + keyAlgorithm);
+ }
+ }
+ throw new NullPointerException("invalid soter public key");
+ } catch (NoSuchAlgorithmException e) {
+ throw new ProviderException("Failed to obtain " + keyAlgorithm + " KeyFactory", e);
+ } catch (InvalidKeySpecException e2) {
+ throw new ProviderException("Invalid X.509 encoding of public key", e2);
+ } catch (JSONException e3) {
+ throw new ProviderException("Not in json format");
+ }
+ }
+}
diff --git a/org.ifaa.android.manager/src/android/security/keystore/SoterKeyStoreSpi.java b/org.ifaa.android.manager/src/android/security/keystore/SoterKeyStoreSpi.java
new file mode 100644
index 0000000..52b1966
--- /dev/null
+++ b/org.ifaa.android.manager/src/android/security/keystore/SoterKeyStoreSpi.java
@@ -0,0 +1,60 @@
+package android.security.keystore;
+
+import android.security.KeyStore;
+import java.security.Key;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+
+public class SoterKeyStoreSpi extends AndroidKeyStoreSpi {
+ private KeyStore mKeyStore;
+
+ public SoterKeyStoreSpi() {
+ this.mKeyStore = null;
+ this.mKeyStore = KeyStore.getInstance();
+ }
+
+ public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException {
+ if (isPrivateKeyEntry(alias)) {
+ String privateKeyAlias = "USRPKEY_" + alias;
+ if (password == null || !"from_soter_ui".equals(String.valueOf(password))) {
+ return SoterKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(this.mKeyStore, privateKeyAlias);
+ }
+ return SoterKeyStoreProvider.loadJsonPublicKeyFromKeystore(this.mKeyStore, privateKeyAlias);
+ } else if (!isSecretKeyEntry(alias)) {
+ return null;
+ } else {
+ return AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore(this.mKeyStore, "USRSKEY_" + alias);
+ }
+ }
+
+ private boolean isPrivateKeyEntry(String alias) {
+ if (alias != null) {
+ return this.mKeyStore.contains("USRPKEY_" + alias);
+ }
+ throw new NullPointerException("alias == null");
+ }
+
+ private boolean isSecretKeyEntry(String alias) {
+ if (alias != null) {
+ return this.mKeyStore.contains("USRSKEY_" + alias);
+ }
+ throw new NullPointerException("alias == null");
+ }
+
+ public void engineDeleteEntry(String alias) throws KeyStoreException {
+ boolean deletePkey = this.mKeyStore.delete("USRPKEY_" + alias);
+ boolean deleteCert = this.mKeyStore.delete("USRCERT_" + alias);
+ if (engineContainsAlias(alias) && !(deletePkey || deleteCert)) {
+ throw new KeyStoreException("Failed to delete entry: " + alias);
+ }
+ }
+
+ public boolean engineContainsAlias(String alias) {
+ if (alias != null) {
+ return this.mKeyStore.contains(new StringBuilder().append("USRPKEY_").append(alias).toString()) || this.mKeyStore.contains("USRCERT_" + alias);
+ } else {
+ throw new NullPointerException("alias == null");
+ }
+ }
+}
diff --git a/org.ifaa.android.manager/src/android/security/keystore/SoterRSAKeyGenParameterSpec.java b/org.ifaa.android.manager/src/android/security/keystore/SoterRSAKeyGenParameterSpec.java
new file mode 100644
index 0000000..3698828
--- /dev/null
+++ b/org.ifaa.android.manager/src/android/security/keystore/SoterRSAKeyGenParameterSpec.java
@@ -0,0 +1,96 @@
+package android.security.keystore;
+
+import java.math.BigInteger;
+import java.security.spec.RSAKeyGenParameterSpec;
+
+public class SoterRSAKeyGenParameterSpec extends RSAKeyGenParameterSpec {
+ private boolean isAutoAddCounterWhenGetPublicKey;
+ private boolean isAutoSignedWithAttkWhenGetPublicKey;
+ private boolean isAutoSignedWithCommonkWhenGetPublicKey;
+ private boolean isForSoter;
+ private boolean isNeedUseNextAttk;
+ private boolean isSecmsgFidCounterSignedWhenSign;
+ private String mAutoSignedKeyNameWhenGetPublicKey;
+
+ public SoterRSAKeyGenParameterSpec(int keysize, BigInteger publicExponent, boolean isForSoter, boolean isAutoSignedWithAttkWhenGetPublicKey, boolean isAutoSignedWithCommonkWhenGetPublicKey, String signedKeyNameWhenGetPublicKey, boolean isSecmsgFidCounterSignedWhenSign, boolean isAutoAddCounterWhenGetPublicKey, boolean isNeedNextAttk) {
+ super(keysize, publicExponent);
+ this.isForSoter = false;
+ this.isAutoSignedWithAttkWhenGetPublicKey = false;
+ this.isAutoSignedWithCommonkWhenGetPublicKey = false;
+ this.mAutoSignedKeyNameWhenGetPublicKey = "";
+ this.isSecmsgFidCounterSignedWhenSign = false;
+ this.isAutoAddCounterWhenGetPublicKey = false;
+ this.isNeedUseNextAttk = false;
+ this.isForSoter = isForSoter;
+ this.isAutoSignedWithAttkWhenGetPublicKey = isAutoSignedWithAttkWhenGetPublicKey;
+ this.isAutoSignedWithCommonkWhenGetPublicKey = isAutoSignedWithCommonkWhenGetPublicKey;
+ this.mAutoSignedKeyNameWhenGetPublicKey = signedKeyNameWhenGetPublicKey;
+ this.isSecmsgFidCounterSignedWhenSign = isSecmsgFidCounterSignedWhenSign;
+ this.isAutoAddCounterWhenGetPublicKey = isAutoAddCounterWhenGetPublicKey;
+ this.isNeedUseNextAttk = isNeedNextAttk;
+ }
+
+ public SoterRSAKeyGenParameterSpec(boolean isForSoter, boolean isAutoSignedWithAttkWhenGetPublicKey, boolean isAutoSignedWithCommonkWhenGetPublicKey, String signedKeyNameWhenGetPublicKey, boolean isSecmsgFidCounterSignedWhenSign, boolean isAutoAddCounterWhenGetPubli, boolean isNeedNextAttkcKey) {
+ this(2048, RSAKeyGenParameterSpec.F4, isForSoter, isAutoSignedWithAttkWhenGetPublicKey, isAutoSignedWithCommonkWhenGetPublicKey, signedKeyNameWhenGetPublicKey, isSecmsgFidCounterSignedWhenSign, isAutoAddCounterWhenGetPubli, isNeedNextAttkcKey);
+ }
+
+ public boolean isForSoter() {
+ return this.isForSoter;
+ }
+
+ public void setIsForSoter(boolean isForSoter) {
+ this.isForSoter = isForSoter;
+ }
+
+ public boolean isAutoSignedWithAttkWhenGetPublicKey() {
+ return this.isAutoSignedWithAttkWhenGetPublicKey;
+ }
+
+ public void setIsAutoSignedWithAttkWhenGetPublicKey(boolean isAutoSignedWithAttkWhenGetPublicKey) {
+ this.isAutoSignedWithAttkWhenGetPublicKey = isAutoSignedWithAttkWhenGetPublicKey;
+ }
+
+ public boolean isAutoSignedWithCommonkWhenGetPublicKey() {
+ return this.isAutoSignedWithCommonkWhenGetPublicKey;
+ }
+
+ public void setIsAutoSignedWithCommonkWhenGetPublicKey(boolean isAutoSignedWithCommonkWhenGetPublicKey) {
+ this.isAutoSignedWithCommonkWhenGetPublicKey = isAutoSignedWithCommonkWhenGetPublicKey;
+ }
+
+ public String getAutoSignedKeyNameWhenGetPublicKey() {
+ return this.mAutoSignedKeyNameWhenGetPublicKey;
+ }
+
+ public void setAutoSignedKeyNameWhenGetPublicKey(String mAutoSignedKeyNameWhenGetPublicKey) {
+ this.mAutoSignedKeyNameWhenGetPublicKey = mAutoSignedKeyNameWhenGetPublicKey;
+ }
+
+ public boolean isSecmsgFidCounterSignedWhenSign() {
+ return this.isSecmsgFidCounterSignedWhenSign;
+ }
+
+ public void setIsSecmsgFidCounterSignedWhenSign(boolean isSecmsgFidCounterSignedWhenSign) {
+ this.isSecmsgFidCounterSignedWhenSign = isSecmsgFidCounterSignedWhenSign;
+ }
+
+ public boolean isAutoAddCounterWhenGetPublicKey() {
+ return this.isAutoAddCounterWhenGetPublicKey;
+ }
+
+ public void setIsAutoAddCounterWhenGetPublicKey(boolean isAutoAddCounterWhenGetPublicKey) {
+ this.isAutoAddCounterWhenGetPublicKey = isAutoAddCounterWhenGetPublicKey;
+ }
+
+ public boolean isNeedUseNextAttk() {
+ return this.isNeedUseNextAttk;
+ }
+
+ public void setIsNeedUseNextAttk(boolean isNeedUseNextAttk) {
+ this.isNeedUseNextAttk = isNeedUseNextAttk;
+ }
+
+ public String toString() {
+ return "SoterRSAKeyGenParameterSpec{isForSoter=" + this.isForSoter + ", isAutoSignedWithAttkWhenGetPublicKey=" + this.isAutoSignedWithAttkWhenGetPublicKey + ", isAutoSignedWithCommonkWhenGetPublicKey=" + this.isAutoSignedWithCommonkWhenGetPublicKey + ", mAutoSignedKeyNameWhenGetPublicKey='" + this.mAutoSignedKeyNameWhenGetPublicKey + '\'' + ", isSecmsgFidCounterSignedWhenSign=" + this.isSecmsgFidCounterSignedWhenSign + ", isAutoAddCounterWhenGetPublicKey=" + this.isAutoAddCounterWhenGetPublicKey + ", isNeedUseNextAttk=" + this.isNeedUseNextAttk + '}';
+ }
+}
diff --git a/org.ifaa.android.manager/src/android/security/keystore/SoterUtil.java b/org.ifaa.android.manager/src/android/security/keystore/SoterUtil.java
new file mode 100644
index 0000000..16c809b
--- /dev/null
+++ b/org.ifaa.android.manager/src/android/security/keystore/SoterUtil.java
@@ -0,0 +1,166 @@
+package android.security.keystore;
+
+import android.util.Base64;
+import android.util.Log;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class SoterUtil {
+ public static final String JSON_KEY_PUBLIC = "pub_key";
+ private static final String PARAM_NEED_AUTO_ADD_COUNTER_WHEN_GET_PUBLIC_KEY = "addcounter";
+ private static final String PARAM_NEED_AUTO_ADD_SECMSG_FID_COUNTER_WHEN_SIGN = "secmsg_and_counter_signed_when_sign";
+ private static final String PARAM_NEED_AUTO_SIGNED_WITH_ATTK_WHEN_GET_PUBLIC_KEY = "auto_signed_when_get_pubkey_attk";
+ private static final String PARAM_NEED_AUTO_SIGNED_WITH_COMMON_KEY_WHEN_GET_PUBLIC_KEY = "auto_signed_when_get_pubkey";
+ private static final String PARAM_NEED_NEXT_ATTK = "next_attk";
+ private static final int RAW_LENGTH_PREFIX = 4;
+ public static final String TAG = "Soter.Util";
+
+ public static SoterRSAKeyGenParameterSpec convertKeyNameToParameterSpec(String name) {
+ if (isNullOrNil(name)) {
+ Log.e(TAG, "hy: null or nil when convert key name to parameter");
+ return null;
+ }
+ String[] splits = name.split("\\.");
+ if (splits == null || splits.length <= 1) {
+ Log.w(TAG, "hy: pure alias, no parameter");
+ return null;
+ }
+ boolean isAutoSignedWithAttkWhenGetPublicKey = false;
+ boolean isAutoSignedWithCommonkWhenGetPublicKey = false;
+ String mAutoSignedKeyNameWhenGetPublicKey = "";
+ boolean isSecmsgFidCounterSignedWhenSign = false;
+ boolean isAutoAddCounterWhenGetPublicKey = false;
+ boolean isNeedNextAttk = false;
+ if (contains(PARAM_NEED_AUTO_SIGNED_WITH_ATTK_WHEN_GET_PUBLIC_KEY, splits)) {
+ isAutoSignedWithAttkWhenGetPublicKey = true;
+ } else {
+ String entireCommonKeyExpr = containsPrefix(PARAM_NEED_AUTO_SIGNED_WITH_COMMON_KEY_WHEN_GET_PUBLIC_KEY, splits);
+ if (!isNullOrNil(entireCommonKeyExpr)) {
+ String commonKeyName = retrieveKeyNameFromExpr(entireCommonKeyExpr);
+ if (!isNullOrNil(commonKeyName)) {
+ isAutoSignedWithCommonkWhenGetPublicKey = true;
+ mAutoSignedKeyNameWhenGetPublicKey = commonKeyName;
+ }
+ }
+ }
+ if (contains(PARAM_NEED_AUTO_ADD_SECMSG_FID_COUNTER_WHEN_SIGN, splits)) {
+ isSecmsgFidCounterSignedWhenSign = true;
+ }
+ if (contains(PARAM_NEED_AUTO_ADD_COUNTER_WHEN_GET_PUBLIC_KEY, splits)) {
+ isAutoAddCounterWhenGetPublicKey = true;
+ if (contains(PARAM_NEED_NEXT_ATTK, splits)) {
+ isNeedNextAttk = true;
+ }
+ }
+ SoterRSAKeyGenParameterSpec spec = new SoterRSAKeyGenParameterSpec(true, isAutoSignedWithAttkWhenGetPublicKey, isAutoSignedWithCommonkWhenGetPublicKey, mAutoSignedKeyNameWhenGetPublicKey, isSecmsgFidCounterSignedWhenSign, isAutoAddCounterWhenGetPublicKey, isNeedNextAttk);
+ Log.i(TAG, "hy: spec: " + spec.toString());
+ return spec;
+ }
+
+ private static String retrieveKeyNameFromExpr(String expr) {
+ if (isNullOrNil(expr)) {
+ Log.e(TAG, "hy: expr is null");
+ return null;
+ }
+ int startPos = expr.indexOf("(");
+ int endPos = expr.indexOf(")");
+ if (startPos >= 0 && endPos > startPos) {
+ return expr.substring(startPos + 1, endPos);
+ }
+ Log.e(TAG, "hy: no key name");
+ return null;
+ }
+
+ private static boolean contains(String target, String[] src) {
+ if (src == null || src.length == 0 || isNullOrNil(target)) {
+ Log.e(TAG, "hy: param error");
+ throw new IllegalArgumentException("param error");
+ }
+ for (String item : src) {
+ if (target.equals(item)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static String containsPrefix(String prefix, String[] src) {
+ if (src == null || src.length == 0 || isNullOrNil(prefix)) {
+ Log.e(TAG, "hy: param error");
+ throw new IllegalArgumentException("param error");
+ }
+ for (String item : src) {
+ if (!isNullOrNil(item) && item.startsWith(prefix)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ public static String getPureKeyAliasFromKeyName(String name) {
+ Log.i(TAG, "hy: retrieving pure name from: " + name);
+ if (isNullOrNil(name)) {
+ Log.e(TAG, "hy: null or nil when get pure key alias");
+ return null;
+ }
+ String[] splits = name.split("\\.");
+ if (splits != null && splits.length > 1) {
+ return splits[0];
+ }
+ Log.d(TAG, "hy: pure alias");
+ return name;
+ }
+
+ public static boolean isNullOrNil(String str) {
+ return str == null || str.equals("");
+ }
+
+ public static byte[] getDataFromRaw(byte[] origin, String jsonKey) throws JSONException {
+ if (isNullOrNil(jsonKey)) {
+ Log.e("Soter", "hy: json keyname error");
+ return null;
+ } else if (origin == null) {
+ Log.e("Soter", "hy: json origin null");
+ return null;
+ } else {
+ JSONObject jsonObj = retriveJsonFromExportedData(origin);
+ if (jsonObj == null || !jsonObj.has(jsonKey)) {
+ return null;
+ }
+ return Base64.decode(jsonObj.getString(jsonKey).replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "").replace("\\n", ""), 0);
+ }
+ }
+
+ private static JSONObject retriveJsonFromExportedData(byte[] origin) {
+ if (origin == null) {
+ Log.e("Soter", "raw data is null");
+ return null;
+ }
+ if (origin.length < RAW_LENGTH_PREFIX) {
+ Log.e("Soter", "raw data length smaller than 4");
+ }
+ byte[] lengthBytes = new byte[RAW_LENGTH_PREFIX];
+ System.arraycopy(origin, 0, lengthBytes, 0, RAW_LENGTH_PREFIX);
+ int rawLength = toInt(lengthBytes);
+ byte[] rawJsonBytes = new byte[rawLength];
+ if (origin.length <= rawLength + RAW_LENGTH_PREFIX) {
+ Log.e("Soter", "length not correct 2");
+ return null;
+ }
+ System.arraycopy(origin, RAW_LENGTH_PREFIX, rawJsonBytes, 0, rawLength);
+ try {
+ return new JSONObject(new String(rawJsonBytes));
+ } catch (JSONException e) {
+ Log.e("Soter", "hy: can not convert to json");
+ return null;
+ }
+ }
+
+ public static int toInt(byte[] bRefArr) {
+ int iOutcome = 0;
+ for (int i = 0; i < bRefArr.length; i++) {
+ iOutcome += (bRefArr[i] & 255) << (i * 8);
+ }
+ return iOutcome;
+ }
+}
diff --git a/org.ifaa.android.manager/src/org/ifaa/android/manager/IFAAManager.java b/org.ifaa.android.manager/src/org/ifaa/android/manager/IFAAManager.java
new file mode 100644
index 0000000..5a945c3
--- /dev/null
+++ b/org.ifaa.android.manager/src/org/ifaa/android/manager/IFAAManager.java
@@ -0,0 +1,23 @@
+package org.ifaa.android.manager;
+
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.Build;
+
+public abstract class IFAAManager
+{
+ static
+ {
+ System.loadLibrary("ifaa_jni");
+ }
+
+ public abstract String getDeviceModel();
+
+ public abstract int getSupportBIOTypes(Context paramContext);
+
+ public abstract int getVersion();
+
+ public native byte[] processCmd(Context paramContext, byte[] paramArrayOfByte);
+
+ public abstract int startBIOManager(Context paramContext, int paramInt);
+}
diff --git a/org.ifaa.android.manager/src/org/ifaa/android/manager/IFAAManagerFactory.java b/org.ifaa.android.manager/src/org/ifaa/android/manager/IFAAManagerFactory.java
new file mode 100644
index 0000000..a747b13
--- /dev/null
+++ b/org.ifaa.android.manager/src/org/ifaa/android/manager/IFAAManagerFactory.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod 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.ifaa.android.manager;
+
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+public class IFAAManagerFactory extends IFAAManager {
+ private static final int ACTIVITY_START_FAILED = -1;
+ private static final int ACTIVITY_START_SUCCESS = 0;
+ private static final int BIOTypeFingerprint = 1;
+ private static final int BIOTypeIris = 2;
+ private static final String TAG = "IFAAManagerFactory";
+ public static IFAAManagerFactory mIFAAManagerFactory = null;
+
+ public int getSupportBIOTypes(Context context) {
+ return BIOTypeFingerprint;
+ }
+
+ public int startBIOManager(Context context, int authType) {
+ try {
+ Intent intent = new Intent();
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.setComponent(new ComponentName("com.android.settings", "com.android.settings.Settings$SecuritySettingsActivity"));
+ context.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ e.printStackTrace();
+ } catch (Throwable th) {
+ }
+ return ACTIVITY_START_SUCCESS;
+ }
+
+ public String getDeviceModel() {
+ return "ZUK-Z2131";
+ }
+
+ public int getVersion() {
+ return BIOTypeFingerprint;
+ }
+
+ public static IFAAManager getIFAAManager(Context context, int authType) {
+ if (mIFAAManagerFactory != null) {
+ return mIFAAManagerFactory;
+ }
+ mIFAAManagerFactory = new IFAAManagerFactory();
+ return mIFAAManagerFactory;
+ }
+}