0
|
1 /*
|
|
2 * ConnectBot: simple, powerful, open-source SSH client for Android
|
|
3 * Copyright 2007 Kenny Root, Jeffrey Sharkey
|
|
4 *
|
|
5 * Licensed under the Apache License, Version 2.0 (the "License");
|
|
6 * you may not use this file except in compliance with the License.
|
|
7 * You may obtain a copy of the License at
|
|
8 *
|
|
9 * http://www.apache.org/licenses/LICENSE-2.0
|
|
10 *
|
|
11 * Unless required by applicable law or agreed to in writing, software
|
|
12 * distributed under the License is distributed on an "AS IS" BASIS,
|
|
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14 * See the License for the specific language governing permissions and
|
|
15 * limitations under the License.
|
|
16 */
|
|
17
|
|
18 package com.five_ten_sg.connectbot.util;
|
|
19
|
|
20 import java.util.Vector;
|
|
21
|
|
22 import com.five_ten_sg.connectbot.R;
|
|
23 import android.content.Context;
|
|
24 import android.graphics.Canvas;
|
|
25 import android.graphics.Color;
|
|
26 import android.graphics.Paint;
|
|
27 import android.graphics.Paint.FontMetrics;
|
|
28 import android.graphics.Typeface;
|
|
29 import android.util.AttributeSet;
|
|
30 import android.view.MotionEvent;
|
|
31 import android.view.View;
|
|
32
|
|
33 public class EntropyView extends View {
|
|
34 private static final int SHA1_MAX_BYTES = 20;
|
|
35 private static final int MILLIS_BETWEEN_INPUTS = 50;
|
|
36
|
|
37 private Paint mPaint;
|
|
38 private FontMetrics mFontMetrics;
|
|
39 private boolean mFlipFlop;
|
|
40 private long mLastTime;
|
|
41 private Vector<OnEntropyGatheredListener> listeners;
|
|
42
|
|
43 private byte[] mEntropy;
|
|
44 private int mEntropyByteIndex;
|
|
45 private int mEntropyBitIndex;
|
|
46
|
|
47 private int splitText = 0;
|
|
48
|
|
49 private float lastX = 0.0f, lastY = 0.0f;
|
|
50
|
|
51 public EntropyView(Context context) {
|
|
52 super(context);
|
|
53 setUpEntropy();
|
|
54 }
|
|
55
|
|
56 public EntropyView(Context context, AttributeSet attrs) {
|
|
57 super(context, attrs);
|
|
58 setUpEntropy();
|
|
59 }
|
|
60
|
|
61 private void setUpEntropy() {
|
|
62 mPaint = new Paint();
|
|
63 mPaint.setAntiAlias(true);
|
|
64 mPaint.setTypeface(Typeface.DEFAULT);
|
|
65 mPaint.setTextAlign(Paint.Align.CENTER);
|
|
66 mPaint.setTextSize(16);
|
|
67 mPaint.setColor(Color.WHITE);
|
|
68 mFontMetrics = mPaint.getFontMetrics();
|
|
69 mEntropy = new byte[SHA1_MAX_BYTES];
|
|
70 mEntropyByteIndex = 0;
|
|
71 mEntropyBitIndex = 0;
|
|
72 listeners = new Vector<OnEntropyGatheredListener>();
|
|
73 }
|
|
74
|
|
75 public void addOnEntropyGatheredListener(OnEntropyGatheredListener listener) {
|
|
76 listeners.add(listener);
|
|
77 }
|
|
78
|
|
79 public void removeOnEntropyGatheredListener(OnEntropyGatheredListener listener) {
|
|
80 listeners.remove(listener);
|
|
81 }
|
|
82
|
|
83 @Override
|
|
84 public void onDraw(Canvas c) {
|
|
85 String prompt = String.format(getResources().getString(R.string.pubkey_touch_prompt),
|
|
86 (int)(100.0 * (mEntropyByteIndex / 20.0)) + (int)(5.0 * (mEntropyBitIndex / 8.0)));
|
|
87
|
|
88 if (splitText > 0 ||
|
|
89 mPaint.measureText(prompt) > (getWidth() * 0.8)) {
|
|
90 if (splitText == 0)
|
|
91 splitText = prompt.indexOf(" ", prompt.length() / 2);
|
|
92
|
|
93 c.drawText(prompt.substring(0, splitText),
|
|
94 getWidth() / 2.0f,
|
|
95 getHeight() / 2.0f + (mPaint.ascent() + mPaint.descent()),
|
|
96 mPaint);
|
|
97 c.drawText(prompt.substring(splitText),
|
|
98 getWidth() / 2.0f,
|
|
99 getHeight() / 2.0f - (mPaint.ascent() + mPaint.descent()),
|
|
100 mPaint);
|
|
101 }
|
|
102 else {
|
|
103 c.drawText(prompt,
|
|
104 getWidth() / 2.0f,
|
|
105 getHeight() / 2.0f - (mFontMetrics.ascent + mFontMetrics.descent) / 2,
|
|
106 mPaint);
|
|
107 }
|
|
108 }
|
|
109
|
|
110 @Override
|
|
111 public boolean onTouchEvent(MotionEvent event) {
|
|
112 if (mEntropyByteIndex >= SHA1_MAX_BYTES
|
|
113 || lastX == event.getX()
|
|
114 || lastY == event.getY())
|
|
115 return true;
|
|
116
|
|
117 // Only get entropy every 200 milliseconds to ensure the user has moved around.
|
|
118 long now = System.currentTimeMillis();
|
|
119
|
|
120 if ((now - mLastTime) < MILLIS_BETWEEN_INPUTS)
|
|
121 return true;
|
|
122 else
|
|
123 mLastTime = now;
|
|
124
|
|
125 byte input;
|
|
126 lastX = event.getX();
|
|
127 lastY = event.getY();
|
|
128
|
|
129 // Get the lowest 4 bits of each X, Y input and concat to the entropy-gathering
|
|
130 // string.
|
|
131 if (mFlipFlop)
|
|
132 input = (byte)((((int)lastX & 0x0F) << 4) | ((int)lastY & 0x0F));
|
|
133 else
|
|
134 input = (byte)((((int)lastY & 0x0F) << 4) | ((int)lastX & 0x0F));
|
|
135
|
|
136 mFlipFlop = !mFlipFlop;
|
|
137
|
|
138 for (int i = 0; i < 4 && mEntropyByteIndex < SHA1_MAX_BYTES; i++) {
|
|
139 if ((input & 0x3) == 0x1) {
|
|
140 mEntropy[mEntropyByteIndex] <<= 1;
|
|
141 mEntropy[mEntropyByteIndex] |= 1;
|
|
142 mEntropyBitIndex++;
|
|
143 input >>= 2;
|
|
144 }
|
|
145 else if ((input & 0x3) == 0x2) {
|
|
146 mEntropy[mEntropyByteIndex] <<= 1;
|
|
147 mEntropyBitIndex++;
|
|
148 input >>= 2;
|
|
149 }
|
|
150
|
|
151 if (mEntropyBitIndex >= 8) {
|
|
152 mEntropyBitIndex = 0;
|
|
153 mEntropyByteIndex++;
|
|
154 }
|
|
155 }
|
|
156
|
|
157 // SHA1PRNG only keeps 160 bits of entropy.
|
|
158 if (mEntropyByteIndex >= SHA1_MAX_BYTES) {
|
|
159 for (OnEntropyGatheredListener listener : listeners) {
|
|
160 listener.onEntropyGathered(mEntropy);
|
|
161 }
|
|
162 }
|
|
163
|
|
164 invalidate();
|
|
165 return true;
|
|
166 }
|
|
167 }
|