| 1 | // Copyright (c) 2022, 2024 blackshirt. All rights reserved. |
| 2 | // Use of this source code is governed by a MIT License |
| 3 | // that can be found in the LICENSE file. |
| 4 | module asn1 |
| 5 | |
| 6 | // default_visiblestring_tag is the default tag of ASN.1 VISIBLESTRING type. |
| 7 | pub const default_visiblestring_tag = Tag{.universal, false, int(TagType.visiblestring)} |
| 8 | |
| 9 | // ASN.1 UNIVERSAL CLASS OF VISIBLESTRING TYPE. |
| 10 | // |
| 11 | // The ASN.1 VisibleString type supports a subset of ASCII characters that does not include control characters. |
| 12 | pub struct VisibleString { |
| 13 | pub: |
| 14 | value string |
| 15 | } |
| 16 | |
| 17 | // from_string creates a new VisibleString from string s. |
| 18 | pub fn VisibleString.new(s string) !VisibleString { |
| 19 | if contains_ctrl_chars(s.bytes()) { |
| 20 | return error('VisibleString: contains control chars') |
| 21 | } |
| 22 | return VisibleString{ |
| 23 | value: s |
| 24 | } |
| 25 | } |
| 26 | |
| 27 | // from_bytes creates a new VisibleString from bytes src |
| 28 | fn VisibleString.from_bytes(src []u8) !VisibleString { |
| 29 | if contains_ctrl_chars(src) { |
| 30 | return error('VisibleString: contains control chars') |
| 31 | } |
| 32 | return VisibleString{ |
| 33 | value: src.bytestr() |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | // tag returns the tag of VisibleString type element. |
| 38 | pub fn (vst VisibleString) tag() Tag { |
| 39 | return default_visiblestring_tag |
| 40 | } |
| 41 | |
| 42 | // payload returns the payload of VisibleString type element. |
| 43 | pub fn (vst VisibleString) payload() ![]u8 { |
| 44 | if contains_ctrl_chars(vst.value.bytes()) { |
| 45 | return error('VisibleString: contains control chars') |
| 46 | } |
| 47 | return vst.value.bytes() |
| 48 | } |
| 49 | |
| 50 | fn (vst VisibleString) str() string { |
| 51 | if vst.value.len == 0 { |
| 52 | return 'VisibleString (<empty>)' |
| 53 | } |
| 54 | return 'VisibleString (${vst.value})' |
| 55 | } |
| 56 | |
| 57 | fn VisibleString.parse(mut p Parser) !VisibleString { |
| 58 | tag := p.read_tag()! |
| 59 | if !tag.equal(default_visiblestring_tag) { |
| 60 | return error('Bad VisibleString tag') |
| 61 | } |
| 62 | length := p.read_length()! |
| 63 | bytes := p.read_bytes(length)! |
| 64 | |
| 65 | res := VisibleString.from_bytes(bytes)! |
| 66 | |
| 67 | return res |
| 68 | } |
| 69 | |
| 70 | fn VisibleString.decode(src []u8) !(VisibleString, int) { |
| 71 | return VisibleString.decode_with_rule(src, .der)! |
| 72 | } |
| 73 | |
| 74 | fn VisibleString.decode_with_rule(bytes []u8, rule EncodingRule) !(VisibleString, int) { |
| 75 | tag, length_pos := Tag.decode_with_rule(bytes, 0, rule)! |
| 76 | if !tag.equal(default_visiblestring_tag) { |
| 77 | return error('Unexpected non-visiblestring tag') |
| 78 | } |
| 79 | length, content_pos := Length.decode_with_rule(bytes, length_pos, rule)! |
| 80 | content := if length == 0 { |
| 81 | []u8{} |
| 82 | } else { |
| 83 | if content_pos >= bytes.len || content_pos + length > bytes.len { |
| 84 | return error('VisibleString: truncated payload bytes') |
| 85 | } |
| 86 | unsafe { bytes[content_pos..content_pos + length] } |
| 87 | } |
| 88 | |
| 89 | vst := VisibleString.from_bytes(content)! |
| 90 | next := content_pos + length |
| 91 | |
| 92 | return vst, next |
| 93 | } |
| 94 | |
| 95 | // Utility function for VisibleString |
| 96 | // |
| 97 | |
| 98 | fn is_ctrl_char(c u8) bool { |
| 99 | return (c >= 0 && c <= 0x1f) || c == 0x7f |
| 100 | } |
| 101 | |
| 102 | fn contains_ctrl_chars(src []u8) bool { |
| 103 | return src.any(is_ctrl_char(it)) |
| 104 | } |
| 105 | |