public void Rfc23279TrySignDataUnderMax()
        {
            KeyDescription keyDescription = GetKey();
            ECDsa          key            = (ECDsa)keyDescription.Key;

            const DSASignatureFormat SignatureFormat = DSASignatureFormat.Rfc3279DerSequence;
            // Make secp521r1 (7/16 chance of being smaller) and mod-8 keys (3/4 chance of being smaller)
            // have the same 1-in-a-billion chance of failure.
            int retryCount = keyDescription.FieldSizeInBits % 8 == 1 ? 36 : 15;
            HashAlgorithmName hashAlgorithm = HashAlgorithmName.SHA1;

            int expectedSize = GetExpectedSize(keyDescription.FieldSizeInBits);
            int maxSize      = key.GetMaxSignatureSize(DSASignatureFormat.Rfc3279DerSequence);

            Assert.True(expectedSize < maxSize, "expectedSize < maxSize");
            byte[] signature = new byte[expectedSize];

            for (int i = 0; i < retryCount; i++)
            {
                if (key.TrySignData(Array.Empty <byte>(), signature, hashAlgorithm, SignatureFormat, out int written))
                {
                    return;
                }

                Assert.Equal(0, written);
            }

            Assert.True(false, $"TrySignData eventually succeeds with a {expectedSize}/{maxSize}-byte destination");
        }
        public void Rfc23279TrySignHashUnderMax()
        {
            KeyDescription keyDescription = GetKey();
            ECDsa          key            = (ECDsa)keyDescription.Key;

            const DSASignatureFormat SignatureFormat = DSASignatureFormat.Rfc3279DerSequence;
            // Make secp521r1 (7/16 chance of being smaller) and mod-8 keys (3/4 chance of being smaller)
            // have the same 1-in-a-billion chance of failure.
            int retryCount = keyDescription.FieldSizeInBits % 8 == 1 ? 36 : 15;

            byte[] hash = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };

            int expectedSize = GetExpectedSize(keyDescription.FieldSizeInBits);
            int maxSize      = key.GetMaxSignatureSize(DSASignatureFormat.Rfc3279DerSequence);

            Assert.True(expectedSize < maxSize, "expectedSize < maxSize");
            byte[] signature = new byte[expectedSize];

            for (int i = 0; i < retryCount; i++)
            {
                if (key.TrySignHash(hash, signature, SignatureFormat, out int written))
                {
                    return;
                }

                Assert.Equal(0, written);
            }

            Assert.True(false, $"TrySignHash eventually succeeds with a {expectedSize}/{maxSize}-byte destination");
        }
        public void Rfc23279TrySignDataUnderMax()
        {
            KeyDescription keyDescription = GetKey();
            ECDsa          key            = (ECDsa)keyDescription.Key;

            const DSASignatureFormat SignatureFormat = DSASignatureFormat.Rfc3279DerSequence;
            const int         RetryCount             = 10;
            HashAlgorithmName hashAlgorithm          = HashAlgorithmName.SHA1;

            int expectedSize = GetExpectedSize(keyDescription.FieldSizeInBits);
            int maxSize      = key.GetMaxSignatureSize(DSASignatureFormat.Rfc3279DerSequence);

            Assert.True(expectedSize < maxSize, "expectedSize < maxSize");
            byte[] signature = new byte[expectedSize];

            for (int i = 0; i < RetryCount; i++)
            {
                if (key.TrySignData(Array.Empty <byte>(), signature, hashAlgorithm, SignatureFormat, out int written))
                {
                    return;
                }

                Assert.Equal(0, written);
            }

            Assert.True(false, $"TrySignData eventually succeeds with a {expectedSize}/{maxSize}-byte destination");
        }
 protected override byte[] SignHash(
     KeyDescription key,
     byte[] hash,
     DSASignatureFormat signatureFormat)
 {
     return(((ECDsa)key.Key).SignHash(hash, signatureFormat));
 }
示例#5
0
        private static void CheckLength(KeyDescription key, byte[] signature, DSASignatureFormat signatureFormat)
        {
            int fieldSizeBytes = (key.FieldSizeInBits + 7) / 8;

            switch (signatureFormat)
            {
            case DSASignatureFormat.IeeeP1363FixedFieldConcatenation:

                Assert.Equal(2 * fieldSizeBytes, signature.Length);
                break;

            case DSASignatureFormat.Rfc3279DerSequence:
            {
                // SEQUENCE(INTEGER, INTEGER) has a minimum length of 8 (30 06 02 01 00 02 01 00)
                // The maximum length is a bit more complicated:
                int elemSize    = fieldSizeBytes + 1;
                int integerMax  = 2 + GetDerLengthLength(elemSize) + elemSize;
                int integersMax = 2 * integerMax;
                int sequenceMax = 2 + GetDerLengthLength(integersMax) + integersMax;

                Assert.InRange(signature.Length, 8, sequenceMax);
                break;
            }

            default:
                throw new InvalidOperationException($"No handler for format {signatureFormat}");
            }
        }
示例#6
0
        public void VerifyInvalidRfc3279Signature()
        {
            KeyDescription key = GetKey();

            // This is SEQUENCE(INTEGER(1), INTEGER(0)), except the second integer uses
            // a length value that exceeds the payload length.
            // This ensures that we don't throw exceptions after finding out the leading bytes
            // are valid sequence for the payload length.
            byte[] invalidSignature = { 0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x04, 0x00 };

            Assert.False(
                VerifyData(
                    key,
                    _typeNameBytes,
                    invalidSignature,
                    HashAlgorithmName.SHA1,
                    DSASignatureFormat.Rfc3279DerSequence),
                "VerifyData with an illegal DER payload");

            Assert.False(
                VerifyHash(
                    key,
                    _typeNameBytes,
                    invalidSignature,
                    DSASignatureFormat.Rfc3279DerSequence),
                "VerifyHash with an illegal DER payload");
        }
示例#7
0
        public void Rfc3279SignatureValidatesLength()
        {
            KeyDescription           key             = GetKey();
            HashAlgorithmName        hashAlgorithm   = HashAlgorithmName.SHA1;
            const DSASignatureFormat SignatureFormat = DSASignatureFormat.Rfc3279DerSequence;

            byte[] hash        = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
            byte[] signature   = SignHash(key, hash, SignatureFormat);
            byte[] rightPadded = signature.Concat(Enumerable.Repeat((byte)0, 4)).ToArray();

            Assert.True(
                VerifyHash(key, hash, signature, SignatureFormat),
                "VerifyHash with the unmodified signature");

            Assert.False(
                VerifyHash(key, hash, rightPadded, SignatureFormat),
                "VerifyHash with the right-padded signature");

            signature   = SignData(key, hash, hashAlgorithm, SignatureFormat);
            rightPadded = signature.Concat(Enumerable.Repeat((byte)0, 4)).ToArray();

            Assert.True(
                VerifyData(key, hash, signature, hashAlgorithm, SignatureFormat),
                "VerifyData with the unmodified signature");

            Assert.False(
                VerifyData(key, hash, rightPadded, hashAlgorithm, SignatureFormat),
                "VerifyData with the right-padded signature");
        }
        protected override byte[] SignData(
            KeyDescription key,
            byte[] data,
            HashAlgorithmName hashAlgorithm,
            DSASignatureFormat signatureFormat)
        {
            ECDsa dsa = (ECDsa)key.Key;

            byte[] predictedMax = new byte[dsa.GetMaxSignatureSize(signatureFormat)];

            Assert.True(
                dsa.TrySignData(data, predictedMax, hashAlgorithm, signatureFormat, out int written),
                "TrySignData with a GetMaxSignatureSize buffer");

            if (signatureFormat == DSASignatureFormat.IeeeP1363FixedFieldConcatenation)
            {
                // GetMaxSignatureSize should be exactly accurate for P1363.
                Assert.Equal(predictedMax.Length, written);
            }

            if (written == predictedMax.Length)
            {
                return(predictedMax);
            }

            return(predictedMax.AsSpan(0, written).ToArray());
        }
        public void Rfc23279TrySignHashUnderMax()
        {
            KeyDescription keyDescription = GetKey();
            ECDsa          key            = (ECDsa)keyDescription.Key;

            const DSASignatureFormat SignatureFormat = DSASignatureFormat.Rfc3279DerSequence;
            const int RetryCount = 10;

            byte[] hash = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };

            int expectedSize = GetExpectedSize(keyDescription.FieldSizeInBits);
            int maxSize      = key.GetMaxSignatureSize(DSASignatureFormat.Rfc3279DerSequence);

            Assert.True(expectedSize < maxSize, "expectedSize < maxSize");
            byte[] signature = new byte[expectedSize];

            for (int i = 0; i < RetryCount; i++)
            {
                if (key.TrySignHash(hash, signature, SignatureFormat, out int written))
                {
                    return;
                }

                Assert.Equal(0, written);
            }

            Assert.True(false, $"TrySignHash eventually succeeds with a {expectedSize}/{maxSize}-byte destination");
        }
 protected override bool VerifyHash(
     KeyDescription key,
     byte[] hash,
     byte[] signature,
     DSASignatureFormat signatureFormat)
 {
     return(((ECDsa)key.Key).VerifyHash(hash, signature, signatureFormat));
 }
 protected override byte[] SignData(
     KeyDescription key,
     byte[] data,
     HashAlgorithmName hashAlgorithm,
     DSASignatureFormat signatureFormat)
 {
     return(((ECDsa)key.Key).SignData(data, hashAlgorithm, signatureFormat));
 }
示例#12
0
        public void SignHashVerifyHash(DSASignatureFormat signatureFormat)
        {
            KeyDescription key = GetKey();

            byte[] hash      = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
            byte[] signature = SignHash(key, hash, signatureFormat);
            CheckLength(key, signature, signatureFormat);
            Assert.True(VerifyHash(key, hash, signature, signatureFormat));
        }
示例#13
0
        public void SignDataVerifyData_SHA1(DSASignatureFormat signatureFormat)
        {
            HashAlgorithmName hashAlgorithm = HashAlgorithmName.SHA1;

            KeyDescription key = GetKey();

            byte[] signature = SignData(key, _typeNameBytes, hashAlgorithm, signatureFormat);
            CheckLength(key, signature, signatureFormat);
            Assert.True(VerifyData(key, _typeNameBytes, signature, hashAlgorithm, signatureFormat));
        }
示例#14
0
        public void EmptyHashAlgorithm()
        {
            KeyDescription key = GetKey();

            byte[] empty = Array.Empty <byte>();

            foreach (DSASignatureFormat format in Enum.GetValues(typeof(DSASignatureFormat)))
            {
                AssertExtensions.Throws <ArgumentException>(
                    "hashAlgorithm",
                    () => SignData(key, empty, default, format));
        protected override bool VerifyHash(
            KeyDescription key,
            byte[] hash,
            byte[] signature,
            DSASignatureFormat signatureFormat)
        {
            ReadOnlySpan <byte> readOnlyHash      = hash;
            ReadOnlySpan <byte> readOnlySignature = signature;

            return(((ECDsa)key.Key).VerifyHash(readOnlyHash, readOnlySignature, signatureFormat));
        }
        protected override bool VerifyData(
            KeyDescription key,
            byte[] data,
            byte[] signature,
            HashAlgorithmName hashAlgorithm,
            DSASignatureFormat signatureFormat)
        {
            ReadOnlySpan <byte> readOnlyData      = data;
            ReadOnlySpan <byte> readOnlySignature = signature;

            return(((ECDsa)key.Key).VerifyData(readOnlyData, readOnlySignature, hashAlgorithm, signatureFormat));
        }
        public void OffsetAndCountOutOfRange()
        {
            KeyDescription keyDescription = GetKey();
            ECDsa          key            = (ECDsa)keyDescription.Key;

            HashAlgorithmName hash = HashAlgorithmName.SHA256;

            byte[] buffer = new byte[10];

            foreach (DSASignatureFormat format in Enum.GetValues(typeof(DSASignatureFormat)))
            {
                AssertExtensions.Throws <ArgumentOutOfRangeException>(
                    "offset",
                    () => key.SignData(buffer, -1, buffer.Length, hash, format));

                AssertExtensions.Throws <ArgumentOutOfRangeException>(
                    "offset",
                    () => key.SignData(buffer, buffer.Length + 1, 0, hash, format));

                AssertExtensions.Throws <ArgumentOutOfRangeException>(
                    "offset",
                    () => key.VerifyData(buffer, -1, buffer.Length, buffer, hash, format));

                AssertExtensions.Throws <ArgumentOutOfRangeException>(
                    "offset",
                    () => key.VerifyData(buffer, buffer.Length + 1, 0, buffer, hash, format));

                AssertExtensions.Throws <ArgumentOutOfRangeException>(
                    "count",
                    () => key.SignData(buffer, 1, buffer.Length, hash, format));

                AssertExtensions.Throws <ArgumentOutOfRangeException>(
                    "count",
                    () => key.SignData(buffer, 0, buffer.Length + 1, hash, format));

                AssertExtensions.Throws <ArgumentOutOfRangeException>(
                    "count",
                    () => key.SignData(buffer, buffer.Length, 1, hash, format));

                AssertExtensions.Throws <ArgumentOutOfRangeException>(
                    "count",
                    () => key.VerifyData(buffer, 1, buffer.Length, buffer, hash, format));

                AssertExtensions.Throws <ArgumentOutOfRangeException>(
                    "count",
                    () => key.VerifyData(buffer, 0, buffer.Length + 1, buffer, hash, format));

                AssertExtensions.Throws <ArgumentOutOfRangeException>(
                    "count",
                    () => key.VerifyData(buffer, buffer.Length, 1, buffer, hash, format));
            }
        }
    public override int StoreToStream(Stream stream, KeyDescription item)
    {
        if (item == null)
        {
            return(WriteVersionNull(stream));
        }
        var count = stream.WriteIntVariableLength(Version);

        count += stream.WriteString(item.StringProperty);
        count += stream.WriteBytes(item.BytesPropery);
        count += stream.WriteIntVariableLength(item.IntegerProperty);
        return(count);
    }
示例#19
0
        public void SignDataVerifyHash_SHA1(DSASignatureFormat signatureFormat)
        {
            HashAlgorithmName hashAlgorithm = HashAlgorithmName.SHA1;

            KeyDescription key = GetKey();

            byte[] signature = SignData(key, _typeNameBytes, hashAlgorithm, signatureFormat);
            CheckLength(key, signature, signatureFormat);

            using (IncrementalHash hash = IncrementalHash.CreateHash(hashAlgorithm))
            {
                hash.AppendData(_typeNameBytes);
                Assert.True(VerifyHash(key, hash.GetHashAndReset(), signature, signatureFormat));
            }
        }
        protected override byte[] SignData(
            KeyDescription key,
            byte[] data,
            HashAlgorithmName hashAlgorithm,
            DSASignatureFormat signatureFormat)
        {
            int offset = 0;
            int count  = 0;

            if (data != null)
            {
                offset = 2;
                count  = data.Length;
                byte[] bigger = new byte[count + 7];
                Buffer.BlockCopy(data, 0, bigger, offset, count);
                data = bigger;
            }

            return(((ECDsa)key.Key).SignData(data, offset, count, hashAlgorithm, signatureFormat));
        }
示例#21
0
        /// <summary>
        /// Make sure existing table is valid to be used as a session store.
        /// </summary>
        private void ValidateTable()
        {
            if (this._table.HashKeys.Count != 1)
            {
                throw new AmazonDynamoDBException(string.Format("Table {0} cannot be used to store session data because it does not define a single hash key", this._tableName));
            }
            string         hashKey            = this._table.HashKeys[0];
            KeyDescription hashKeyDescription = this._table.Keys[hashKey];

            if (hashKeyDescription.Type != DynamoDBEntryType.String)
            {
                throw new AmazonDynamoDBException(string.Format("Table {0} cannot be used to store session data because hash key is not a string.", this._tableName));
            }

            if (this._table.RangeKeys.Count > 0)
            {
                throw new AmazonDynamoDBException(string.Format("Table {0} cannot be used to store session data because it contains a range key in its schema.", this._tableName));
            }

            ATTRIBUTE_SESSION_ID = hashKey;
        }
示例#22
0
        public void SignatureFormatsAreNotCompatible(DSASignatureFormat signFormat, DSASignatureFormat verifyFormat)
        {
            if (!SupportsSha2)
            {
                return;
            }

            HashAlgorithmName hashAlgorithm = HashAlgorithmName.SHA1;
            const int         RetryCount    = 10;

            KeyDescription key = GetKey();

            byte[] hash;

            using (IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithm))
            {
                hasher.AppendData(_typeNameBytes);
                hash = hasher.GetHashAndReset();
            }

            for (int i = 0; i < RetryCount; i++)
            {
                byte[] signature = SignData(
                    key,
                    _typeNameBytes,
                    hashAlgorithm,
                    signFormat);

                if (!VerifyData(key, _typeNameBytes, signature, hashAlgorithm, verifyFormat))
                {
                    Assert.False(
                        VerifyHash(key, hash, signature, verifyFormat),
                        $"VerifyHash({verifyFormat}) verifies after VerifyData({verifyFormat}) fails");

                    return;
                }
            }

            Assert.False(true, $"{RetryCount} {signFormat} signatures verified as {verifyFormat} signatures");
        }
        static KeyDescription[]         FindKeys()
        {
            var foundKeys = new List <KeyDescription>();
            var type      = typeof(KeyEventType);
            var fields    = type.GetFields(BindingFlags.Static | BindingFlags.Public);

            foreach (var field in fields)
            {
                var pref      = new KeyDescription();
                var attribute = field.GetCustomAttributes(typeof(KeyDescriptionAttribute), false).FirstOrDefault() as KeyDescriptionAttribute;

                if (attribute == null)
                {
                    continue;
                }

                pref.Value    = (KeyEventType)field.GetValue(null);
                pref.KeyEvent = attribute.KeyEvent;
                foundKeys.Add(pref);
            }
            return(foundKeys.ToArray());
        }
示例#24
0
        public void BadSignatureFormat()
        {
            KeyDescription key = GetKey();

            const DSASignatureFormat SignatureFormat = (DSASignatureFormat)3;

            byte[] empty = Array.Empty <byte>();

            AssertExtensions.Throws <ArgumentOutOfRangeException>(
                "signatureFormat",
                () => SignData(key, empty, HashAlgorithmName.SHA1, SignatureFormat));

            AssertExtensions.Throws <ArgumentOutOfRangeException>(
                "signatureFormat",
                () => VerifyData(key, empty, empty, HashAlgorithmName.SHA1, SignatureFormat));

            AssertExtensions.Throws <ArgumentOutOfRangeException>(
                "signatureFormat",
                () => SignHash(key, empty, SignatureFormat));

            AssertExtensions.Throws <ArgumentOutOfRangeException>(
                "signatureFormat",
                () => VerifyHash(key, empty, empty, SignatureFormat));
        }
示例#25
0
        private void LoadTableInfo()
        {
            ClearTableData();

            bool staleCacheData;
            TableDescription table = TableInfoCache.GetValue(TableName, this.DescribeTable, out staleCacheData);
            this.ContainsCachedData = staleCacheData;
            if (this.ContainsCachedData)
                LoggerInstance.InfoFormat("Description for table [{0}] loaded from SDK Cache", TableName);

            foreach (var key in table.KeySchema)
            {
                string keyName = key.AttributeName;
                AttributeDefinition attributeDefinition = table.AttributeDefinitions
                    .FirstOrDefault(a => string.Equals(a.AttributeName, keyName, StringComparison.Ordinal));
                if (attributeDefinition == null) throw new InvalidOperationException("No attribute definition found for key " + key.AttributeName);
                KeyDescription keyDescription = new KeyDescription
                {
                    IsHash = string.Equals(key.KeyType, "HASH", StringComparison.OrdinalIgnoreCase),
                    Type = GetType(attributeDefinition.AttributeType)
                };
                if (keyDescription.IsHash)
                    HashKeys.Add(keyName);
                else
                    RangeKeys.Add(keyName);
                Keys[keyName] = keyDescription;
            }

            if (table.LocalSecondaryIndexes != null)
            {
                foreach (var index in table.LocalSecondaryIndexes)
                {
                    LocalSecondaryIndexes[index.IndexName] = index;
                    LocalSecondaryIndexNames.Add(index.IndexName);
                }
            }

            if (table.GlobalSecondaryIndexes != null)
            {
                foreach (var index in table.GlobalSecondaryIndexes)
                {
                    GlobalSecondaryIndexes[index.IndexName] = index;
                    GlobalSecondaryIndexNames.Add(index.IndexName);
                }
            }

            foreach (var attribute in table.AttributeDefinitions)
            {
                Attributes.Add(attribute);
            }

            KeyNames = Keys.Keys.ToArray();
        }
示例#26
0
 protected abstract bool VerifyData(
     KeyDescription key,
     byte[] data,
     byte[] signature,
     HashAlgorithmName hashAlgorithm,
     DSASignatureFormat signatureFormat);
示例#27
0
 protected abstract bool VerifyHash(
     KeyDescription key,
     byte[] hash,
     byte[] signature,
     DSASignatureFormat signatureFormat);
示例#28
0
 protected abstract byte[] SignHash(
     KeyDescription key,
     byte[] hash,
     DSASignatureFormat signatureFormat);
示例#29
0
        private void GetTableInfo()
        {
            DescribeTableRequest req = new DescribeTableRequest
            {
                TableName = TableName
            };
            req.BeforeRequestEvent += new RequestEventHandler(this.UserAgentRequestEventHandlerSync);
            DescribeTableResult info = this.DDBClient.DescribeTable(req);

            if (info.Table == null)
            {
                throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Table name {0} does not exist", TableName));
            }

            Keys.Clear();
            HashKeys.Clear();
            RangeKeys.Clear();
            TableDescription table = info.Table;
            foreach (var key in table.KeySchema)
            {
                string keyName = key.AttributeName;
                AttributeDefinition attributeDefinition = table.AttributeDefinitions
                    .FirstOrDefault(a => string.Equals(a.AttributeName, keyName, StringComparison.Ordinal));
                if (attributeDefinition == null) throw new InvalidOperationException("No attribute definition found for key " + key.AttributeName);
                KeyDescription keyDescription = new KeyDescription
                {
                    IsHash = string.Equals(key.KeyType, "HASH", StringComparison.OrdinalIgnoreCase),
                    Type = GetType(attributeDefinition.AttributeType)
                };
                if (keyDescription.IsHash)
                    HashKeys.Add(keyName);
                else
                    RangeKeys.Add(keyName);
                Keys[keyName] = keyDescription;
            }

            LocalSecondaryIndexes.Clear();
            LocalSecondaryIndexNames.Clear();
            if (table.LocalSecondaryIndexes != null)
            {
                foreach (var index in table.LocalSecondaryIndexes)
                {
                    LocalSecondaryIndexes[index.IndexName] = index;
                    LocalSecondaryIndexNames.Add(index.IndexName);
                }
            }

            GlobalSecondaryIndexes.Clear();
            GlobalSecondaryIndexNames.Clear();
            if (table.GlobalSecondaryIndexes != null)
            {
                foreach (var index in table.GlobalSecondaryIndexes)
                {
                    GlobalSecondaryIndexes[index.IndexName] = index;
                    GlobalSecondaryIndexNames.Add(index.IndexName);
                }
            }

            Attributes.Clear();
            foreach (var attribute in table.AttributeDefinitions)
            {
                Attributes.Add(attribute);
            }

            keyNames = Keys.Keys.ToArray();
        }
        /// <summary>
        ///     Deserialize graph from graphml file
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xml"></param>
        /// <param name="includeCost">if true the returned list will contain two Ops: [root, cost]</param>
        /// <returns></returns>
        public static List <Op <T> > FromXml <T>(string xml, bool includeCost) where T : struct, IEquatable <T>, IFormattable
        {
            var root  = "";
            var cost  = "";
            var keys  = new Dictionary <string, KeyDescription>();
            var nodes = new Dictionary <string, Node>();
            var edges = new List <Edge>();

            using (var sw = new StringReader(xml))
            {
                using (var reader = XmlReader.Create(sw))
                {
                    reader.MoveToContent();
                    while (reader.Read())
                    {
                        if (reader.NodeType == XmlNodeType.Element)
                        {
                            switch (reader.Name)
                            {
                            case "key":
                            {
                                var id       = reader.GetAttribute("id");
                                var name     = reader.GetAttribute("attr.name");
                                var firstDot = name.IndexOf('.');
                                if (firstDot != -1)
                                {
                                    name = name.Substring(name.IndexOf('.') + 1, name.Length - name.IndexOf('.') - 1);
                                }

                                keys[id] = new KeyDescription {
                                    Id = id, Name = name
                                };
                            }
                            break;

                            case "node":
                            {
                                var id   = reader.GetAttribute("id");
                                var node = new Node {
                                    Id = id
                                };
                                nodes[id] = node;

                                // Move to data section
                                do
                                {
                                    reader.Read();
                                } while (reader.Name != "data");

                                // Parse data
                                do
                                {
                                    var key     = reader.GetAttribute("key");
                                    var keyDesc = keys[key];
                                    node.Data[keyDesc.Name] = reader.ReadElementContentAsString();

                                    reader.Read();
                                } while (reader.Name == "data");
                            }
                            break;

                            case "edge":
                            {
                                var source = reader.GetAttribute("source");
                                var target = reader.GetAttribute("target");
                                edges.Add(new Edge {
                                        Source = source, Target = target
                                    });
                            }
                            break;

                            case "data":
                            {
                                var key     = reader.GetAttribute("key");
                                var keyDesc = keys[key];
                                if (keyDesc.Name == "root")
                                {
                                    root = reader.ReadElementContentAsString();
                                }
                                else if (keyDesc.Name == "cost")
                                {
                                    cost = reader.ReadElementContentAsString();
                                }
                            }
                            break;
                            }
                        }
                    }
                }
            }

            // Create Ops
            var ops = new Dictionary <string, Op <T> >();

            foreach (var node in nodes)
            {
                var type = Type.GetType((string)node.Value.Data["type"]);
                var op   = (Op <T>)Activator.CreateInstance(type, node.Value.Data);
                ops[node.Key] = op;
            }

            // Link Ops
            foreach (var edge in edges)
            {
                var source = ops[edge.Source];
                var target = ops[edge.Target];

                target.AddParent(source);
            }

            var result = new List <Op <T> > {
                ops[root]
            };

            if (includeCost)
            {
                if (cost != null)
                {
                    result.Add(ops[cost]);
                }
            }
            return(result);
        }
        /// <summary>
        ///     Serialize graph to graphml
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="op">Root op</param>
        /// <param name="costOp">Optional cost Op</param>
        /// <returns></returns>
        public static string ToXml <T>(this Op <T> op, Op <T> costOp = null) where T : struct, IEquatable <T>, IFormattable
        {
            var id   = 0;
            var set  = new Dictionary <Op <T>, string>();
            var keys = new Dictionary <string, KeyDescription>
            {
                { "d0", new KeyDescription {
                      Id = "d0", Name = "type"
                  } },
                { "d1", new KeyDescription {
                      Id = "d1", Name = "root", @for = "graph"
                  } },
                { "d2", new KeyDescription {
                      Id = "d2", Name = "cost", @for = "graph"
                  } }
            };

            // Retrieve all ops and assign an Id
            var visitor = new OpVisitor <T>(o =>
            {
                if (!set.ContainsKey(o))
                {
                    set.Add(o, "n" + id++);
                }
            });

            op.Accept(visitor);
            costOp?.Accept(visitor);

            using (var sw = new StringWriter())
            {
                using (var writer = XmlWriter.Create(sw, new XmlWriterSettings {
                    NewLineOnAttributes = true, Indent = true
                }))
                {
                    var ns = "http://graphml.graphdrawing.org/xmlns";

                    writer.WriteStartDocument();
                    writer.WriteStartElement("graphml", ns);
                    writer.WriteAttributeString("xmlns", ns);

                    // Get all keys
                    var keyId = 3;
                    foreach (var pair in set)
                    {
                        var data = pair.Key.GetData();
                        if (data.Any())
                        {
                            foreach (var o in data)
                            {
                                var name = pair.Key.GetType().Name + "." + o.Key;
                                if (!keys.ContainsKey(name))
                                {
                                    keys[name] = new KeyDescription {
                                        Id = "d" + keyId++, Name = name
                                    };
                                }
                            }
                        }
                    }

                    // Generate key xml
                    foreach (var keyDescription in keys.Values)
                    {
                        writer.WriteStartElement("key");
                        writer.WriteAttributeString("id", keyDescription.Id);
                        writer.WriteAttributeString("for", "node");
                        writer.WriteAttributeString("attr.name", keyDescription.Name);
                        writer.WriteAttributeString("attr.type", "string");
                        writer.WriteEndElement();
                    }

                    writer.WriteStartElement("graph");
                    writer.WriteAttributeString("id", "G");
                    writer.WriteAttributeString("edgedefault", "directed");

                    // Root
                    writer.WriteStartElement("data");
                    writer.WriteAttributeString("key", "d1");
                    writer.WriteString(set[op]);
                    writer.WriteEndElement();

                    // Cost if provided
                    if (costOp != null)
                    {
                        writer.WriteStartElement("data");
                        writer.WriteAttributeString("key", "d2");
                        writer.WriteString(set[costOp]);
                        writer.WriteEndElement();
                    }

                    foreach (var pair in set)
                    {
                        writer.WriteStartElement("node");
                        writer.WriteAttributeString("id", pair.Value);

                        writer.WriteStartElement("data");
                        writer.WriteAttributeString("key", "d0");
                        writer.WriteString(pair.Key.GetType().FullName);
                        writer.WriteEndElement();

                        var data = pair.Key.GetData();
                        if (data.Any())
                        {
                            foreach (var o in data)
                            {
                                var name    = pair.Key.GetType().Name + "." + o.Key;
                                var keyDesc = keys[name];

                                writer.WriteStartElement("data");
                                writer.WriteAttributeString("key", keyDesc.Id);
                                writer.WriteString(o.Value.ToString());
                                writer.WriteEndElement();
                            }
                        }

                        writer.WriteEndElement();
                    }

                    foreach (var pair in set)
                    {
                        foreach (var valueParent in pair.Key.Parents)
                        {
                            writer.WriteStartElement("edge");
                            writer.WriteAttributeString("source", set[valueParent]);
                            writer.WriteAttributeString("target", set[pair.Key]);
                            writer.WriteEndElement();
                        }
                    }

                    writer.WriteEndElement();

                    writer.WriteEndElement();
                }

                return(sw.ToString());
            }
        }