120 lines
4.2 KiB
Go
120 lines
4.2 KiB
Go
// 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
|
|||
|
|
}
|
|||
|
|
}
|