Files
2026-09-30 15:49:12 +08:00

120 lines
4.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package hexx 提供十六进制编解码的语义清晰封装。
//
// 本包基于 encoding/hex,额外提供:
//
// - 大小写混合解码(Decode 语义上接受 "DEADbeef" 这类输入);
// - 严格大写校验解码(DecodeUpper),用于需要规范化格式的场景;
// - 合法性检查(IsValid)与友好的中文错误信息。
//
// 安全注意:十六进制是**编码**而非加密,不提供任何机密性,
// 任何人都能还原原文;密钥、口令、随机数等敏感数据即使转成十六进制
// 也依然是明文,禁止据此认为可以安全地打印或记录到日志。
// 另外十六进制编码会使数据膨胀一倍,解码不可信输入前应限制输入长度。
package hexx
import (
"encoding/hex"
"fmt"
"strings"
)
// Encode 把字节串编码为小写十六进制字符串(长度是输入的两倍)。
//
// 参数:
// - data: 待编码的字节串,可以为 nil(返回空字符串)。
//
// 返回值:仅含 0-9a-f 的小写十六进制字符串。
//
// 安全注意:十六进制不提供机密性,编码后的秘密仍是明文,不要写入日志。
func Encode(data []byte) string {
return hex.EncodeToString(data)
}
// EncodeUpper 把字节串编码为大写十六进制字符串(长度是输入的两倍)。
//
// 参数:
// - data: 待编码的字节串,可以为 nil(返回空字符串)。
//
// 返回值:仅含 0-9A-F 的大写十六进制字符串。
//
// 安全注意:十六进制不提供机密性;大小写不影响解码结果,
// 但如与外部系统约定固定大小写,请统一使用本函数或 Encode。
func EncodeUpper(data []byte) string {
return strings.ToUpper(hex.EncodeToString(data))
}
// Decode 解码十六进制字符串,语义上接受大小写混合的输入。
//
// 参数:
// - s: 待解码的十六进制字符串,长度必须为偶数。
//
// 返回值:解码后的字节串;失败时返回 nil 与包装后的 error。
//
// 安全注意:失败时不返回部分结果;本函数对 "0x" 前缀、空白字符、
// 奇数长度都会报错,便于尽早发现被截断或污染的输入。
func Decode(s string) ([]byte, error) {
decoded, err := hex.DecodeString(s)
if err != nil {
return nil, fmt.Errorf("十六进制解码失败(长度 %d): %w", len(s), err)
}
return decoded, nil
}
// DecodeUpper 解码十六进制字符串,并要求输入**确实是大写**形式。
//
// 参数:
// - s: 待解码的十六进制字符串,只能包含 0-9 与 A-F,长度必须为偶数。
//
// 返回值:解码后的字节串;包含小写字母或非法字符时返回 nil 与包装后的 error。
//
// 安全注意:用于严格模式(例如校验外部系统给出的规范化大写格式),
// 可避免因大小写不一致导致的比对歧义;普通场景请使用 Decode。
func DecodeUpper(s string) ([]byte, error) {
if strings.ContainsFunc(s, func(r rune) bool {
return r >= 'a' && r <= 'f'
}) {
return nil, fmt.Errorf("十六进制解码(严格大写)失败: 输入包含小写字母")
}
decoded, err := hex.DecodeString(s)
if err != nil {
return nil, fmt.Errorf("十六进制解码(严格大写)失败(长度 %d): %w", len(s), err)
}
return decoded, nil
}
// IsValid 判断字符串是否为合法的十六进制串。
//
// 参数:
// - s: 待检查的字符串。
//
// 返回值:长度偶数且每个字符都属于 [0-9a-fA-F] 时返回 true;
// 空串、奇数长度、含非法字符或空白时返回 false。
//
// 安全注意:空串被视为非法,避免把"缺失的字段"误判为合法值;
// 本函数只做格式检查,不代表内容可信,来源不明的数据仍需按不可信输入处理。
func IsValid(s string) bool {
if len(s) == 0 || len(s)%2 != 0 {
return false
}
for i := 0; i < len(s); i++ {
if !isHexDigit(s[i]) {
return false
}
}
return true
}
// isHexDigit 判断字节是否为十六进制数字字符(0-9、a-f、A-F)。
func isHexDigit(c byte) bool {
switch {
case c >= '0' && c <= '9':
return true
case c >= 'a' && c <= 'f':
return true
case c >= 'A' && c <= 'F':
return true
default:
return false
}
}