| 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 | import encoding.utf8 |
| 7 | |
| 8 | // default_utf8string_tag is the default tag of ASN.1 UTF8STRING type. |
| 9 | pub const default_utf8string_tag = Tag{.universal, false, int(TagType.utf8string)} |
| 10 | |
| 11 | // ASN.1 UNIVERSAL CLASS OF UTF8STRING TYPE. |
| 12 | // |
| 13 | // UTF8STRING is UTF8 unicode charset |
| 14 | pub struct Utf8String { |
| 15 | pub: |
| 16 | value string |
| 17 | } |
| 18 | |
| 19 | // new creates a new Utf8String element from string s. |
| 20 | pub fn Utf8String.new(s string) !Utf8String { |
| 21 | if !utf8.validate_str(s) { |
| 22 | return error('Utf8String: invalid UTF-8 string') |
| 23 | } |
| 24 | return Utf8String{ |
| 25 | value: s |
| 26 | } |
| 27 | } |
| 28 | |
| 29 | // from_bytes creates a new Utf8String element from bytes in src. |
| 30 | pub fn Utf8String.from_bytes(src []u8) !Utf8String { |
| 31 | if !utf8.validate_str(src.bytestr()) { |
| 32 | return error('Utf8String: invalid UTF-8 string') |
| 33 | } |
| 34 | return Utf8String{ |
| 35 | value: src.bytestr() |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | // tag returns the tag of Utf8String element. |
| 40 | pub fn (uts Utf8String) tag() Tag { |
| 41 | return default_utf8string_tag |
| 42 | } |
| 43 | |
| 44 | // payload returns the payload of Utf8String element. |
| 45 | pub fn (uts Utf8String) payload() ![]u8 { |
| 46 | return uts.payload_with_rule(.der)! |
| 47 | } |
| 48 | |
| 49 | fn (uts Utf8String) str() string { |
| 50 | if uts.value.len == 0 { |
| 51 | return 'Utf8String (<empty>)' |
| 52 | } |
| 53 | return 'Utf8String (${uts.value})' |
| 54 | } |
| 55 | |
| 56 | fn (uts Utf8String) payload_with_rule(rule EncodingRule) ![]u8 { |
| 57 | if rule != .der && rule != .ber { |
| 58 | return error('Utf8String: Unsupported rule') |
| 59 | } |
| 60 | if !utf8.validate_str(uts.value) { |
| 61 | return error('Utf8String: invalid UTF-8 string') |
| 62 | } |
| 63 | return uts.value.bytes() |
| 64 | } |
| 65 | |
| 66 | fn Utf8String.parse(mut p Parser) !Utf8String { |
| 67 | tag := p.read_tag()! |
| 68 | if !tag.equal(default_utf8string_tag) { |
| 69 | return error('Bad Utf8String tag') |
| 70 | } |
| 71 | length := p.read_length()! |
| 72 | bytes := p.read_bytes(length)! |
| 73 | |
| 74 | res := Utf8String.from_bytes(bytes)! |
| 75 | |
| 76 | return res |
| 77 | } |
| 78 | |
| 79 | fn Utf8String.decode(src []u8) !(Utf8String, int) { |
| 80 | return Utf8String.decode_with_rule(src, .der)! |
| 81 | } |
| 82 | |
| 83 | fn Utf8String.decode_with_rule(bytes []u8, rule EncodingRule) !(Utf8String, int) { |
| 84 | tag, length_pos := Tag.decode_with_rule(bytes, 0, rule)! |
| 85 | if !tag.equal(default_utf8string_tag) { |
| 86 | return error('Unexpected non-utf8string tag') |
| 87 | } |
| 88 | length, content_pos := Length.decode_with_rule(bytes, length_pos, rule)! |
| 89 | content := if length == 0 { |
| 90 | []u8{} |
| 91 | } else { |
| 92 | // non-null length should contains non-null bytes |
| 93 | if content_pos >= bytes.len || content_pos + length > bytes.len { |
| 94 | return error('Utf8String: truncated payload bytes') |
| 95 | } |
| 96 | unsafe { bytes[content_pos..content_pos + length] } |
| 97 | } |
| 98 | |
| 99 | ust := Utf8String.from_bytes(content)! |
| 100 | next := content_pos + length |
| 101 | |
| 102 | return ust, next |
| 103 | } |
| 104 | |