private IEnumerable <byte> P_hash(byte[] secret, byte[] seed) { var hmac = new HMAC(_digest, secret); var a = seed; while (true) { hmac.Reset(); hmac.Update(a); a = hmac.DigestBuffer(); hmac.Reset(); hmac.Update(a); hmac.Update(seed); var b = hmac.DigestBuffer(); foreach (var x in b) { yield return(x); } } }
public void VerifyHMAC(MessageDigest digest, string[] results) { byte[] hash = HMAC.Digest(digest, key, small_data); string str = BitConverter.ToString(hash); if (str != results[0]) { Console.WriteLine("{0} - Failed to calculate hash on {1}", digest.Name, small_data); Console.WriteLine("got {0} instead of {1}", str, results[0]); } else { Console.WriteLine("{0} - Test 1 passed.", digest.Name); } // Compute the large hash using (HMAC hmac = new HMAC()) { byte[] buf = Encoding.ASCII.GetBytes(new string('a', 1000)); hmac.Init(key, digest); for (int i = 0; i < 1000; i++) { hmac.Update(buf); } hash = hmac.DigestFinal(); // Check for error str = BitConverter.ToString(hash); if (str != results[1]) { Console.WriteLine("{0} - Failed to calculate hash on a*1000", digest.Name); Console.WriteLine("got {0} instead of {1}", str, results[1]); } else { Console.WriteLine("{0} - Test 2 passed.", digest.Name); } } }
/* * This function computes Phash with the specified HMAC * engine, XORing the output with the current contents of * the outBuf[] buffer. */ static void Phash(HMAC hm, byte[] s, int soff, int slen, byte[] bufa, byte[] bufb, byte[] label, byte[] seed, byte[] outBuf, int outOff, int outLen) { /* * Set the key for HMAC. */ hm.SetKey(s, soff, slen); /* * Compute A(1) = HMAC(secret, seed). */ hm.Update(label); hm.Update(seed); hm.DoFinal(bufa, 0); while (outLen > 0) { /* * Next chunk: HMAC(secret, A(i) + label + seed) */ hm.Update(bufa); hm.Update(label); hm.Update(seed); hm.DoFinal(bufb, 0); int clen = Math.Min(hm.MACSize, outLen); for (int i = 0; i < clen; i++) { outBuf[outOff++] ^= bufb[i]; } outLen -= clen; /* * If we are not finished, then compute: * A(i+1) = HMAC(secret, A(i)) */ if (outLen > 0) { hm.Update(bufa); hm.DoFinal(bufa, 0); } } }
void DecryptStream(Stream encryptedStream, Stream output) { using (BinaryReader reader = new BinaryReader(encryptedStream)) using (BinaryWriter writer = new BinaryWriter(output)) { //Read file header byte[] headerNonce; byte[] ciphertextPayload; byte[] mac; byte[] contentKey; byte[] cleartextPayload; headerNonce = reader.ReadBytes(16); ciphertextPayload = reader.ReadBytes(40); mac = reader.ReadBytes(32); HMAC headerHmac = new HMAC(macKey); headerHmac.Update(headerNonce); headerHmac.DoFinal(ciphertextPayload); if (!headerHmac.Hash.SequenceEqual(mac)) { throw new IOException("Encrypted file fails integrity check."); } cleartextPayload = AesCtr(ciphertextPayload, masterKey, headerNonce); contentKey = Slice(cleartextPayload, 8, 32); HMAC chunkHmac = new HMAC(macKey); //Process all chunks for (int blocknum = 0; ; ++blocknum) { //read file content payload byte[] chunk; chunk = reader.ReadBytes(32768 + 48); if (chunk.Length == 0) { break; } var chunkNonce = Slice(chunk, 0, 16); var chunkpayload = Slice(chunk, chunkNonce.Length, chunk.Length - 48); var chunkmac = Slice(chunk, chunkNonce.Length + chunkpayload.Length, 32); byte[] beBlockNum = BitConverter.GetBytes((long)blocknum); if (BitConverter.IsLittleEndian) { Array.Reverse(beBlockNum); } chunkHmac.Initialize(); chunkHmac.Update(headerNonce); chunkHmac.Update(beBlockNum); chunkHmac.Update(chunkNonce); chunkHmac.DoFinal(chunkpayload); if (!chunkHmac.Hash.SequenceEqual(chunkmac)) { throw new IOException("Encrypted file fails integrity check."); } var decryptedContent = AesCtr(chunkpayload, contentKey, chunkNonce); writer.Write(decryptedContent); } } }
void EncryptInner(int recordType, int version, byte[] data, ref int off, ref int len) { int blen = bc.BlockSize; int mlen = hm.MACSize; int doff = off; int dlen = len; if (explicitIV) { /* * To make pseudorandom IV, we reuse HMAC, computed * over the encoded sequence number. Since this * input is distinct from all other HMAC inputs with * the same key, this should be randomish enough * (assuming HMAC is a good imitation of a random * oracle). */ IO.Enc64be(seq, tmp, 0); hm.Update(tmp, 0, 8); hm.DoFinal(tmp, 0); Array.Copy(tmp, 0, data, off - blen, blen); off -= blen; len += blen; } /* * Compute HMAC. */ IO.Enc64be(seq, tmp, 0); IO.WriteHeader(recordType, version, dlen, tmp, 8); hm.Update(tmp, 0, 13); hm.Update(data, doff, dlen); hm.DoFinal(data, off + len); len += mlen; seq++; /* * Add padding. */ int plen = blen - (len & (blen - 1)); for (int i = 0; i < plen; i++) { data[off + len + i] = (byte)(plen - 1); } len += plen; /* * Perform CBC encryption. We use our saved IV. If there is * an explicit IV, then it gets encrypted, which is fine * (CBC encryption of randomness is equally good randomness). */ bc.CBCEncrypt(iv, data, off, len); Array.Copy(data, off + len - blen, iv, 0, blen); /* * Add the header. */ off -= 5; IO.WriteHeader(recordType, version, len, data, off); len += 5; }
internal override bool Decrypt(int recordType, int version, byte[] data, ref int off, ref int len) { int blen = bc.BlockSize; int hlen = hm.MACSize; /* * Grab a copy of the last encrypted block; this is * the "saved IV" for the next record. */ Array.Copy(data, off + len - blen, ivTmp, 0, blen); /* * Decrypt the data. The length has already been * checked. If there is an explicit IV, it gets * "decrypted" as well, which is not a problem. */ bc.CBCDecrypt(iv, data, off, len); Array.Copy(ivTmp, 0, iv, 0, blen); if (explicitIV) { off += blen; len -= blen; } /* * Compute minimum and maximum length of plaintext + MAC. * These can be inferred from the observable record length, * and thus are not secret. */ int minLen = (hlen + 256 < len) ? len - 256 : hlen; int maxLen = len - 1; /* * Get the actual padding length and check padding. The * padding length must match the minLen/maxLen range. */ int padLen = data[off + len - 1]; int good = ~(((maxLen - minLen) - padLen) >> 31); int lenWithMAC = minLen ^ (good & (minLen ^ (maxLen - padLen))); int dbb = 0; for (int i = minLen; i < maxLen; i++) { dbb |= ~((i - lenWithMAC) >> 31) & (data[off + i] ^ padLen); } good &= ~((dbb | -dbb) >> 31); /* * Extract the MAC value; this is done in one pass, but * results in a "rotate" MAC value. The rotation count * is kept in 'rotCount': this is the offset of the * first MAC value byte in tmp1[]. */ int lenNoMAC = lenWithMAC - hlen; minLen -= hlen; int rotCount = 0; for (int i = 0; i < hlen; i++) { tmp1[i] = 0; } int v = 0; for (int i = minLen; i < maxLen; i++) { int m = ~((i - lenNoMAC) >> 31) & ((i - lenWithMAC) >> 31); tmp1[v] |= (byte)(m & data[off + i]); m = i - lenNoMAC; rotCount |= ~((m | -m) >> 31) & v; if (++v == hlen) { v = 0; } } maxLen -= hlen; /* * Rotate back the MAC value. We do it bit by bit, with * 6 iterations; this is good for all MAC value up to * and including 64 bytes. */ for (int i = 5; i >= 0; i--) { int rc = 1 << i; if (rc >= hlen) { continue; } int ctl = -((rotCount >> i) & 1); for (int j = 0, k = rc; j < hlen; j++) { int b1 = tmp1[j]; int b2 = tmp1[k]; tmp2[j] = (byte)(b1 ^ (ctl & (b1 ^ b2))); if (++k == hlen) { k = 0; } } Array.Copy(tmp2, 0, tmp1, 0, hlen); rotCount &= ~rc; } /* * Recompute the HMAC value. At that point, minLen and * maxLen have been adjusted to match the plaintext * without the MAC. */ IO.Enc64be(seq++, tmp2, 0); IO.WriteHeader(recordType, version, lenNoMAC, tmp2, 8); hm.Update(tmp2, 0, 13); hm.ComputeCT(data, off, lenNoMAC, minLen, maxLen, tmp2, 0); /* * Compare MAC values. */ dbb = 0; for (int i = 0; i < hlen; i++) { dbb |= tmp1[i] ^ tmp2[i]; } good &= ~((dbb | -dbb) >> 31); /* * We must also check that the plaintext length fits in * the maximum allowed by the standard (previous check * was on the encrypted length). */ good &= (lenNoMAC - 16385) >> 31; len = lenNoMAC; return(good != 0); }