{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/corefx/issues/37748","repository_url":"https://api.github.com/repos/dotnet/corefx","labels_url":"https://api.github.com/repos/dotnet/corefx/issues/37748/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/corefx/issues/37748/comments","events_url":"https://api.github.com/repos/dotnet/corefx/issues/37748/events","html_url":"https://github.com/dotnet/corefx/issues/37748","id":445461805,"node_id":"MDU6SXNzdWU0NDU0NjE4MDU=","number":37748,"title":"One-shot PEM reader","user":{"login":"owlstead","id":1474270,"node_id":"MDQ6VXNlcjE0NzQyNzA=","avatar_url":"https://avatars3.githubusercontent.com/u/1474270?v=4","gravatar_id":"","url":"https://api.github.com/users/owlstead","html_url":"https://github.com/owlstead","followers_url":"https://api.github.com/users/owlstead/followers","following_url":"https://api.github.com/users/owlstead/following{/other_user}","gists_url":"https://api.github.com/users/owlstead/gists{/gist_id}","starred_url":"https://api.github.com/users/owlstead/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/owlstead/subscriptions","organizations_url":"https://api.github.com/users/owlstead/orgs","repos_url":"https://api.github.com/users/owlstead/repos","events_url":"https://api.github.com/users/owlstead/events{/privacy}","received_events_url":"https://api.github.com/users/owlstead/received_events","type":"User","site_admin":false},"labels":[{"id":703517252,"node_id":"MDU6TGFiZWw3MDM1MTcyNTI=","url":"https://api.github.com/repos/dotnet/corefx/labels/api-suggestion","name":"api-suggestion","color":"1d76db","default":false},{"id":163633782,"node_id":"MDU6TGFiZWwxNjM2MzM3ODI=","url":"https://api.github.com/repos/dotnet/corefx/labels/area-System.Security","name":"area-System.Security","color":"d4c5f9","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/dotnet/corefx/milestones/5","html_url":"https://github.com/dotnet/corefx/milestone/5","labels_url":"https://api.github.com/repos/dotnet/corefx/milestones/5/labels","id":1328080,"node_id":"MDk6TWlsZXN0b25lMTMyODA4MA==","number":5,"title":"Future","description":"Milestone tracking issues not triaged to a planned release. Work in this milestone may happen, or may not: it is not committed.","creator":{"login":"joshfree","id":1302850,"node_id":"MDQ6VXNlcjEzMDI4NTA=","avatar_url":"https://avatars1.githubusercontent.com/u/1302850?v=4","gravatar_id":"","url":"https://api.github.com/users/joshfree","html_url":"https://github.com/joshfree","followers_url":"https://api.github.com/users/joshfree/followers","following_url":"https://api.github.com/users/joshfree/following{/other_user}","gists_url":"https://api.github.com/users/joshfree/gists{/gist_id}","starred_url":"https://api.github.com/users/joshfree/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/joshfree/subscriptions","organizations_url":"https://api.github.com/users/joshfree/orgs","repos_url":"https://api.github.com/users/joshfree/repos","events_url":"https://api.github.com/users/joshfree/events{/privacy}","received_events_url":"https://api.github.com/users/joshfree/received_events","type":"User","site_admin":false},"open_issues":2037,"closed_issues":175,"state":"open","created_at":"2015-09-29T00:25:45Z","updated_at":"2019-05-17T23:41:03Z","due_on":null,"closed_at":null},"comments":5,"created_at":"2019-05-17T14:10:22Z","updated_at":"2019-05-18T15:08:52Z","closed_at":null,"author_association":"NONE","body":"I was giving some hints on this [StackOverflow question](https://stackoverflow.com/questions/56144550/loading-an-ecc-private-key-in-net?noredirect=1#comment98936659_56152165) where the idea of a PEM reader / writer for .NET was coming up. Jeremy then posted that if such a reader was available that it would be a nice idea to make it a one-shot, [RFC 7468](https://tools.ietf.org/html/rfc7468) compliant reader using the new `Span<char>` framework.\r\n\r\nSo I've created a sample implementation / API proposal which performs the parsing of the \"lax\"  (i.e. permissive). I've used the following additional goals / non-goals:\r\n\r\nGoals:\r\n\r\n - no dynamic memory allocation that depends on the size of the input;\r\n - possibility to skip a description (in front) of the PEM structure;\r\n - possibility to detect the end of the PEM structure, if present;\r\n - related: allow for parsing multiple PEM structures from a single `Span`;\r\n - design in the spirit of other methods that use `Span`;\r\n\r\nNon-goals:\r\n\r\n - returning the description in front of the PEM file;\r\n - detailed error reporting;\r\n - streaming implementation (requiring state machine or buffering);\r\n - parsing the base64 inside the header / footer lines (pre/post encapsulation boundaries);\r\n - support for stricter versions of PEM;\r\n - return or comparison of the label in the footer.\r\n\r\nNiceties:\r\n\r\n - validation of the base64 inside the header / footer lines\r\n - return the amount of bytes so that a correctly sized buffer can be created in advance for the base64 decryption (yes, implemented).\r\n\r\nParsing the base64 is easily done afterwards with the existing parser, and we may just want to skip the PEM.\r\n\r\nNote that I didn't get my Unit testing working for Core 3, so the implementation has not yet received any significant testing. First let me know what you think.\r\n\r\nImplementation notes: this implementation searches for the post-encapsulation boundary (footer) before validating the base64. It might be possible to use a state machine instead to create a single pass parser. This adds significant complexity to the parsing though, and searching for a static string should be relatively performant already.\r\n\r\nQuestions remaining:\r\n\r\n - What is the exact base64 support by `Convert.TryFromBase64Chars` (e.g. with regards to whitespace / eols between padding characters?\r\n - Where to put this method and supporting methods / constants?\r\n - How to properly test this using Unit testing on Core 3?\r\n\r\nCode in followup comment for now, as I don't know yet where to put it."},"comment":{"url":"https://api.github.com/repos/dotnet/corefx/issues/comments/493684051","html_url":"https://github.com/dotnet/corefx/issues/37748#issuecomment-493684051","issue_url":"https://api.github.com/repos/dotnet/corefx/issues/37748","id":493684051,"node_id":"MDEyOklzc3VlQ29tbWVudDQ5MzY4NDA1MQ==","user":{"login":"owlstead","id":1474270,"node_id":"MDQ6VXNlcjE0NzQyNzA=","avatar_url":"https://avatars3.githubusercontent.com/u/1474270?v=4","gravatar_id":"","url":"https://api.github.com/users/owlstead","html_url":"https://github.com/owlstead","followers_url":"https://api.github.com/users/owlstead/followers","following_url":"https://api.github.com/users/owlstead/following{/other_user}","gists_url":"https://api.github.com/users/owlstead/gists{/gist_id}","starred_url":"https://api.github.com/users/owlstead/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/owlstead/subscriptions","organizations_url":"https://api.github.com/users/owlstead/orgs","repos_url":"https://api.github.com/users/owlstead/repos","events_url":"https://api.github.com/users/owlstead/events{/privacy}","received_events_url":"https://api.github.com/users/owlstead/received_events","type":"User","site_admin":false},"created_at":"2019-05-18T15:08:51Z","updated_at":"2019-05-18T15:08:51Z","author_association":"NONE","body":"As for that last comment, I don't see any advantage myself to keep `label` to a `Span`. Sure `ToString` or `new String` is easily performed, but you've got to ask yourself why the `Span` is ever needed in the first place. From a user point of view it doesn't make sense and copying such small amounts doesn't have any performance benefits, in my opinion. It feels like over-optimization to me.\r\n\r\nSo what seems natural to me is to supply:\r\n\r\n```\r\n// assembly: System.Security.Cryptography.Encoding\r\nnamespace System.Security.Cryptography\r\n{\r\n    public partial class PEM\r\n    {\r\n        public ref struct FieldsWithBase64\r\n        {\r\n            public string label;\r\n            public ReadOnlySpan<char> contentInBase64;\r\n            public int contentSizeBytes;\r\n        }\r\n\r\n        public ref struct Fields\r\n        {\r\n            public string label;\r\n            public ReadOnlySpan<byte> content;\r\n        }\r\n\r\n        public static bool TryFindPem(ReadOnlySpan<char> pemData, out FieldsWithBase64 fields, out Range location);\r\n\r\n        public static int GetOutputSizePem(Fields fields);\r\n\r\n        public static bool TryWritePem(Span<char> destination, Fields fields, out int pemSize);\r\n    }\r\n}\r\n```\r\nI've used \"Smurf naming conventions\" to allow for `using static` to be used for the static methods.\r\n\r\nOther changes in order of appearance:\r\n\r\n - renamed `PEMEncoding` to just `PEM` for simplicity sake (debatable, but PEM *is* an encoding);\r\n - structs introduced (`FieldsWithBase64` should really be `FieldsWithContentsInBase64` but yeah...);\r\n - renamed method to `TryFindPem` as it may be trying to find the *first* PEM struture;\r\n - renamed method `TryWriteData` to `TryWritePem` as I found `Data` to be too generic, and for symmetry with `TryFindPem`.\r\n\r\nMost importantly I removed `WriteData` from the class in its entirety as it would require a full rewrite (unless you would count creating a `Span<char>` over a new `char[]` as acceptable solution). It also mixes high level streaming and low level spanning components, which seems a bad idea in itself (where would it end?). It could of course be added later.\r\n\r\nVery much interested in your opinions!\r\n\r\nBTW my stupid Samsung tablet with Bluetooth keyboard generates U+2CB instead of U+60 when using it in US international setting. That stupid bug showed the backticks instead of the code inlined. I was already wondering why MarkDown suddenly stopped working on GitHub :)"}}
{"actor":{"display_login":"owlstead"}}