示例#1
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;
                }
            }
        }
示例#2
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);
        }