diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org')
4 files changed, 496 insertions, 0 deletions
| diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConstants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConstants.java index ba2a54b1a..90991ba15 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConstants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConstants.java @@ -29,6 +29,8 @@ public class PgpConstants {      public static ArrayList<Integer> sPreferredHashAlgorithms = new ArrayList<>();      public static ArrayList<Integer> sPreferredCompressionAlgorithms = new ArrayList<>(); +    // TODO: use hashmaps for contains in O(1) and intersections! +      /*       * Most preferred is first       * These arrays are written as preferred algorithms into the keys on creation. diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java index ac74e87ed..115614808 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java @@ -33,6 +33,7 @@ import android.widget.EditText;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; +import org.sufficientlysecure.keychain.ui.widget.passwordstrengthindicator.PasswordStrengthView;  import org.sufficientlysecure.keychain.util.ContactHelper;  import java.util.regex.Matcher; @@ -41,6 +42,7 @@ public class CreateKeyInputFragment extends Fragment {      CreateKeyActivity mCreateKeyActivity; +    PasswordStrengthView mPassphraseStrengthView;      AutoCompleteTextView mNameEdit;      AutoCompleteTextView mEmailEdit;      EditText mPassphraseEdit; @@ -69,6 +71,8 @@ public class CreateKeyInputFragment extends Fragment {      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {          View view = inflater.inflate(R.layout.create_key_input_fragment, container, false); +        mPassphraseStrengthView = (PasswordStrengthView) view.findViewById(R.id +                .create_key_passphrase_strength);          mNameEdit = (AutoCompleteTextView) view.findViewById(R.id.create_key_name);          mEmailEdit = (AutoCompleteTextView) view.findViewById(R.id.create_key_email);          mPassphraseEdit = (EditText) view.findViewById(R.id.create_key_passphrase); @@ -131,6 +135,28 @@ public class CreateKeyInputFragment extends Fragment {                          )          ); +        // Edit text padding doesn't work via xml (http://code.google.com/p/android/issues/detail?id=77982) +        // so we set the right padding programmatically. +        mPassphraseEdit.setPadding(mPassphraseEdit.getPaddingLeft(), +                mPassphraseEdit.getPaddingTop(), +                (int) (56 * getResources().getDisplayMetrics().density), +                mPassphraseEdit.getPaddingBottom()); +        mPassphraseEdit.addTextChangedListener(new TextWatcher() { +            @Override +            public void beforeTextChanged(CharSequence s, int start, int count, int after) { +            } + +            @Override +            public void onTextChanged(CharSequence s, int start, int before, int count) { +            } + +            @Override +            public void afterTextChanged(Editable editable) { +                String passphrase = editable.toString(); +                mPassphraseStrengthView.setPassword(passphrase); +            } +        }); +          mCreateButton.setOnClickListener(new View.OnClickListener() {              @Override              public void onClick(View v) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/passwordstrengthindicator/PasswordStrengthBarView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/passwordstrengthindicator/PasswordStrengthBarView.java new file mode 100644 index 000000000..c1db823b3 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/passwordstrengthindicator/PasswordStrengthBarView.java @@ -0,0 +1,118 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Matt Allen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.sufficientlysecure.keychain.ui.widget.passwordstrengthindicator; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.util.AttributeSet; + +/** + * Created by matt on 04/07/2014. + * https://github.com/matt-allen/android-password-strength-indicator + * + */ +public class PasswordStrengthBarView extends PasswordStrengthView { + +    public PasswordStrengthBarView(Context context, AttributeSet attrs) { +        super(context, attrs); +        mMinHeight = 80; +        mMinWidth = 300; +    } + +    @Override +    protected void onDraw(Canvas canvas) { +        super.onDraw(canvas); +        generateIndicatorColor(); +        // Default to full width +        int indWidth = mIndicatorWidth; +        // If score, leave it as full - can cause it to become +        // less than full width in this calculation +        if (mCurrentScore < 20) indWidth = (mIndicatorWidth / 20) * mCurrentScore; +        // Draw indicator +        canvas.drawRect( +                getPaddingLeft(), +                getPaddingTop(), +                indWidth, +                mIndicatorHeight, +                mIndicatorPaint +        ); +        // Draw guides if true +        if (mShowGuides) { +            // TODO: Try and do this with a loop, for efficiency +            // Draw bottom guide border +            float positionY = getHeight()-getPaddingBottom()-getPaddingTop(); +            float notchHeight = (float)(positionY * 0.8); +            canvas.drawLine( +                    getPaddingLeft(), +                    positionY, +                    getWidth()-getPaddingRight(), +                    positionY, +                    mGuidePaint); +            // Show left-most notch +            canvas.drawLine( +                    getPaddingLeft(), +                    positionY, +                    getPaddingLeft(), +                    notchHeight, +                    mGuidePaint +            ); +            // Show middle-left notch +            canvas.drawLine( +                    (float)(mIndicatorWidth*0.25)+getPaddingLeft(), +                    positionY, +                    (float)(mIndicatorWidth*0.25)+getPaddingLeft(), +                    notchHeight, +                    mGuidePaint +            ); +            // Show the middle notch +            canvas.drawLine( +                    (float)(mIndicatorWidth*0.5)+getPaddingLeft(), +                    positionY, +                    (float)(mIndicatorWidth*0.5)+getPaddingLeft(), +                    notchHeight, +                    mGuidePaint +            ); +            // Show the middle-right notch +            canvas.drawLine( +                    (float)(mIndicatorWidth*0.75)+getPaddingLeft(), +                    positionY, +                    (float)(mIndicatorWidth*0.75)+getPaddingLeft(), +                    notchHeight, +                    mGuidePaint +            ); +            // Show the right-most notch +            canvas.drawLine( +                    mIndicatorWidth+getPaddingLeft(), +                    positionY, +                    mIndicatorWidth+getPaddingLeft(), +                    notchHeight, +                    mGuidePaint +            ); +        } +    } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/passwordstrengthindicator/PasswordStrengthView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/passwordstrengthindicator/PasswordStrengthView.java new file mode 100644 index 000000000..47e6d9527 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/passwordstrengthindicator/PasswordStrengthView.java @@ -0,0 +1,350 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Matt Allen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.sufficientlysecure.keychain.ui.widget.passwordstrengthindicator; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.view.View; + +import org.sufficientlysecure.keychain.R; + +/** + * Created by Matt Allen + * 01/07/14 + * http://www.mattallensoftware.co.uk + * mattallen092@gmail.com + * + *  https://github.com/matt-allen/android-password-strength-indicator + * + * <p> + * This View is designed to indicate how secure a user-entered password is in a visual way to + * relay to the user if they need to make it stronger. The strength of the password can be set + * at creation (or after) which will decide whether their password is strong enough. + * </p> + * + * <p> + * The password strength is decided by an index of 20. The minimum score needed to pass is 10 + * which means the String has met the conditions imposed by the strength test, but can be improved. + * If the password scores 10-19 it is considered weak, and only if it scores 20 will it be + * considered strong. + * </p> + */ +public class PasswordStrengthView extends View { + +    protected static final int COLOR_FAIL = Color.parseColor("#e74c3c"); +    protected static final int COLOR_WEAK = Color.parseColor("#e67e22"); +    protected static final int COLOR_STRONG = Color.parseColor("#2ecc71"); + +    protected int mMinWidth; +    protected int mMinHeight; + +    protected Paint mIndicatorPaint; +    protected Paint mGuidePaint; + +    protected int mIndicatorHeight; +    protected int mIndicatorWidth; +    protected int mCurrentScore; + +    protected int mColorFail; +    protected int mColorWeak; +    protected int mColorStrong; + +    protected boolean mShowGuides = true; + +    /** +     * Used to define that the indicator should only be looking +     * for a weak password. The bare minimum is used here to let +     * the user continue. +     */ +    public static final int STRENGTH_WEAK = 0; + +    /** +     * A fairly strict rule for generating a password. It encourages a password that is +     * less easy to crack. +     */ +    public static final int STRENGTH_MEDIUM = 1; + +    /** +     * A strong algorithm that encourages very strong passwords that should be fairly long, with +     * non-alphanumeric, numbers, and upper case. +     */ +    public static final int STRENGTH_STRONG = 2; + +    private int mStrengthRequirement = -1; +    protected String mPassword; + +    public PasswordStrengthView(Context context, AttributeSet attrs) { +        super(context, attrs); +        TypedArray style = context.getTheme().obtainStyledAttributes( +                attrs, +                R.styleable.PasswordStrengthView, +                0, 0); + +        try { +            mStrengthRequirement = style.getInteger(R.styleable.PasswordStrengthView_strength, +                    STRENGTH_MEDIUM); +            mShowGuides = style.getBoolean(R.styleable.PasswordStrengthView_showGuides, true); +            mColorFail = style.getColor(R.styleable.PasswordStrengthView_color_fail, COLOR_FAIL); +            mColorWeak = style.getColor(R.styleable.PasswordStrengthView_color_weak, COLOR_WEAK); +            mColorStrong = style.getColor(R.styleable.PasswordStrengthView_color_strong, +                    COLOR_STRONG); +        } catch (Exception e){ +            e.printStackTrace(); +        } +        // Create and style the paint used for drawing the guide on the indicator +        mGuidePaint = new Paint(Paint.ANTI_ALIAS_FLAG); +        mGuidePaint.setStyle(Paint.Style.FILL_AND_STROKE); +        mGuidePaint.setColor(Color.BLACK); +        // Create and style paint for indicator +        mIndicatorPaint = new Paint(Paint.ANTI_ALIAS_FLAG); +        mIndicatorPaint.setStyle(Paint.Style.FILL); +    } + +    /** +     * This view can determine if the password entered by the user is acceptable for +     * use by your use case. This is based on the strength requirement you have set. +     * @return True if requirement has been met +     */ +    public boolean isStrengthRequirementMet() { +        return (mCurrentScore >= 10); +    } + +    /** +     * Change the strength requirement of the password entered by the user. This will also +     * re-check the password already entered against these new requirements. +     * @param requiredStrength Use the public constants of this class to set +     */ +    public void setStrengthRequirement(int requiredStrength) { +        if(requiredStrength >= 0 && requiredStrength <= 2){ +            mStrengthRequirement = requiredStrength; +            if (mPassword != null && mPassword.length() > 0) { +                generatePasswordScore(); +                // Update view with new score +                invalidate(); +                requestLayout(); +            } +        } else { +            throw new IndexOutOfBoundsException("Input out of expected range"); +        } +    } + +    /** +     * Update the password string to check strength of +     * @param passwordString String representation of user-input +     */ +    public void setPassword(String passwordString) { +        if(passwordString != null && passwordString.length() > 0) { +            mPassword = passwordString; +            generatePasswordScore(); +        } else { +            mPassword = ""; +            mCurrentScore = 0; +        } + +        // Update view with new score +        invalidate(); +        requestLayout(); +    } + +    /** +     * Private convenience method for adding to the password score +     * @param score Amount to be added to current score +     */ +    protected void addToPasswordScore(int score) { +        int newScore = mCurrentScore + score; + +        // Limit max score +        if (newScore > 20){ +            mCurrentScore = 20; +        } else { +            mCurrentScore = newScore; +        } +    } + +    /** +     * Call this to determine the current strength requirement set on the algorithm +     * @return Int representation of the current strength set for the indicator +     */ +    public int getStrengthRequirement() { +        return mStrengthRequirement; +    } + +    /** +     * Generate a score based on the password. The password will already need to be stored +     * as a class member before running this. +     */ +    protected void generatePasswordScore() { +        mCurrentScore = 0; +        int upperCase = getUppercaseCount(mPassword); +        int nonAlpha = getNonAlphanumericCount(mPassword); +        int numbers = getNumberCount(mPassword); +        switch (mStrengthRequirement){ +            case STRENGTH_WEAK: +                addToPasswordScore(mPassword.length()*2); +                addToPasswordScore(upperCase*2); +                addToPasswordScore(nonAlpha*2); +                addToPasswordScore(numbers*2); +                break; + +            case STRENGTH_MEDIUM: +                addToPasswordScore(mPassword.length()); +                addToPasswordScore(upperCase); +                addToPasswordScore(nonAlpha*2); +                addToPasswordScore(numbers); +                break; + +            case STRENGTH_STRONG: +                addToPasswordScore(mPassword.length()/2); +                // Cut the score in half to make this a very high requirement +                addToPasswordScore(upperCase); +                addToPasswordScore(nonAlpha); +                addToPasswordScore(numbers); +                break; +        } +    } + +    @Override +    protected void onSizeChanged(int w, int h, int oldW, int oldH) { +        super.onSizeChanged(w, h, oldW, oldH); +        int paddingX = getPaddingLeft(); +        int paddingY = getPaddingTop(); +        mIndicatorHeight = h - paddingY; +        mIndicatorWidth = w - paddingX; +    } + +    /** +     * The standard parts of the onMeasure needed to create the password strength +     * indicator. Subclasses should call super.onMeasure, but also need to set +     * the minimum height and width in the constructor. +     * @param widthMeasureSpec The measurement given by the system +     * @param heightMeasureSpec The measurement given by the system +     */ +    @Override +    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { +        // Set minimum space for the view to do it's thing +        int minW = getPaddingLeft() + getPaddingRight() + mMinWidth; +        int w = resolveSizeAndState(minW, widthMeasureSpec, 1); +        // And give it enough height so it's visible +        int minH = mMinHeight + getPaddingBottom() + getPaddingTop(); +        int h = resolveSizeAndState(minH, heightMeasureSpec, 0); +        // Feed these back into UIKit +        setMeasuredDimension(w, h); +    } + +    /** +     * Set the colour of the indicator {@code Paint} to one that is appropriate +     * for the strength of the password. +     */ +    protected void generateIndicatorColor() { +        int color = mColorFail; +        if (mCurrentScore >= 18) { +            color = mColorStrong; +        } +        else if (mCurrentScore >= 10) { +            color = mColorWeak; +        } +        mIndicatorPaint.setColor(color); +    } + +    /** +     * Quick method to determine how many of the characters in a given string are upper case +     * @param stringToCheck The string to examine +     * @return Number of upper case characters +     */ +    protected int getUppercaseCount(String stringToCheck) { +        int score = 0; +        int loops = stringToCheck.length()-1; +        for (int i=0;i<=loops;i++){ +            if(Character.isUpperCase(stringToCheck.charAt(i))) { +                score++; +            } +        } +        return score; +    } + +    /** +     * A convenience method to determine how many characters in the given String aren't +     * letters or numbers. +     * @param stringToCheck +     * @return Number of characters that aren't numbers or letters +     */ +    protected int getNonAlphanumericCount(String stringToCheck) { +        int score = 0; +        int loops = stringToCheck.length()-1; +        for (int i=0;i<=loops;i++) { +            if(!Character.isLetter(stringToCheck.charAt(i)) && +                    !Character.isDigit(stringToCheck.charAt(i))){ +                score++; +            } +        } +        return score; +    } + +    /** +     * A convenience method for returning the count of numbers in a given String. +     * @param stringToCheck +     * @return The numbers of digits in the String +     */ +    protected int getNumberCount(String stringToCheck) { +        int score = 0; +        int loops = stringToCheck.length()-1; +        for (int i=0;i<=loops;i++) { +            if(Character.isDigit(stringToCheck.charAt(i))) { +                score++; +            } +        } +        return score; +    } + +    /** +     * Set the guides to show on the view.<br /> +     * On the line style, the guides will show underneath<br /> +     * On the rounded style, the guides will be shown on the outer edges.<br /> +     * The view will be redrawn after the method is called. +     * @param showGuides True if you want the guides to be shown +     */ +    public void setShowGuides(boolean showGuides) { +        mShowGuides = showGuides; +        if (mPassword != null && mPassword.length() > 0) { +            generatePasswordScore(); +        } else { +            mCurrentScore = 0; +        } + +        invalidate(); +        requestLayout(); +    } + +    /** +     * Determine whether the view is showing the guides for the password score +     * @return True if the guides are being shown +     */ +    public boolean isShowingGuides() { +        return mShowGuides; +    } +} | 
