///|
///|
/// Parser functions
///|
///|
///|
fn[T] df_parse_dictionary(
input : String,
value_parser : (String) -> Result[T, DigestFieldsError],
) -> Result[Array[(String, T)], DigestFieldsError] {
let s = df_trim(input)
if s.length() == 0 {
return Err(Empty)
}
let parts = df_split_string(s, ",")
let entries : Array[(String, T)] = []
let mut idx = 0
while idx < parts.length() {
let part = df_trim(parts[idx])
if part.length() == 0 {
return Err(InvalidFormat)
}
// Split at '='
let eq_pos = df_find_char(part, 61)
match eq_pos {
None => return Err(InvalidFormat)
Some(pos) => {
let algorithm = df_trim(df_substring(part, 0, pos))
let value_str = df_trim(df_substring_from(part, pos + 1))
if algorithm.length() == 0 {
return Err(InvalidAlgorithm)
}
if !df_is_valid_token(algorithm) {
return Err(InvalidAlgorithm)
}
match value_parser(value_str) {
Ok(value) => entries.push((df_to_lower_case(algorithm), value))
Err(e) => return Err(e)
}
}
}
idx = idx + 1
}
if entries.length() == 0 {
return Err(Empty)
}
Ok(entries)
}
///|
fn df_parse_byte_sequence(
input : String,
) -> Result[DigestValue, DigestFieldsError] {
// Format: :base64:
// Find first ':'
let colon1_pos = df_find_char(input, 58)
match colon1_pos {
None => Err(InvalidByteSequence)
Some(pos) => {
// Find second ':'
let rest = df_substring_from(input, pos + 1)
let colon2_pos = df_find_char(rest, 58)
match colon2_pos {
None => Err(InvalidByteSequence)
Some(cpos) => {
// Extract base64 between the colons
let base64 = df_substring(rest, 0, cpos)
let rest2 = df_substring_from(rest, cpos + 1)
if !df_trim(rest2).is_empty() {
return Err(InvalidByteSequence)
}
match df_base64_decode(base64) {
Ok(bytes) => Ok(DigestValue::new(bytes))
Err(_) => Err(InvalidBase64)
}
}
}
}
}
}
///|
fn df_parse_preference(input : String) -> Result[Int, DigestFieldsError] {
let s = df_trim(input)
if s.length() == 0 {
return Err(InvalidPreference)
}
// Check if all characters are digits
let mut idx = 0
while idx < s.length() {
let ch = s[idx]
let ch_code = ch.to_int()
if ch_code < 48 || ch_code > 57 {
return Err(InvalidPreference)
}
idx = idx + 1
}
match df_parse_u8(s) {
Some(v) => if v > 10 { Err(InvalidPreference) } else { Ok(v) }
None => Err(InvalidPreference)
}
}
///|
fn df_is_valid_token(s : String) -> Bool {
if s.length() == 0 {
return false
}
let mut idx = 0
while idx < s.length() {
let code = s[idx]
let ch = Int::unsafe_to_char(code.to_int())
if !df_is_token_char(ch) {
return false
}
idx = idx + 1
}
true
}
///|
fn df_is_token_char(ch : Char) -> Bool {
let code = ch.to_int()
code == 33 ||
code == 35 ||
code == 36 ||
code == 37 ||
code == 38 ||
code == 39 ||
code == 42 ||
code == 43 ||
code == 45 ||
code == 46 ||
(code >= 48 && code <= 57) ||
(code >= 65 && code <= 90) ||
code == 94 ||
code == 95 ||
code == 96 ||
(code >= 97 && code <= 122) ||
code == 124 ||
code == 126
}
///|
/// String helper functions
///|
///|
fn df_trim(s : String) -> String {
s.trim().to_string()
}
///|
fn df_split_string(s : String, sep : String) -> Array[String] {
str_split_string(s, sep)
}
///|
fn df_substring(s : String, start : Int, end : Int) -> String {
(try! s[start:end]).to_string()
}
///|
fn df_substring_from(s : String, start : Int) -> String {
(try! s[start:s.length()]).to_string()
}
///|
fn df_to_lower_case(s : String) -> String {
str_to_lower_case(s)
}
///|
fn df_find_char(s : String, target : Int) -> Int? {
let mut idx = 0
while idx < s.length() {
let ch = s[idx]
if ch.to_int() == target {
return Some(idx)
}
idx = idx + 1
}
None
}
///|
fn df_int_to_string(n : Int) -> String {
if n == 0 {
return "0"
}
if n < 0 {
"-" + df_int_to_string(-n)
} else {
let mut result = ""
let mut num = n
while num > 0 {
result = result + Int::unsafe_to_char(48 + num % 10).to_string()
num = num / 10
}
result
}
}
///|
///|
/// Base64 encoding/decoding (RFC 4648)
/// Uses same implementation as auth.mbt
///|
///|
fn df_base64_encode(input : Array[Byte]) -> String {
// Convert ByteArray to Array[Int] for processing
let len = input.length()
let mut result = ""
let mut i = 0
// Base64 alphabet
// ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
while i < len {
let b0 = input[i].to_int()
let b1 = if i + 1 < len { input[i + 1].to_int() } else { 0 }
let b2 = if i + 2 < len { input[i + 2].to_int() } else { 0 }
let n = b0 * 65536 + b1 * 256 + b2
// Encode 6-bit values
let i0 = n / 262144 // 2^18
let i1 = n / 4096 % 64 // 2^12
let i2 = n / 64 % 64
let i3 = n % 64
// Map to base64 alphabet
let c0 = df_base64_char(i0)
let c1 = df_base64_char(i1)
let c2 = if i + 1 < len { df_base64_char(i2) } else { "=" }
let c3 = if i + 2 < len { df_base64_char(i3) } else { "=" }
result = result + c0 + c1 + c2 + c3
i = i + 3
}
result
}
///|
fn df_base64_char(index : Int) -> String {
let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
if index >= 0 && index < 64 {
(try! alphabet[index:index + 1]).to_string()
} else {
"="
}
}
///|
fn df_base64_decode(input : String) -> Result[Array[Byte], DigestFieldsError] {
// Remove padding '=' from end
let s = input.trim().to_string()
let mut s_len = s.length()
// Remove trailing '='
while s_len > 0 && s[s_len - 1] == '=' {
s_len = s_len - 1
}
let result : Array[Byte] = []
let mut buf = 0
let mut bits = 0
let mut idx = 0
while idx < s_len {
let ch = s[idx]
let ch_code = ch.to_int()
let val = if ch_code >= 65 && ch_code <= 90 {
// A-Z
ch_code - 65
} else if ch_code >= 97 && ch_code <= 122 {
// a-z
ch_code - 97 + 26
} else if ch_code >= 48 && ch_code <= 57 {
// 0-9
ch_code - 48 + 52
} else if ch_code == 43 {
// +
62
} else if ch_code == 47 {
// /
63
} else {
return Err(InvalidBase64)
}
buf = buf * 64 + val
bits = bits + 6
if bits >= 8 {
bits = bits - 8
let byte = buf / df_pow2(bits)
result.push(byte.to_byte())
buf = buf % df_pow2(bits)
}
idx = idx + 1
}
Ok(result)
}
///|
fn df_pow2(n : Int) -> Int {
if n == 0 {
1
} else if n == 1 {
2
} else if n == 2 {
4
} else if n == 3 {
8
} else if n == 4 {
16
} else if n == 5 {
32
} else if n == 6 {
64
} else if n == 7 {
128
} else if n == 8 {
256
} else if n == 9 {
512
} else if n == 10 {
1024
} else {
1
}
}
///|
fn df_parse_u8(s : String) -> Int? {
if s.length() == 0 {
return None
}
let mut result = 0
let mut idx = 0
let len = s.length()
while idx < len {
let ch = s[idx]
let ch_code = ch.to_int()
if ch_code < 48 || ch_code > 57 {
return None
}
result = result * 10 + (ch_code - 48)
idx = idx + 1
}
Some(result)
}