comparison src/com/five_ten_sg/connectbot/util/EntropyView.java @ 0:0ce5cc452d02

initial version
author Carl Byington <carl@five-ten-sg.com>
date Thu, 22 May 2014 10:41:19 -0700
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:0ce5cc452d02
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 }