| 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_generalstring_tag is the default tag of ASN.1 GENERALSTRING type. |
| 7 | pub const default_generalstring_tag = Tag{.universal, false, int(TagType.generalstring)} |
| 8 | |
| 9 | // ASN.1 GENERALSTRING Handling |
| 10 | // It may contain any characters from a "G" and "C" set of any standardized character sets. |
| 11 | // A "G" set contains some specified set of graphic (i.e., printable) characters, |
| 12 | // while a "C" set contains a group of control characters. |
| 13 | // For example, the "G" set in the ASCII character set consists of the characters with ASCII numbers 33 through 126, |
| 14 | // while the "C" set is those characters with ASCII numbers 0 through 31. |
| 15 | // For historical reasons, the characters SPACE (number 32) and DELETE (number 127) |
| 16 | // are not considered to be in either the C set or the G set, but instead stand on their own |
| 17 | // We only treated GeneralString as an us-ascii charset |
| 18 | pub struct GeneralString { |
| 19 | pub: |
| 20 | value string |
| 21 | } |
| 22 | |
| 23 | fn (g GeneralString) check() ! { |
| 24 | if !g.value.is_ascii() { |
| 25 | return error('GeneralString: contains non-ascii chars') |
| 26 | } |
| 27 | } |
| 28 | |
| 29 | // new creates a GeneralString element from string s. |
| 30 | pub fn GeneralString.new(s string) !GeneralString { |
| 31 | if !s.is_ascii() { |
| 32 | return error('GeneralString: contains non-ascii chars') |
| 33 | } |
| 34 | return GeneralString{ |
| 35 | value: s |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | fn (gst GeneralString) str() string { |
| 40 | if gst.value.len == 0 { |
| 41 | return 'GeneralString (<empty>)' |
| 42 | } |
| 43 | return 'GeneralString (${gst.value})' |
| 44 | } |
| 45 | |
| 46 | // tag returns the tag of GeneralString type element. |
| 47 | pub fn (gst GeneralString) tag() Tag { |
| 48 | return default_generalstring_tag |
| 49 | } |
| 50 | |
| 51 | // payload returns the payload of GeneralString type element. |
| 52 | pub fn (gst GeneralString) payload() ![]u8 { |
| 53 | return gst.payload_with_rule(.der)! |
| 54 | } |
| 55 | |
| 56 | fn (gst GeneralString) payload_with_rule(rule EncodingRule) ![]u8 { |
| 57 | if rule != .der && rule != .ber { |
| 58 | return error('GeneralString: not supported rule') |
| 59 | } |
| 60 | gst.check()! |
| 61 | return gst.value.bytes() |
| 62 | } |
| 63 | |
| 64 | // from_bytes creates GeneralString from bytes b |
| 65 | fn GeneralString.from_bytes(b []u8) !GeneralString { |
| 66 | if b.any(it < u8(` `) || it > u8(`~`)) { |
| 67 | return error('GeneralString: bytes contains non-ascii chars') |
| 68 | } |
| 69 | return GeneralString{ |
| 70 | value: b.bytestr() |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | // parse tries to read into GeneralString from parser p or return error on fails. |
| 75 | fn GeneralString.parse(mut p Parser) !GeneralString { |
| 76 | tag := p.read_tag()! |
| 77 | if !tag.equal(default_generalstring_tag) { |
| 78 | return error('Bad GeneralString tag') |
| 79 | } |
| 80 | length := p.read_length()! |
| 81 | bytes := p.read_bytes(length)! |
| 82 | |
| 83 | res := GeneralString.from_bytes(bytes)! |
| 84 | |
| 85 | return res |
| 86 | } |
| 87 | |
| 88 | // decode tries to decode bytes array into GeneralString or return error on fails. |
| 89 | fn GeneralString.decode(src []u8) !(GeneralString, int) { |
| 90 | return GeneralString.decode_with_rule(src, .der)! |
| 91 | } |
| 92 | |
| 93 | fn GeneralString.decode_with_rule(bytes []u8, rule EncodingRule) !(GeneralString, int) { |
| 94 | tag, length_pos := Tag.decode_with_rule(bytes, 0, rule)! |
| 95 | if !tag.equal(default_generalstring_tag) { |
| 96 | return error('Unexpected non-generalstring tag') |
| 97 | } |
| 98 | length, content_pos := Length.decode_with_rule(bytes, length_pos, rule)! |
| 99 | content := if length == 0 { |
| 100 | []u8{} |
| 101 | } else { |
| 102 | // non-null length should contains non-null bytes |
| 103 | if content_pos >= bytes.len || content_pos + length > bytes.len { |
| 104 | return error('GeneralString: truncated payload bytes') |
| 105 | } |
| 106 | unsafe { bytes[content_pos..content_pos + length] } |
| 107 | } |
| 108 | |
| 109 | gst := GeneralString.from_bytes(content)! |
| 110 | next := content_pos + length |
| 111 | |
| 112 | return gst, next |
| 113 | } |
| 114 | |
| 115 | // Utility function |
| 116 | fn validate_general_string(s string) bool { |
| 117 | if !s.is_ascii() { |
| 118 | return false |
| 119 | } |
| 120 | return true |
| 121 | } |
| 122 | |