-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for CRLF line endings to PEMDocument
#68
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,7 +19,7 @@ import Foundation | |
/// | ||
/// Users implementing this type are expected to just provide the ``defaultPEMDiscriminator`` | ||
/// | ||
/// A PEM `String` can be serialized by constructing a ``PEMDocument`` by calling ``PEMSerializable/serializeAsPEM()`` and then accessing the ``PEMDocument/pemString`` preropty. | ||
/// A PEM `String` can be serialized by constructing a ``PEMDocument`` by calling ``PEMSerializable/serializeAsPEM()`` and then accessing the ``PEMDocument/pemString`` property. | ||
public protocol PEMSerializable: DERSerializable { | ||
/// The PEM discriminator identifying this object type. | ||
/// | ||
|
@@ -38,7 +38,7 @@ public protocol PEMSerializable: DERSerializable { | |
/// | ||
/// Users implementing this type are expected to just provide the ``defaultPEMDiscriminator``. | ||
/// | ||
/// Objects that are ``PEMParseable`` can be construct from a PEM `String` through ``PEMParseable/init(pemEncoded:)``. | ||
/// Objects that are ``PEMParseable`` can be constructed from a PEM `String` through ``PEMParseable/init(pemEncoded:)``. | ||
public protocol PEMParseable: DERParseable { | ||
/// The PEM discriminator identifying this object type. | ||
/// | ||
|
@@ -69,8 +69,8 @@ extension PEMParseable { | |
/// This will check that the discriminator matches ``PEMParseable/defaultPEMDiscriminator``, decode the base64 encoded string and | ||
/// then decode the DER encoded bytes using ``DERParseable/init(derEncoded:)-i2rf``. | ||
/// | ||
/// - parameters: | ||
/// - pemEncoded: The PEM-encoded string representing this object. | ||
/// - Parameters: | ||
/// - pemString: The PEM-encoded string representing this object. | ||
@inlinable | ||
public init(pemEncoded pemString: String) throws { | ||
try self.init(pemDocument: try PEMDocument(pemString: pemString)) | ||
|
@@ -80,8 +80,8 @@ extension PEMParseable { | |
/// This will check that the ``PEMParseable/pemDiscriminator`` matches and | ||
/// forward the DER encoded bytes to ``DERParseable/init(derEncoded:)-i2rf``. | ||
/// | ||
/// - parameters: | ||
/// - pemDocument: DER-encoded PEM document | ||
/// - Parameters: | ||
/// - pemDocument: DER-encoded PEM document | ||
@inlinable | ||
public init(pemDocument: PEMDocument) throws { | ||
guard pemDocument.discriminator == Self.defaultPEMDiscriminator else { | ||
|
@@ -218,6 +218,25 @@ struct LazyPEMDocument { | |
} | ||
|
||
extension Substring.UTF8View { | ||
/// Checks whether `self` starts with a new line character. | ||
/// - Returns: `true` if `self` starts with a new line character; otherwise, `false`. | ||
fileprivate func startsWithNewLine() -> Bool { | ||
return self.starts(with: LineEnding.LF.utf8) || self.starts(with: LineEnding.CRLF.utf8) | ||
} | ||
|
||
/// An optional `Index` value that is offset from `self.startIndex` by the new line character sequence. | ||
/// - Returns: The `Index` denoting the position immediately after the new line character sequence. | ||
/// `nil` is returned if `self` does not start with a new line. | ||
fileprivate func offsetNewLine() -> Index? { | ||
aryan-25 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if self.starts(with: LineEnding.LF.utf8) { | ||
return self.index(after: self.startIndex) | ||
} | ||
if self.starts(with: LineEnding.CRLF.utf8) { | ||
return self.index(self.startIndex, offsetBy: 2) | ||
} | ||
return nil | ||
} | ||
|
||
/// A PEM document looks like this: | ||
/// ``` | ||
/// -----BEGIN <SOME DISCRIMINATOR>----- | ||
|
@@ -235,8 +254,10 @@ extension Substring.UTF8View { | |
beginDiscriminatorSuffix | ||
) = self.firstRangesOf( | ||
prefix: "-----BEGIN ", | ||
suffix: "-----\n" | ||
) | ||
suffix: "-----" | ||
), | ||
self[beginDiscriminatorSuffix.upperBound...].startsWithNewLine(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line is redundant: the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. Removed this and just keeping the call to |
||
let messageStart = self[beginDiscriminatorSuffix.upperBound...].offsetNewLine() | ||
else { | ||
return nil | ||
} | ||
|
@@ -270,7 +291,7 @@ extension Substring.UTF8View { | |
} | ||
|
||
/// everything between the BEGIN and END markers is considered the base64 encoded string | ||
let base64EncodedDERString = self[beginDiscriminatorSuffix.upperBound..<endDiscriminatorPrefix.lowerBound] | ||
let base64EncodedDERString = self[messageStart..<endDiscriminatorPrefix.lowerBound] | ||
|
||
try base64EncodedDERString.checkLineLengthsOfBase64EncodedString() | ||
|
||
|
@@ -298,15 +319,14 @@ extension Substring.UTF8View { | |
let expectedNewLineIndex = | ||
message.index(message.startIndex, offsetBy: 64, limitedBy: lastIndex) ?? lastIndex | ||
|
||
guard | ||
let actualNewLineIndex = message.firstIndex(of: UInt8(ascii: "\n")), | ||
actualNewLineIndex == expectedNewLineIndex | ||
// The current line cannot contain any "\n" and the end index must be a new line. | ||
guard message[..<expectedNewLineIndex].firstIndex(of: UInt8(ascii: "\n")) == nil, | ||
message[expectedNewLineIndex...].startsWithNewLine(), | ||
let nextLineStart = message[expectedNewLineIndex...].offsetNewLine() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This guard let also supersedes the other call. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, like above. |
||
else { | ||
throw ASN1Error.invalidPEMDocument(reason: "PEMDocument has incorrect line lengths") | ||
} | ||
|
||
let nextLineStart = message.index(after: expectedNewLineIndex) | ||
|
||
message = message[nextLineStart...] | ||
} | ||
} | ||
|
@@ -384,4 +404,10 @@ extension Substring.UTF8View { | |
} | ||
} | ||
|
||
/// Represents new line delimiters. | ||
public enum LineEnding { | ||
aryan-25 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public static let LF = "\n" | ||
public static let CRLF = "\r\n" | ||
} | ||
|
||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
startsWithNewLine
entirely since there are no longer any references to it