| 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_ia5string_tag is the default tag of ASN.1 IA5STRING type. |
| 7 | pub const default_ia5string_tag = Tag{.universal, false, int(TagType.ia5string)} |
| 8 | |
| 9 | // ASN.1 IA5String type handling routine. |
| 10 | // IA5String is a standard ASCII characters |
| 11 | pub struct IA5String { |
| 12 | pub: |
| 13 | value string |
| 14 | } |
| 15 | |
| 16 | // new creates a IA5String element from string s. |
| 17 | pub fn IA5String.new(s string) !IA5String { |
| 18 | if !valid_ia5string(s) { |
| 19 | return error('IA5String: contains non-ascii chars') |
| 20 | } |
| 21 | return IA5String{ |
| 22 | value: s |
| 23 | } |
| 24 | } |
| 25 | |
| 26 | // tag returns the tag of IA5String type element. |
| 27 | pub fn (v IA5String) tag() Tag { |
| 28 | return default_ia5string_tag |
| 29 | } |
| 30 | |
| 31 | // payload returns the payload of IA5String type element. |
| 32 | pub fn (v IA5String) payload() ![]u8 { |
| 33 | if !v.value.is_ascii() { |
| 34 | return error('IA5String: contains non-ascii chars') |
| 35 | } |
| 36 | return v.value.bytes() |
| 37 | } |
| 38 | |
| 39 | fn (v IA5String) str() string { |
| 40 | if v.value.len == 0 { |
| 41 | return 'IA5String (<empty>)' |
| 42 | } |
| 43 | return 'IA5String (${v.value})' |
| 44 | } |
| 45 | |
| 46 | fn IA5String.parse(mut p Parser) !IA5String { |
| 47 | tag := p.read_tag()! |
| 48 | if !tag.equal(default_ia5string_tag) { |
| 49 | return error('Bad Ia5String tag') |
| 50 | } |
| 51 | length := p.read_length()! |
| 52 | bytes := p.read_bytes(length)! |
| 53 | |
| 54 | res := IA5String.from_bytes(bytes)! |
| 55 | |
| 56 | return res |
| 57 | } |
| 58 | |
| 59 | fn IA5String.decode(bytes []u8) !(IA5String, int) { |
| 60 | bs, next := IA5String.decode_with_rule(bytes, .der)! |
| 61 | return bs, next |
| 62 | } |
| 63 | |
| 64 | fn IA5String.decode_with_rule(bytes []u8, rule EncodingRule) !(IA5String, int) { |
| 65 | tag, length_pos := Tag.decode_with_rule(bytes, 0, rule)! |
| 66 | if !tag.equal(default_ia5string_tag) { |
| 67 | return error('Unexpected non-ia5string tag') |
| 68 | } |
| 69 | length, content_pos := Length.decode_with_rule(bytes, length_pos, rule)! |
| 70 | |
| 71 | // if the length is 0, this mean if the payload is empty |
| 72 | // otherwise, check for bound |
| 73 | content := if length == 0 { |
| 74 | []u8{} |
| 75 | } else { |
| 76 | if content_pos >= bytes.len || content_pos + length > bytes.len { |
| 77 | return error('IA5String: truncated payload bytes') |
| 78 | } |
| 79 | unsafe { bytes[content_pos..content_pos + length] } |
| 80 | } |
| 81 | |
| 82 | result := IA5String.from_bytes(content)! |
| 83 | next := content_pos + length |
| 84 | |
| 85 | return result, next |
| 86 | } |
| 87 | |
| 88 | // from_bytes creates a new IA5String from bytes b |
| 89 | fn IA5String.from_bytes(b []u8) !IA5String { |
| 90 | if b.any(it < u8(` `) || it > u8(`~`)) { |
| 91 | return error('IA5String: bytes contains non-ascii chars') |
| 92 | } |
| 93 | return IA5String{ |
| 94 | value: b.bytestr() |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | // Utility function |
| 99 | fn valid_ia5string(s string) bool { |
| 100 | return s.is_ascii() |
| 101 | } |
| 102 | |