// Copyright (c) 2022, 2024 blackshirt. All rights reserved. // Use of this source code is governed by a MIT License // that can be found in the LICENSE file. module asn1 // max_definite_length_count is a limit tells how many bytes to represent this length. // We're going to limit this to 6 bytes following when the length is in long-definite form. const max_definite_length_count = 6 const max_definite_length_value = max_int // ASN.1 length handling routines. // // The standard of X.690 ITU document defines two length types - definite and indefinite. // In DER encoding only uses the definite length. The length field must be encoded in the minimum number of octets. // There are two forms of definite length octets: short (for lengths value between 0 and 127), // and long definite (for lengths value between 0 and 2^1008 -1). // In short form contains one octet. Bit 8 has value "0" and bits 7-1 give the length (length value from 0 to 127) // In long form, its required two or more octets, limited to 127 octets. Bit 8 of first octet has value "1" and bits 7-1 gives // the number of additional length octets. // Second and following octets give the length, base 256, most significant digit first. // // This module only support definite length, in short or long form. Its required for DER encoding // the length octets should be in definite length. // // Length represent ASN.1 length value pub type Length = int // new creates Length from integer value. Passing negative value (<0) for length // is not make a sense, so just return error instead if it happen. pub fn Length.new(v int) !Length { if v < 0 { return error('Length: supply with positive int') } if v > max_definite_length_value { return error('Length: value provided exceed limit') } return Length(v) } // Length.from_bytes tries to read length from bytes array and return the length // and the rest of remaining bytes on success, or error on fails. pub fn Length.from_bytes(bytes []u8) !(Length, []u8) { length, next := Length.decode(bytes)! if next < bytes.len { rest := unsafe { bytes[next..] } return length, rest } if next == bytes.len { return length, []u8{} } return error('Length: too short data') } // encode serializes the length v into bytes array stored into buffer buf in .der rule. pub fn (v Length) encode(mut dst []u8) ! { v.encode_with_rule(mut dst, .der)! } // encode_with_rule serializes Length v into bytes and append it into `dst`. // it would use rule of `Encodingrule` to drive how encode operation would be done. // By default the .der rule is only currently supported. fn (v Length) encode_with_rule(mut dst []u8, rule EncodingRule) ! { // we currently only support .der and (stricter) .ber if rule != .der && rule != .ber { return error('Length: unsupported rule') } // TODO: add supports for undefinite form // Long form if v >= 128 { // First, we count how many bytes occupied by this length value. // if the count exceed the limit, we return error. count := v.bytes_len() if count > max_definite_length_count { return error('something bad in your length') } // In definite long form, msb bit of first byte is set into 1, and the remaining bits // of first byte tells exact count how many bytes following representing this length value. dst << 0x80 | u8(count) v.to_bytes(mut dst) } else { // short form, already tells the length value. dst << u8(v) } } // bytes_len tells how many bytes needed to represent this length fn (v Length) bytes_len() int { mut i := v mut num := 1 for i > 255 { num++ i >>= 8 } return num } // to_bytes packs Length v into bytes and apends it into `dst` bytes. fn (v Length) to_bytes(mut dst []u8) { mut n := v.bytes_len() for ; n > 0; n-- { // pay attention to the brackets dst << u8(v >> ((n - 1) * 8)) } } // length_size gets length of length v in .der rule fn (v Length) length_size() !int { n := v.length_size_with_rule(.der)! return n } // length_size_with_rule calculates the length of bytes needed to store the Length value `v` // includes one byte marker for long definite form of length value, for value >= 128 fn (v Length) length_size_with_rule(rule EncodingRule) !int { // we currently only support .der or (stricter) .ber if rule != .der && rule != .ber { return error('Length: unsupported rule') } n := if v < 128 { 1 } else { v.bytes_len() + 1 } return n } // decode read length from bytes src with default context or return error on fails. fn Length.decode(src []u8) !(Length, int) { ret, next := Length.decode_from_offset(src, 0)! return ret, next } // decode_from_offset read length from src bytes start from offset pos or return error on fails. fn Length.decode_from_offset(src []u8, pos int) !(Length, int) { ret, next := Length.decode_with_rule(src, pos, .der)! return ret, next } // decode_with_rule tries to decode and deserializes buffer in src into Length form, start from offset loc in the buffer. // Its return Length and next offset in the buffer to process on, or return error on fails. fn Length.decode_with_rule(src []u8, loc int, rule EncodingRule) !(Length, int) { if src.len < 1 { return error('Length: truncated length') } // preliminary check if rule != .der && rule != .ber { return error('Length: unsupported rule') } // consider b := src[loc] would lead to panic if loc >= src.len { return error('Length: invalid pos') } mut pos := loc mut b := src[pos] pos += 1 mut length := 0 // check for the most bit is set or not if b & 0x80 == 0 { // for lengths between 0 and 127, the one-octet short form can be used. // The bit 7 of the length octet is set to 0, and the length is encoded // as an unsigned binary value in the octet's rightmost seven bits. length = b & 0x7f } else { if pos > src.len { return error('truncated tag or length') } // Otherwise, its a Long definite form or undefinite form num_bytes := b & 0x7f if num_bytes == 0 { // TODO: add support for undefinite length return error('Length: unsupported undefinite length') } if num_bytes == 0x7f { return error('Length: 0x7f is for reserved use') } // we limit the bytes count for length definite form to `max_definite_length_count` if num_bytes > max_definite_length_count { return error('Length: count bytes exceed limit') } for i := 0; i < num_bytes; i++ { if pos >= src.len { return error('Length: truncated length') } b = src[pos] pos += 1 // currently, we're only support limited length. // The length is in int range if length > max_definite_length_value { return error('Length: length exceed limit value') } length <<= 8 length |= b if length == 0 { // TODO: leading zeros is allowed in Long form of BER encoding, but // not allowed in DER encoding if rule == .der { return error('Length: leading zeros') } } } // do not allow values < 0x80 to be encoded in long form if length < 0x80 { // TODO: allow in BER return error('Length: dont needed in long form') } } ret := Length.new(length)! return ret, pos }