//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You 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 mx.utils { import flash.utils.ByteArray; /** * Implementation of SHA-256 hash algorithm as described in * Federal Information Processing Standards Publication 180-2 * at http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public class SHA256 { /** * Identifies this hash is of type "SHA-256". * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public static const TYPE_ID:String = "SHA-256"; private static var k:Array = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2]; /** * Computes the digest of a message using the SHA-256 hash algorithm. * * @param byteArray - the message, may not be null. * * return String - 64 character hexidecimal representation of the digest. * * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public static function computeDigest(byteArray:ByteArray):String { // Preprocessing // 0. Set the ByteArray's position to zero var originalPosition:uint = byteArray.position; byteArray.position = 0; // 1. Pad the message var paddingLength:int = byteArray.length % 64; paddingLength = 64 - paddingLength; if (paddingLength < (1 + 8)) { paddingLength += 64; // need to pad a partial block plus a full block } var messagePadding:Array = new Array(paddingLength); var n:int = (byteArray.length + paddingLength) / 64; // number of message blocks var messageLengthBits:uint = byteArray.length * 8; messagePadding[0] = 128; // put message size in last 32 bits of the message padding var i:int; for (i = 1; i < paddingLength - 8; i++) { messagePadding[i] = 0; } var lastIndex:int = messagePadding.length - 1; // last index of messagePadding for (i = 0; i < 4; i++) { messagePadding[lastIndex - i] = (messageLengthBits >> (i << 3)) & 0xff; } // 2. Set initial hash H(0) var h0:int = 0x6a09e667; var h1:int = 0xbb67ae85; var h2:int = 0x3c6ef372; var h3:int = 0xa54ff53a; var h4:int = 0x510e527f; var h5:int = 0x9b05688c; var h6:int = 0x1f83d9ab; var h7:int = 0x5be0cd19; var a:int; var b:int; var c:int; var d:int; var e:int; var f:int; var g:int; var h:int; // Hash computation // for all message blocks var m:ByteArray = new ByteArray(); // message block; 16 32-bit words or 64 bytes var w:Array = new Array(64); // message schedule, 64 32-bit words var paddingStart:uint = 0; // index to start padding message var paddingSize:uint = 0; // amount of padding to copy to message var j:uint; var t1:int; // temporary storage in hash loop var t2:int; // temporary storage in hash loop var t:uint; var msgIndex:uint; var wt2:int; // w[t - 2] var wt15:int; // w[t -15] //var messageSchTime:int = 0; //var hashTime:int = 0; //var startTime:int; //var endTime:int; for (i = 0; i < n; i++) { // get the next message block of 512 bits or 64 bytes. getMessageBlock(byteArray, m); // append pass to end of last message block if (i == (n - 2) && messagePadding.length > 64) { // pad end of message before last block paddingStart = 64 - (messagePadding.length % 64); paddingSize = 64 - paddingStart; for (j = 0; j < paddingSize; j++) { m[j + paddingStart] = messagePadding[j]; } } else if (i == n - 1) { var prevPaddingSize:int = paddingSize; if (messagePadding.length > 64) { paddingStart = 0; paddingSize = 64; } else { paddingStart = 64 - messagePadding.length; paddingSize = messagePadding.length; } for (j = 0; j < paddingSize; j++) { m[j + paddingStart] = messagePadding[j + prevPaddingSize]; } } // prepare the message schedule, w //startTime= getTimer(); for (t = 0; t < 64; t++) { if (t < 16) { msgIndex = t << 2; w[t] = int((m[msgIndex] << 24) | (m[msgIndex + 1] << 16) | (m[msgIndex + 2] << 8) | m[msgIndex + 3]); } else { // inline functions to boost performance. keep orginal code for reference. // w[t] = divisor1(w[t - 2]) + uint(w[t - 7]) + divisor0(w[t - 15]) + uint(w[t - 16]); wt2 = w[t -2]; wt15 = w[t-15]; w[t] = int(int((((wt2 >>> 17) | (wt2 << 15)) ^ ((wt2 >>> 19) | (wt2 << 13)) ^ (wt2 >>> 10))) + // divisor1(w[t - 2]) int(w[t - 7]) + int((((wt15 >>> 7) | (wt15 << 25)) ^ ((wt15 >>> 18) | (wt15 << 14)) ^ (wt15 >>> 3))) + // divisor0(w[t - 15]) int(w[t - 16])); } } //endTime= getTimer(); //messageSchTime += endTime - startTime; //startTime= getTimer(); a = h0; b = h1; c = h2; d = h3; e = h4; f = h5; g = h6; h = h7; for (t = 0; t < 64; t++) { // inline functions to boost performance. keep orginal code for reference. //t1 = h + sum1(e) + Ch(e, f, g) + uint(k[t]) + uint(w[t]); //t2 = sum0(a) + Maj(a, b, c); t1 = h + int((((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7)))) + // sum1(e) int(((e & f) ^ (~e & g))) + // Ch(e, f, g) int(k[t]) + int(w[t]); t2 = int((((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10)))) + // sum0(a) int(((a & b) ^ (a & c) ^ (b & c))); // Maj(a, b, c) h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2; //trace("t = " + t + " a = " + uint(a).toString(16) + " b = " + uint(b).toString(16) + // " c = " + uint(c).toString(16) + " d = " + uint(d).toString(16) + "\n"); //trace("t = " + t + " e = " + uint(e).toString(16) + " f = " + uint(f).toString(16) + // " g = " + uint(g).toString(16) + " h = " + uint(h).toString(16) + "\n"); } h0 += a; h1 += b; h2 += c; h3 += d; h4 += e; h5 += f; h6 += g; h7 += h; //endTime= getTimer(); //hashTime += endTime - startTime; } // Reset the ByteArray's position to where it was previously byteArray.position = originalPosition; //trace("messageSchTime = " + messageSchTime); //trace("hashTime = " + hashTime); // final digest is h1 | h2 | h3 | h4 | h5 | h6 | h7 // convert H(i) variables to hex strings and concatinate return toHex(h0) + toHex(h1) + toHex(h2) + toHex(h3) + toHex(h4) + toHex(h5) + toHex(h6) + toHex(h7); } /** * get the next n bytes of the message from the byteArray and move it to the message block. * * @param byteArray - message * @param m - message block (output) * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ private static function getMessageBlock(byteArray:ByteArray, m:ByteArray):void { byteArray.readBytes(m, 0, Math.min(byteArray.bytesAvailable, 64)); // for (var i:int; i < length && (i + startingIndex) < byteArray.length; i++) // { // m[i] = byteArray[i + startingIndex]; // } } private static function toHex(n:uint):String { var s:String = n.toString(16); if (s.length < 8) { // add leading zeros var zeros:String = "0"; var count:int = 8 - s.length; for (var i:int = 1; i < count; i++) { zeros = zeros.concat("0"); } return zeros + s; } return s; } // The below functions are defined in Federal Information // Processing Standards Publication 180-2 // at // http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf /* The functions have been inlined to boost performance. They are kept here for reference. private static function Ch(x:uint, y:uint, z:uint):uint { return (x & y) ^ (~x & z); } private static function Maj(x:uint, y:uint, z:uint):uint { return (x & y) ^ (x & z) ^ (y & z); } private static function sum0(x:uint):uint { return ((x >>> 2) | (x << 30)) ^ ((x >>> 13) | (x << 19)) ^ ((x >>> 22) | (x << 10)); //return rotr(2, x) ^ rotr(13, x) ^ rotr(22, x); } private static function sum1(x:uint):uint { return ((x >>> 6) | (x << 26)) ^ ((x >>> 11) | (x << 21)) ^ ((x >>> 25) | (x << 7)); // return rotr(6, x) ^ rotr(11, x) ^ rotr(25, x); } private static function divisor0(x:uint):uint { return ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3); // return rotr(7, x) ^ rotr(18, x) ^ shr(3, x); } private static function divisor1(x:uint):uint { return ((x >>> 17) | (x << 15)) ^ ((x >>> 19) | (x << 13)) ^ (x >>> 10); // return rotr(17, x) ^ rotr(19, x) ^ shr(10, x); } private static function rotr(n:uint, x:uint):uint { return (x >>> n) | (x << 32 - n); } private static function shr(n:uint, x:uint):uint { return x >>> n; } * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ } }