This commit is contained in:
Shen Junzheng
2025-03-21 21:45:08 +08:00
parent 78cce92ce3
commit 80c7e67106
86 changed files with 7061 additions and 2316 deletions

View File

@@ -0,0 +1,138 @@
package common
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"encoding/binary"
"fmt"
"hash"
"io"
"os"
"github.com/sjzar/chatlog/internal/errors"
)
const (
KeySize = 32
SaltSize = 16
AESBlockSize = 16
SQLiteHeader = "SQLite format 3\x00"
IVSize = 16
)
type DBFile struct {
Path string
Salt []byte
TotalPages int64
FirstPage []byte
}
func OpenDBFile(dbPath string, pageSize int) (*DBFile, error) {
fp, err := os.Open(dbPath)
if err != nil {
return nil, errors.DecryptOpenFileFailed(dbPath, err)
}
defer fp.Close()
fileInfo, err := fp.Stat()
if err != nil {
return nil, errors.WeChatDecryptFailed(err)
}
fileSize := fileInfo.Size()
totalPages := fileSize / int64(pageSize)
if fileSize%int64(pageSize) > 0 {
totalPages++
}
buffer := make([]byte, pageSize)
n, err := io.ReadFull(fp, buffer)
if err != nil {
return nil, errors.DecryptReadFileFailed(dbPath, err)
}
if n != pageSize {
return nil, errors.DecryptIncompleteRead(fmt.Errorf("read %d bytes, expected %d", n, pageSize))
}
if bytes.Equal(buffer[:len(SQLiteHeader)-1], []byte(SQLiteHeader[:len(SQLiteHeader)-1])) {
return nil, errors.ErrAlreadyDecrypted
}
return &DBFile{
Path: dbPath,
Salt: buffer[:SaltSize],
FirstPage: buffer,
TotalPages: totalPages,
}, nil
}
func XorBytes(a []byte, b byte) []byte {
result := make([]byte, len(a))
for i := range a {
result[i] = a[i] ^ b
}
return result
}
func ValidateKey(page1 []byte, key []byte, salt []byte, hashFunc func() hash.Hash, hmacSize int, reserve int, pageSize int, deriveKeys func([]byte, []byte) ([]byte, []byte)) bool {
if len(key) != KeySize {
return false
}
_, macKey := deriveKeys(key, salt)
mac := hmac.New(hashFunc, macKey)
dataEnd := pageSize - reserve + IVSize
mac.Write(page1[SaltSize:dataEnd])
pageNoBytes := make([]byte, 4)
binary.LittleEndian.PutUint32(pageNoBytes, 1)
mac.Write(pageNoBytes)
calculatedMAC := mac.Sum(nil)
storedMAC := page1[dataEnd : dataEnd+hmacSize]
return hmac.Equal(calculatedMAC, storedMAC)
}
func DecryptPage(pageBuf []byte, encKey []byte, macKey []byte, pageNum int64, hashFunc func() hash.Hash, hmacSize int, reserve int, pageSize int) ([]byte, error) {
offset := 0
if pageNum == 0 {
offset = SaltSize
}
mac := hmac.New(hashFunc, macKey)
mac.Write(pageBuf[offset : pageSize-reserve+IVSize])
pageNoBytes := make([]byte, 4)
binary.LittleEndian.PutUint32(pageNoBytes, uint32(pageNum+1))
mac.Write(pageNoBytes)
hashMac := mac.Sum(nil)
hashMacStartOffset := pageSize - reserve + IVSize
hashMacEndOffset := hashMacStartOffset + hmacSize
if !bytes.Equal(hashMac, pageBuf[hashMacStartOffset:hashMacEndOffset]) {
return nil, errors.ErrDecryptHashVerificationFailed
}
iv := pageBuf[pageSize-reserve : pageSize-reserve+IVSize]
block, err := aes.NewCipher(encKey)
if err != nil {
return nil, errors.DecryptCreateCipherFailed(err)
}
mode := cipher.NewCBCDecrypter(block, iv)
encrypted := make([]byte, pageSize-reserve-offset)
copy(encrypted, pageBuf[offset:pageSize-reserve])
mode.CryptBlocks(encrypted, encrypted)
decryptedPage := append(encrypted, pageBuf[pageSize-reserve:pageSize]...)
return decryptedPage, nil
}

View File

@@ -0,0 +1,184 @@
package darwin
import (
"context"
"crypto/sha1"
"encoding/hex"
"hash"
"io"
"os"
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/wechat/decrypt/common"
"golang.org/x/crypto/pbkdf2"
)
// 常量定义
const (
V3PageSize = 1024
HmacSHA1Size = 20
)
// V3Decryptor 实现 macOS V3 版本的解密器
type V3Decryptor struct {
// macOS V3 特定参数
hmacSize int
hashFunc func() hash.Hash
reserve int
pageSize int
version string
}
// NewV3Decryptor 创建 macOS V3 解密器
func NewV3Decryptor() *V3Decryptor {
hashFunc := sha1.New
hmacSize := HmacSHA1Size
reserve := common.IVSize + hmacSize
if reserve%common.AESBlockSize != 0 {
reserve = ((reserve / common.AESBlockSize) + 1) * common.AESBlockSize
}
return &V3Decryptor{
hmacSize: hmacSize,
hashFunc: hashFunc,
reserve: reserve,
pageSize: V3PageSize,
version: "macOS v3",
}
}
// deriveKeys 派生 MAC 密钥
// 注意macOS V3 版本直接使用提供的密钥作为加密密钥,不进行 PBKDF2 派生
func (d *V3Decryptor) deriveKeys(key []byte, salt []byte) ([]byte, []byte) {
// 对于 macOS V3直接使用密钥作为加密密钥
encKey := key
// 生成 MAC 密钥
macSalt := common.XorBytes(salt, 0x3a)
macKey := pbkdf2.Key(encKey, macSalt, 2, common.KeySize, d.hashFunc)
return encKey, macKey
}
// Validate 验证密钥是否有效
func (d *V3Decryptor) Validate(page1 []byte, key []byte) bool {
if len(page1) < d.pageSize || len(key) != common.KeySize {
return false
}
salt := page1[:common.SaltSize]
return common.ValidateKey(page1, key, salt, d.hashFunc, d.hmacSize, d.reserve, d.pageSize, d.deriveKeys)
}
// Decrypt 解密数据库
func (d *V3Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string, output io.Writer) error {
// 解码密钥
key, err := hex.DecodeString(hexKey)
if err != nil {
return errors.DecryptDecodeKeyFailed(err)
}
// 打开数据库文件并读取基本信息
dbInfo, err := common.OpenDBFile(dbfile, d.pageSize)
if err != nil {
return err
}
// 验证密钥
if !d.Validate(dbInfo.FirstPage, key) {
return errors.ErrDecryptIncorrectKey
}
// 计算密钥
encKey, macKey := d.deriveKeys(key, dbInfo.Salt)
// 打开数据库文件
dbFile, err := os.Open(dbfile)
if err != nil {
return errors.DecryptOpenFileFailed(dbfile, err)
}
defer dbFile.Close()
// 写入 SQLite 头
_, err = output.Write([]byte(common.SQLiteHeader))
if err != nil {
return errors.DecryptWriteOutputFailed(err)
}
// 处理每一页
pageBuf := make([]byte, d.pageSize)
for curPage := int64(0); curPage < dbInfo.TotalPages; curPage++ {
// 检查是否取消
select {
case <-ctx.Done():
return errors.DecryptOperationCanceled()
default:
// 继续处理
}
// 读取一页
n, err := io.ReadFull(dbFile, pageBuf)
if err != nil {
if err == io.EOF || err == io.ErrUnexpectedEOF {
// 处理最后一部分页面
if n > 0 {
break
}
}
return errors.DecryptReadFileFailed(dbfile, err)
}
// 检查页面是否全为零
allZeros := true
for _, b := range pageBuf {
if b != 0 {
allZeros = false
break
}
}
if allZeros {
// 写入零页面
_, err = output.Write(pageBuf)
if err != nil {
return errors.DecryptWriteOutputFailed(err)
}
continue
}
// 解密页面
decryptedData, err := common.DecryptPage(pageBuf, encKey, macKey, curPage, d.hashFunc, d.hmacSize, d.reserve, d.pageSize)
if err != nil {
return err
}
// 写入解密后的页面
_, err = output.Write(decryptedData)
if err != nil {
return errors.DecryptWriteOutputFailed(err)
}
}
return nil
}
// GetPageSize 返回页面大小
func (d *V3Decryptor) GetPageSize() int {
return d.pageSize
}
// GetReserve 返回保留字节数
func (d *V3Decryptor) GetReserve() int {
return d.reserve
}
// GetHMACSize 返回HMAC大小
func (d *V3Decryptor) GetHMACSize() int {
return d.hmacSize
}
// GetVersion 返回解密器版本
func (d *V3Decryptor) GetVersion() string {
return d.version
}

View File

@@ -0,0 +1,194 @@
package darwin
import (
"context"
"crypto/sha512"
"encoding/hex"
"hash"
"io"
"os"
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/wechat/decrypt/common"
"golang.org/x/crypto/pbkdf2"
)
// Darwin Version 4 same as WIndows Version 4
// V4 版本特定常量
const (
V4PageSize = 4096
V4IterCount = 256000
HmacSHA512Size = 64
)
// V4Decryptor 实现Windows V4版本的解密器
type V4Decryptor struct {
// V4 特定参数
iterCount int
hmacSize int
hashFunc func() hash.Hash
reserve int
pageSize int
version string
}
// NewV4Decryptor 创建Windows V4解密器
func NewV4Decryptor() *V4Decryptor {
hashFunc := sha512.New
hmacSize := HmacSHA512Size
reserve := common.IVSize + hmacSize
if reserve%common.AESBlockSize != 0 {
reserve = ((reserve / common.AESBlockSize) + 1) * common.AESBlockSize
}
return &V4Decryptor{
iterCount: V4IterCount,
hmacSize: hmacSize,
hashFunc: hashFunc,
reserve: reserve,
pageSize: V4PageSize,
version: "macOS v4",
}
}
// deriveKeys 派生加密密钥和MAC密钥
func (d *V4Decryptor) deriveKeys(key []byte, salt []byte) ([]byte, []byte) {
// 生成加密密钥
encKey := pbkdf2.Key(key, salt, d.iterCount, common.KeySize, d.hashFunc)
// 生成MAC密钥
macSalt := common.XorBytes(salt, 0x3a)
macKey := pbkdf2.Key(encKey, macSalt, 2, common.KeySize, d.hashFunc)
return encKey, macKey
}
// Validate 验证密钥是否有效
func (d *V4Decryptor) Validate(page1 []byte, key []byte) bool {
if len(page1) < d.pageSize || len(key) != common.KeySize {
return false
}
salt := page1[:common.SaltSize]
return common.ValidateKey(page1, key, salt, d.hashFunc, d.hmacSize, d.reserve, d.pageSize, d.deriveKeys)
}
// Decrypt 解密数据库
func (d *V4Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string, output io.Writer) error {
// 解码密钥
key, err := hex.DecodeString(hexKey)
if err != nil {
return errors.DecryptDecodeKeyFailed(err)
}
// 打开数据库文件并读取基本信息
dbInfo, err := common.OpenDBFile(dbfile, d.pageSize)
if err != nil {
return err
}
// 验证密钥
if !d.Validate(dbInfo.FirstPage, key) {
return errors.ErrDecryptIncorrectKey
}
// 计算密钥
encKey, macKey := d.deriveKeys(key, dbInfo.Salt)
// 打开数据库文件
dbFile, err := os.Open(dbfile)
if err != nil {
return errors.DecryptOpenFileFailed(dbfile, err)
}
defer dbFile.Close()
// 写入SQLite头
_, err = output.Write([]byte(common.SQLiteHeader))
if err != nil {
return errors.DecryptWriteOutputFailed(err)
}
// 处理每一页
pageBuf := make([]byte, d.pageSize)
for curPage := int64(0); curPage < dbInfo.TotalPages; curPage++ {
// 检查是否取消
select {
case <-ctx.Done():
return errors.DecryptOperationCanceled()
default:
// 继续处理
}
// 读取一页
n, err := io.ReadFull(dbFile, pageBuf)
if err != nil {
if err == io.EOF || err == io.ErrUnexpectedEOF {
// 处理最后一部分页面
if n > 0 {
break
}
}
return errors.DecryptReadFileFailed(dbfile, err)
}
// 检查页面是否全为零
allZeros := true
for _, b := range pageBuf {
if b != 0 {
allZeros = false
break
}
}
if allZeros {
// 写入零页面
_, err = output.Write(pageBuf)
if err != nil {
return errors.DecryptWriteOutputFailed(err)
}
continue
}
// 解密页面
decryptedData, err := common.DecryptPage(pageBuf, encKey, macKey, curPage, d.hashFunc, d.hmacSize, d.reserve, d.pageSize)
if err != nil {
return err
}
// 写入解密后的页面
_, err = output.Write(decryptedData)
if err != nil {
return errors.DecryptWriteOutputFailed(err)
}
}
return nil
}
// GetPageSize 返回页面大小
func (d *V4Decryptor) GetPageSize() int {
return d.pageSize
}
// GetReserve 返回保留字节数
func (d *V4Decryptor) GetReserve() int {
return d.reserve
}
// GetHMACSize 返回HMAC大小
func (d *V4Decryptor) GetHMACSize() int {
return d.hmacSize
}
// GetVersion 返回解密器版本
func (d *V4Decryptor) GetVersion() string {
return d.version
}
// GetIterCount 返回迭代次数Windows特有
func (d *V4Decryptor) GetIterCount() int {
return d.iterCount
}

View File

@@ -0,0 +1,55 @@
package decrypt
import (
"context"
"fmt"
"io"
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/wechat/decrypt/darwin"
"github.com/sjzar/chatlog/internal/wechat/decrypt/windows"
)
// 错误定义
var (
ErrInvalidVersion = fmt.Errorf("invalid version, must be 3 or 4")
ErrUnsupportedPlatform = fmt.Errorf("unsupported platform")
)
// Decryptor 定义数据库解密的接口
type Decryptor interface {
// Decrypt 解密数据库
Decrypt(ctx context.Context, dbfile string, key string, output io.Writer) error
// Validate 验证密钥是否有效
Validate(page1 []byte, key []byte) bool
// GetPageSize 返回页面大小
GetPageSize() int
// GetReserve 返回保留字节数
GetReserve() int
// GetHMACSize 返回HMAC大小
GetHMACSize() int
// GetVersion 返回解密器版本
GetVersion() string
}
// NewDecryptor 创建一个新的解密器
func NewDecryptor(platform string, version int) (Decryptor, error) {
// 根据平台返回对应的实现
switch {
case platform == "windows" && version == 3:
return windows.NewV3Decryptor(), nil
case platform == "windows" && version == 4:
return windows.NewV4Decryptor(), nil
case platform == "darwin" && version == 3:
return darwin.NewV3Decryptor(), nil
case platform == "darwin" && version == 4:
return darwin.NewV4Decryptor(), nil
default:
return nil, errors.PlatformUnsupported(platform, version)
}
}

View File

@@ -0,0 +1,56 @@
package decrypt
import (
"path/filepath"
"github.com/sjzar/chatlog/internal/wechat/decrypt/common"
)
type Validator struct {
platform string
version int
dbPath string
decryptor Decryptor
dbFile *common.DBFile
}
// NewValidator 创建一个仅用于验证的验证器
func NewValidator(dataDir string, platform string, version int) (*Validator, error) {
decryptor, err := NewDecryptor(platform, version)
if err != nil {
return nil, err
}
dbFile := GetSimpleDBFile(platform, version)
dbPath := filepath.Join(dataDir + "/" + dbFile)
d, err := common.OpenDBFile(dbPath, decryptor.GetPageSize())
if err != nil {
return nil, err
}
return &Validator{
platform: platform,
version: version,
dbPath: dbPath,
decryptor: decryptor,
dbFile: d,
}, nil
}
func (v *Validator) Validate(key []byte) bool {
return v.decryptor.Validate(v.dbFile.FirstPage, key)
}
func GetSimpleDBFile(platform string, version int) string {
switch {
case platform == "windows" && version == 3:
return "Msg\\Misc.db"
case platform == "windows" && version == 4:
return "db_storage\\message\\message_0.db"
case platform == "darwin" && version == 3:
return "Message/msg_0.db"
case platform == "darwin" && version == 4:
return "db_storage/message/message_0.db"
}
return ""
}

View File

@@ -0,0 +1,192 @@
package windows
import (
"context"
"crypto/sha1"
"encoding/hex"
"hash"
"io"
"os"
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/wechat/decrypt/common"
"golang.org/x/crypto/pbkdf2"
)
// V3 版本特定常量
const (
PageSize = 4096
V3IterCount = 64000
HmacSHA1Size = 20
)
// V3Decryptor 实现Windows V3版本的解密器
type V3Decryptor struct {
// V3 特定参数
iterCount int
hmacSize int
hashFunc func() hash.Hash
reserve int
pageSize int
version string
}
// NewV3Decryptor 创建Windows V3解密器
func NewV3Decryptor() *V3Decryptor {
hashFunc := sha1.New
hmacSize := HmacSHA1Size
reserve := common.IVSize + hmacSize
if reserve%common.AESBlockSize != 0 {
reserve = ((reserve / common.AESBlockSize) + 1) * common.AESBlockSize
}
return &V3Decryptor{
iterCount: V3IterCount,
hmacSize: hmacSize,
hashFunc: hashFunc,
reserve: reserve,
pageSize: PageSize,
version: "Windows v3",
}
}
// deriveKeys 派生加密密钥和MAC密钥
func (d *V3Decryptor) deriveKeys(key []byte, salt []byte) ([]byte, []byte) {
// 生成加密密钥
encKey := pbkdf2.Key(key, salt, d.iterCount, common.KeySize, d.hashFunc)
// 生成MAC密钥
macSalt := common.XorBytes(salt, 0x3a)
macKey := pbkdf2.Key(encKey, macSalt, 2, common.KeySize, d.hashFunc)
return encKey, macKey
}
// Validate 验证密钥是否有效
func (d *V3Decryptor) Validate(page1 []byte, key []byte) bool {
if len(page1) < d.pageSize || len(key) != common.KeySize {
return false
}
salt := page1[:common.SaltSize]
return common.ValidateKey(page1, key, salt, d.hashFunc, d.hmacSize, d.reserve, d.pageSize, d.deriveKeys)
}
// Decrypt 解密数据库
func (d *V3Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string, output io.Writer) error {
// 解码密钥
key, err := hex.DecodeString(hexKey)
if err != nil {
return errors.DecryptDecodeKeyFailed(err)
}
// 打开数据库文件并读取基本信息
dbInfo, err := common.OpenDBFile(dbfile, d.pageSize)
if err != nil {
return err
}
// 验证密钥
if !d.Validate(dbInfo.FirstPage, key) {
return errors.ErrDecryptIncorrectKey
}
// 计算密钥
encKey, macKey := d.deriveKeys(key, dbInfo.Salt)
// 打开数据库文件
dbFile, err := os.Open(dbfile)
if err != nil {
return errors.DecryptOpenFileFailed(dbfile, err)
}
defer dbFile.Close()
// 写入SQLite头
_, err = output.Write([]byte(common.SQLiteHeader))
if err != nil {
return errors.DecryptWriteOutputFailed(err)
}
// 处理每一页
pageBuf := make([]byte, d.pageSize)
for curPage := int64(0); curPage < dbInfo.TotalPages; curPage++ {
// 检查是否取消
select {
case <-ctx.Done():
return errors.DecryptOperationCanceled()
default:
// 继续处理
}
// 读取一页
n, err := io.ReadFull(dbFile, pageBuf)
if err != nil {
if err == io.EOF || err == io.ErrUnexpectedEOF {
// 处理最后一部分页面
if n > 0 {
break
}
}
return errors.DecryptReadFileFailed(dbfile, err)
}
// 检查页面是否全为零
allZeros := true
for _, b := range pageBuf {
if b != 0 {
allZeros = false
break
}
}
if allZeros {
// 写入零页面
_, err = output.Write(pageBuf)
if err != nil {
return errors.DecryptWriteOutputFailed(err)
}
continue
}
// 解密页面
decryptedData, err := common.DecryptPage(pageBuf, encKey, macKey, curPage, d.hashFunc, d.hmacSize, d.reserve, d.pageSize)
if err != nil {
return err
}
// 写入解密后的页面
_, err = output.Write(decryptedData)
if err != nil {
return errors.DecryptWriteOutputFailed(err)
}
}
return nil
}
// GetPageSize 返回页面大小
func (d *V3Decryptor) GetPageSize() int {
return d.pageSize
}
// GetReserve 返回保留字节数
func (d *V3Decryptor) GetReserve() int {
return d.reserve
}
// GetHMACSize 返回HMAC大小
func (d *V3Decryptor) GetHMACSize() int {
return d.hmacSize
}
// GetVersion 返回解密器版本
func (d *V3Decryptor) GetVersion() string {
return d.version
}
// GetIterCount 返回迭代次数Windows特有
func (d *V3Decryptor) GetIterCount() int {
return d.iterCount
}

View File

@@ -0,0 +1,190 @@
package windows
import (
"context"
"crypto/sha512"
"encoding/hex"
"hash"
"io"
"os"
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/wechat/decrypt/common"
"golang.org/x/crypto/pbkdf2"
)
// V4 版本特定常量
const (
V4IterCount = 256000
HmacSHA512Size = 64
)
// V4Decryptor 实现Windows V4版本的解密器
type V4Decryptor struct {
// V4 特定参数
iterCount int
hmacSize int
hashFunc func() hash.Hash
reserve int
pageSize int
version string
}
// NewV4Decryptor 创建Windows V4解密器
func NewV4Decryptor() *V4Decryptor {
hashFunc := sha512.New
hmacSize := HmacSHA512Size
reserve := common.IVSize + hmacSize
if reserve%common.AESBlockSize != 0 {
reserve = ((reserve / common.AESBlockSize) + 1) * common.AESBlockSize
}
return &V4Decryptor{
iterCount: V4IterCount,
hmacSize: hmacSize,
hashFunc: hashFunc,
reserve: reserve,
pageSize: PageSize,
version: "Windows v4",
}
}
// deriveKeys 派生加密密钥和MAC密钥
func (d *V4Decryptor) deriveKeys(key []byte, salt []byte) ([]byte, []byte) {
// 生成加密密钥
encKey := pbkdf2.Key(key, salt, d.iterCount, common.KeySize, d.hashFunc)
// 生成MAC密钥
macSalt := common.XorBytes(salt, 0x3a)
macKey := pbkdf2.Key(encKey, macSalt, 2, common.KeySize, d.hashFunc)
return encKey, macKey
}
// Validate 验证密钥是否有效
func (d *V4Decryptor) Validate(page1 []byte, key []byte) bool {
if len(page1) < d.pageSize || len(key) != common.KeySize {
return false
}
salt := page1[:common.SaltSize]
return common.ValidateKey(page1, key, salt, d.hashFunc, d.hmacSize, d.reserve, d.pageSize, d.deriveKeys)
}
// Decrypt 解密数据库
func (d *V4Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string, output io.Writer) error {
// 解码密钥
key, err := hex.DecodeString(hexKey)
if err != nil {
return errors.DecryptDecodeKeyFailed(err)
}
// 打开数据库文件并读取基本信息
dbInfo, err := common.OpenDBFile(dbfile, d.pageSize)
if err != nil {
return err
}
// 验证密钥
if !d.Validate(dbInfo.FirstPage, key) {
return errors.ErrDecryptIncorrectKey
}
// 计算密钥
encKey, macKey := d.deriveKeys(key, dbInfo.Salt)
// 打开数据库文件
dbFile, err := os.Open(dbfile)
if err != nil {
return errors.DecryptOpenFileFailed(dbfile, err)
}
defer dbFile.Close()
// 写入SQLite头
_, err = output.Write([]byte(common.SQLiteHeader))
if err != nil {
return errors.DecryptWriteOutputFailed(err)
}
// 处理每一页
pageBuf := make([]byte, d.pageSize)
for curPage := int64(0); curPage < dbInfo.TotalPages; curPage++ {
// 检查是否取消
select {
case <-ctx.Done():
return errors.DecryptOperationCanceled()
default:
// 继续处理
}
// 读取一页
n, err := io.ReadFull(dbFile, pageBuf)
if err != nil {
if err == io.EOF || err == io.ErrUnexpectedEOF {
// 处理最后一部分页面
if n > 0 {
break
}
}
return errors.DecryptReadFileFailed(dbfile, err)
}
// 检查页面是否全为零
allZeros := true
for _, b := range pageBuf {
if b != 0 {
allZeros = false
break
}
}
if allZeros {
// 写入零页面
_, err = output.Write(pageBuf)
if err != nil {
return errors.DecryptWriteOutputFailed(err)
}
continue
}
// 解密页面
decryptedData, err := common.DecryptPage(pageBuf, encKey, macKey, curPage, d.hashFunc, d.hmacSize, d.reserve, d.pageSize)
if err != nil {
return err
}
// 写入解密后的页面
_, err = output.Write(decryptedData)
if err != nil {
return errors.DecryptWriteOutputFailed(err)
}
}
return nil
}
// GetPageSize 返回页面大小
func (d *V4Decryptor) GetPageSize() int {
return d.pageSize
}
// GetReserve 返回保留字节数
func (d *V4Decryptor) GetReserve() int {
return d.reserve
}
// GetHMACSize 返回HMAC大小
func (d *V4Decryptor) GetHMACSize() int {
return d.hmacSize
}
// GetVersion 返回解密器版本
func (d *V4Decryptor) GetVersion() string {
return d.version
}
// GetIterCount 返回迭代次数Windows特有
func (d *V4Decryptor) GetIterCount() int {
return d.iterCount
}