1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

crypto.pem: add decode_only and general improvements to decoding (#18908)

This commit is contained in:
phoebe 2023-07-20 01:34:08 +02:00 committed by GitHub
parent 511274a8d4
commit d66b097ddc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 37 additions and 6 deletions

View File

@ -2,25 +2,50 @@ module pem
import encoding.base64 import encoding.base64
// decode_only reads `data` and returns the first parsed PEM Block. `none` is returned
// when a header is expected, but not present or when a start of '-----BEGIN' or end of '-----END'
// can't be found.
//
// use decode if you still need the unparsed rest of the string.
[inline]
pub fn decode_only(data string) ?Block {
block, _ := decode_internal(data)?
return block
}
// decode reads `data` and returns the first parsed PEM Block along with the rest of // decode reads `data` and returns the first parsed PEM Block along with the rest of
// the string. `none` is returned when a header is expected, but not present // the string. `none` is returned when a header is expected, but not present
// or when a start of '-----BEGIN' or end of '-----END' can't be found in `data` // or when a start of '-----BEGIN' or end of '-----END' can't be found.
//
// use decode_only if you do not need the unparsed rest of the string.
[direct_array_access; inline]
pub fn decode(data string) ?(Block, string) { pub fn decode(data string) ?(Block, string) {
block, rest := decode_internal(data)?
return block, rest[rest.index(pem_end)? + pem_end.len..].all_after_first(pem_eol)
}
// decode_internal allows `decode` variations to deal with the rest of the data as
// they want to. for example Block.decode could have hindered performance with the final
// indexing into `rest` that `decode_partial` does.
[direct_array_access]
fn decode_internal(data string) ?(Block, string) {
// direct_array_access safety: since we use the string.index method here,
// we won't get an invalid index since it would otherwise return `none`
mut rest := data[data.index(pem_begin)?..] mut rest := data[data.index(pem_begin)?..]
mut block := Block.new(rest[pem_begin.len..].all_before(pem_eol)) mut block := Block.new(rest[pem_begin.len..].all_before(pem_eol))
block.headers, rest = parse_headers(rest[pem_begin.len..].all_after(pem_eol).trim_left(' \n\t\v\f\r'))? block.headers, rest = parse_headers(rest[pem_begin.len..].all_after(pem_eol).trim_left(' \n\t\v\f\r'))?
block_end_index := rest.index(pem_end)? block_end_index := rest.index(pem_end)?
b64_data := rest[..block_end_index].replace_each(['\r', '', '\n', '', '\t', '', ' ', '']) b64_data := rest[..block_end_index].replace_each(['\r', '', '\n', '', '\t', '', ' ', ''])
block_data_len := block_end_index / 4 * 3 block_data_len := block_end_index / 4 * 3
block.data = []u8{len: block_data_len, cap: block_data_len + 3, init: 0} block.data = []u8{len: block_data_len, cap: block_data_len + 3, init: 0}
decoded_len := base64.decode_in_buffer(&b64_data, &block.data[0]) decoded_len := base64.decode_in_buffer(&b64_data, &block.data[0])
block.data = block.data[..decoded_len] block.data = block.data[..decoded_len]
return block, rest[rest.index(pem_end)? + pem_end.len..].all_after_first(pem_eol) return block, rest
} }
[direct_array_access]
fn parse_headers(block string) ?(map[string][]string, string) { fn parse_headers(block string) ?(map[string][]string, string) {
headers_str := block.all_before(pem_end).all_before('\n\n') headers_str := block.all_before(pem_end).all_before('\n\n')

View File

@ -5,6 +5,7 @@ fn test_decode_rfc1421() {
for i in 0 .. pem.test_data_rfc1421.len { for i in 0 .. pem.test_data_rfc1421.len {
decoded, rest := decode(pem.test_data_rfc1421[i]) or { Block{}, '' } decoded, rest := decode(pem.test_data_rfc1421[i]) or { Block{}, '' }
assert decoded == pem.expected_results_rfc1421[i] assert decoded == pem.expected_results_rfc1421[i]
assert decoded == decode_only(pem.test_data_rfc1421[i]) or { Block{} }
assert rest == '' assert rest == ''
} }
} }
@ -13,6 +14,7 @@ fn test_decode() {
for i in 0 .. pem.test_data.len { for i in 0 .. pem.test_data.len {
decoded, rest := decode(pem.test_data[i]) or { Block{}, '' } decoded, rest := decode(pem.test_data[i]) or { Block{}, '' }
assert decoded == pem.expected_results[i] assert decoded == pem.expected_results[i]
assert decoded == decode_only(pem.test_data[i]) or { Block{} }
assert rest == pem.expected_rest[i] assert rest == pem.expected_rest[i]
} }
} }
@ -23,6 +25,7 @@ fn test_encode_rfc1421() {
decoded, rest := decode(encoded) or { Block{}, '' } decoded, rest := decode(encoded) or { Block{}, '' }
assert rest == '' assert rest == ''
assert decoded == pem.expected_results_rfc1421[i] assert decoded == pem.expected_results_rfc1421[i]
assert decoded == decode_only(encoded) or { Block{} }
} }
} }
@ -32,6 +35,7 @@ fn test_encode() {
decoded, rest := decode(encoded) or { Block{}, '' } decoded, rest := decode(encoded) or { Block{}, '' }
assert rest == '' assert rest == ''
assert decoded == pem.expected_results[i] assert decoded == pem.expected_results[i]
assert decoded == decode_only(encoded) or { Block{} }
} }
} }
@ -41,15 +45,17 @@ fn test_encode_config() {
decoded, rest := decode(encoded) or { Block{}, '' } decoded, rest := decode(encoded) or { Block{}, '' }
assert rest == '' assert rest == ''
assert decoded == pem.expected_results[i] assert decoded == pem.expected_results[i]
assert decoded == decode_only(encoded) or { Block{} }
} }
} }
fn test_decode_no_pem() { fn test_decode_no_pem() {
for test in pem.test_data_no_pem { for test in pem.test_data_no_pem {
if _, _ := decode(test) { if _, _ := decode(test) {
assert false, 'Block.decode_partial should return `none` on input without PEM data' assert false, 'decode should return `none` on input without PEM data'
} else { }
assert true if _ := decode_only(test) {
assert false, 'decode_only should return `none` on input without PEM data'
} }
} }
} }