コード例 #1
0
ファイル: PayloadMux.cs プロジェクト: viruswevh/ObscurCore
 /// <summary>
 ///     Determine the index of the next stream to use in an I/O operation
 ///     (whether to completion or otherwise, depending on implementation).
 /// </summary>
 /// <remarks>May be overriden in a derived class to provide for advanced stream selection logic.</remarks>
 /// <returns>The next stream index.</returns>
 protected virtual void NextSource()
 {
     if (++Index == PayloadItems.Count)
     {
         Index = 0;
     }
     Debug.Print(DebugUtility.CreateReportString("PayloadMux", "NextSource", "Generated index",
                                                 Index));
 }
コード例 #2
0
        /// <summary>
        ///     If variable striping mode is enabled, advances the state of the selection CSPRNG,
        ///     and returns the output to be used as the length of the next operation.
        ///     Otherwise, fixed length is returned.
        /// </summary>
        /// <returns>Operation length to perform.</returns>
        private int NextOperationLength()
        {
            int operationLength = _stripeMode == FabricStripeMode.VariableLength
                ? EntropySource.Next(_minStripe, _maxStripe)
                : _maxStripe;

            Debug.Print(DebugUtility.CreateReportString("FabricPayloadMux", "NextOperationLength", "Generated stripe length value",
                                                        operationLength));
            return(operationLength);
        }
コード例 #3
0
        private int NextPaddingLength()
        {
            int paddingLength = (_paddingMode == FrameshiftPaddingMode.VariableLength)
                ? EntropySource.NextPositive(_minPadding, _maxPadding)
                : _maxPadding;

            Debug.Print(DebugUtility.CreateReportString("FrameshiftPayloadMux", "NextPaddingLength", "Generated length value",
                                                        paddingLength));

            return(paddingLength);
        }
コード例 #4
0
ファイル: PayloadMux.cs プロジェクト: viruswevh/ObscurCore
 /// <summary>
 ///     Executes multiplexing operations until source(s) are exhausted.
 /// </summary>
 public void Execute()
 {
     while (ItemsCompleted < PayloadItems.Count)
     {
         do
         {
             NextSource();
         } while (ItemsCompleted < PayloadItems.Count && ItemCompletionRegister[Index]);
         Debug.Print(DebugUtility.CreateReportString("PayloadMux", "Execute", "Selected stream",
                                                     Index));
         ExecuteOperation();
     }
 }
コード例 #5
0
        /// <summary>
        ///     Generate a verified output of a function given the correct key, to be used as a key confirmation.
        ///     Uses confirmation canary.
        /// </summary>
        /// <param name="configuration">Configuration of the verification function.</param>
        /// <param name="senderKeypair">Sender keypair to generate a confirmation output verification for.</param>
        /// <param name="recipientKey">Recipient key to generate a confirmation output verification for.</param>
        /// <returns>Output of the verification function, given the correct key.</returns>
        /// <exception cref="ArgumentException">Key is null or zero-length.</exception>
        /// <seealso cref="SymmetricKey"/>
        /// <seealso cref="ECKeypair"/>
        /// <seealso cref="IPossessConfirmationCanary"/>
        public static byte[] GenerateVerifiedOutput(AuthenticationConfiguration configuration, ECKeypair senderKeypair, ECKey recipientKey)
        {
            Func <byte[], byte[]> validator = GetValidator(configuration, TagConstantBytes,
                                                           configuration.SerialiseDto());

            byte[] canary         = XorCanaryBytes(senderKeypair.ConfirmationCanary, recipientKey.ConfirmationCanary);
            byte[] verifiedOutput = validator(canary);

            Debug.Print(DebugUtility.CreateReportString("ConfirmationUtility", "GenerateVerifiedOutput",
                                                        "Verified output",
                                                        verifiedOutput.ToHexString()));

            return(verifiedOutput);
        }
コード例 #6
0
        protected override void ExecuteOperation()
        {
            PayloadItem item = PayloadItems[Index];

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

            if (skip == false)
            {
                CipherStream itemEncryptor;
                MacStream    itemAuthenticator;
                CreateEtMDecorator(item, out itemEncryptor, out itemAuthenticator);

                if (Writing)
                {
                    EmitHeader(itemAuthenticator);
                }
                else
                {
                    ConsumeHeader(itemAuthenticator);
                }

                if (Writing)
                {
                    int iterIn;
                    do
                    {
                        iterIn = item.StreamBinding.Read(Buffer, 0, BufferSize);
                        itemEncryptor.Write(Buffer, 0, iterIn);
                    } while (iterIn > 0);
                }
                else
                {
                    itemEncryptor.ReadExactly(item.StreamBinding, item.InternalLength, true);
                }

                FinishItem(item, itemEncryptor, itemAuthenticator);
            }
            else
            {
                // Skipping
                long skipLength = GetHeaderLength() + item.InternalLength + GetTrailerLength();
                PayloadStream.Seek(skipLength, SeekOrigin.Current);
                // Mark the item as completed in the register
                ItemCompletionRegister[Index] = true;
                ItemsCompleted++;
                Debug.Print(DebugUtility.CreateReportString("SimplePayloadMux", "ExecuteOperation",
                                                            "[*** SKIPPED ITEM", String.Format("{0} ({1}) ***]", Index, item.Identifier)));
            }
        }
コード例 #7
0
        /// <summary>
        ///     Generate a verified output of a function given the correct canary, to be used as a key confirmation.
        /// </summary>
        /// <param name="configuration">Configuration of the verification function.</param>
        /// <param name="canary">Confirmation canary to generate a confirmation output verification for.</param>
        /// <returns>Output of the verification function, given the correct canary.</returns>
        /// <exception cref="ArgumentException">Key is null or zero-length.</exception>
        /// <seealso cref="SymmetricKey"/>
        /// <seealso cref="ECKeypair"/>
        /// <seealso cref="IPossessConfirmationCanary"/>
        public static byte[] GenerateVerifiedOutput(AuthenticationConfiguration configuration, byte[] canary)
        {
            if (canary.IsNullOrZeroLength())
            {
                throw new ArgumentException("Canary is null or zero-length.", "canary");
            }

            Func <byte[], byte[]> validator = GetValidator(configuration, TagConstantBytes,
                                                           configuration.SerialiseDto());

            byte[] verifiedOutput = validator(canary);

            Debug.Print(DebugUtility.CreateReportString("ConfirmationUtility", "GenerateVerifiedOutput",
                                                        "Verified output",
                                                        verifiedOutput.ToHexString()));

            return(verifiedOutput);
        }
コード例 #8
0
            public void GenerateSeed(byte[] buffer, int offset, int count, bool fast)
            {
                _counter = 0;
                _stop    = false;

                int last = 0;
                int end  = fast ? count : count * 8;

                ThreadPool.QueueUserWorkItem(Run);

                for (int i = 0; i < end; i++)
                {
                    while (_counter == last)
                    {
                        try {
                            Thread.Sleep(1);
                        } catch (Exception e) {
                            Debug.Print(DebugUtility.CreateReportString("ThreadedSeedRng", "GenerateSeed",
                                                                        "Thread sleep threw exception", e.ToString()));
                        }
                    }

                    last = _counter;

                    if (fast)
                    {
                        buffer[offset + i] = (byte)last;
                    }
                    else
                    {
                        int bytepos = i / 8;
                        buffer[offset + bytepos] = (byte)((buffer[offset + bytepos] << 1) | (last & 1));
                    }
                }

                _stop = true;
            }
コード例 #9
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 + " ***]"));
        }
コード例 #10
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;
                }
            }
        }
コード例 #11
0
 /// <summary>
 ///     Advances and returns the index of the next stream to use in an I/O operation (whether to completion or just a
 ///     buffer-full).
 /// </summary>
 /// <remarks>May be overriden in a derived class to provide for advanced stream selection logic.</remarks>
 /// <returns>The next stream index.</returns>
 protected override sealed void NextSource()
 {
     Index = EntropySource.NextPositive(0, PayloadItems.Count - 1);
     Debug.Print(DebugUtility.CreateReportString("SimplePayloadMux", "NextSource", "Generated index",
                                                 Index));
 }
コード例 #12
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)));
        }
コード例 #13
0
        /// <summary>
        ///     Read the payload.
        /// </summary>
        /// <remarks>
        ///     All payload items to be read must have have valid stream bindings
        ///     (<see cref="PayloadItem.StreamBinding" />) prior to calling this.
        /// </remarks>
        /// <param name="payloadKeys">Potential keys for payload items (optional).</param>
        /// <exception cref="KeyConfirmationException">Key confirmation for payload items failed.</exception>
        /// <exception cref="ConfigurationInvalidException">Payload layout scheme malformed/missing.</exception>
        /// <exception cref="InvalidDataException">Package data structure malformed.</exception>
        /// <exception cref="EndOfStreamException">Package is truncated or otherwise shorter than expected.</exception>
        private void ReadPayload(IEnumerable <SymmetricKey> payloadKeys = null)
        {
            if (_readingPayloadStreamOffset != 0 && _readingStream.Position != _readingPayloadStreamOffset)
            {
                _readingStream.Seek(_readingPayloadStreamOffset, SeekOrigin.Begin);
            }
            Debug.Print(DebugUtility.CreateReportString("PackageReader", "Read", "Payload offset (absolute)",
                                                        _readingStream.Position));

            // Check that all payload items have decryption keys - if they do not, confirm them from potentials
            try {
                ConfirmItemPreKeys(payloadKeys);
            } catch (Exception e) {
                throw new KeyConfirmationException("Error in key confirmation of payload items.", e);
            }

            // Read the payload
            PayloadMux mux;

            try {
                var payloadScheme = _manifest.PayloadConfiguration.SchemeName.ToEnum <PayloadLayoutScheme>();
                mux = PayloadMuxFactory.CreatePayloadMultiplexer(payloadScheme, false, _readingStream,
                                                                 _manifest.PayloadItems,
                                                                 _itemPreKeys, _manifest.PayloadConfiguration);
            } catch (EnumerationParsingException e) {
                throw new ConfigurationInvalidException(
                          "Payload layout scheme specified is unsupported/unknown or missing.", e);
            } catch (Exception e) {
                throw new Exception("Error in creation of payload demultiplexer.", e);
            }

            // Demux the payload
            try {
                mux.Execute();
            } catch (Exception e) {
                // Catch different kinds of exception in future
                throw new Exception("Error in demultiplexing payload.", e);
            }

            // Read the trailer
            byte[] referenceTrailerTag = Athena.Packaging.GetPackageTrailerTag();
            var    trailerTag          = new byte[referenceTrailerTag.Length];

            Debug.Print(DebugUtility.CreateReportString("PackageReader", "ReadPayload", "Trailer offset (absolute)",
                                                        _readingStream.Position));
            int trailerBytesRead = _readingStream.Read(trailerTag, 0, trailerTag.Length);

            if (trailerTag.SequenceEqualShortCircuiting(referenceTrailerTag) == false)
            {
                const string pragmatist =
                    "It would appear, however, that the package has unpacked successfully despite this.";
                if (trailerBytesRead != referenceTrailerTag.Length)
                {
                    throw new EndOfStreamException("Insufficient data to read package trailer tag. " + pragmatist);
                }
                throw new InvalidDataException("Package is malformed. Trailer tag is either absent or malformed."
                                               + pragmatist);
            }

            Debug.Print(DebugUtility.CreateReportString("PackageReader", "ReadPayload",
                                                        "[* PACKAGE END *] Offset (absolute)", _readingStream.Position));
        }
コード例 #14
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);
        }
コード例 #15
0
        /// <summary>
        ///     Reads a package manifest header from a stream.
        /// </summary>
        /// <param name="sourceStream">Stream to read the header from.</param>
        /// <param name="cryptoScheme">Manifest cryptography scheme parsed from the header.</param>
        /// <param name="cryptoConfig">Manifest cryptography configuration deserialised from the header.</param>
        /// <returns>Package manifest header.</returns>
        /// <exception cref="DataLengthException">End of stream encountered unexpectedly (contents truncated).</exception>
        /// <exception cref="InvalidDataException">Package data structure is out of specification or otherwise malformed.</exception>
        /// <exception cref="NotSupportedException">Version format specified is unknown to the local version.</exception>
        private static ManifestHeader ReadManifestHeader(Stream sourceStream,
                                                         out ManifestCryptographyScheme cryptoScheme,
                                                         out IManifestCryptographySchemeConfiguration cryptoConfig)
        {
            Debug.Print(DebugUtility.CreateReportString("PackageReader", "ReadManifestHeader",
                                                        "[* PACKAGE START* ] Header offset (absolute)",
                                                        sourceStream.Position));

            byte[] referenceHeaderTag = Athena.Packaging.GetPackageHeaderTag();
            var    readHeaderTag      = new byte[referenceHeaderTag.Length];
            int    headerTagBytesRead = sourceStream.Read(readHeaderTag, 0, readHeaderTag.Length);

            if (readHeaderTag.SequenceEqualShortCircuiting(referenceHeaderTag) == false)
            {
                if (headerTagBytesRead != referenceHeaderTag.Length)
                {
                    throw new DataLengthException("Insufficient data to read package header tag.");
                }
                throw new InvalidDataException(
                          "Package is malformed. Expected header tag is either absent or malformed.");
            }

            Debug.Print(DebugUtility.CreateReportString("PackageReader", "ReadManifestHeader",
                                                        "Manifest header offset (absolute)",
                                                        sourceStream.Position));

            var manifestHeader = StratCom.DeserialiseDataTransferObject <ManifestHeader>(sourceStream, true);

            if (manifestHeader.FormatVersion <= 0)
            {
                throw new InvalidDataException("Package format descriptor is 0 or less (must be 1 or more).");
            }
            if (manifestHeader.FormatVersion > Athena.Packaging.PackageFormatVersion)
            {
                throw new NotSupportedException(
                          String.Format("Package version {0} as specified by the manifest header is unsupported/unknown.\n" +
                                        "The local version of ObscurCore supports up to version {1}.",
                                        manifestHeader.FormatVersion, Athena.Packaging.PackageFormatVersion));
                // In later versions, can redirect to diff. behaviour (and DTO objects) for diff. versions.
            }

            cryptoScheme = manifestHeader.CryptographyScheme;
            switch (cryptoScheme)
            {
            case ManifestCryptographyScheme.SymmetricOnly:
                cryptoConfig =
                    manifestHeader.CryptographySchemeConfiguration
                    .DeserialiseDto <SymmetricManifestCryptographyConfiguration>();
                break;

            case ManifestCryptographyScheme.Um1Hybrid:
                cryptoConfig =
                    manifestHeader.CryptographySchemeConfiguration
                    .DeserialiseDto <Um1HybridManifestCryptographyConfiguration>();
                break;

            default:
                throw new NotSupportedException(String.Format(
                                                    "Package manifest cryptography scheme \"{0}\" as specified by the manifest header is unsupported.",
                                                    manifestHeader.CryptographyScheme));
            }

            return(manifestHeader);
        }