Skip to content

Commit d66b097

Browse files
author
phoebe
authored
crypto.pem: add decode_only and general improvements to decoding (#18908)
1 parent 511274a commit d66b097

File tree

2 files changed

+37
-6
lines changed

2 files changed

+37
-6
lines changed

vlib/crypto/pem/decode.v

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,50 @@ module pem
22

33
import encoding.base64
44

5+
// decode_only reads `data` and returns the first parsed PEM Block. `none` is returned
6+
// when a header is expected, but not present or when a start of '-----BEGIN' or end of '-----END'
7+
// can't be found.
8+
//
9+
// use decode if you still need the unparsed rest of the string.
10+
[inline]
11+
pub fn decode_only(data string) ?Block {
12+
block, _ := decode_internal(data)?
13+
return block
14+
}
15+
516
// decode reads `data` and returns the first parsed PEM Block along with the rest of
617
// the string. `none` is returned when a header is expected, but not present
7-
// or when a start of '-----BEGIN' or end of '-----END' can't be found in `data`
18+
// or when a start of '-----BEGIN' or end of '-----END' can't be found.
19+
//
20+
// use decode_only if you do not need the unparsed rest of the string.
21+
[direct_array_access; inline]
822
pub fn decode(data string) ?(Block, string) {
23+
block, rest := decode_internal(data)?
24+
return block, rest[rest.index(pem_end)? + pem_end.len..].all_after_first(pem_eol)
25+
}
26+
27+
// decode_internal allows `decode` variations to deal with the rest of the data as
28+
// they want to. for example Block.decode could have hindered performance with the final
29+
// indexing into `rest` that `decode_partial` does.
30+
[direct_array_access]
31+
fn decode_internal(data string) ?(Block, string) {
32+
// direct_array_access safety: since we use the string.index method here,
33+
// we won't get an invalid index since it would otherwise return `none`
934
mut rest := data[data.index(pem_begin)?..]
1035
mut block := Block.new(rest[pem_begin.len..].all_before(pem_eol))
1136
block.headers, rest = parse_headers(rest[pem_begin.len..].all_after(pem_eol).trim_left(' \n\t\v\f\r'))?
1237

1338
block_end_index := rest.index(pem_end)?
1439
b64_data := rest[..block_end_index].replace_each(['\r', '', '\n', '', '\t', '', ' ', ''])
15-
1640
block_data_len := block_end_index / 4 * 3
1741
block.data = []u8{len: block_data_len, cap: block_data_len + 3, init: 0}
1842
decoded_len := base64.decode_in_buffer(&b64_data, &block.data[0])
1943
block.data = block.data[..decoded_len]
2044

21-
return block, rest[rest.index(pem_end)? + pem_end.len..].all_after_first(pem_eol)
45+
return block, rest
2246
}
2347

48+
[direct_array_access]
2449
fn parse_headers(block string) ?(map[string][]string, string) {
2550
headers_str := block.all_before(pem_end).all_before('\n\n')
2651

vlib/crypto/pem/pem_test.v

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ fn test_decode_rfc1421() {
55
for i in 0 .. pem.test_data_rfc1421.len {
66
decoded, rest := decode(pem.test_data_rfc1421[i]) or { Block{}, '' }
77
assert decoded == pem.expected_results_rfc1421[i]
8+
assert decoded == decode_only(pem.test_data_rfc1421[i]) or { Block{} }
89
assert rest == ''
910
}
1011
}
@@ -13,6 +14,7 @@ fn test_decode() {
1314
for i in 0 .. pem.test_data.len {
1415
decoded, rest := decode(pem.test_data[i]) or { Block{}, '' }
1516
assert decoded == pem.expected_results[i]
17+
assert decoded == decode_only(pem.test_data[i]) or { Block{} }
1618
assert rest == pem.expected_rest[i]
1719
}
1820
}
@@ -23,6 +25,7 @@ fn test_encode_rfc1421() {
2325
decoded, rest := decode(encoded) or { Block{}, '' }
2426
assert rest == ''
2527
assert decoded == pem.expected_results_rfc1421[i]
28+
assert decoded == decode_only(encoded) or { Block{} }
2629
}
2730
}
2831

@@ -32,6 +35,7 @@ fn test_encode() {
3235
decoded, rest := decode(encoded) or { Block{}, '' }
3336
assert rest == ''
3437
assert decoded == pem.expected_results[i]
38+
assert decoded == decode_only(encoded) or { Block{} }
3539
}
3640
}
3741

@@ -41,15 +45,17 @@ fn test_encode_config() {
4145
decoded, rest := decode(encoded) or { Block{}, '' }
4246
assert rest == ''
4347
assert decoded == pem.expected_results[i]
48+
assert decoded == decode_only(encoded) or { Block{} }
4449
}
4550
}
4651

4752
fn test_decode_no_pem() {
4853
for test in pem.test_data_no_pem {
4954
if _, _ := decode(test) {
50-
assert false, 'Block.decode_partial should return `none` on input without PEM data'
51-
} else {
52-
assert true
55+
assert false, 'decode should return `none` on input without PEM data'
56+
}
57+
if _ := decode_only(test) {
58+
assert false, 'decode_only should return `none` on input without PEM data'
5359
}
5460
}
5561
}

0 commit comments

Comments
 (0)