<commit_before>/*
 *    Copyright 2016 <NAME>
 *
 *    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 com.github.cjnosal.secret_storage.keymanager;

import android.Manifest;
import android.annotation.TargetApi;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.hardware.fingerprint.FingerprintManagerCompat;
import android.support.v4.os.CancellationSignal;

import com.github.cjnosal.secret_storage.keymanager.strategy.cipher.CipherSpec;
import com.github.cjnosal.secret_storage.keymanager.strategy.keygen.KeyGenSpec;
import com.github.cjnosal.secret_storage.storage.DataStorage;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.Key;

import javax.crypto.Cipher;

@TargetApi(Build.VERSION_CODES.M)
public class FingerprintWrapper extends KeyStoreWrapper {

    public FingerprintWrapper(CipherSpec keyProtectionSpec, KeyGenSpec keyGenSpec, DataStorage configStorage, DataStorage keyStorage) {
        super(keyProtectionSpec, keyGenSpec, configStorage, keyStorage);
    }

    @Override
    public Editor getEditor(String storeId) {
        return new FingerprintEditor(storeId);
    }

    @Override
    void unlock(String keyAlias, UnlockParams params) throws IOException, GeneralSecurityException {
        FingerprintParams fingerprintParams = (FingerprintParams) params;
        FingerprintManagerCompat fingerprintManagerCompat = FingerprintManagerCompat.from(fingerprintParams.getContext());
        checkFingerprintStatus(fingerprintParams, fingerprintManagerCompat);

        FingerprintCallback fingerprintCallback;
        Cipher kekCipher;
        String storageField = getStorageField(keyAlias, ENCRYPTION_KEY);
        if (!kekExists(keyAlias)) {
            Key kek = androidCrypto.generateSecretKey(keyGenSpec.getKeygenAlgorithm(), getKeyGenParameterSpec(storageField));
            kekCipher = keyWrap.initWrapCipher(kek, keyProtectionSpec.getCipherTransformation(), keyProtectionSpec.getParamsAlgorithm());
            fingerprintCallback = new FingerprintCallback(keyAlias, fingerprintParams.getAuthenticationCallback(), fingerprintParams.getListener(), true);
        } else {
            Key kek = androidCrypto.loadSecretKey(storageField);
            kekCipher = keyWrap.initUnwrapCipher(kek, getKekCipherParams(keyAlias), keyProtectionSpec.getCipherTransformation());
            fingerprintCallback = new FingerprintCallback(keyAlias, fingerprintParams.getAuthenticationCallback(), fingerprintParams.getListener(), false);
        }

        fingerprintManagerCompat.authenticate(new FingerprintManagerCompat.CryptoObject(kekCipher), 0, fingerprintParams.getCancellationSignal(), fingerprintCallback, fingerprintParams.getHandler());
    }

    void verify(String keyAlias, UnlockParams params) throws IOException, GeneralSecurityException {
        FingerprintParams fingerprintParams = (FingerprintParams) params;
        FingerprintManagerCompat fingerprintManagerCompat = FingerprintManagerCompat.from(fingerprintParams.getContext());
        checkFingerprintStatus(fingerprintParams, fingerprintManagerCompat);

        FingerprintCallback fingerprintCallback = new FingerprintCallback(keyAlias, fingerprintParams.getAuthenticationCallback(), fingerprintParams.getListener(), false);

        fingerprintManagerCompat.authenticate(null, 0, fingerprintParams.getCancellationSignal(), fingerprintCallback, fingerprintParams.getHandler());
    }

    private void checkFingerprintStatus(FingerprintParams fingerprintParams, FingerprintManagerCompat fingerprintManagerCompat) throws FingerprintException {
        KeyguardManager keyguardManager = (KeyguardManager) fingerprintParams.getContext().getSystemService(Context.KEYGUARD_SERVICE);

        if (!fingerprintManagerCompat.isHardwareDetected()) {
            throw new FingerprintException(Type.NoHardware, -1, "No fingerprint sensor on device");
        }
        if (!keyguardManager.isDeviceSecure()) {
            throw new FingerprintException(Type.NoLockscreen, -1, "User has not set up a lockscreen");
        }
        if (!fingerprintManagerCompat.hasEnrolledFingerprints()) {
            throw new FingerprintException(Type.NoFingerprint, -1, "User has not enrolled a fingerprint");
        }
    }

    public class FingerprintEditor extends BaseEditor {

        public FingerprintEditor(String keyAlias) {
            super(keyAlias);
        }

        public void unlock(@NonNull Context context, @NonNull CancellationSignal cancellationSignal, @NonNull FingerprintManagerCompat.AuthenticationCallback authenticationCallback, @Nullable Handler handler) throws IOException, GeneralSecurityException {
            FingerprintWrapper.this.unlock(keyAlias, new FingerprintParams(context, null, cancellationSignal, authenticationCallback, handler));
        }

        public void unlock(@NonNull Context context, @NonNull CancellationSignal cancellationSignal, @NonNull Listener listener, @Nullable Handler handler) {
            try {
                FingerprintWrapper.this.unlock(keyAlias, new FingerprintParams(context, listener, cancellationSignal, null, handler));
            } catch (GeneralSecurityException | IOException e) {
                listener.onError(e);
            }
        }

        public void verify(@NonNull Context context, @NonNull CancellationSignal cancellationSignal, @NonNull FingerprintManagerCompat.AuthenticationCallback authenticationCallback, @Nullable Handler handler) throws IOException, GeneralSecurityException {
            FingerprintWrapper.this.verify(keyAlias, new FingerprintParams(context, null, cancellationSignal, authenticationCallback, handler));
        }

        public void verify(@NonNull Context context, @NonNull CancellationSignal cancellationSignal, @NonNull Listener listener, @Nullable Handler handler) {
            try {
                FingerprintWrapper.this.verify(keyAlias, new FingerprintParams(context, listener, cancellationSignal, null, handler));
            } catch (GeneralSecurityException | IOException e) {
                listener.onError(e);
            }
        }
    }

    class FingerprintCallback extends FingerprintManagerCompat.AuthenticationCallback {

        private final String keyAlias;
        private final FingerprintManagerCompat.AuthenticationCallback wrappedCallback;
        private Listener listener;
        private final boolean firstUnlock;

        public FingerprintCallback(String keyAlias, @Nullable FingerprintManagerCompat.AuthenticationCallback wrappedCallback, @Nullable Listener listener, boolean firstUnlock) {
            this.keyAlias = keyAlias;
            this.wrappedCallback = wrappedCallback;
            this.listener = listener;
            this.firstUnlock = firstUnlock;
        }

        @Override
        public void onAuthenticationError(int errMsgId, CharSequence errString) {
            if (wrappedCallback != null) {
                wrappedCallback.onAuthenticationError(errMsgId, errString);
            }<commit_msg>Implement FingerprintEditor.isInitialized to check if  the keystore and intermediate keys have been generated<commit_after>/*
 *    Copyright 2016 <NAME>
 *
 *    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 com.github.cjnosal.secret_storage.keymanager;

import android.annotation.TargetApi;
import android.app.KeyguardManager;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.hardware.fingerprint.FingerprintManagerCompat;
import android.support.v4.os.CancellationSignal;

import com.github.cjnosal.secret_storage.keymanager.strategy.cipher.CipherSpec;
import com.github.cjnosal.secret_storage.keymanager.strategy.keygen.KeyGenSpec;
import com.github.cjnosal.secret_storage.storage.DataStorage;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.Key;

import javax.crypto.Cipher;

@TargetApi(Build.VERSION_CODES.M)
public class FingerprintWrapper extends KeyStoreWrapper {

    public FingerprintWrapper(CipherSpec keyProtectionSpec, KeyGenSpec keyGenSpec, DataStorage configStorage, DataStorage keyStorage) {
        super(keyProtectionSpec, keyGenSpec, configStorage, keyStorage);
    }

    @Override
    public Editor getEditor(String storeId) {
        return new FingerprintEditor(storeId);
    }

    @Override
    void unlock(String keyAlias, UnlockParams params) throws IOException, GeneralSecurityException {
        FingerprintParams fingerprintParams = (FingerprintParams) params;
        FingerprintManagerCompat fingerprintManagerCompat = FingerprintManagerCompat.from(fingerprintParams.getContext());
        checkFingerprintStatus(fingerprintParams, fingerprintManagerCompat);

        FingerprintCallback fingerprintCallback;
        Cipher kekCipher;
        String storageField = getStorageField(keyAlias, ENCRYPTION_KEY);
        if (!kekExists(keyAlias)) {
            Key kek = androidCrypto.generateSecretKey(keyGenSpec.getKeygenAlgorithm(), getKeyGenParameterSpec(storageField));
            kekCipher = keyWrap.initWrapCipher(kek, keyProtectionSpec.getCipherTransformation(), keyProtectionSpec.getParamsAlgorithm());
            fingerprintCallback = new FingerprintCallback(keyAlias, fingerprintParams.getAuthenticationCallback(), fingerprintParams.getListener(), true);
        } else {
            Key kek = androidCrypto.loadSecretKey(storageField);
            kekCipher = keyWrap.initUnwrapCipher(kek, getKekCipherParams(keyAlias), keyProtectionSpec.getCipherTransformation());
            fingerprintCallback = new FingerprintCallback(keyAlias, fingerprintParams.getAuthenticationCallback(), fingerprintParams.getListener(), false);
        }

        fingerprintManagerCompat.authenticate(new FingerprintManagerCompat.CryptoObject(kekCipher), 0, fingerprintParams.getCancellationSignal(), fingerprintCallback, fingerprintParams.getHandler());
    }

    void verify(String keyAlias, UnlockParams params) throws IOException, GeneralSecurityException {
        FingerprintParams fingerprintParams = (FingerprintParams) params;
        FingerprintManagerCompat fingerprintManagerCompat = FingerprintManagerCompat.from(fingerprintParams.getContext());
        checkFingerprintStatus(fingerprintParams, fingerprintManagerCompat);

        FingerprintCallback fingerprintCallback = new FingerprintCallback(keyAlias, fingerprintParams.getAuthenticationCallback(), fingerprintParams.getListener(), false);

        fingerprintManagerCompat.authenticate(null, 0, fingerprintParams.getCancellationSignal(), fingerprintCallback, fingerprintParams.getHandler());
    }

    private void checkFingerprintStatus(FingerprintParams fingerprintParams, FingerprintManagerCompat fingerprintManagerCompat) throws FingerprintException {
        KeyguardManager keyguardManager = (KeyguardManager) fingerprintParams.getContext().getSystemService(Context.KEYGUARD_SERVICE);

        if (!fingerprintManagerCompat.isHardwareDetected()) {
            throw new FingerprintException(Type.NoHardware, -1, "No fingerprint sensor on device");
        }
        if (!keyguardManager.isDeviceSecure()) {
            throw new FingerprintException(Type.NoLockscreen, -1, "User has not set up a lockscreen");
        }
        if (!fingerprintManagerCompat.hasEnrolledFingerprints()) {
            throw new FingerprintException(Type.NoFingerprint, -1, "User has not enrolled a fingerprint");
        }
    }

    public class FingerprintEditor extends BaseEditor {

        public FingerprintEditor(String keyAlias) {
            super(keyAlias);
        }

        public void unlock(@NonNull Context context, @NonNull CancellationSignal cancellationSignal, @NonNull FingerprintManagerCompat.AuthenticationCallback authenticationCallback, @Nullable Handler handler) throws IOException, GeneralSecurityException {
            FingerprintWrapper.this.unlock(keyAlias, new FingerprintParams(context, null, cancellationSignal, authenticationCallback, handler));
        }

        public void unlock(@NonNull Context context, @NonNull CancellationSignal cancellationSignal, @NonNull Listener listener, @Nullable Handler handler) {
            try {
                FingerprintWrapper.this.unlock(keyAlias, new FingerprintParams(context, listener, cancellationSignal, null, handler));
            } catch (GeneralSecurityException | IOException e) {
                listener.onError(e);
            }
        }

        public void verify(@NonNull Context context, @NonNull CancellationSignal cancellationSignal, @NonNull FingerprintManagerCompat.AuthenticationCallback authenticationCallback, @Nullable Handler handler) throws IOException, GeneralSecurityException {
            FingerprintWrapper.this.verify(keyAlias, new FingerprintParams(context, null, cancellationSignal, authenticationCallback, handler));
        }

        public void verify(@NonNull Context context, @NonNull CancellationSignal cancellationSignal, @NonNull Listener listener, @Nullable Handler handler) {
            try {
                FingerprintWrapper.this.verify(keyAlias, new FingerprintParams(context, listener, cancellationSignal, null, handler));
            } catch (GeneralSecurityException | IOException e) {
                listener.onError(e);
            }
        }

        public boolean isInitialized() {
            return kekExists(keyAlias);
        }
    }

    class FingerprintCallback extends FingerprintManagerCompat.AuthenticationCallback {

        private final String keyAlias;
        private final FingerprintManagerCompat.AuthenticationCallback wrappedCallback;
        private Listener listener;
        private final boolean firstUnlock;

        public FingerprintCallback(String keyAlias, @Nullable FingerprintManagerCompat.AuthenticationCallback wrappedCallback, @Nullable Listener listener, boolean firstUnlock) {
            this.keyAlias = keyAlias;
            this.wrappedCallback = wrappedCallback;
            this.listener = listener;
            this.firstUnlock = firstUnlock;
        }

        @Override
        public void onAuthenticationError(int errMsgId, CharSequence errString) {
            if (wrappedCallback != null) {
                wrappedCallback.onAuthenticationError(errMsgId, errString);
            }
{'fraction_non_alphanumeric': 0.07209049658081422, 'fraction_numerical': 0.0019173004409791014, 'mean_word_length': 4.149062191510366, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3145530', 'n_tokens_mistral': 3807, 'n_tokens_neox': 3705, 'n_words': 907}