diff options
author | mw <megan@cryptoworkshop.com> | 2021-09-03 14:35:32 +1000 |
---|---|---|
committer | mw <megan@cryptoworkshop.com> | 2021-09-03 14:35:32 +1000 |
commit | 33c6a78371a5f6789c36186d7b89576a7c19d942 (patch) | |
tree | 98b77aee1360d479ece0de5642b82c8ad67a47d4 /crypto/src | |
parent | moved test (diff) | |
download | BouncyCastle.NET-ed25519-33c6a78371a5f6789c36186d7b89576a7c19d942.tar.xz |
Updated PemReader to support differing whitespace assumptions.
Added tests. Issue #2
Diffstat (limited to 'crypto/src')
-rw-r--r-- | crypto/src/util/io/pem/PemHeader.cs | 7 | ||||
-rw-r--r-- | crypto/src/util/io/pem/PemReader.cs | 355 |
2 files changed, 324 insertions, 38 deletions
diff --git a/crypto/src/util/io/pem/PemHeader.cs b/crypto/src/util/io/pem/PemHeader.cs index 72da8a4f7..c6236f534 100644 --- a/crypto/src/util/io/pem/PemHeader.cs +++ b/crypto/src/util/io/pem/PemHeader.cs @@ -51,5 +51,10 @@ namespace Org.BouncyCastle.Utilities.IO.Pem return s.GetHashCode(); } - } + + public override string ToString() + { + return name + ":" + val; + } + } } diff --git a/crypto/src/util/io/pem/PemReader.cs b/crypto/src/util/io/pem/PemReader.cs index 7e6252b9b..008a03524 100644 --- a/crypto/src/util/io/pem/PemReader.cs +++ b/crypto/src/util/io/pem/PemReader.cs @@ -1,24 +1,32 @@ using System; using System.Collections; using System.IO; -using System.Text; + using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Utilities.IO.Pem { - public class PemReader - { - private const string BeginString = "-----BEGIN "; - private const string EndString = "-----END "; + public class PemReader + { private readonly TextReader reader; + private readonly MemoryStream buffer; + private readonly StreamWriter textBuffer; + private readonly IList pushback = Platform.CreateArrayList(); + int c = 0; + + public PemReader(TextReader reader) { if (reader == null) throw new ArgumentNullException("reader"); + + buffer = new MemoryStream(); + textBuffer = new StreamWriter(buffer); + this.reader = reader; } @@ -27,64 +35,337 @@ namespace Org.BouncyCastle.Utilities.IO.Pem get { return reader; } } + /// <returns> /// A <see cref="PemObject"/> /// </returns> - /// <exception cref="IOException"></exception> + /// <exception cref="IOException"></exception> public PemObject ReadPemObject() - { - string line = reader.ReadLine(); + { - while (line != null && !Platform.StartsWith(line, BeginString)) + // + // Look for BEGIN + // + + for (; ; ) + { + + // Seek a leading dash, ignore anything up to that point. + if (!seekDash()) + { + // There are no pem objects here. + return null; + } + + + // consume dash [-----]BEGIN ... + if (!consumeDash()) + { + throw new IOException("no data after consuming leading dashes"); + } + + + skipWhiteSpace(); + + + if (!expect("BEGIN")) + { + continue; + } + + break; + + } + + + skipWhiteSpace(); + + // + // Consume type, accepting whitespace + // + + if (!bufferUntilStopChar('-',false) ) { - line = reader.ReadLine(); - } + throw new IOException("ran out of data before consuming type"); + } + + string type = bufferedString().Trim(); - if (line != null) + + // Consume dashes after type. + + if (!consumeDash()) + { + throw new IOException("ran out of data consuming header"); + } + + skipWhiteSpace(); + + + // + // Read ahead looking for headers. + // Look for a colon for up to 64 characters, as an indication there might be a header. + // + + IList headers = Platform.CreateArrayList(); + + while (seekColon(64)) { - line = line.Substring(BeginString.Length); - int index = line.IndexOf('-'); - if (index > 0 && Platform.EndsWith(line, "-----") && (line.Length - index) == 5) + if (!bufferUntilStopChar(':',false)) { - string type = line.Substring(0, index); + throw new IOException("ran out of data reading header key value"); + } + + string key = bufferedString().Trim(); + - return LoadObject(type); + c = Read(); + if (c != ':') + { + throw new IOException("expected colon"); } - } + + + // + // We are going to look for well formed headers, if they do not end with a "LF" we cannot + // discern where they end. + // + + if (!bufferUntilStopChar('\n', false)) // Now read to the end of the line. + { + throw new IOException("ran out of data before consuming header value"); + } + + skipWhiteSpace(); + + string value = bufferedString().Trim(); + headers.Add(new PemHeader(key,value)); + } + + + // + // Consume payload, ignoring all white space until we encounter a '-' + // + + skipWhiteSpace(); + + if (!bufferUntilStopChar('-',true)) + { + throw new IOException("ran out of data before consuming payload"); + } + + string payload = bufferedString(); + + // Seek the start of the end. + if (!seekDash()) + { + throw new IOException("did not find leading '-'"); + } + + if (!consumeDash()) + { + throw new IOException("no data after consuming trailing dashes"); + } + + if (!expect("END "+type)) + { + throw new IOException("END "+type+" was not found."); + } + + + + if (!seekDash()) + { + throw new IOException("did not find ending '-'"); + } + + + // consume trailing dashes. + consumeDash(); + + + return new PemObject(type, headers, Base64.Decode(payload)); - return null; } - private PemObject LoadObject(string type) + + + private string bufferedString() + { + textBuffer.Flush(); + string value = Strings.FromUtf8ByteArray(buffer.ToArray()); + buffer.Position = 0; + buffer.SetLength(0); + return value; + } + + + private bool seekDash() + { + c = 0; + while((c = Read()) >=0) + { + if (c == '-') + { + break; + } + } + + PushBack(c); + + return c == '-'; + } + + + /// <summary> + /// Seek ':" up to the limit. + /// </summary> + /// <param name="upTo"></param> + /// <returns></returns> + private bool seekColon(int upTo) { - string endMarker = EndString + type; - IList headers = Platform.CreateArrayList(); - StringBuilder buf = new StringBuilder(); + c = 0; + bool colonFound = false; + IList read = Platform.CreateArrayList(); + + for (; upTo>=0 && c >=0; upTo--) + { + c = Read(); + read.Add(c); + if (c == ':') + { + colonFound = true; + break; + } + } - string line; - while ((line = reader.ReadLine()) != null) + while(read.Count>0) + { + PushBack((int)read[read.Count-1]); + read.RemoveAt(read.Count-1); + } + + return colonFound; + } + + + + /// <summary> + /// Consume the dashes + /// </summary> + /// <returns></returns> + private bool consumeDash() + { + c = 0; + while ((c = Read()) >= 0) { - int colonPos = line.IndexOf(':'); - if (colonPos >= 0) + if (c != '-') { - string hdr = line.Substring(0, colonPos); - string val = line.Substring(colonPos + 1).Trim(); + break; + } + } - headers.Add(new PemHeader(hdr, val)); - continue; + PushBack(c); + + return c != -1; + } + + /// <summary> + /// Skip white space leave char in stream. + /// </summary> + private void skipWhiteSpace() + { + while ((c = Read()) >= 0) + { + if (c > ' ') + { + break; } + } + PushBack(c); + } - if (Platform.IndexOf(line, endMarker) >= 0) - break; + /// <summary> + /// Read forward consuming the expected string. + /// </summary> + /// <param name="value">expected string</param> + /// <returns>false if not consumed</returns> - buf.Append(line.Trim()); + private bool expect(String value) + { + for (int t=0; t<value.Length; t++) + { + c = Read(); + if (c == value[t]) + { + continue; + } else + { + return false; + } } - if (line == null) - throw new IOException(endMarker + " not found"); + return true; + } + + + /// <summary> + /// Consume until dash. + /// </summary> + /// <returns>true if stream end not met</returns> + private bool bufferUntilStopChar(char stopChar, bool skipWhiteSpace) + { + while ((c = Read()) >= 0) + { + if (skipWhiteSpace && c <=' ') + { + continue; + } - return new PemObject(type, headers, Base64.Decode(buf.ToString())); + if (c != stopChar) + { + textBuffer.Write((char)c); + textBuffer.Flush(); + + } else + { + PushBack(c); + break; + } + } + + return c > -1; } + + + + private void PushBack(int value) + { + if (pushback.Count == 0) + { + pushback.Add(value); + } else + { + pushback.Insert(0, value); + } + } + + + private int Read() + { + if (pushback.Count>0) + { + int i = (int)pushback[0]; + pushback.RemoveAt(0); + return i; + } + + return reader.Read(); + } + + + + } } |