| 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.hex |
| 7 | |
| 8 | // default_octetstring_tag is the default tag of ASN.1 OCTETSTRING type. |
| 9 | pub const default_octetstring_tag = Tag{.universal, false, int(TagType.octetstring)} |
| 10 | |
| 11 | const max_octetstring_size = 1 << 32 - 1 |
| 12 | |
| 13 | // ASN.1 UNIVERSAL TYPE OF OCTETSTRING. |
| 14 | // |
| 15 | // The ASN.1 OCTET STRING type contains arbitrary strings of octets. |
| 16 | // This type is very similar to BIT STRING, except that all values must |
| 17 | // be an integral number of eight bits. |
| 18 | // You can use constraints to specify a maximum length for an OCTET STRING type. |
| 19 | pub struct OctetString { |
| 20 | pub: |
| 21 | value string |
| 22 | } |
| 23 | |
| 24 | // tag returns the tag of OctetString type. |
| 25 | pub fn (oct OctetString) tag() Tag { |
| 26 | return default_octetstring_tag |
| 27 | } |
| 28 | |
| 29 | // payload returns the payload of OctetString type. |
| 30 | pub fn (oct OctetString) payload() ![]u8 { |
| 31 | return oct.payload_with_rule(.der)! |
| 32 | } |
| 33 | |
| 34 | fn (oct OctetString) payload_with_rule(rule EncodingRule) ![]u8 { |
| 35 | if rule != .der && rule != .ber { |
| 36 | return error('not supported rule') |
| 37 | } |
| 38 | return oct.value.bytes() |
| 39 | } |
| 40 | |
| 41 | fn (oct OctetString) str() string { |
| 42 | if oct.value.len == 0 { |
| 43 | return 'OctetString (<empty>)' |
| 44 | } |
| 45 | return 'OctetString (${oct.value})' |
| 46 | } |
| 47 | |
| 48 | // new creates a new OctetString element from string s. |
| 49 | pub fn OctetString.new(s string) !OctetString { |
| 50 | if !valid_octet_string(s) { |
| 51 | return error('not valid octet string') |
| 52 | } |
| 53 | return OctetString{ |
| 54 | value: s |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | // from_hexstring creates a new OctetString element from valid hex string or error on fails. |
| 59 | pub fn OctetString.from_hexstring(hs string) !OctetString { |
| 60 | bytes := hex.decode(hs)! |
| 61 | oct := OctetString.from_bytes(bytes)! |
| 62 | |
| 63 | return oct |
| 64 | } |
| 65 | |
| 66 | // parse an OctetString from ongoing Parser |
| 67 | fn OctetString.parse(mut p Parser) !OctetString { |
| 68 | tag := p.read_tag()! |
| 69 | if !tag.equal(default_octetstring_tag) { |
| 70 | return error('Bad octetstring tag') |
| 71 | } |
| 72 | length := p.read_length()! |
| 73 | content := p.read_bytes(length)! |
| 74 | |
| 75 | payload := if length == 0 { []u8{} } else { content } |
| 76 | |
| 77 | oct := OctetString.from_bytes(payload)! |
| 78 | return oct |
| 79 | } |
| 80 | |
| 81 | fn OctetString.decode(src []u8) !(OctetString, int) { |
| 82 | return OctetString.decode_with_rule(src, .der)! |
| 83 | } |
| 84 | |
| 85 | fn OctetString.decode_with_rule(bytes []u8, rule EncodingRule) !(OctetString, int) { |
| 86 | tag, length_pos := Tag.decode_with_rule(bytes, 0, rule)! |
| 87 | if !tag.equal(default_octetstring_tag) { |
| 88 | return error('Unexpected non-octetstring tag') |
| 89 | } |
| 90 | length, content_pos := Length.decode_with_rule(bytes, length_pos, rule)! |
| 91 | content := if length == 0 { |
| 92 | []u8{} |
| 93 | } else { |
| 94 | if content_pos >= bytes.len || content_pos + length > bytes.len { |
| 95 | return error('OctetString: truncated payload bytes') |
| 96 | } |
| 97 | unsafe { bytes[content_pos..content_pos + length] } |
| 98 | } |
| 99 | |
| 100 | os := OctetString.from_bytes(content)! |
| 101 | next := content_pos + length |
| 102 | |
| 103 | return os, next |
| 104 | } |
| 105 | |
| 106 | fn OctetString.from_bytes(src []u8) !OctetString { |
| 107 | return OctetString.new(src.bytestr())! |
| 108 | } |
| 109 | |
| 110 | // UTILITY for OCTETSTRING |
| 111 | fn valid_octet_string(_s string) bool { |
| 112 | // just return true |
| 113 | return true |
| 114 | } |
| 115 | |