Esempio n. 1
0
        /// <summary>
        ///     Create decorator streams implementing the Encrypt-then-MAC scheme (CipherStream bound to a MacStream).
        /// </summary>
        /// <param name="item">Item to create resources for.</param>
        /// <param name="encryptor">Cipher stream (output).</param>
        /// <param name="authenticator">MAC stream (output).</param>
        protected void CreateEtMDecorator(PayloadItem item, out CipherStream encryptor, out MacStream authenticator)
        {
            byte[] encryptionKey, authenticationKey;
            if (item.SymmetricCipherKey.IsNullOrZeroLength() == false && item.AuthenticationKey.IsNullOrZeroLength() == false)
            {
                encryptionKey     = item.SymmetricCipherKey;
                authenticationKey = item.AuthenticationKey;
            }
            else if (PayloadItemPreKeys.ContainsKey(item.Identifier))
            {
                if (item.Authentication.KeySizeBits.HasValue == false)
                {
                    throw new ConfigurationInvalidException(
                              "Payload item authentication configuration is missing size specification of MAC key.");
                }
                KeyStretchingUtility.DeriveWorkingKeys(PayloadItemPreKeys[item.Identifier],
                                                       item.SymmetricCipher.KeySizeBits / 8,
                                                       item.Authentication.KeySizeBits.Value / 8, item.KeyDerivation, out encryptionKey,
                                                       out authenticationKey);
            }
            else
            {
                throw new ItemKeyMissingException(item);
            }

            authenticator = new MacStream(PayloadStream, Writing, item.Authentication,
                                          authenticationKey, false);
            encryptor = new CipherStream(authenticator, Writing, item.SymmetricCipher,
                                         encryptionKey, false);
        }
Esempio n. 2
0
        /// <inheritdoc />
        protected override void EmitHeader(MacStream authenticator)
        {
            int paddingLength = GetHeaderLength();

            StratCom.EntropySupplier.NextBytes(_paddingBuffer, 0, paddingLength);
            authenticator.Write(_paddingBuffer, 0, paddingLength);
            Overhead += paddingLength;
        }
Esempio n. 3
0
 public CmsAuthenticatedDataOutputStream(
     MacStream macStream,
     BerSequenceGenerator cGen,
     BerSequenceGenerator authGen,
     BerSequenceGenerator eiGen)
 {
     this.macStream = macStream;
     this.cGen      = cGen;
     this.authGen   = authGen;
     this.eiGen     = eiGen;
 }
Esempio n. 4
0
 private byte[] MacTest2(string FileName, byte[] IKm)
 {
     using (FileStream inStream = new FileStream(FileName, FileMode.Open))
     {
         using (MacStream mac = new MacStream(new HMAC(new SHA512(), IKm)))
         {
             mac.Initialize(inStream);
             mac.IsConcurrent = false;
             return(mac.ComputeMac());
         }
     }
 }
Esempio n. 5
0
 public MuxItemResourceContainer(CipherStream encryptor, MacStream authenticator, int?bufferCapacity)
 {
     Encryptor     = encryptor;
     Authenticator = authenticator;
     if (bufferCapacity != null)
     {
         Buffer = new Lazy <RingBufferStream>(() => new RingBufferStream(bufferCapacity.Value, false));
     }
     else
     {
         SkippedLength = 0;
     }
 }
Esempio n. 6
0
        protected void RunMacTest(MacFunction function, byte[] config = null, byte[] nonce = null, byte[] overrideKey = null, byte[] overrideSalt = null)
        {
            byte[] outputMac;
            var    sw = new Stopwatch();

            using (var output = new MemoryStream((int)LargeBinaryFile.Length)) {
                using (var macS = new MacStream(output, true, function, out outputMac, overrideKey ?? Key, overrideSalt ?? Salt, config, nonce, false)) {
                    sw.Start();
                    LargeBinaryFile.CopyTo(macS);
                    sw.Stop();
                }
            }

            Debug.Print(outputMac.ToHexString());
            Assert.Pass("{0:N0} ms ({1:N2} MB/s)", sw.ElapsedMilliseconds, ((double)LargeBinaryFile.Length / 1048576) / sw.Elapsed.TotalSeconds);
        }
Esempio n. 7
0
        /// <inheritdoc />
        protected override void ConsumeHeader(MacStream authenticator)
        {
            int paddingLength = GetHeaderLength();
            int bytesRead     = authenticator.Read(_paddingBuffer, 0, paddingLength);

            if (bytesRead < paddingLength)
            {
#if DEBUG
                string exStr = String.Format("Unable to read frameshift padding bytes ({0} bytes returned of a requested {1}).",
                                             bytesRead, paddingLength);
#else
                string exStr = "Unable to read frameshift padding bytes.";
#endif
                throw new IOException(exStr);
            }
            Overhead += paddingLength;
        }
Esempio n. 8
0
        private bool MacTest3(byte[] IKm)
        {
            byte[] data = new CSPPrng().GetBytes(33033);
            byte[] hash1;
            byte[] hash2;

            using (MacStream mac1 = new MacStream(new HMAC(new SHA512(), IKm)))
            {
                mac1.Initialize(new MemoryStream(data));
                mac1.IsConcurrent = false;
                hash1             = mac1.ComputeMac();
            }

            using (HMAC mac2 = new HMAC(new SHA512(), IKm))
                hash2 = mac2.ComputeMac(data);

            return(Evaluate.AreEqual(hash1, hash2));
        }
Esempio n. 9
0
        private void HmacDescriptionTest()
        {
            CSPPrng rng = new CSPPrng();

            byte[] data = rng.GetBytes(rng.Next(100, 400));
            byte[] key  = rng.GetBytes(64);
            HMAC   mac  = new HMAC(Digests.SHA256);

            mac.Initialize(key);
            byte[]         c1  = mac.ComputeMac(data);
            MacDescription mds = new MacDescription(64, Digests.SHA256);
            MacStream      mst = new MacStream(mds, new KeyParams(key));

            mst.Initialize(new MemoryStream(data));
            byte[] c2 = mst.ComputeMac();

            if (!Evaluate.AreEqual(c1, c2))
            {
                throw new Exception("MacStreamTest: HMAC code arrays are not equal!");
            }
        }
Esempio n. 10
0
        private void CmacDescriptionTest()
        {
            CSPPrng rng = new CSPPrng();

            byte[] data = rng.GetBytes(rng.Next(100, 400));
            byte[] key  = rng.GetBytes(32);
            byte[] iv   = rng.GetBytes(16);
            CMAC   mac  = new CMAC(BlockCiphers.Rijndael);

            mac.Initialize(key, iv);
            byte[]         c1  = mac.ComputeMac(data);
            MacDescription mds = new MacDescription(32, BlockCiphers.Rijndael, IVSizes.V128, BlockSizes.B128, RoundCounts.R14);
            MacStream      mst = new MacStream(mds, new KeyParams(key, iv));

            mst.Initialize(new MemoryStream(data));
            byte[] c2 = mst.ComputeMac();

            if (!Evaluate.AreEqual(c1, c2))
            {
                throw new Exception("MacStreamTest: CMAC code arrays are not equal!");
            }
        }
Esempio n. 11
0
        /// <summary>
        /// Test the MacStream class implementation
        /// <para>Throws an Exception on failure</</para>
        /// </summary>
        public static void StreamMacTest()
        {
            byte[]       data;
            byte[]       key;
            MemoryStream instrm;
            MemoryStream outstrm = new MemoryStream();

            using (KeyGenerator kg = new KeyGenerator())
            {
                data = kg.GetBytes(512);
                key  = kg.GetBytes(64);
            }

            // data to digest
            instrm = new MemoryStream(data);
            byte[] code1;
            byte[] code2;

            using (MacStream sm = new MacStream(new HMAC(new SHA512(), key)))
            {
                sm.Initialize(instrm);
                code1 = sm.ComputeMac();
            }

            using (HMAC hm = new HMAC(new SHA512()))
            {
                hm.Initialize(key);
                code2 = hm.ComputeMac(data);
            }

            // compare the hash codes
            if (!Evaluate.AreEqual(code1, code2))
            {
                throw new Exception();
            }
        }
Esempio n. 12
0
        /// <inheritdoc />
        protected override void FinishItem(PayloadItem item, CipherStream encryptor, MacStream authenticator)
        {
            if (Writing)
            {
                if (item.ExternalLength > 0 && encryptor.BytesIn != item.ExternalLength)
                {
                    throw new InvalidDataException("Length written is not equal to predefined item external length.");
                }
            }
            else
            {
                if (encryptor.BytesIn != item.InternalLength)
                {
                    throw new InvalidDataException("Length read is not equal to item internal length.");
                }
                if (encryptor.BytesOut != item.ExternalLength)
                {
                    throw new InvalidDataException("Demultiplexed and decrypted length is not equal to specified item external length.");
                }
                encryptor.Close();
            }

            if (Writing)
            {
                // Commit the determined internal length to item in payload manifest
                item.InternalLength = encryptor.BytesOut;
                EmitTrailer(authenticator);
            }
            else
            {
                ConsumeTrailer(authenticator);
            }

            // Final stages of Encrypt-then-MAC authentication scheme
            PayloadItem itemDto = item.CreateAuthenticatibleClone();

            byte[] itemDtoAuthBytes = itemDto.SerialiseDto();

            Debug.Print(DebugUtility.CreateReportString("FabricPayloadMux", "FinishItem", "Item DTO length",
                                                        itemDtoAuthBytes.Length));

            if (Writing)
            {
                authenticator.Update(itemDtoAuthBytes, 0, itemDtoAuthBytes.Length);
                authenticator.Close();
                // Commit the MAC to item in payload manifest
                item.AuthenticationVerifiedOutput = authenticator.Mac.DeepCopy();
            }
            else
            {
                authenticator.Update(itemDtoAuthBytes, 0, itemDtoAuthBytes.Length);
                authenticator.Close();
                // Verify the authenticity of the item ciphertext and configuration
                if (authenticator.Mac.SequenceEqual_ConstantTime(item.AuthenticationVerifiedOutput) == false)
                {
                    // Verification failed!
                    throw new CiphertextAuthenticationException("Payload item not authenticated.");
                }
            }


            // Release the item's resources (implicitly - no references remain)
            _activeItemResources.Remove(item.Identifier);

            // Mark the item as completed in the register
            ItemCompletionRegister[Index] = true;
            ItemsCompleted++;
            // Close the source/destination
            item.StreamBinding.Close();

            Debug.Print(DebugUtility.CreateReportString("FabricPayloadMux", "FinishItem", "[*** END OF ITEM",
                                                        Index + " ***]"));
        }
Esempio n. 13
0
        protected override void ExecuteOperation()
        {
            Debug.Assert(ItemCompletionRegister[Index] == false);

            PayloadItem item           = PayloadItems[Index];
            Guid        itemIdentifier = item.Identifier;

            bool skip = ItemSkipRegister != null && ItemSkipRegister.Contains(itemIdentifier);

            MuxItemResourceContainer itemContainer;
            bool activeResource = _activeItemResources.ContainsKey(itemIdentifier);

            if (activeResource)
            {
                itemContainer = _activeItemResources[itemIdentifier];
            }
            else
            {
                if (skip == false)
                {
                    itemContainer = CreateEtMSchemeResources(item);
                    if (Writing)
                    {
                        EmitHeader(itemContainer.Authenticator);
                    }
                    else
                    {
                        ConsumeHeader(itemContainer.Authenticator);
                    }
                }
                else
                {
                    itemContainer = new MuxItemResourceContainer(null, null, null);
                }
                _activeItemResources.Add(itemIdentifier, itemContainer);
            }

            int opLength = NextOperationLength();

            if (skip == false)
            {
                CipherStream itemEncryptor     = itemContainer.Encryptor;
                MacStream    itemAuthenticator = itemContainer.Authenticator;

                if (Writing)
                {
                    // Writing/multiplexing
                    if (itemEncryptor.BytesIn + opLength < item.ExternalLength)
                    {
                        // Normal operation
                        itemEncryptor.WriteExactly(item.StreamBinding, opLength);
                    }
                    else
                    {
                        // Final operation, or just prior to
                        if (itemContainer.Buffer.IsValueCreated == false)
                        {
                            // Redirect final ciphertext to buffer to account for possible expansion
                            itemAuthenticator.ReassignBinding(itemContainer.Buffer.Value, false, finish: false);
                        }
                        var remaining = (int)(item.ExternalLength - itemEncryptor.BytesIn);
                        if (remaining > 0)
                        {
                            while (remaining > 0)
                            {
                                int toRead = Math.Min(remaining, BufferSize);
                                int iterIn = item.StreamBinding.Read(Buffer, 0, toRead);
                                if (iterIn < toRead)
                                {
                                    throw new EndOfStreamException();
                                }
                                itemEncryptor.Write(Buffer, 0, iterIn); // Writing into recently-lazy-inited buffer
                                remaining -= iterIn;
                            }
                            itemEncryptor.Close();
                        }
                        var toWrite = (int)Math.Min(opLength, itemContainer.Buffer.Value.Length);
                        Debug.Print(DebugUtility.CreateReportString("FabricPayloadMux", "ExecuteOperation",
                                                                    "Multiplexing item: final stripe length", toWrite));

                        itemContainer.Buffer.Value.ReadTo(PayloadStream, toWrite);
                    }
                }
                else
                {
                    // Reading/demultiplexing
                    long readRemaining = item.InternalLength - itemEncryptor.BytesIn;
                    bool finalOp       = false;
                    if (readRemaining <= opLength)
                    {
                        // Final operation
                        opLength = (int)readRemaining;
                        finalOp  = true;
                        Debug.Print(DebugUtility.CreateReportString("FabricPayloadMux", "ExecuteOperation",
                                                                    "Demultiplexing item: final stripe length", opLength));
                    }
                    itemEncryptor.ReadExactly(item.StreamBinding, opLength, finalOp);
                }

                if ((Writing && itemEncryptor.BytesIn >= item.ExternalLength && itemContainer.Buffer.Value.Length == 0) ||
                    (Writing == false && itemEncryptor.BytesIn >= item.InternalLength))
                {
                    // Now that we're finished we need to do some extra things, then clean up
                    FinishItem(item, itemEncryptor, itemAuthenticator);
                }
            }
            else
            {
                // Skipping
                Debug.Assert(Writing == false, "Should not be skipping when writing!");

                if (itemContainer.SkippedLength == 0)
                {
                    // Start of item
                    PayloadStream.Seek(opLength, SeekOrigin.Current);
                    itemContainer.SkippedLength += opLength;
                }
                else if (itemContainer.SkippedLength + opLength >= item.InternalLength)
                {
                    int remainingToSkip = (int)(item.InternalLength - itemContainer.SkippedLength);
                    itemContainer.SkippedLength += remainingToSkip;
                    PayloadStream.Seek(remainingToSkip + GetTrailerLength(), SeekOrigin.Current);
                    // "Finish" item
                    _activeItemResources.Remove(item.Identifier);
                    // Mark the item as completed in the register
                    ItemCompletionRegister[Index] = true;
                    ItemsCompleted++;
                    Debug.Print(DebugUtility.CreateReportString("FabricPayloadMux", "ExecuteOperation", "[*** SKIPPED ITEM",
                                                                Index + " ***]"));
                }
                else
                {
                    PayloadStream.Seek(opLength, SeekOrigin.Current);
                    itemContainer.SkippedLength += opLength;
                }
            }
        }
Esempio n. 14
0
 /// <inheritdoc />
 protected override void ConsumeTrailer(MacStream authenticator)
 {
     ConsumeHeader(authenticator);
 }
Esempio n. 15
0
 /// <inheritdoc />
 protected override void EmitTrailer(MacStream authenticator)
 {
     EmitHeader(authenticator);
 }
Esempio n. 16
0
 /// <summary>
 ///     Finish processing the current item.
 /// </summary>
 /// <param name="item">Payload item to finish.</param>
 /// <param name="encryptor">Item encryptor/cipher.</param>
 /// <param name="authenticator">Item authenticator/MAC.</param>
 protected abstract void FinishItem(PayloadItem item, CipherStream encryptor, MacStream authenticator);
        internal CmsTypedStream GetContentFromSessionKey(
            KeyParameter sKey)
        {
            try
            {
                Stream content = data;

                if (encAlg != null)
                {
                    IBufferedCipher cipher = CipherUtilities.GetCipher(encAlg.ObjectID);

                    Asn1Encodable asn1Enc    = encAlg.Parameters;
                    Asn1Object    asn1Params = asn1Enc == null ? null : asn1Enc.ToAsn1Object();

                    ICipherParameters cipherParameters = sKey;

                    if (asn1Params != null && !(asn1Params is Asn1Null))
                    {
                        cipherParameters = ParameterUtilities.GetCipherParameters(
                            encAlg.ObjectID, cipherParameters, asn1Params);
                    }
                    else
                    {
                        string alg = encAlg.ObjectID.Id;
                        if (alg.Equals(CmsEnvelopedDataGenerator.DesEde3Cbc) ||
                            alg.Equals(CmsEnvelopedDataGenerator.IdeaCbc) ||
                            alg.Equals(CmsEnvelopedDataGenerator.Cast5Cbc))
                        {
                            cipherParameters = new ParametersWithIV(cipherParameters, new byte[8]);
                        }
                    }

                    cipher.Init(false, cipherParameters);

                    content = new CipherStream(content, cipher, null);
                }

                // If authenticated, need to wrap in MacStream to calculate MAC
                if (macAlg != null)
                {
                    content = this.macStream = CreateMacStream(macAlg, sKey, content);
                }

                if (authEncAlg != null)
                {
                    // TODO Create AEAD cipher instance to decrypt and calculate tag ( MAC)
                    throw new CmsException("AuthEnveloped data decryption not yet implemented");

//              RFC 5084 ASN.1 Module
//                -- Parameters for AigorithmIdentifier
//
//                CCMParameters ::= SEQUENCE {
//                  aes-nonce         OCTET STRING (SIZE(7..13)),
//                  aes-ICVlen        AES-CCM-ICVlen DEFAULT 12 }
//
//                AES-CCM-ICVlen ::= INTEGER (4 | 6 | 8 | 10 | 12 | 14 | 16)
//
//                GCMParameters ::= SEQUENCE {
//                  aes-nonce        OCTET STRING, -- recommended size is 12 octets
//                  aes-ICVlen       AES-GCM-ICVlen DEFAULT 12 }
//
//                AES-GCM-ICVlen ::= INTEGER (12 | 13 | 14 | 15 | 16)
                }

                return(new CmsTypedStream(content));
            }
            catch (SecurityUtilityException e)
            {
                throw new CmsException("couldn't create cipher.", e);
            }
            catch (InvalidKeyException e)
            {
                throw new CmsException("key invalid in message.", e);
            }
            catch (IOException e)
            {
                throw new CmsException("error decoding algorithm parameters.", e);
            }
        }
Esempio n. 18
0
        protected Stream Open(
            Stream outStr,
            AlgorithmIdentifier macAlgId,
            ICipherParameters cipherParameters,
            Asn1EncodableVector recipientInfos)
        {
            try
            {
                //
                // ContentInfo
                //
                BerSequenceGenerator cGen = new BerSequenceGenerator(outStr);

                cGen.AddObject(CmsObjectIdentifiers.AuthenticatedData);

                //
                // Authenticated Data
                //
                BerSequenceGenerator authGen = new BerSequenceGenerator(
                    cGen.GetRawOutputStream(), 0, true);

                authGen.AddObject(new DerInteger(AuthenticatedData.CalculateVersion(null)));

                Stream        authRaw  = authGen.GetRawOutputStream();
                Asn1Generator recipGen = _berEncodeRecipientSet
                                        ?       (Asn1Generator) new BerSetGenerator(authRaw)
                                        :       new DerSetGenerator(authRaw);

                foreach (Asn1Encodable ae in recipientInfos)
                {
                    recipGen.AddObject(ae);
                }

                recipGen.Close();

                authGen.AddObject(macAlgId);

                BerSequenceGenerator eiGen = new BerSequenceGenerator(authRaw);
                eiGen.AddObject(CmsObjectIdentifiers.Data);

                Stream octetOutputStream = CmsUtilities.CreateBerOctetOutputStream(
                    eiGen.GetRawOutputStream(), 0, false, _bufferSize);

                IMac mac = MacUtilities.GetMac(macAlgId.ObjectID);
                // TODO Confirm no ParametersWithRandom needed
                mac.Init(cipherParameters);
                MacStream mOut = new MacStream(octetOutputStream, null, mac);

                return(new CmsAuthenticatedDataOutputStream(mOut, cGen, authGen, eiGen));
            }
            catch (SecurityUtilityException e)
            {
                throw new CmsException("couldn't create cipher.", e);
            }
            catch (InvalidKeyException e)
            {
                throw new CmsException("key invalid in message.", e);
            }
            catch (IOException e)
            {
                throw new CmsException("exception decoding algorithm parameters.", e);
            }
        }
Esempio n. 19
0
 /// <summary>
 ///     Read an item trailer from the payload stream.
 /// </summary>
 /// <param name="authenticator">
 ///     Authenticator for the item, if trailer is to be authenticated.
 /// </param>
 protected virtual void ConsumeTrailer(MacStream authenticator)
 {
     // Unused in this version
     // Could throw an exception in an implementation where a trailer must be present
 }
Esempio n. 20
0
 /// <summary>
 ///     Generate and write an item trailer into the payload stream.
 /// </summary>
 /// <param name="authenticator">
 ///     Authenticator for the item, if trailer is to be authenticated.
 /// </param>
 protected virtual void EmitTrailer(MacStream authenticator)
 {
     // Unused in this version
 }
Esempio n. 21
0
        /// <summary>
        ///     Close the item decorator, check lengths, authenticate the item (emit or verify),
        ///     and if writing, commit the authentication value to the payload item DTO.
        /// </summary>
        /// <param name="item">Payload item to finish.</param>
        /// <param name="encryptor">Item encryptor/cipher.</param>
        /// <param name="authenticator">Item authenticator/MAC.</param>
        protected override void FinishItem(PayloadItem item, CipherStream encryptor, MacStream authenticator)
        {
            try {
                encryptor.Close();
            } catch (Exception e) {
                throw new Exception("Unknown error when finalising/closing cipher.", e);
            }

            try {
                if (Writing)
                {
                    EmitTrailer(authenticator);
                }
                else
                {
                    ConsumeTrailer(authenticator);
                }
            } catch (Exception e) {
                throw new Exception(String.Format("Unknown error when {0} item trailer.", Writing ? "emitting" : "consuming"), e);
            }

            // Length checks & commits
            if (Writing)
            {
                // Check if pre-stated length matches what was actually written
                if (item.ExternalLength > 0 && encryptor.BytesIn != item.ExternalLength)
                {
                    throw new InvalidDataException(
                              "Mismatch between stated item external length and actual input length.");
                }
                // Commit the determined internal length to item in payload manifest
                item.InternalLength = encryptor.BytesOut;
            }
            else
            {
                if (encryptor.BytesIn != item.InternalLength)
                {
                    throw new InvalidOperationException("Probable decorator stack malfunction.");
                }
                if (encryptor.BytesOut != item.ExternalLength)
                {
                    throw new InvalidDataException(
                              "Mismatch between stated item external length and actual output length.");
                }
            }

            // Final stages of Encrypt-then-MAC authentication scheme
            PayloadItem itemDto = item.CreateAuthenticatibleClone();

            byte[] itemDtoAuthBytes = itemDto.SerialiseDto();
#if PRINT_DTO_LENGTH
            Debug.Print(DebugUtility.CreateReportString("SimplePayloadMux", "FinishItem", "Payload item DTO length",
                                                        itemDtoAuthBytes.Length));
#endif
            authenticator.Update(itemDtoAuthBytes, 0, itemDtoAuthBytes.Length);
            authenticator.Close();

            // Authentication
            if (Writing)
            {
                // Commit the MAC to item in payload manifest
                item.AuthenticationVerifiedOutput = authenticator.Mac.DeepCopy();
            }
            else
            {
                // Verify the authenticity of the item ciphertext and configuration
                if (authenticator.Mac.SequenceEqual_ConstantTime(item.AuthenticationVerifiedOutput) == false)
                {
                    // Verification failed!
                    throw new CiphertextAuthenticationException("Payload item not authenticated.");
                }
            }

            // Close the source/destination
            item.StreamBinding.Close();

            // Mark the item as completed in the register
            ItemCompletionRegister[Index] = true;
            ItemsCompleted++;

            Debug.Print(DebugUtility.CreateReportString("SimplePayloadMux", "ExecuteOperation",
                                                        "[*** END OF ITEM", String.Format("{0} ({1}) ***]", Index, item.Identifier)));
        }
        /**
         * generate an enveloped object that contains an CMS Enveloped Data
         * object using the given provider and the passed in key generator.
         */
        private CmsAuthenticatedData Generate(
            CmsProcessable content,
            string macOid,
            CipherKeyGenerator keyGen)
        {
            AlgorithmIdentifier macAlgId;
            KeyParameter        encKey;
            Asn1OctetString     encContent;
            Asn1OctetString     macResult;

            try
            {
                // FIXME Will this work for macs?
                byte[] encKeyBytes = keyGen.GenerateKey();
                encKey = ParameterUtilities.CreateKeyParameter(macOid, encKeyBytes);

                Asn1Encodable asn1Params = GenerateAsn1Parameters(macOid, encKeyBytes);

                ICipherParameters cipherParameters;
                macAlgId = GetAlgorithmIdentifier(
                    macOid, encKey, asn1Params, out cipherParameters);

                IMac mac = MacUtilities.GetMac(macOid);
                // TODO Confirm no ParametersWithRandom needed
                // FIXME Only passing key at the moment
//	            mac.Init(cipherParameters);
                mac.Init(encKey);

                MemoryStream bOut = new MemoryStream();
                MacStream    mOut = new MacStream(bOut, null, mac);

                content.Write(mOut);

                mOut.Close();
                bOut.Close();

                encContent = new BerOctetString(bOut.ToArray());

                byte[] macOctets = MacUtilities.DoFinal(mOut.WriteMac());
                macResult = new DerOctetString(macOctets);
            }
            catch (SecurityUtilityException e)
            {
                throw new CmsException("couldn't create cipher.", e);
            }
            catch (InvalidKeyException e)
            {
                throw new CmsException("key invalid in message.", e);
            }
            catch (IOException e)
            {
                throw new CmsException("exception decoding algorithm parameters.", e);
            }


            Asn1EncodableVector recipientInfos = new Asn1EncodableVector();

            foreach (RecipientInfoGenerator rig in recipientInfoGenerators)
            {
                try
                {
                    recipientInfos.Add(rig.Generate(encKey, rand));
                }
                catch (InvalidKeyException e)
                {
                    throw new CmsException("key inappropriate for algorithm.", e);
                }
                catch (GeneralSecurityException e)
                {
                    throw new CmsException("error making encrypted content.", e);
                }
            }

            ContentInfo eci = new ContentInfo(CmsObjectIdentifiers.Data, encContent);

            ContentInfo contentInfo = new ContentInfo(
                CmsObjectIdentifiers.AuthenticatedData,
                new AuthenticatedData(null, new DerSet(recipientInfos), macAlgId, null, eci, null, macResult, null));

            return(new CmsAuthenticatedData(contentInfo));
        }
Esempio n. 23
0
        /// <summary>
        ///     Reads the manifest from the package.
        /// </summary>
        /// <remarks>
        ///     Call method, supplying (all of) only the keys associated with the sender and the context.
        ///     This maximises the chance that: <br />
        ///     <list type="number">
        ///         <item>
        ///             <description>
        ///                 The package will be successfully decrypted if multiple
        ///                 keys are in use by both parties.
        ///             </description>
        ///         </item>
        ///         <item>
        ///             <description>
        ///                 Minimises the time spent validating potential key pairs.
        ///             </description>
        ///         </item>
        ///     </list>
        /// </remarks>
        /// <param name="keyProvider">Provider to get possible keys for the manifest from.</param>
        /// <param name="manifestScheme">Cryptography scheme used in the manifest.</param>
        /// <returns>Package manifest object.</returns>
        /// <exception cref="ArgumentException">Key provider absent or could not supply any keys.</exception>
        /// <exception cref="NotSupportedException">Manifest cryptography scheme unsupported/unknown or missing.</exception>
        /// <exception cref="CryptoException">
        ///     A cryptographic operation failed (additional data maybe available in <see cref="CryptoException.InnerException" />
        ///     ).
        /// </exception>
        /// <exception cref="KeyConfirmationException">
        ///     Key confirmation failed to determine a key, or failed unexpectedly
        ///     (additional data maybe available in <see cref="KeyConfirmationException.InnerException" />)
        /// </exception>
        /// <exception cref="InvalidDataException">
        ///     Deserialisation of manifest failed unexpectedly (manifest malformed, or incorrect key).
        /// </exception>
        /// <exception cref="CiphertextAuthenticationException">Manifest not authenticated.</exception>
        private Manifest ReadManifest(IKeyProvider keyProvider, ManifestCryptographyScheme manifestScheme)
        {
            // Determine the pre-key for the package manifest decryption (different schemes use different approaches)
            byte[] preMKey = null;
            switch (manifestScheme)
            {
            case ManifestCryptographyScheme.SymmetricOnly: {
                if (keyProvider.SymmetricKeys.Any() == false)
                {
                    throw new ArgumentException("No symmetric keys available for decryption of this manifest.",
                                                "keyProvider");
                }
                SymmetricKey symmetricKey = null;
                if (_manifestCryptoConfig.KeyConfirmation != null)
                {
                    try {
                        symmetricKey = ConfirmationUtility.ConfirmKeyFromCanary(
                            ((SymmetricManifestCryptographyConfiguration)_manifestCryptoConfig).KeyConfirmation,
                            _manifestCryptoConfig.KeyConfirmationVerifiedOutput, keyProvider.SymmetricKeys);
                    } catch (Exception e) {
                        throw new KeyConfirmationException("Key confirmation failed in an unexpected way.", e);
                    }
                }
                else
                {
                    if (keyProvider.SymmetricKeys.Count() > 1)
                    {
                        // Possibly allow to proceed anyway and just look for a serialisation failure? (not implemented)
                        throw new ArgumentException(
                                  "Multiple symmetric keys are available, but confirmation is unavailable.",
                                  "keyProvider",
                                  new ConfigurationInvalidException("Package manifest includes no key confirmation data."));
                    }
                    preMKey = keyProvider.SymmetricKeys.First().Key;
                }
                if (symmetricKey != null)
                {
                    preMKey = symmetricKey.Key;
                }
                break;
            }

            case ManifestCryptographyScheme.Um1Hybrid: {
                ECKey     um1SenderKey;
                ECKeypair um1RecipientKeypair;
                ECKey     um1EphemeralKey =
                    ((Um1HybridManifestCryptographyConfiguration)_manifestCryptoConfig).EphemeralKey;
                if (_manifestCryptoConfig.KeyConfirmation != null)
                {
                    try {
                        ConfirmationUtility.ConfirmKeyFromCanary(_manifestCryptoConfig.KeyConfirmation,
                                                                 _manifestCryptoConfig.KeyConfirmationVerifiedOutput,
                                                                 keyProvider.ForeignEcKeys,
                                                                 um1EphemeralKey,
                                                                 keyProvider.EcKeypairs,
                                                                 out um1SenderKey, out um1RecipientKeypair);
                    } catch (Exception e) {
                        throw new KeyConfirmationException("Key confirmation failed in an unexpected way.", e);
                    }
                }
                else
                {
                    // No key confirmation capability available
                    if (keyProvider.ForeignEcKeys.Count() > 1 || keyProvider.EcKeypairs.Count() > 1)
                    {
                        throw new KeyConfirmationException(
                                  "Multiple EC keys have been provided where the package provides no key confirmation capability.");
                    }
                    um1SenderKey        = keyProvider.ForeignEcKeys.First();
                    um1RecipientKeypair = keyProvider.EcKeypairs.First();
                }
                // Perform the UM1 key agreement
                try {
                    preMKey = Um1Exchange.Respond(um1SenderKey, um1RecipientKeypair.GetPrivateKey(),
                                                  um1EphemeralKey);
                } catch (Exception e) {
                    throw new CryptoException("Unexpected error in UM1 key agreement.", e);
                }
                break;
            }

            default:
                throw new NotSupportedException(
                          String.Format("Manifest cryptography scheme \"{0}\" is unsupported/unknown.", manifestScheme));
            }

            if (preMKey.IsNullOrZeroLength())
            {
                throw new KeyConfirmationException(String.Format(
                                                       "None of the keys provided to decrypt the manifest (cryptographic scheme: {0}) were confirmed as being able to do so.",
                                                       manifestScheme));
            }
            Debug.Print(DebugUtility.CreateReportString("PackageReader", "ReadManifest", "Manifest pre-key",
                                                        preMKey.ToHexString()));

            // Derive working manifest encryption & authentication keys from the manifest pre-key
            byte[] workingManifestCipherKey, workingManifestMacKey;
            try {
                int cipherKeySizeBytes = _manifestCryptoConfig.SymmetricCipher.KeySizeBits.BitsToBytes();
                if (_manifestCryptoConfig.Authentication.KeySizeBits.HasValue == false)
                {
                    throw new ConfigurationInvalidException("Manifest authentication key size is missing.");
                }
                int macKeySizeBytes = _manifestCryptoConfig.Authentication.KeySizeBits.Value.BitsToBytes();
                // Derive working cipher and MAC keys from the pre-key
                KeyStretchingUtility.DeriveWorkingKeys(
                    preMKey,
                    cipherKeySizeBytes, macKeySizeBytes,
                    _manifestCryptoConfig.KeyDerivation,
                    out workingManifestCipherKey, out workingManifestMacKey);
            } catch (Exception e) {
                throw new CryptoException("Unexpected error in manifest key derivation.", e);
                // TODO: make a specialised exception to communicate the failure type
            }

            // Clear the manifest pre-key
            preMKey.SecureWipe();

            Debug.Print(DebugUtility.CreateReportString("PackageReader", "ReadManifest", "Manifest MAC working key",
                                                        workingManifestMacKey.ToHexString()));
            Debug.Print(DebugUtility.CreateReportString("PackageReader", "ReadManifest", "Manifest cipher working key",
                                                        workingManifestCipherKey.ToHexString()));
            Debug.Print(DebugUtility.CreateReportString("PackageReader", "ReadManifest",
                                                        "Manifest length prefix offset (absolute)",
                                                        _readingStream.Position));

            // Read manifest length prefix
            var manifestLengthLe        = new byte[sizeof(UInt32)]; // in little-endian form
            int manifestLengthBytesRead = _readingStream.Read(manifestLengthLe, 0, sizeof(UInt32));

            if (manifestLengthBytesRead != sizeof(UInt32))
            {
                throw new DataLengthException("Manifest length prefix could not be read. Insufficient data.");
            }
            manifestLengthLe.XorInPlaceInternal(0, workingManifestMacKey, 0, sizeof(UInt32)); // deobfuscate length
            UInt32 mlUInt         = manifestLengthLe.LittleEndianToUInt32();
            var    manifestLength = (int)mlUInt;

            Debug.Print(DebugUtility.CreateReportString("PackageReader", "ReadManifest", "Manifest length",
                                                        manifestLength));
            Debug.Print(DebugUtility.CreateReportString("PackageReader", "ReadManifest", "Manifest offset (absolute)",
                                                        _readingStream.Position));

            /* Read manifest */
            Manifest manifest;

            using (var decryptedManifestStream = new MemoryStream(manifestLength)) {
                byte[] manifestMac;
                try {
                    using (
                        var authenticator = new MacStream(_readingStream, false, _manifestCryptoConfig.Authentication,
                                                          out manifestMac, workingManifestMacKey, false)) {
                        using (var cs = new CipherStream(authenticator, false, _manifestCryptoConfig.SymmetricCipher,
                                                         workingManifestCipherKey, false)) {
                            cs.ReadExactly(decryptedManifestStream, manifestLength, true);
                        }
                        // Authenticate manifest length tag
                        authenticator.Update(manifestLengthLe, 0, manifestLengthLe.Length);

                        Contract.Assert(authenticator.BytesIn == manifestLength);

                        byte[] manifestCryptoDtoForAuth;
                        switch (manifestScheme)
                        {
                        case ManifestCryptographyScheme.SymmetricOnly:
                            manifestCryptoDtoForAuth =
                                ((SymmetricManifestCryptographyConfiguration)_manifestCryptoConfig)
                                .CreateAuthenticatibleClone().SerialiseDto();
                            break;

                        case ManifestCryptographyScheme.Um1Hybrid:
                            manifestCryptoDtoForAuth =
                                ((Um1HybridManifestCryptographyConfiguration)_manifestCryptoConfig)
                                .CreateAuthenticatibleClone().SerialiseDto();
                            break;

                        default:
                            throw new NotSupportedException();
                        }
                        // Authenticate manifest cryptography configuration (from manifest header)
                        authenticator.Update(manifestCryptoDtoForAuth, 0, manifestCryptoDtoForAuth.Length);
                    }
                } catch (Exception e) {
                    throw new CryptoException("Unexpected error in manifest decrypt-then-MAC operation.", e);
                }

                // Verify that manifest authenticated successfully
                if (manifestMac.SequenceEqual_ConstantTime(_manifestCryptoConfig.AuthenticationVerifiedOutput) == false)
                {
                    throw new CiphertextAuthenticationException("Manifest failed authentication.");
                }
                decryptedManifestStream.Seek(0, SeekOrigin.Begin);

                try {
                    manifest = decryptedManifestStream.DeserialiseDto <Manifest>(false);
                } catch (Exception e) {
                    throw new InvalidDataException("Manifest failed to deserialise.", e);
                }
            }

            _readingPayloadStreamOffset = _readingStream.Position;

            // Clear the manifest encryption & authentication keys
            workingManifestCipherKey.SecureWipe();
            workingManifestMacKey.SecureWipe();

            return(manifest);
        }