Exemple #1
0
        // TODO
        //void decodeCmcContentInfo(IntPtr rgTaggedRequest, UInt32 cTaggedRequest) {

        //}
        //void decodeCmcOtherMsg(IntPtr rgTaggedRequest, UInt32 cTaggedRequest) {

        //}
        void decodeCerts(Wincrypt.CRYPTOAPI_BLOB blob)
        {
            if (blob.cbData == 0)
            {
                return;
            }
            Byte[] certBytes = new Byte[blob.cbData];
            Marshal.Copy(blob.pbData, certBytes, 0, certBytes.Length);
            var asn = new Asn1Reader(certBytes);

            asn.MoveNext();
            do
            {
                _certificates.Add(new X509Certificate2(asn.GetTagRawData()));
            } while (asn.MoveNextCurrentLevel());
        }
Exemple #2
0
        void decodeCrls(Wincrypt.CRYPTOAPI_BLOB blob)
        {
            if (blob.cbData == 0)
            {
                return;
            }
            Byte[] crlBytes = new Byte[blob.cbData];
            Marshal.Copy(blob.pbData, crlBytes, 0, (Int32)blob.cbData);
            var asn = new Asn1Reader(crlBytes);

            asn.MoveNext();
            do
            {
                _crls.Add(new X509CRL2(asn.GetTagRawData()));
            } while (asn.MoveNextCurrentLevel());
        }
Exemple #3
0
        void m_decode(Byte[] rawData)
        {
            List <X509DistributionPoint> urls = new List <X509DistributionPoint>();
            Asn1Reader asn = new Asn1Reader(rawData);

            if (asn.Tag != 48)
            {
                throw new Asn1InvalidTagException(asn.Offset);
            }
            asn.MoveNext();
            do
            {
                urls.Add(new X509DistributionPoint(asn.GetTagRawData()));
            } while (asn.MoveNextCurrentLevel());
            CRLDistributionPoints = urls.ToArray();
        }
Exemple #4
0
        void decodeSignerInfos(Wincrypt.CRYPTOAPI_BLOB blob)
        {
            if (blob.cbData == 0)
            {
                return;
            }
            Byte[] signerBytes = new Byte[blob.cbData];
            Marshal.Copy(blob.pbData, signerBytes, 0, signerBytes.Length);
            var asn = new Asn1Reader(signerBytes);

            asn.MoveNext();
            do
            {
                _signerInfos.Add(new PkcsSignerInfo(asn.GetTagRawData(), _certificates));
            } while (asn.MoveNextCurrentLevel());
        }
Exemple #5
0
        static void buildTree(Asn1Reader root, Asn1TreeNode tree)
        {
            root.MoveNext();
            Int32 index = 0;

            do
            {
                tree.AddChild(new Asn1Lite(root, tree, index));
                index++;
            } while (root.MoveNextCurrentLevel());
            root.Reset();
            foreach (Asn1TreeNode node in tree.Children.Where(node => node.Value.IsContainer && node.Value.PayloadLength > 0))
            {
                root.MoveToPosition(node.Value.Offset);
                buildTree(root, node);
            }
        }
Exemple #6
0
        void decodeDigestAlgorithms(Asn1Reader asn)
        {
            // asn tag -> SET (0x31)
            Int32 offset = asn.Offset;

            if (asn.PayloadLength == 0)
            {
                return;
            }
            asn.MoveNext();
            do
            {
                _digestAlgorithms.Add(new AlgorithmIdentifier(asn.GetTagRawData()));
            } while (asn.MoveNextCurrentLevel());

            asn.MoveToPosition(offset);
        }
Exemple #7
0
        void m_decode(Byte[] rawData)
        {
            Asn1Reader asn = new Asn1Reader(rawData);

            asn.MoveNext();
            do
            {
                var integer = Asn1Utils.Encode(asn.GetPayload(), (Byte)Asn1Type.INTEGER);
                switch (asn.Tag)
                {
                case 0x80: RequireExplicitPolicy = (Int32)Asn1Utils.DecodeInteger(integer); break;

                case 0x81: InhibitPolicyMapping = (Int32)Asn1Utils.DecodeInteger(integer); break;

                default: throw new InvalidDataException("The data is invalid");
                }
            } while (asn.MoveNextCurrentLevel());
        }
        void m_decode(Byte[] rawData)
        {
            var asn = new Asn1Reader(rawData);

            asn.MoveNext();
            do
            {
                if (asn.PayloadLength > 0)
                {
                    switch (asn.Tag)
                    {
                    case 0xa0: _permittedSubtree.AddRange(decodeNamesFromAsn(asn.GetTagRawData())); break;

                    case 0xa1: _excludedSubtree.AddRange(decodeNamesFromAsn(asn.GetTagRawData())); break;
                    }
                }
            } while (asn.MoveNextSibling());
        }
Exemple #9
0
        /// <summary>
        /// Decodes ASN.1-encoded attribute (with envelope) to an instance of <strong>X509Attribute</strong> class.
        /// </summary>
        /// <param name="rawData">ASN.1-encoded attribute full data.</param>
        /// <exception cref="ArgumentNullException"><strong>rawData</strong> parameter is null.</exception>
        /// <exception cref="Asn1InvalidTagException">Invalid tag identifier occured.</exception>
        /// <returns>Instance of <strong>X509Attribute</strong> class</returns>
        public static X509Attribute Decode(Byte[] rawData)
        {
            if (rawData == null)
            {
                throw new ArgumentNullException(nameof(rawData));
            }
            Asn1Reader asn = new Asn1Reader(rawData);

            if (asn.Tag != 48)
            {
                throw new Asn1InvalidTagException(asn.Offset);
            }
            asn.MoveNext();
            Oid oid = Asn1Utils.DecodeObjectIdentifier(asn.GetTagRawData());

            asn.MoveNextAndExpectTags(0x31);
            return(new X509Attribute(oid, asn.GetPayload()));
        }
Exemple #10
0
        void decodeCertificates(Asn1Reader asn)
        {
            if (asn.PayloadLength == 0)
            {
                return;
            }
            Int32 offset = asn.Offset;

            asn.MoveNext();
            do
            {
                // sometimes we can get thing called ExtendedCertificate. I have no idea what it is.
                try {
                    _certificates.Add(new X509Certificate2(asn.GetTagRawData()));
                } catch { }
            } while (asn.MoveNextCurrentLevel());
            asn.MoveToPosition(offset);
        }
Exemple #11
0
        Byte[] extractContent(Asn1Reader asn)
        {
            Int32 offset = asn.Offset;

            asn.MoveNext();
            Byte[] payload = null;
            ContentType = new Asn1ObjectIdentifier(asn.GetTagRawData()).Value;
            if (asn.MoveNextCurrentLevel())
            {
                // content [0] EXPLICIT ANY DEFINED BY contentType
                asn.MoveNextAndExpectTags((Byte)Asn1Type.OCTET_STRING, 48); // octet string or sequence
                payload       = asn.GetPayload();
                contentOffset = asn.Offset;
                contentSize   = asn.TagLength;
            }
            asn.MoveToPosition(offset);
            return(payload);
        }
        /// <summary>
        /// Decodes ASN.1-encoded attribute collection.
        /// </summary>
        /// <param name="rawData">ASN.1-encoded byte array that represents attribute collection.</param>
        /// <exception cref="ArgumentNullException">
        /// <strong>rawData</strong> parameter is null.
        /// </exception>
        public void Decode(Byte[] rawData)
        {
            if (rawData == null)
            {
                throw new ArgumentNullException(nameof(rawData));
            }
            Clear();
            Asn1Reader asn = new Asn1Reader(rawData);

            if (asn.PayloadLength == 0)
            {
                return;
            }
            asn.MoveNext();
            do
            {
                _list.Add(X509Attribute.Decode(asn.GetTagRawData()));
            } while (asn.MoveNextCurrentLevel());
        }
        void decode(Byte[] rawData)
        {
            var asn = new Asn1Reader(rawData);

            asn.MoveNextAndExpectTags((Byte)Asn1Type.OCTET_STRING);
            Thumbprint = AsnFormatter.BinaryToString(asn.GetPayload(), format: EncodingFormat.NOCRLF, forceUpperCase: true);
            // check if there are attributes
            if (asn.MoveNext() && asn.Tag == 49)
            {
                Byte[] attrBytes = asn.GetTagRawData();
                // in CTL attributes are encoded as SET, but we need SEQUENCE, so change first byte to SEQUENCE (48)
                attrBytes[0] = 48;
                var attributes = new X509AttributeCollection();
                // decode attributes into collection
                attributes.Decode(attrBytes);
                // and then add decoded attributes to internal list.
                _attributes.AddRange(attributes);
            }
        }
        /// <summary>
        /// Decodes a collection of certificate policies from a ASN.1-encoded byte array.
        /// <para>
        /// Byte array in the <strong>rawData</strong> parameter must represent certificate policies extension value.
        /// </para>
        /// </summary>
        /// <param name="rawData">ASN.1-encoded byte array that represents certificate policies extension value.</param>
        /// <exception cref="InvalidDataException">The data in the <strong>rawData</strong> parameter is not valid
        /// extension value.</exception>
        /// <exception cref="ArgumentNullException"><strong>rawData</strong> is null.</exception>
        public void Decode(Byte[] rawData)
        {
            if (rawData == null)
            {
                throw new ArgumentNullException(nameof(rawData));
            }
            _list.Clear();
            Asn1Reader asn = new Asn1Reader(rawData);

            if (asn.Tag != 48)
            {
                throw new InvalidDataException("The data is invalid.");
            }
            asn.MoveNext();
            do
            {
                _list.Add(new X509CertificatePolicy(asn.GetTagRawData()));
            } while (asn.MoveNextCurrentLevel());
        }
Exemple #15
0
        /// <summary>
        /// Decodes a collection of certificate policies from a ASN.1-encoded byte array.
        /// <para>
        /// Byte array in the <strong>rawData</strong> parameter must represent certificate policies extension value.
        /// </para>
        /// </summary>
        /// <param name="rawData">ASN.1-encoded byte array that represents certificate policies extension value.</param>
        /// <exception cref="Asn1InvalidTagException">The data in the <strong>rawData</strong> parameter is not valid
        /// extension value.</exception>
        /// <exception cref="ArgumentNullException"><strong>rawData</strong> is null.</exception>
        public void Decode(Byte[] rawData)
        {
            if (rawData == null)
            {
                throw new ArgumentNullException(nameof(rawData));
            }
            InternalList.Clear();
            Asn1Reader asn = new Asn1Reader(rawData);

            if (asn.Tag != 48)
            {
                throw new Asn1InvalidTagException(asn.Offset);
            }
            asn.MoveNext();
            do
            {
                InternalList.Add(new X509CertificatePolicy(asn.GetTagRawData()));
            } while (asn.MoveNextCurrentLevel());
        }
        /// <summary>
        /// Decodes ASN.1-encoded algorithm identifier collection.
        /// </summary>
        /// <param name="rawData">ASN.1-encoded byte array that represents algorithm identifier collection.</param>
        /// <exception cref="ArgumentNullException">
        /// <strong>rawData</strong> parameter is null.
        /// </exception>
        public void Decode(Byte[] rawData)
        {
            if (rawData == null)
            {
                throw new ArgumentNullException(nameof(rawData));
            }
            Clear();
            var asn = new Asn1Reader(rawData);

            if (asn.PayloadLength == 0)
            {
                return;
            }
            asn.MoveNext();
            do
            {
                InternalList.Add(new AlgorithmIdentifier(asn.GetTagRawData()));
            } while (asn.MoveNextCurrentLevel());
        }
        /// <summary>
        /// Decodes ASN.1-encoded certificate trust list collection.
        /// </summary>
        /// <param name="rawData">ASN.1-encoded byte array that represents certificate trust list collection.</param>
        /// <exception cref="ArgumentNullException">
        /// <strong>rawData</strong> parameter is null.
        /// </exception>
        public void Decode(Byte[] rawData)
        {
            if (rawData == null)
            {
                throw new ArgumentNullException(nameof(rawData));
            }
            Clear();
            var asn = new Asn1Reader(rawData);

            if (asn.PayloadLength == 0)
            {
                return;
            }
            asn.MoveNext();
            do
            {
                var entry = new X509CertificateTrustListEntry(new AsnEncodedData(asn.GetTagRawData()));
                InternalList.Add(entry);
            } while (asn.MoveNextCurrentLevel());
        }
Exemple #18
0
        void decodeAlgorithms(Wincrypt.CRYPTOAPI_BLOB blob)
        {
            if (blob.cbData == 0)
            {
                return;
            }
            Byte[] rawData = new Byte[blob.cbData];
            Marshal.Copy(blob.pbData, rawData, 0, (Int32)blob.cbData);
            Asn1Reader asn = new Asn1Reader(rawData);

            if (asn.Tag != 49)
            {
                throw new Asn1InvalidTagException(asn.Offset);
            }
            asn.MoveNext();
            do
            {
                _digestAlgs.Add(new AlgorithmIdentifier(asn.GetTagRawData()).AlgorithmId);
            } while (asn.MoveNextCurrentLevel());
        }
Exemple #19
0
        void decode(Byte[] rawData)
        {
            Asn1Reader asn = new Asn1Reader(rawData);

            asn.MoveNext();
            Version = (Int32)Asn1Utils.DecodeInteger(asn.GetTagRawData());
            asn.MoveNextCurrentLevel();
            Issuer = new SubjectIdentifier2(asn.GetTagRawData());
            asn.MoveNextCurrentLevel();
            HashAlgorithm = new AlgorithmIdentifier(asn.GetTagRawData());
            asn.MoveNextCurrentLevel();
            if (asn.Tag == 0xa0)
            {
                AuthenticatedAttributes.Decode(asn.GetTagRawData());
                asn.MoveNextCurrentLevel();
            }
            EncryptedHashAlgorithm = new AlgorithmIdentifier(asn.GetTagRawData());
            asn.MoveNextCurrentLevel();
            EncryptedHash = asn.GetPayload();
        }
        void decodeAlgorithms(Wincrypt.CRYPTOAPI_BLOB blob)
        {
            if (blob.cbData == 0)
            {
                return;
            }
            digestAlgs = new List <Oid>();
            Byte[] rawData = new Byte[blob.cbData];
            Marshal.Copy(blob.pbData, rawData, 0, (Int32)blob.cbData);
            Asn1Reader asn = new Asn1Reader(rawData);

            if (asn.Tag != 49)
            {
                throw new InvalidDataException("The data is invalid.");
            }
            asn.MoveNext();
            do
            {
                digestAlgs.Add(new PKI.ManagedAPI.StructClasses.AlgorithmIdentifier(asn.GetTagRawData()).AlgorithmId);
            } while (asn.MoveNextCurrentLevel());
        }
Exemple #21
0
        void initializeFromAsn(Byte[] rawData)
        {
            Asn1Reader asn = new Asn1Reader(rawData);

            if (asn.Tag != 48)
            {
                throw new Asn1InvalidTagException(asn.Offset);
            }
            asn.MoveNext();
            HashingAlgorithm = new AlgorithmIdentifier(Asn1Utils.Encode(asn.GetPayload(), 48)).AlgorithmId;
            asn.MoveNextCurrentLevelAndExpectTags((Byte)Asn1Type.OCTET_STRING);
            // issuerNameHash
            IssuerNameId = AsnFormatter.BinaryToString(asn.GetPayload()).Trim();
            asn.MoveNextCurrentLevelAndExpectTags((Byte)Asn1Type.OCTET_STRING);
            // issuerKeyId
            IssuerKeyId = AsnFormatter.BinaryToString(asn.GetPayload()).Trim();
            asn.MoveNextCurrentLevelAndExpectTags((Byte)Asn1Type.INTEGER);
            // serialnumber
            serialNumber = asn.GetPayload();
            IsReadOnly   = true;
        }
Exemple #22
0
 void getAttributes(Asn1Reader asn)
 {
     asn.MoveNext();
     if (asn.PayloadLength == 0)
     {
         return;
     }
     do
     {
         X509Attribute attribute = X509Attribute.Decode(asn.GetTagRawData());
         if (attribute.Oid.Value == X509CertExtensions.X509CertificateExtensions)
         {
             //Extensions
             Extensions.Decode(attribute.RawData);
         }
         else
         {
             Attributes.Add(attribute);
         }
     } while (asn.MoveNextCurrentLevel());
 }
        /// <summary>
        /// Decodes a ASN.1-encoded byte array that contains revoked certificate information to a collection.
        /// </summary>
        /// <param name="rawData">ASN.1-encoded byte array.</param>
        /// <exception cref="InvalidDataException">The encoded data is not valid.</exception>
        /// <exception cref="ArgumentNullException">The <strong>rawData</strong> parameter is null reference.</exception>
        public void Decode(Byte[] rawData)
        {
            if (rawData == null)
            {
                throw new ArgumentNullException(nameof(rawData));
            }
            Asn1Reader asn = new Asn1Reader(rawData);

            if (asn.Tag != 48)
            {
                throw new InvalidDataException();
            }
            if (!asn.MoveNext())
            {
                throw new InvalidDataException();
            }
            do
            {
                _list.Add(new X509CRLEntry(asn.GetTagRawData()));
            } while (asn.MoveNextCurrentLevel());
        }
Exemple #24
0
        /// <summary>
        /// Decodes ASN.1-encoded byte array that represents a collection of <see cref="X509Certificate2"/> objects.
        /// </summary>
        /// <param name="collection">Destination collection where decoded certificates will be added.</param>
        /// <param name="rawData">ASN.1-encoded byte array that represents certificate collection.</param>
        /// <exception cref="ArgumentNullException">
        /// <strong>extensions</strong> and/or <strong>rawData</strong> parameter is null.
        /// </exception>
        /// <remarks>
        /// If current collection contains items, decoded items will be appended to existing items.
        /// <para>This method is not the same as <see cref="X509Certificate2Collection.Import(Byte[])">
        /// X509Certificate2Collection.Import</see> method and accepts ASN.1-style collection.</para>
        /// </remarks>
        ///
        public static void Decode(this X509Certificate2Collection collection, Byte[] rawData)
        {
            if (collection == null)
            {
                throw new ArgumentNullException(nameof(collection));
            }
            if (rawData == null)
            {
                throw new ArgumentNullException(nameof(rawData));
            }

            Asn1Reader asn = new Asn1Reader(rawData);

            if (!asn.MoveNext() || asn.NextOffset == 0)
            {
                return;
            }
            do
            {
                collection.Add(new X509Certificate2(asn.GetTagRawData()));
            } while (asn.MoveNextCurrentLevel());
        }
Exemple #25
0
        /// <summary>
        /// Decodes ASN.1 encoded byte array to an array of <see cref="X509AlternativeName"/> objects.
        /// </summary>
        /// <param name="rawData">ASN.1-encoded byte array.</param>
        /// <exception cref="ArgumentNullException">
        ///     <strong>rawData</strong> parameter is null.
        /// </exception>
        /// <exception cref="Asn1InvalidTagException">
        /// The data in the <strong>rawData</strong> parameter is not valid array of <see cref="X509AlternativeName"/> objects.
        /// </exception>
        public void Decode(Byte[] rawData)
        {
            if (IsReadOnly)
            {
                throw new AccessViolationException(Error.E_COLLECTIONCLOSED);
            }
            if (rawData == null)
            {
                throw new ArgumentNullException(nameof(rawData));
            }
            InternalList.Clear();
            var asn = new Asn1Reader(rawData);

            if (!asn.MoveNext())
            {
                return;
            }
            do
            {
                InternalList.Add(new X509AlternativeName(asn.GetTagRawData()));
            } while (asn.MoveNextCurrentLevel());
        }
Exemple #26
0
        // non-default, some platforms may not support all of them

        public static PublicKey FromRawData(Byte[] rawData)
        {
            if (rawData == null)
            {
                throw new ArgumentNullException(nameof(rawData));
            }
            Asn1Reader asn = new Asn1Reader(rawData);

            asn.MoveNext();
            Asn1Reader pubKeyOidIdReader = new Asn1Reader(asn.GetTagRawData());

            pubKeyOidIdReader.MoveNext();
            Oid pubKeyOid = Asn1Utils.DecodeObjectIdentifier(pubKeyOidIdReader.GetTagRawData());

            pubKeyOidIdReader.MoveNext();
            AsnEncodedData encodedParams = new AsnEncodedData(pubKeyOid, pubKeyOidIdReader.GetTagRawData());

            asn.MoveNextCurrentLevel();
            AsnEncodedData encodedKey = new AsnEncodedData(pubKeyOid, new Asn1BitString(asn.GetTagRawData()).Value.ToArray());

            return(new PublicKey(pubKeyOid, encodedParams, encodedKey));
        }
Exemple #27
0
        /// <summary>
        /// Decodes ASN.1 encoded byte array to an array of <see cref="X509AlternativeName"/> objects.
        /// </summary>
        /// <param name="rawData">ASN.1-encoded byte array.</param>
        /// <exception cref="InvalidDataException">
        /// The data in the <strong>rawData</strong> parameter is not valid array of <see cref="X509AlternativeName"/> objects.
        /// </exception>
        public void Decode(Byte[] rawData)
        {
            if (IsReadOnly)
            {
                throw new AccessViolationException("An object is encoded and is write-protected.");
            }
            if (rawData == null)
            {
                throw new ArgumentNullException("rawData");
            }
            _list.Clear();
            Asn1Reader asn = new Asn1Reader(rawData);

            if (asn.Tag != 48)
            {
                throw new ArgumentException("The parameter is incorrect.");
            }
            asn.MoveNext();
            do
            {
                _list.Add(new X509AlternativeName(asn.GetTagRawData()));
            } while (asn.MoveNextCurrentLevel());
        }
        /// <summary>
        /// Decodes ASN.1-encoded byte array that represents a collection of <see cref="X509Extension"/> objects.
        /// </summary>
        /// <param name="extensions">Destination collection where decoded extensions will be added.</param>
        /// <param name="asn">ASN.1 reader which points to the beginning of the extenstion collection structure.</param>
        /// <exception cref="Asn1InvalidTagException">Decoder encountered an unexpected ASN.1 type identifier.</exception>
        /// <exception cref="ArgumentNullException">
        /// <strong>extensions</strong> and/or <strong>asn</strong> parameter is null.
        /// </exception>
        /// <remarks> If current collection contains items, decoded items will be appended to existing items.</remarks>
        public static void Decode(this X509ExtensionCollection extensions, Asn1Reader asn)
        {
            if (extensions == null)
            {
                throw new ArgumentNullException(nameof(extensions));
            }
            if (asn == null)
            {
                throw new ArgumentNullException(nameof(asn));
            }
            Int32 offset = asn.Offset;

            if (!asn.MoveNext() || asn.PayloadLength == 0)
            {
                return;
            }

            do
            {
                extensions.Add(X509ExtensionExtensions.Decode(asn));
            } while (asn.MoveNextSibling());
            asn.Seek(offset);
        }
Exemple #29
0
        static void buildTree(Asn1Reader asn, Asn1Node node, Asn1Tree rootTree)
        {
            asn.MoveNext();
            List <Int32> subNodeIndexes = new List <Int32>();
            Int32        index          = 0;

            do
            {
                node.AddUnsafe(new Asn1Node(asn, rootTree));
                if (asn.IsConstructed)
                {
                    subNodeIndexes.Add(index);
                }
                index++;
            } while (asn.MoveNextCurrentLevel());
            asn.Reset();
            foreach (Int32 subNodeIndex in subNodeIndexes)
            {
                Asn1Node subNode = node.Children[subNodeIndex];
                asn.MoveToPoisition(subNode.Offset);
                buildTree(asn, subNode, rootTree);
            }
        }
        void decodeCMC(Byte[] contentBytes)
        {
            Asn1Reader asn = new Asn1Reader(contentBytes);

            asn.MoveNext();
            UInt32 pcbStructInfo = 0;

            if (!Crypt32.CryptDecodeObject(1, Wincrypt.CMC_DATA, asn.GetTagRawData(), (UInt32)asn.TagLength, 0, IntPtr.Zero, ref pcbStructInfo))
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
            IntPtr pvStructInfo = Marshal.AllocHGlobal((Int32)pcbStructInfo);

            try {
                Crypt32.CryptDecodeObject(1, Wincrypt.CMC_DATA, asn.GetTagRawData(), (UInt32)asn.TagLength, 0, pvStructInfo, ref pcbStructInfo);
                Wincrypt.CMC_DATA_INFO cmc = (Wincrypt.CMC_DATA_INFO)Marshal.PtrToStructure(pvStructInfo, typeof(Wincrypt.CMC_DATA_INFO));
                decodeAttributes(cmc.rgTaggedAttribute, cmc.cTaggedAttribute);
                decodeRequest(cmc.rgTaggedRequest, cmc.cTaggedRequest);
                Content = requests.ToArray();
            } finally {
                Marshal.FreeHGlobal(pvStructInfo);
            }
        }