1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. 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 19 package org.apache.harmony.security.provider.crypto; 20 21 22 import java.security.DigestException; 23 import java.security.MessageDigestSpi; 24 import java.util.Arrays; 25 26 27 /** 28 * This class extends the MessageDigestSpi class implementing all its abstract methods; 29 * it overrides the "Object clone()" and "int engineGetDigestLength()" methods. <BR> 30 * The class implements the Cloneable interface. 31 */ 32 33 34 public class SHA1_MessageDigestImpl extends MessageDigestSpi implements Cloneable, SHA1_Data { 35 private int[] buffer; // buffer has the following structure: 36 // - 0-16 - frame for accumulating a message 37 // - 17-79 - for SHA1Impl methods 38 // - 80 - unused 39 // - 81 - to store length of the message 40 // - 82-86 - frame for current message digest 41 42 private byte[] oneByte; // one byte buffer needed to use in engineUpdate(byte) 43 // having buffer as private field is just optimization 44 45 private long messageLength; // total length of bytes supplied by user 46 47 48 /** 49 * The constructor creates needed buffers and sets the engine at initial state 50 */ 51 public SHA1_MessageDigestImpl() { 52 53 // BYTES_OFFSET +6 is minimal length required by methods in SHA1Impl 54 buffer = new int[BYTES_OFFSET +6]; 55 56 oneByte = new byte[1]; 57 58 engineReset(); 59 } 60 61 62 /** 63 * The method performs final actions and invokes the "computeHash(int[])" method. 64 * In case if there is no enough words in current frame 65 * after processing its data, extra frame is prepared and 66 * the "computeHash(int[])" method is invoked second time. <BR> 67 * 68 * After processing, the method resets engine's state 69 * 70 * @param 71 * digest - byte array 72 * @param 73 * offset - offset in digest 74 */ 75 private void processDigest(byte[] digest, int offset) { 76 77 int i, j; // implementation variables 78 int lastWord; // 79 80 long nBits = messageLength <<3 ; // length has to be calculated before padding 81 82 engineUpdate( (byte) 0x80 ); // beginning byte in padding 83 84 i = 0; // i contains number of beginning word for following loop 85 86 lastWord = (buffer[BYTES_OFFSET] + 3)>>2 ; // computing of # of full words by shifting 87 // # of bytes 88 89 // possible cases: 90 // 91 // - buffer[BYTES_OFFSET] == 0 - buffer frame is empty, 92 // padding byte was 64th in previous frame 93 // current frame should contain only message's length 94 // 95 // - lastWord < 14 - two last, these are 14 & 15, words in 16 word frame are free; 96 // no extra frame needed 97 // - lastWord = 14 - only one last, namely 15-th, word in frame doesn't contain bytes; 98 // extra frame is needed 99 // - lastWord > 14 - last word in frame is not full; 100 // extra frame is needed 101 102 if ( buffer[BYTES_OFFSET] != 0 ) { 103 104 if ( lastWord < 15 ) { 105 i = lastWord; 106 } else { 107 if ( lastWord == 15 ) { 108 buffer[15] = 0; // last word in frame is set to "0" 109 } 110 SHA1Impl.computeHash(buffer); 111 i = 0; 112 } 113 } 114 Arrays.fill(buffer, i, 14, 0); 115 116 buffer[14] = (int)( nBits >>>32 ); 117 buffer[15] = (int)( nBits & 0xFFFFFFFF ); 118 SHA1Impl.computeHash(buffer); 119 120 // converting 5-word frame into 20 bytes 121 j = offset; 122 for ( i = HASH_OFFSET; i < HASH_OFFSET +5; i++ ) { 123 int k = buffer[i]; 124 digest[j ] = (byte) ( k >>>24 ); // getting first byte from left 125 digest[j+1] = (byte) ( k >>>16 ); // getting second byte from left 126 digest[j+2] = (byte) ( k >>> 8 ); // getting third byte from left 127 digest[j+3] = (byte) ( k ); // getting fourth byte from left 128 j += 4; 129 } 130 131 engineReset(); 132 } 133 134 // methods specified in java.security.MessageDigestSpi 135 136 /** 137 * Returns a "deep" copy of this SHA1MDImpl object. <BR> 138 * 139 * The method overrides "clone()" in class Object. <BR> 140 * 141 * @return 142 * a clone of this object 143 */ 144 public Object clone() throws CloneNotSupportedException { 145 SHA1_MessageDigestImpl cloneObj = (SHA1_MessageDigestImpl) super.clone(); 146 cloneObj.buffer = buffer.clone(); 147 cloneObj.oneByte = oneByte.clone(); 148 return cloneObj; 149 } 150 151 152 /** 153 * Computes a message digest value. <BR> 154 * 155 * The method resets the engine. <BR> 156 * 157 * The method overrides "engineDigest()" in class MessageDigestSpi. <BR> 158 * 159 * @return 160 * byte array containing message digest value 161 */ 162 protected byte[] engineDigest() { 163 byte[] hash = new byte[DIGEST_LENGTH]; 164 processDigest(hash, 0); 165 return hash; 166 } 167 168 169 /** 170 * Computes message digest value. 171 * Upon return, the value is stored in "buf" buffer beginning "offset" byte. <BR> 172 * 173 * The method resets the engine. <BR> 174 * 175 * The method overrides "engineDigest(byte[],int,int) in class MessageDigestSpi. 176 * 177 * @param 178 * buf byte array to store a message digest returned 179 * @param 180 * offset a position in the array for first byte of the message digest 181 * @param 182 * len number of bytes within buffer allotted for the message digest; 183 * as this implementation doesn't provide partial digests, 184 * len should be >= 20, DigestException is thrown otherwise 185 * @return 186 * the length of the message digest stored in the "buf" buffer; 187 * in this implementation the length=20 188 * 189 * @throws IllegalArgumentException 190 * if null is passed to the "buf" argument <BR> 191 * if offset + len > buf.length <BR> 192 * if offset > buf.length or len > buf.length 193 * 194 * @throws DigestException 195 * if len < 20 196 * 197 * @throws ArrayIndexOutOfBoundsException 198 * if offset < 0 199 */ 200 protected int engineDigest(byte[] buf, int offset, int len) throws DigestException { 201 if (buf == null) { 202 throw new IllegalArgumentException("buf == null"); 203 } 204 if (offset > buf.length || len > buf.length || (len + offset) > buf.length) { 205 throw new IllegalArgumentException(); 206 } 207 if (len < DIGEST_LENGTH) { 208 throw new DigestException("len < DIGEST_LENGTH"); 209 } 210 if (offset < 0) { 211 throw new ArrayIndexOutOfBoundsException(offset); 212 } 213 214 processDigest(buf, offset); 215 216 return DIGEST_LENGTH; 217 } 218 219 220 /** 221 * Returns a message digest length. <BR> 222 * 223 * The method overrides "engineGetDigestLength()" in class MessageDigestSpi. <BR> 224 * 225 * @return 226 * total length of current message digest as an int value 227 */ 228 protected int engineGetDigestLength() { 229 return DIGEST_LENGTH; 230 } 231 232 233 /** 234 * Resets the engine. <BR> 235 * 236 * The method overrides "engineReset()" in class MessageDigestSpi. <BR> 237 */ 238 protected void engineReset() { 239 240 messageLength = 0; 241 242 buffer[BYTES_OFFSET] = 0; 243 buffer[HASH_OFFSET ] = H0; 244 buffer[HASH_OFFSET +1] = H1; 245 buffer[HASH_OFFSET +2] = H2; 246 buffer[HASH_OFFSET +3] = H3; 247 buffer[HASH_OFFSET +4] = H4; 248 } 249 250 251 /** 252 * Supplements a byte to current message. <BR> 253 * 254 * The method overrides "engineUpdate(byte)" in class MessageDigestSpi. <BR> 255 * 256 * @param 257 * input byte to add to current message 258 */ 259 protected void engineUpdate(byte input) { 260 261 oneByte[0] = input; 262 SHA1Impl.updateHash( buffer, oneByte, 0, 0 ); 263 messageLength++; 264 } 265 266 267 /** 268 * Updates current message. <BR> 269 * 270 * The method overrides "engineUpdate(byte[],int,int)" in class MessageDigestSpi. <BR> 271 * 272 * The method silently returns if "len" <= 0. 273 * 274 * @param 275 * input a byte array 276 * @param 277 * offset a number of first byte in the "input" array to use for updating 278 * @param 279 * len a number of bytes to use 280 * 281 * @throws NullPointerException 282 * if null is passed to the "buf" argument 283 * 284 * @throws IllegalArgumentException 285 * if offset > buf.length or len > buf.length or 286 * (len + offset) > buf.length 287 * @throws ArrayIndexOutOfBoundsException 288 * offset < 0 289 */ 290 protected void engineUpdate(byte[] input, int offset, int len) { 291 if (input == null) { 292 throw new IllegalArgumentException("input == null"); 293 } 294 if (len <= 0) { 295 return; 296 } 297 if (offset < 0) { 298 throw new ArrayIndexOutOfBoundsException(offset); 299 } 300 if (offset > input.length || len > input.length || (len + offset) > input.length) { 301 throw new IllegalArgumentException(); 302 } 303 304 SHA1Impl.updateHash(buffer, input, offset, offset + len -1 ); 305 messageLength += len; 306 } 307 308 } 309