Example #1
0
        public static void ImportEncryptedPem <TPass>(
            ReadOnlySpan <char> input,
            ReadOnlySpan <TPass> password,
            ImportEncryptedKeyAction <TPass> importAction)
        {
            bool                foundEncryptedPem = false;
            PemFields           foundFields       = default;
            ReadOnlySpan <char> foundSlice        = default;

            ReadOnlySpan <char> pem = input;

            while (PemEncoding.TryFind(pem, out PemFields fields))
            {
                ReadOnlySpan <char> label = pem[fields.Label];

                if (label.SequenceEqual(PemLabels.EncryptedPkcs8PrivateKey))
                {
                    if (foundEncryptedPem)
                    {
                        throw new ArgumentException(SR.Argument_PemImport_AmbiguousPem, nameof(input));
                    }

                    foundEncryptedPem = true;
                    foundFields       = fields;
                    foundSlice        = pem;
                }

                Index offset = fields.Location.End;
                pem = pem[offset..];
Example #2
0
        /// <summary>
        /// Attempts to find the first PEM-encoded data.
        /// </summary>
        /// <param name="pemData">
        /// The text containing the PEM-encoded data.
        /// </param>
        /// <param name="fields">
        /// When this method returns, contains a value
        /// that specifies the location, label, and data location of the encoded data;
        /// or that specifies those locations as empty if no PEM-encoded data is found.
        /// This parameter is treated as uninitialized.
        /// </param>
        /// <returns>
        /// <c>true</c> if PEM-encoded data was found; otherwise <c>false</c>.
        /// </returns>
        /// <remarks>
        /// IETF RFC 7468 permits different decoding rules. This method
        /// always uses lax rules.
        /// </remarks>
        public static bool TryFind(ReadOnlySpan <char> pemData, out PemFields fields)
        {
            // Check for the minimum possible encoded length of a PEM structure
            // and exit early if there is no way the input could contain a well-formed
            // PEM.
            if (pemData.Length < PreEBPrefix.Length + Ending.Length * 2 + PostEBPrefix.Length)
            {
                fields = default;
                return(false);
            }

            const int   PostebStackBufferSize = 256;
            Span <char> postebStackBuffer     = stackalloc char[PostebStackBufferSize];
            int         areaOffset            = 0;
            int         preebIndex;

            while ((preebIndex = pemData.IndexOfByOffset(PreEBPrefix, areaOffset)) >= 0)
            {
                int labelStartIndex = preebIndex + PreEBPrefix.Length;

                // If there are any previous characters, the one prior to the PreEB
                // must be a white space character.
                if (preebIndex > 0 && !IsWhiteSpaceCharacter(pemData[preebIndex - 1]))
                {
                    areaOffset = labelStartIndex;
                    continue;
                }

                int preebEndIndex = pemData.IndexOfByOffset(Ending, labelStartIndex);

                // There is no ending sequence, -----, in the remainder of
                // the document. Therefore, there can never be a complete PreEB
                // and we can exit.
                if (preebEndIndex < 0)
                {
                    fields = default;
                    return(false);
                }

                Range labelRange          = labelStartIndex..preebEndIndex;
                ReadOnlySpan <char> label = pemData[labelRange];

                // There could be a preeb that is valid after this one if it has an invalid
                // label, so move from there.
                if (!IsValidLabel(label))
                {
                    goto NextAfterLabel;
                }

                int contentStartIndex = preebEndIndex + Ending.Length;
                int postebLength      = PostEBPrefix.Length + label.Length + Ending.Length;

                Span <char> postebBuffer = postebLength > PostebStackBufferSize
                    ? new char[postebLength]
                    : postebStackBuffer;
                ReadOnlySpan <char> posteb = WritePostEB(label, postebBuffer);
                int postebStartIndex       = pemData.IndexOfByOffset(posteb, contentStartIndex);

                if (postebStartIndex < 0)
                {
                    goto NextAfterLabel;
                }

                int pemEndIndex = postebStartIndex + postebLength;

                // The PostEB must either end at the end of the string, or
                // have at least one white space character after it.
                if (pemEndIndex < pemData.Length - 1 &&
                    !IsWhiteSpaceCharacter(pemData[pemEndIndex]))
                {
                    goto NextAfterLabel;
                }

                Range contentRange = contentStartIndex..postebStartIndex;

                if (!TryCountBase64(pemData[contentRange], out int base64start, out int base64end, out int decodedSize))
                {
                    goto NextAfterLabel;
                }

                Range pemRange    = preebIndex..pemEndIndex;
                Range base64range = (contentStartIndex + base64start)..(contentStartIndex + base64end);
                fields = new PemFields(labelRange, base64range, pemRange, decodedSize);
                return(true);

NextAfterLabel:
                if (preebEndIndex <= areaOffset)
                {
                    // We somehow ended up in a situation where we will advance
                    // backward or not at all, which means we'll probably end up here again,
                    // advancing backward, in a loop. To avoid getting stuck,
                    // detect this situation and return.
                    fields = default;
                    return(false);
                }
                areaOffset = preebEndIndex;
            }

            fields = default;
            return(false);
Example #3
0
 public PemFieldItem(ReadOnlySpan <char> contents, PemFields pemFields)
 {
     _contents  = contents;
     _pemFields = pemFields;
 }
Example #4
0
 public void Deconstruct(out ReadOnlySpan <char> contents, out PemFields pemFields)
 {
     contents  = _contents;
     pemFields = _pemFields;
 }
Example #5
0
 public Enumerator(ReadOnlySpan <char> contents)
 {
     _contents  = contents;
     _pemFields = default;
 }