diff options
Diffstat (limited to 'org.ifaa.android.manager')
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; + } +} |